微信支付native回调
1.`直接引导回调代码及微信支付sdk相关类
use WeChatPay\Builder;
use WeChatPay\Crypto\AesGcm;
use WeChatPay\Crypto\Rsa;
use WeChatPay\Util\PemUtil;
use think\facade\Log;
use think\Request;
//公开回调函数及调用下面的方式执行 更新订单和用户重置等相关操作
public function notify(Request $request)
{
Log::info("开始处理微信支付回调通知");
// 检查请求方法是否为 POST
if ($request->isPost()) {
$notifyData = $request->getContent();
Log::info("接收到的回调数据");
Log::info(json_encode($notifyData));
if (empty($notifyData)) {
Log::error("接收到的回调数据为空");
return '数据为空';
}
$notifyData = json_decode($notifyData, true);
if (json_last_error() !== JSON_ERROR_NONE) {
Log::error("JSON解码失败");
return '解码错误';
}
Log::info("解码后的数据"); // 将数组转换为字符串
Log::info(json_encode($notifyData));
/*
* 格式如下:
* "id": "7349d5d5-1baa-502d-8ec3-41f54b329e0f",
"create_time": "2024-11-03T14:01:31+08:00",
"resource_type": "encrypt-resource",
"event_type": "TRANSACTION.SUCCESS",
"summary": "支付成功",
"resource": {
"original_type": "transaction",
"algorithm": "AEAD_AES_256_GCM",
"ciphertext": "yyoNR79YGuOgtO3820q3fNO26e/fKPFScaz9fqZigaRXAdH8khNHgOC81psrUY7N+365Y9k+sa6QPn7+aZ2C/k0tWqhqtwKff7JhIKJTW6upK7le1BFGwTHCmkDFegjX422bzoAe8qin4Mq9hXXIcqdZcWLkyCnWwQvpKWdB0pyctUe0vdzjepTuDW0E0/QbgOV8MM/Vy1ZAspf+XTO+OKR/iJE2CE+oRpS1OrkuIvF+AETs3DrNSEboM+FwXa933lGhJmE2XPLxOnCU0Pn9g5DBzf2IPTmrJl22VbN+j1fcLZJJ1f1DJ8gnx2ZkT9CVZWILQEFcY4pDXfqD1cLaIlzmrEnNBk8kdM36U7IJUhsD2NevM2WaNg/4PlTxkn0iozidgqpSI76DFBeb9GOFwR4fbHe8h8G8MMclv7As0FIpwIbwLY8LO/wuMFxNfcozina9F6lyjRIdNpLAeRCLXvJFTEo+oYxJxvA44OxOmgZDYysSfYZ9XOZV/NzsKUothdAagR5aFKAPeNnUz9NbYpf0dkwV7ItBp5Rs5z7inB1CRx7lOrgCDGnRJ01PLYDEn4Nrq9rgKUYPGGRYhUSR5kO4RNcwpWWKcgmrMuiS3g==",
"associated_data": "transaction",
"nonce": "NJSkDVqP5ogD"
}""
* */
// 解密 resource 字段中的数据
if (isset($notifyData['resource'])) {
$resource = $notifyData['resource'];
$decryptedResource = $this->decryptResource($resource);
Log::info("解密后的数据");
Log::info(json_encode($decryptedResource));
if ($decryptedResource === false) {
Log::error("解密 resource 失败");
return '解密失败';
}
$notifyData['resource'] = $decryptedResource;
}
Log::info("notifyData:");
log::info(json_encode($notifyData));
//没有返回签名字段无法验证,此处注释
// $sign = $this->makeSign($notifyData['resource'], $this->apiV3Key);
// if ($sign != $notifyData['resource']['sign']) {
// Log::error("签名验证失败"); // 将数组转换为字符串
// return '签名错误';
// }
if ($notifyData['resource']['trade_state'] == 'SUCCESS') {
Log::info("回调结果成功,开始处理业务逻辑");
$this->setNotifyAct($notifyData);// 将完整数组传入
} else {
Log::error("回调结果失败"); // 将数组转换为字符串
return error('回调结果失败');
}
Log::info("回调处理完成");
return suncess('回调处理完成');
} else {
Log::error("请求方法不正确,必须是 POST 方法");
return error('请求方法不正确, 必须是 POST 方法');
}
}`
2.设置数据库操作的方法:
private static function setNotifyAct($notifyData)
{
Log::info("setNotifyAct方法执行数据库操作");
$data = [
'status' => 1,
'w_transaction_id' => $notifyData['resource']['transaction_id'],
'payment_status' => 1,
'payment_type' => 1,
'w_payment_time' => date('Y-m-d H:i:s'),
'w_trade_type' => $notifyData['resource']['trade_type'], // 保留最后一次赋值
'w_notify_content' => json_encode($notifyData),
];
// 更新订单状态
Log::info("更新订单状态结果");
$res = orderService::updateOrderByField('sys_order_no',$notifyData['resource']['out_trade_no'], $data);
if ($res) {
if (strpos($notifyData['resource']['out_trade_no'], 'SDCQ') !== false) {
// 抽取订单支付1.只更新订单状态
Log::info("抽取订单支付成功out_trade_no: " . $notifyData['resource']['out_trade_no']);
return ['status' => 1, 'msg' => '更新订单成功'];
} elseif (strpos($notifyData['resource']['out_trade_no'], 'SDCZ') !== false) {
// 充值,这里是更新订单和用户余额
$user_id = user_id(true);
$order_info = orderService::getUserPrice($notifyData['resource']['attach']);
Log::info("获取用户订单信息");
Log::info(json_encode($order_info)); // 将数组转换为字符串
if (empty($order_info) || !isset($order_info['price'])) {
Log::error("获取用户订单信息失败attach: " . $notifyData['resource']['attach']);
return ['status' => 0, 'msg' => '获取用户订单信息失败'];
}
$res1 = userService::updateUserBalance($user_id, $order_info['price']);
Log::info("更新用户余额结果result: " . json_encode($res1)); // 将数组转换为字符串
if ($res1['status'] == 1) {
Log::info("用户余额更新成功user_id: " . $user_id . " amount: " . $order_info['price']);
return ['status' => 1, 'msg' => '更新用户余额成功'];
} else {
Log::error("用户余额更新失败user_id: " . $user_id . " amount: " . $order_info['price'] . " result: " . json_encode($res1)); // 将数组转换为字符串
return ['status' => 0, 'msg' => '更新用户余额错误'];
}
}
} else {
Log::error("更新订单状态失败out_trade_no: " . $notifyData['resource']['out_trade_no'] . " result: " . json_encode($res)); // 将数组转换为字符串
return ['status' => 0, 'msg' => '更新订单错误'];
}
}
3.解密源数据
/**
* 解密资源数据
*
* @param array $resource 资源数据
* @return array 解密后的数据
*/
private function decryptResource(array $resource): array
{
// 获取解密所需的参数
$algorithm = $resource['algorithm']; // 加密算法
$ciphertext = $resource['ciphertext']; // 密文
$associatedData = $resource['associated_data']; // 关联数据
$nonce = $resource['nonce']; // 随机数
// 使用 API v3 密钥进行解密
$key = $this->apiV3Key;
// 创建 AES-GCM 解密器
$aesGcm = new AesGcm();
// 解密数据
$decryptedData = $aesGcm::decrypt($ciphertext, $key, $nonce, $associatedData);
// 解析解密后的 JSON 数据
$decryptedData = json_decode($decryptedData, true);
return $decryptedData;
}
5.调用回调方法
public function wxpayNotify(Request $request){
$WxPay = new WxPay();
$response = $WxPay->notify($request);
// 返回响应给微信服务器
return $response;
}
6.开发建议:1.编写回调记录的记录不然不知道具体的回调内容是什么就没有进行后续业务开展,本法使用日志log;2.本人使用ngrok进行外网穿透;每次回调会进来一次请求,利于判断回调使用有响应。