更新
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\adminapi\controller\order;
|
||||
|
||||
use app\common\model\Order;
|
||||
use think\response\Xml;
|
||||
|
||||
/**
|
||||
* 微信支付回调控制器
|
||||
* Class WechatNotifyController
|
||||
* @package app\adminapi\controller\order
|
||||
*/
|
||||
class WechatNotifyController
|
||||
{
|
||||
/**
|
||||
* @notes 微信异步通知
|
||||
* @return Xml
|
||||
*/
|
||||
public function notify()
|
||||
{
|
||||
try {
|
||||
// 获取微信配置
|
||||
$wechatConfig = config('pay.wechat');
|
||||
|
||||
// 获取XML数据
|
||||
$xmlData = file_get_contents('php://input');
|
||||
$data = $this->xmlToArray($xmlData);
|
||||
|
||||
// 验证签名
|
||||
if (!$this->verifyWechatSign($data, $wechatConfig['api_key'])) {
|
||||
return $this->xmlResponse('FAIL', '签名验证失败');
|
||||
}
|
||||
|
||||
// 验证金额
|
||||
$order = Order::where('order_no', $data['out_trade_no'])->find();
|
||||
if (!$order) {
|
||||
return $this->xmlResponse('FAIL', '订单不存在');
|
||||
}
|
||||
|
||||
if ((int)$data['total_fee'] != (int)($order->amount * 100)) {
|
||||
return $this->xmlResponse('FAIL', '金额不匹配');
|
||||
}
|
||||
|
||||
// 验证交易状态
|
||||
if ($data['result_code'] == 'SUCCESS') {
|
||||
// 更新订单状态
|
||||
if ($order->status == 1) { // 只有待支付的订单才能更新
|
||||
$order->status = 2; // 已支付
|
||||
$order->payment_method = 'wechat';
|
||||
$order->payment_time = date('Y-m-d H:i:s');
|
||||
$order->trade_no = $data['transaction_id'];
|
||||
$order->save();
|
||||
}
|
||||
}
|
||||
|
||||
return $this->xmlResponse('SUCCESS', '支付成功');
|
||||
} catch (\Exception $e) {
|
||||
return $this->xmlResponse('FAIL', $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 验证微信签名
|
||||
* @param array $data
|
||||
* @param string $apiKey
|
||||
* @return bool
|
||||
*/
|
||||
private function verifyWechatSign(array $data, string $apiKey): bool
|
||||
{
|
||||
try {
|
||||
// 获取签名
|
||||
$sign = $data['sign'] ?? '';
|
||||
unset($data['sign']);
|
||||
|
||||
// 按键排序
|
||||
ksort($data);
|
||||
|
||||
// 构建签名字符串
|
||||
$signStr = '';
|
||||
foreach ($data as $key => $value) {
|
||||
if ($value !== '' && $value !== null) {
|
||||
$signStr .= $key . '=' . $value . '&';
|
||||
}
|
||||
}
|
||||
$signStr .= 'key=' . $apiKey;
|
||||
|
||||
// MD5签名
|
||||
$computedSign = strtoupper(md5($signStr));
|
||||
|
||||
return $computedSign === $sign;
|
||||
} catch (\Exception $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes XML转数组
|
||||
* @param string $xml
|
||||
* @return array
|
||||
*/
|
||||
private function xmlToArray(string $xml): array
|
||||
{
|
||||
try {
|
||||
$data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
|
||||
return is_array($data) ? $data : [];
|
||||
} catch (\Exception $e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 返回XML响应
|
||||
* @param string $returnCode
|
||||
* @param string $returnMsg
|
||||
* @return Xml
|
||||
*/
|
||||
private function xmlResponse(string $returnCode, string $returnMsg): Xml
|
||||
{
|
||||
$xml = '<xml>';
|
||||
$xml .= '<return_code><![CDATA[' . $returnCode . ']]></return_code>';
|
||||
$xml .= '<return_msg><![CDATA[' . $returnMsg . ']]></return_msg>';
|
||||
$xml .= '</xml>';
|
||||
|
||||
return response($xml, 200, ['Content-Type' => 'application/xml']);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user