83 lines
3.0 KiB
PHP
83 lines
3.0 KiB
PHP
<?php
|
|
namespace Core\Payment;
|
|
|
|
/**
|
|
* 支付宝网关
|
|
* - demo:返回模拟支付入口
|
|
* - live:电脑网站支付(alipay.trade.page.pay),RSA2 签名
|
|
*/
|
|
class AlipayGateway extends Gateway
|
|
{
|
|
public function pay(array $order): array
|
|
{
|
|
$simulate = site_url('order/demo/' . $order['order_no']);
|
|
$hasCred = !empty($this->config['appid']) && !empty($this->config['private_key']);
|
|
|
|
if ($this->isDemo() || !$hasCred) {
|
|
return [
|
|
'mode' => 'demo',
|
|
'channel' => 'alipay',
|
|
'simulate' => $simulate,
|
|
'config_missing' => !$hasCred,
|
|
];
|
|
}
|
|
|
|
$biz = [
|
|
'out_trade_no' => $order['order_no'],
|
|
'product_code' => 'FAST_INSTANT_TRADE_PAY',
|
|
'total_amount' => $this->money($order['amount']),
|
|
'subject' => $order['product_name'] ?: '商品购买',
|
|
];
|
|
$params = [
|
|
'app_id' => $this->config['appid'],
|
|
'method' => 'alipay.trade.page.pay',
|
|
'format' => 'JSON',
|
|
'charset' => 'utf-8',
|
|
'sign_type' => 'RSA2',
|
|
'timestamp' => date('Y-m-d H:i:s'),
|
|
'version' => '1.0',
|
|
'notify_url' => site_url('pay/notify/alipay'),
|
|
'return_url' => site_url('order/success/' . $order['order_no']),
|
|
'biz_content' => json_encode($biz, JSON_UNESCAPED_UNICODE),
|
|
];
|
|
$params['sign'] = $this->sign($params);
|
|
$gateway = $this->config['gateway'] ?: 'https://openapi.alipay.com/gateway.do';
|
|
return ['mode' => 'redirect', 'channel' => 'alipay', 'url' => $gateway . '?' . http_build_query($params)];
|
|
}
|
|
|
|
/** RSA2 签名 */
|
|
private function sign(array $params): string
|
|
{
|
|
ksort($params);
|
|
$str = '';
|
|
foreach ($params as $k => $v) {
|
|
if ($v === '' || $v === null) continue;
|
|
$str .= $k . '=' . $v . '&';
|
|
}
|
|
$str = rtrim($str, '&');
|
|
$key = $this->normalizeKey($this->config['private_key'] ?? '', false);
|
|
openssl_sign($str, $sign, $key, OPENSSL_ALGO_SHA256);
|
|
return base64_encode($sign);
|
|
}
|
|
|
|
private function normalizeKey(string $key, bool $isPublic): string
|
|
{
|
|
$key = trim($key);
|
|
if (strpos($key, '-----BEGIN') === 0) return $key;
|
|
$head = $isPublic ? "-----BEGIN PUBLIC KEY-----\n" : "-----BEGIN RSA PRIVATE KEY-----\n";
|
|
$foot = $isPublic ? "\n-----END PUBLIC KEY-----" : "\n-----END RSA PRIVATE KEY-----";
|
|
return $head . chunk_split($key, 64, "\n") . $foot;
|
|
}
|
|
|
|
public function verifyNotify(array $data): ?string
|
|
{
|
|
if (empty($data['out_trade_no'])) return null;
|
|
$status = $data['trade_status'] ?? '';
|
|
if ($status === 'TRADE_SUCCESS' || $status === 'TRADE_FINISHED') {
|
|
// 生产环境应使用支付宝公钥对签名做严格验签后再返回
|
|
return $data['out_trade_no'];
|
|
}
|
|
return null;
|
|
}
|
|
}
|