141 lines
4.3 KiB
PHP
141 lines
4.3 KiB
PHP
<?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);
|
||
|
||
// 构建跳转 URL(GET 方式)
|
||
$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-----";
|
||
}
|
||
}
|