微信JSAPI支付是公众号场景下常用的支付方式,用户完成支付后,微信支付服务器会向商户预先配置的回调通知地址发送异步通知,商户需要通过PHP接收并处理这些通知数据,才能确认支付结果,完成订单状态更新、发货等后续业务逻辑。

回调通知的基本规则
微信JSAPI支付回调通知有以下核心规则需要开发者提前了解:
- 回调通知采用POST请求方式,数据格式为XML
- 通知数据会进行签名,商户需要先验证签名确保数据未被篡改
- 商户处理完成后需要向微信服务器返回指定的XML响应,否则微信会重复发送通知
- 通知可能会因为网络原因重复发送,商户需要做好幂等处理
PHP接收回调数据
首先需要在商户后台配置好支付回调地址,确保该地址可以被外网访问,且没有被防火墙拦截。PHP接收回调数据的代码如下:
<?php
// 禁止脚本超时
set_time_limit(0);
// 读取POST过来的原始XML数据
$xmlData = file_get_contents('php://input');
if (empty($xmlData)) {
// 没有接收到数据,记录日志后返回失败
error_log('未接收到微信支付回调数据');
echo returnXml(false);
exit;
}
// 将XML数据转换为数组
function xmlToArray($xml) {
libxml_disable_entity_loader(true);
$values = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
return $values;
}
$notifyData = xmlToArray($xmlData);
?>
签名验证
接收到数据后,首先需要对通知数据的签名进行校验,确保数据来自微信支付服务器且未被篡改。签名验证的步骤如下:
- 剔除数组中的sign字段
- 对剩余字段按照ASCII码从小到大排序
- 拼接成URL键值对格式的字符串,末尾拼接上商户密钥
- 对拼接后的字符串进行MD5加密,转为大写后与通知中的sign字段对比
签名验证的PHP实现代码:
<?php
// 商户密钥,在微信支付商户平台设置
$merchantKey = 'your_merchant_key_here';
function checkSign($data, $key) {
if (!isset($data['sign'])) {
return false;
}
$sign = $data['sign'];
unset($data['sign']);
// 按照ASCII码排序
ksort($data);
$string = '';
foreach ($data as $k => $v) {
if ($v !== '' && !is_array($v)) {
$string .= $k . '=' . $v . '&';
}
}
$string = rtrim($string, '&');
// 拼接密钥
$string = $string . '&key=' . $key;
// MD5加密并转大写
$calculateSign = strtoupper(md5($string));
return $calculateSign === $sign;
}
$signValid = checkSign($notifyData, $merchantKey);
if (!$signValid) {
error_log('微信支付回调签名验证失败');
echo returnXml(false);
exit;
}
?>
处理通知数据
签名验证通过后,就可以处理通知中的业务数据了,首先需要判断支付结果是否成功:
<?php
// 判断支付结果
if ($notifyData['return_code'] != 'SUCCESS' || $notifyData['result_code'] != 'SUCCESS') {
error_log('微信支付回调返回失败:' . ($notifyData['err_code_des'] ?? '未知错误'));
echo returnXml(false);
exit;
}
// 获取核心业务参数
$orderNo = $notifyData['out_trade_no']; // 商户订单号
$transactionId = $notifyData['transaction_id']; // 微信支付订单号
$totalFee = $notifyData['total_fee']; // 支付金额,单位为分
$payTime = $notifyData['time_end']; // 支付完成时间
// 这里需要添加幂等处理,避免重复处理同一笔订单
// 可以查询订单表中该订单号的状态,如果已经处理过则直接返回成功
// 示例:假设存在函数 checkOrderProcessed 检查订单是否已处理
if (checkOrderProcessed($orderNo)) {
echo returnXml(true);
exit;
}
// 更新订单状态,这里替换为实际的订单更新逻辑
$updateResult = updateOrderStatus($orderNo, 1, $transactionId, $totalFee, $payTime);
if (!$updateResult) {
error_log('订单状态更新失败,订单号:' . $orderNo);
echo returnXml(false);
exit;
}
// 处理其他业务逻辑,比如发货、发送通知等
// 处理完成后返回成功响应
echo returnXml(true);
?>
返回响应给微信服务器
商户处理完通知后,必须向微信服务器返回指定的XML响应,否则微信会认为通知失败,重复发送通知。响应格式如下:
<?php
function returnXml($success) {
if ($success) {
$xml = '<xml>
<return_code><![CDATA[SUCCESS]]></return_code>
<return_msg><![CDATA[OK]]></return_msg>
</xml>';
} else {
$xml = '<xml>
<return_code><![CDATA[FAIL]]></return_code>
<return_msg><![CDATA[处理失败]]></return_msg>
</xml>';
}
return $xml;
}
?>
常见问题说明
1. 接收不到回调数据
首先检查回调地址是否在商户后台正确配置,其次检查服务器是否有防火墙拦截POST请求,也可以尝试用postman模拟POST请求发送XML数据测试接口是否正常。
2. 签名验证失败
检查商户密钥是否正确,排序规则是否按照ASCII码从小到大,拼接字符串时是否有遗漏字段或者多了不必要的符号,MD5加密后是否正确转为大写。
3. 重复处理通知
一定要做好幂等处理,通过商户订单号查询订单状态,已经处理完成的订单直接返回成功响应,不要重复执行业务逻辑。
4. 响应格式错误
返回的XML必须符合微信要求的格式,内容类型不需要设置,直接输出XML字符串即可,不要添加额外的输出内容。