Rewrite Alipay Notify logic

This commit is contained in:
BrettonYe
2023-11-20 23:51:44 +08:00
committed by BrettonYe
parent 5554c831c1
commit 2c09bd9fba
2 changed files with 71 additions and 32 deletions

View File

@@ -1,8 +1,8 @@
<?php
/*
* 作者BrettonYe
* 功能ProxyPanel 支付宝面对面 【收单线下交易预创建】 【收单交易查询】 接口实现库
* 时间2022/12/28
* 功能ProxyPanel 支付宝面对面【收单线下交易预创建】【收单交易查询】接口实现库
* 更新时间2023/10/29
* 参考资料https://opendocs.alipay.com/open/02ekfg?scene=19 riverslei/payment
*/
@@ -13,7 +13,7 @@ use RuntimeException;
class AlipayF2F
{
private static string $gatewayUrl = 'https://openapi.alipay.com/gateway.do';
private static string $gatewayUrl = 'https://openapi.alipay.com/gateway.do'; //https://openapi-sandbox.dl.alipaydev.com/gateway.do
private array $config;
@@ -98,7 +98,7 @@ class AlipayF2F
throw new RuntimeException('请求错误-看起来是请求失败');
}
if (! $this->rsaVerify($response[$resKey], $response['sign'])) {
if (! $this->validate_response_sign($response[$resKey], $response['sign'])) {
throw new RuntimeException('验签错误-'.$response[$resKey]['msg'].' | '.($response[$resKey]['sub_msg'] ?? var_export($response, true)));
}
@@ -113,14 +113,14 @@ class AlipayF2F
private function buildParams(): array
{
$params = [
'app_id' => $this->config['app_id'] ?? '',
'method' => $this->config['method'] ?? '',
'app_id' => $this->config['app_id'],
'method' => $this->config['method'],
'charset' => 'utf-8',
'sign_type' => 'RSA2',
'timestamp' => date('Y-m-d H:m:s'),
'biz_content' => $this->config['biz_content'] ?? [],
'biz_content' => $this->config['biz_content'],
'version' => '1.0',
'notify_url' => $this->config['notify_url'] ?? '',
'notify_url' => $this->config['notify_url'],
];
$params = array_filter($params);
$params['sign'] = $this->encrypt($this->buildQuery($params));
@@ -155,21 +155,39 @@ class AlipayF2F
}
/**
* RSA2验签.
* 同步返回验签.
*
* @param array $data 待签名数据
* @param array $body 待签名数据
*
* @throws RuntimeException
*/
public function rsaVerify(array $data, string $sign): bool
public function validate_response_sign(array $body, string $sign): bool
{
unset($data['sign'], $data['sign_type']);
unset($body['sign'], $body['sign_type']);
$publicKey = openssl_pkey_get_public($this->config['public_key']);
if (empty($publicKey)) {
throw new RuntimeException('支付宝RSA公钥错误。请检查公钥文件格式是否正确');
}
return (bool) openssl_verify(json_encode($data), base64_decode($sign), $publicKey, OPENSSL_ALGO_SHA256);
return (bool) openssl_verify(json_encode($body), base64_decode($sign), $publicKey, OPENSSL_ALGO_SHA256);
}
/**
* 异步通知验签.
*
* @param array $body 待签名数据
*
* @throws RuntimeException
*/
public function validate_notification_sign(array $body, string $sign): bool
{
unset($body['sign'], $body['sign_type']);
$publicKey = openssl_pkey_get_public($this->config['public_key']);
if (empty($publicKey)) {
throw new RuntimeException('支付宝RSA公钥错误。请检查公钥文件格式是否正确');
}
return (bool) openssl_verify($this->buildQuery($body), base64_decode($sign), $publicKey, OPENSSL_ALGO_SHA256);
}
public function qrCharge(array $content): array

View File

@@ -2,6 +2,7 @@
namespace App\Utils\Payments;
use App\Models\Payment;
use App\Services\PaymentService;
use App\Utils\Library\AlipayF2F;
use App\Utils\Library\Templates\Gateway;
@@ -14,16 +15,16 @@ use Response;
class F2Fpay extends PaymentService implements Gateway
{
private static array $aliConfig;
private static AlipayF2F $aliClient;
public function __construct()
{
self::$aliConfig = [
self::$aliClient = new AlipayF2F([
'app_id' => sysConfig('f2fpay_app_id'),
'ali_public_key' => sysConfig('f2fpay_public_key'),
'rsa_private_key' => sysConfig('f2fpay_private_key'),
'notify_url' => route('payment.notify', ['method' => 'f2fpay']),
];
]);
}
public function purchase(Request $request): JsonResponse
@@ -37,8 +38,7 @@ class F2Fpay extends PaymentService implements Gateway
];
try {
$gateWay = new AlipayF2F(self::$aliConfig);
$result = $gateWay->qrCharge($data);
$result = self::$aliClient->qrCharge($data);
$payment->update(['qr_code' => 1, 'url' => $result['qr_code']]);
} catch (Exception $e) {
Log::alert('【支付宝当面付】支付错误: '.$e->getMessage());
@@ -52,26 +52,47 @@ class F2Fpay extends PaymentService implements Gateway
public function notify(Request $request): void
{
try {
$result = (new AlipayF2F(self::$aliConfig))->tradeQuery($request->only('out_trade_no', 'trade_no'));
Log::notice('【支付宝当面付】回调验证验证:'.var_export($result, true));
if (sysConfig('f2fpay_app_id') === $request->input('app_id') && self::$aliClient->validate_notification_sign($request->except('method'), $request->input('sign'))) {
$payment = Payment::whereTradeNo($request->input('out_trade_no'))->with('order')->first();
if ($payment && abs($payment->amount - $request->input('total_amount')) < 0.01 && in_array($request->input('trade_status'), ['TRADE_FINISHED', 'TRADE_SUCCESS']) && $this->paymentReceived($request->input('out_trade_no'))) {
exit('success');
}
}
Log::error('【支付宝当面付】异步验证失败,尝试订单查询');
if ($this->capture($request->input('out_trade_no'), $request->input('trade_no'))) {
exit('success');
}
Log::notice('【支付宝当面付】异步验证失败:'.var_export($request->all(), true));
} catch (Exception $e) {
Log::alert('【支付宝当面付】回调信息错误: '.$e->getMessage());
exit;
}
if ($result['code'] === '10000' && $result['msg'] === 'Success') {
if ($request->has('out_trade_no') && in_array($request->input('trade_status'), ['TRADE_FINISHED', 'TRADE_SUCCESS'])) {
if ($this->paymentReceived($request->input('out_trade_no'))) {
exit('success');
}
} else {
Log::error('【支付宝当面付】交易失败:'.var_export($request->all(), true));
}
} else {
Log::error('【支付宝当面付】验证失败:'.var_export($result, true));
}
// 返回验证结果
exit('fail');
}
public function capture(string $trade_no = null, string $ali_trade_no = null): bool
{
$result = self::$aliClient->tradeQuery(array_filter([
'out_trade_no' => $trade_no,
'trade_no' => $ali_trade_no,
]));
if ($result['code'] === '10000' && $result['msg'] === 'Success') {
if ($result['out_trade_no'] && in_array($result['trade_status'], ['TRADE_FINISHED', 'TRADE_SUCCESS'])) {
if ($this->paymentReceived($result['out_trade_no'])) {
return true;
}
Log::error('【支付宝当面付】收单交易订单结算失败:'.var_export($result, true));
return false;
}
} else {
Log::error('【支付宝当面付】收单交易查询失败:'.var_export($result, true));
}
return false;
}
}