Files
2026-08-14 10:55:06 +08:00

141 lines
4.3 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* 支付宝支付网关(电脑网站支付 / 当面付)
*
* 配置项(settings 表):
* pay_alipay_enabled - 是否启用 (1/0)
* pay_alipay_appid - 应用 APPID
* pay_alipay_private_key - 应用私钥
* pay_alipay_public_key - 支付宝公钥
*
* 使用 RSA2 签名方式。
*/
if (!defined('IN_APP')) exit('Forbidden');
class AlipayGateway implements PaymentGatewayInterface
{
private $appId;
private $privateKey;
private $publicKey;
private $gatewayUrl = 'https://openapi.alipay.com/gateway.do';
private $sandboxUrl = 'https://openapi-sandbox.dl.alipaydev.com/gateway.do';
public function __construct()
{
$this->appId = getSetting('pay_alipay_appid', '');
$this->privateKey = getSetting('pay_alipay_private_key', '');
$this->publicKey = getSetting('pay_alipay_public_key', '');
// 沙箱模式(开发测试用)
if (getSetting('pay_alipay_sandbox', '0') === '1') {
$this->gatewayUrl = $this->sandboxUrl;
}
}
public function getName()
{
return '支付宝';
}
/**
* 创建支付宝电脑网站支付订单。
*/
public function createOrder(array $order)
{
$params = [
'app_id' => $this->appId,
'method' => 'alipay.trade.page.pay',
'format' => 'JSON',
'return_url' => $order['return_url'],
'notify_url' => $order['notify_url'],
'charset' => 'utf-8',
'sign_type' => 'RSA2',
'timestamp' => date('Y-m-d H:i:s'),
'version' => '1.0',
'biz_content' => json_encode([
'out_trade_no' => $order['order_no'],
'total_amount' => sprintf('%.2f', $order['amount']),
'subject' => $order['subject'],
'product_code' => 'FAST_INSTANT_TRADE_PAY',
], JSON_UNESCAPED_UNICODE),
];
$params['sign'] = $this->sign($params);
// 构建跳转 URLGET 方式)
$url = $this->gatewayUrl . '?' . http_build_query($params);
return ['url' => $url, 'form' => ''];
}
public function verifyNotify(array $params)
{
if (empty($params['sign'])) return false;
$sign = $params['sign'];
unset($params['sign'], $params['sign_type']);
return $this->rsaVerify($this->buildSignData($params), $sign);
}
public function getTradeNo(array $params)
{
return $params['trade_no'] ?? '';
}
public function isPaid(array $params)
{
return ($params['trade_status'] ?? '') === 'TRADE_SUCCESS';
}
public function respondSuccess()
{
echo 'success';
}
// ─── 签名相关 ───
private function sign($params)
{
ksort($params);
$data = $this->buildSignData($params);
$res = openssl_sign($data, $signature, $this->loadPrivateKey(), OPENSSL_ALGO_SHA256);
return base64_encode($signature);
}
private function rsaVerify($data, $sign)
{
$result = openssl_verify($data, base64_decode($sign), $this->loadPublicKey(), OPENSSL_ALGO_SHA256);
return $result === 1;
}
private function buildSignData($params)
{
$pairs = [];
foreach ($params as $k => $v) {
if ($v === '' || is_array($v)) continue;
$pairs[] = $k . '=' . $v;
}
return implode('&', $pairs);
}
private function loadPrivateKey()
{
$key = trim($this->privateKey);
$key = preg_replace('/-----BEGIN RSA PRIVATE KEY-----/', '', $key);
$key = preg_replace('/-----END RSA PRIVATE KEY-----/', '', $key);
$key = str_replace(["\r", "\n", " "], '', $key);
return "-----BEGIN RSA PRIVATE KEY-----\n" .
chunk_split($key, 64, "\n") .
"-----END RSA PRIVATE KEY-----";
}
private function loadPublicKey()
{
$key = trim($this->publicKey);
$key = preg_replace('/-----BEGIN PUBLIC KEY-----/', '', $key);
$key = preg_replace('/-----END PUBLIC KEY-----/', '', $key);
$key = str_replace(["\r", "\n", " "], '', $key);
return "-----BEGIN PUBLIC KEY-----\n" .
chunk_split($key, 64, "\n") .
"-----END PUBLIC KEY-----";
}
}