支付回调提交
This commit is contained in:
@@ -33,25 +33,24 @@ class PaymentService
|
||||
*/
|
||||
public function createOrder(array $order)
|
||||
{
|
||||
Log::info('支付创建');
|
||||
$params = [
|
||||
'service' => 'unified.trade.native',
|
||||
'sign_type' => PaymentUtil::SIGN_TYPE_MD5,
|
||||
'mch_id' => Env::get('payment.mchId'),
|
||||
'out_trade_no' => $order['orderNo'],
|
||||
'body' => $order['goodsName'] ?? '',
|
||||
'total_fee' => $order['money'] ?? 0,
|
||||
'mch_create_ip' => Request::ip(),
|
||||
'notify_url' => Env::get('payment.notify_url','127.0.0.1'),
|
||||
'nonce_str' => PaymentUtil::generateNonceStr(),
|
||||
'service' => 'unified.trade.native',
|
||||
'sign_type' => PaymentUtil::SIGN_TYPE_MD5,
|
||||
'mch_id' => Env::get('payment.mchId'),
|
||||
'out_trade_no' => $order['orderNo'],
|
||||
'body' => $order['goodsName'] ?? '',
|
||||
'total_fee' => $order['money'] ?? 0,
|
||||
'mch_create_ip' => Request::ip(),
|
||||
'notify_url' => Env::get('payment.notify_url', '127.0.0.1'),
|
||||
'nonce_str' => PaymentUtil::generateNonceStr(),
|
||||
];
|
||||
|
||||
Db::startTrans();
|
||||
try {
|
||||
// 过滤空值签名
|
||||
$secret = Env::get('payment.key');
|
||||
$secret = Env::get('payment.key');
|
||||
$params['sign_type'] = 'MD5';
|
||||
$params['sign'] = PaymentUtil::generateSign($params, $secret, 'MD5');
|
||||
$params['sign'] = PaymentUtil::generateSign($params, $secret, 'MD5');
|
||||
|
||||
$url = Env::get('payment.url');
|
||||
if (empty($url)) {
|
||||
@@ -76,14 +75,14 @@ class PaymentService
|
||||
|
||||
|
||||
// XML POST 请求
|
||||
$xmlBody = $this->arrayToXml($params);
|
||||
$xmlBody = $this->arrayToXml($params);
|
||||
$response = $this->postXml($url, $xmlBody);
|
||||
$parsed = $this->parseXmlOrRaw($response);
|
||||
$parsed = $this->parseXmlOrRaw($response);
|
||||
|
||||
if ($parsed['status'] == 0 && $parsed['result_code'] == 0) {
|
||||
Db::commit();
|
||||
return json(['code' => 200, 'msg' => '订单创建成功','data' => $parsed['code_url']]);
|
||||
}else{
|
||||
return json(['code' => 200, 'msg' => '订单创建成功', 'data' => $parsed['code_url']]);
|
||||
} else {
|
||||
Db::rollback();
|
||||
return json(['code' => 500, 'msg' => '订单创建失败:' . $parsed['err_msg']]);
|
||||
}
|
||||
@@ -95,7 +94,6 @@ class PaymentService
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* POST 请求(x-www-form-urlencoded)
|
||||
*/
|
||||
@@ -202,112 +200,74 @@ class PaymentService
|
||||
*/
|
||||
public function notify()
|
||||
{
|
||||
Log::info('支付结果异步通知');
|
||||
$rawBody = file_get_contents('php://input');
|
||||
Log::info('[SwiftPass][notify] raw: ' . $rawBody);
|
||||
//$rawBody = file_get_contents('php://input');
|
||||
$rawBody = '<xml><bank_type><![CDATA[OTHERS]]></bank_type>
|
||||
<cash_fee><![CDATA[1]]></cash_fee>
|
||||
<charset><![CDATA[UTF-8]]></charset>
|
||||
<coupon_fee><![CDATA[0]]></coupon_fee>
|
||||
<fee_type><![CDATA[CNY]]></fee_type>
|
||||
<mch_id><![CDATA[1030239780]]></mch_id>
|
||||
<mdiscount><![CDATA[0]]></mdiscount>
|
||||
<nonce_str><![CDATA[8c8993d51c1f4afcb4f8a265c4fe2084]]></nonce_str>
|
||||
<op_user_id><![CDATA[1030239780]]></op_user_id>
|
||||
<openid><![CDATA[o8jhotw0PnO8TLuRT9WurJ9dhpPU]]></openid>
|
||||
<out_trade_no><![CDATA[20250924142316868008]]></out_trade_no>
|
||||
<out_transaction_id><![CDATA[4200002761202509243272187350]]></out_transaction_id>
|
||||
<pay_result><![CDATA[0]]></pay_result>
|
||||
<result_code><![CDATA[0]]></result_code>
|
||||
<sign><![CDATA[9F324511AF49B6261F62B1004D6350BF]]></sign>
|
||||
<sign_type><![CDATA[MD5]]></sign_type>
|
||||
<status><![CDATA[0]]></status>
|
||||
<sub_appid><![CDATA[wx753bec1f52890a42]]></sub_appid>
|
||||
<sub_openid><![CDATA[o6SVY6LB1obH6KD2uAtDMkqM9cgg]]></sub_openid>
|
||||
<third_order_no><![CDATA[288550038027202509242227783238]]></third_order_no>
|
||||
<time_end><![CDATA[20250924142412]]></time_end>
|
||||
<total_fee><![CDATA[1]]></total_fee>
|
||||
<trade_type><![CDATA[pay.wechat.jspay]]></trade_type>
|
||||
<transaction_id><![CDATA[202509248102020491536020483421]]></transaction_id>
|
||||
<version><![CDATA[2.0]]></version>
|
||||
</xml>';
|
||||
|
||||
$payload = $this->parseXmlOrRaw($rawBody);
|
||||
if (!is_array($payload) || empty($payload)) {
|
||||
Log::error('[SwiftPass][notify] parse fail');
|
||||
echo 'fail';
|
||||
return;
|
||||
return json_encode(['code' => 500, 'msg' => 'XML解析错误']);
|
||||
}
|
||||
|
||||
// 基础字段
|
||||
$status = (string)($payload['status'] ?? '');
|
||||
$resultCode = (string)($payload['result_code'] ?? '');
|
||||
$outTradeNo = (string)($payload['out_trade_no'] ?? '');
|
||||
$totalFee = (int)($payload['total_fee'] ?? 0);
|
||||
$mchIdNotify = (string)($payload['mch_id'] ?? '');
|
||||
$signInNotify = (string)($payload['sign'] ?? '');
|
||||
$signType = (string)($payload['sign_type'] ?? 'MD5');
|
||||
|
||||
if ($status !== '0' || $resultCode !== '0') {
|
||||
Log::warning('[SwiftPass][notify] business not success', $payload);
|
||||
echo 'fail';
|
||||
return;
|
||||
if ($payload['status'] != 0 || $payload['result_code'] != 0) {
|
||||
$errMsg = (isset($payload['err_msg']) ? $payload['err_msg'] : isset($payload['err_msg'])) ? $payload['err_msg'] : '未知错误';
|
||||
return json_encode(['code' => 500, 'msg' => $errMsg]);
|
||||
}
|
||||
|
||||
// 验签
|
||||
$secret = Env::get('payment.key');
|
||||
if (empty($secret)) {
|
||||
Log::error('[SwiftPass][notify] payment.key not configured');
|
||||
echo 'fail';
|
||||
return;
|
||||
}
|
||||
|
||||
$verifyData = $payload;
|
||||
unset($verifyData['sign']);
|
||||
$calcSign = PaymentUtil::generateSign($verifyData, $secret, $signType ?: 'MD5');
|
||||
if (strcasecmp($calcSign, $signInNotify) !== 0) {
|
||||
Log::error('[SwiftPass][notify] sign mismatch', [
|
||||
'calc' => $calcSign,
|
||||
'recv' => $signInNotify,
|
||||
]);
|
||||
echo 'fail';
|
||||
return;
|
||||
}
|
||||
|
||||
// 校验商户号
|
||||
$mchIdConfig = Env::get('payment.mchId');
|
||||
if (!empty($mchIdConfig) && $mchIdConfig !== $mchIdNotify) {
|
||||
Log::error('[SwiftPass][notify] mch_id mismatch', [
|
||||
'config' => $mchIdConfig,
|
||||
'notify' => $mchIdNotify,
|
||||
]);
|
||||
echo 'fail';
|
||||
return;
|
||||
}
|
||||
|
||||
// 业务处理:更新订单
|
||||
Db::startTrans();
|
||||
try {
|
||||
/** @var Order|null $order */
|
||||
$order = Order::where('orderNo', $outTradeNo)->lock(true)->find();
|
||||
$outTradeNo = $payload['out_trade_no'];
|
||||
$pay_result = $payload['pay_result'];
|
||||
$time_end = $payload['time_end'];
|
||||
$order = Order::where('orderNo', $outTradeNo)->find();
|
||||
if (!$order) {
|
||||
Db::rollback();
|
||||
Log::error('[SwiftPass][notify] order not found', ['out_trade_no' => $outTradeNo]);
|
||||
echo 'fail';
|
||||
return;
|
||||
return json_encode(['code' => 500, 'msg' => '该订单不存在']);
|
||||
}
|
||||
|
||||
// 金额校验(单位:分)
|
||||
if ((int)$order['money'] !== $totalFee) {
|
||||
Db::rollback();
|
||||
Log::error('[SwiftPass][notify] amount mismatch', [
|
||||
'order_money' => (int)$order['money'],
|
||||
'notify_fee' => $totalFee,
|
||||
]);
|
||||
echo 'fail';
|
||||
return;
|
||||
}
|
||||
|
||||
// 幂等:已支付直接返回成功
|
||||
if ((int)$order['status'] === 1) {
|
||||
if ($pay_result != 0) {
|
||||
$order->payInfo = $payload['pay_info'];
|
||||
$order->payType = $payload['trade_type'] == 'pay.wechat.jspay' ? 1 : 2;
|
||||
$order->status = 3;
|
||||
$order->save();
|
||||
Db::commit();
|
||||
echo 'success';
|
||||
return;
|
||||
return json_encode(['code' => 500, 'msg' => $payload['pay_info']]);
|
||||
}
|
||||
|
||||
$transactionId = (string)($payload['transaction_id'] ?? '');
|
||||
$timeEndStr = (string)($payload['time_end'] ?? '');
|
||||
$paidAt = $this->parsePayTime($timeEndStr) ?: time();
|
||||
|
||||
$order->save([
|
||||
'status' => 1,
|
||||
'transactionId' => $transactionId,
|
||||
'payTime' => $paidAt,
|
||||
'updateTime' => time(),
|
||||
]);
|
||||
|
||||
$order->status = 1;
|
||||
$order->payTime = $this->parsePayTime($time_end);
|
||||
$order->save();
|
||||
Db::commit();
|
||||
echo 'success';
|
||||
return;
|
||||
} catch (\Throwable $e) {
|
||||
return json_encode(['code' => 200, 'msg' => '付款成功']);
|
||||
} catch (\Exception $e) {
|
||||
Db::rollback();
|
||||
Log::error('[SwiftPass][notify] exception: ' . $e->getMessage());
|
||||
echo 'fail';
|
||||
return;
|
||||
return json_encode(['code' => 500, 'msg' => '付款失败' . $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -338,7 +298,7 @@ class PaymentService
|
||||
*/
|
||||
public function queryOrder(array $query)
|
||||
{
|
||||
$outTradeNo = $query['out_trade_no'] ?? ($query['orderNo'] ?? '');
|
||||
$outTradeNo = $query['out_trade_no'] ?? ($query['orderNo'] ?? '');
|
||||
$transactionId = $query['transaction_id'] ?? '';
|
||||
|
||||
if ($outTradeNo === '' && $transactionId === '') {
|
||||
@@ -346,11 +306,11 @@ class PaymentService
|
||||
}
|
||||
|
||||
$params = [
|
||||
'service' => 'unified.trade.query',
|
||||
'mch_id' => Env::get('payment.mchId'),
|
||||
'out_trade_no' => $outTradeNo ?: null,
|
||||
'nonce_str' => PaymentUtil::generateNonceStr(),
|
||||
'sign_type' => 'MD5',
|
||||
'service' => 'unified.trade.query',
|
||||
'mch_id' => Env::get('payment.mchId'),
|
||||
'out_trade_no' => $outTradeNo ?: null,
|
||||
'nonce_str' => PaymentUtil::generateNonceStr(),
|
||||
'sign_type' => 'MD5',
|
||||
];
|
||||
|
||||
// 过滤空值后签名
|
||||
@@ -372,9 +332,9 @@ class PaymentService
|
||||
}
|
||||
|
||||
// 请求网关
|
||||
$xmlBody = $this->arrayToXml($filtered);
|
||||
$xmlBody = $this->arrayToXml($filtered);
|
||||
$response = $this->postXml($url, $xmlBody);
|
||||
$parsed = $this->parseXmlOrRaw($response);
|
||||
$parsed = $this->parseXmlOrRaw($response);
|
||||
|
||||
if (!is_array($parsed)) {
|
||||
return json(['code' => 500, 'msg' => '响应解析失败', 'data' => $response]);
|
||||
@@ -387,20 +347,20 @@ class PaymentService
|
||||
if (($parsed['result_code'] ?? '') !== '0') {
|
||||
return json(['code' => 200, 'msg' => '业务失败', 'data' => [
|
||||
'err_code' => $parsed['err_code'] ?? '',
|
||||
'err_msg' => $parsed['err_msg'] ?? '',
|
||||
'err_msg' => $parsed['err_msg'] ?? '',
|
||||
]]);
|
||||
}
|
||||
|
||||
$tradeState = $parsed['trade_state'] ?? '';
|
||||
$resp = [
|
||||
'trade_state' => $tradeState,
|
||||
'trade_state_desc'=> $parsed['trade_state_desc'] ?? '',
|
||||
'transaction_id' => $parsed['transaction_id'] ?? '',
|
||||
'out_trade_no' => $parsed['out_trade_no'] ?? $outTradeNo,
|
||||
'total_fee' => isset($parsed['total_fee']) ? (int)$parsed['total_fee'] : null,
|
||||
'time_end' => $parsed['time_end'] ?? '',
|
||||
'buyer_logon_id' => $parsed['buyer_logon_id'] ?? '',
|
||||
'bank_type' => $parsed['bank_type'] ?? '',
|
||||
'trade_state' => $tradeState,
|
||||
'trade_state_desc' => $parsed['trade_state_desc'] ?? '',
|
||||
'transaction_id' => $parsed['transaction_id'] ?? '',
|
||||
'out_trade_no' => $parsed['out_trade_no'] ?? $outTradeNo,
|
||||
'total_fee' => isset($parsed['total_fee']) ? (int)$parsed['total_fee'] : null,
|
||||
'time_end' => $parsed['time_end'] ?? '',
|
||||
'buyer_logon_id' => $parsed['buyer_logon_id'] ?? '',
|
||||
'bank_type' => $parsed['bank_type'] ?? '',
|
||||
];
|
||||
|
||||
// 若已支付,同步本地订单
|
||||
@@ -413,10 +373,10 @@ class PaymentService
|
||||
$paidAt = $this->parsePayTime($resp['time_end'] ?? '') ?: time();
|
||||
if ((int)$order['status'] !== 1) {
|
||||
$order->save([
|
||||
'status' => 1,
|
||||
'status' => 1,
|
||||
'transactionId' => $resp['transaction_id'] ?? '',
|
||||
'payTime' => $paidAt,
|
||||
'updateTime' => time(),
|
||||
'payTime' => $paidAt,
|
||||
'updateTime' => time(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user