支付成功(TRANSACTION.SUCCESS)通知(JSON)
微信支付通过支付通知接口将用户支付成功消息通知给商户
注意:
- 同样的通知可能会多次发送给商户系统。商户系统必须能够正确处理重复的通知。 推荐的做法是,当商户系统收到通知进行处理时,先检查对应业务数据的状态,并判断该通知是否已经处理。如果未处理,则再进行处理;如果已处理,则直接返回结果成功。在对业务数据进行状态检查和处理之前,要采用数据锁进行并发控制,以避免函数重入造成的数据混乱。
- 如果在所有通知频率后没有收到微信侧回调,商户应调用查询订单接口确认订单状态。
- 特别提醒:商户系统对于开启结果通知的内容一定要做签名验证,并校验通知的信息是否与商户侧的信息一致,防止数据泄露导致出现“假通知”,造成资金损失。
- 用户支付完成后,微信会把相关支付结果和用户信息发送给商户,商户需要接收处理该消息,并返回应答。
- 对后台通知交互时,如果微信收到商户的应答不符合规范或超时,微信认为通知失败,微信会通过一定的策略定期重新发起通知,尽可能提高通知的成功率,但微信不保证通知最终能成功。(通知频率为15s/15s/30s/3m/10m/20m/30m/30m/30m/60m/3h/3h/3h/6h/6h - 总计 24h4m)
请求参数 | 类型 | 描述 |
---|---|---|
headers | object | 通知的头参数 |
Content-Type | string | application/json |
Request-ID | string | 通知的唯一标识 |
Wechatpay-Nonce | string | 数据签名使用的随机串 |
Wechatpay-Serial | string | 微信支付公钥ID/平台证书序列号 |
Wechatpay-Signature | string | 签名串 |
Wechatpay-Signature-Type | string | 签名算法WECHATPAY2-SHA256-RSA2048 枚举值 |
Wechatpay-Timestamp | string | 时间戳 |
body | object | 通知的JSON 数据结构 |
id | string | 通知的唯一ID |
create_time | string | 通知创建的时间 |
event_type | string | 通知的类型TRANSACTION.SUCCESS 枚举值 |
resource_type | string | 通知的资源数据类型 |
summary | string | 回调摘要 |
resource | object | 通知资源数据 |
algorithm | string | 对数据进行加密的加密算法AEAD_AES_256_GCM 枚举值 |
associated_data | string | 数据加密的附加数据 |
nonce | string | 加密使用的随机串 |
ciphertext | string | 加密后的密文数据 |
original_type | string | 原始回调类型 |
sp_mchid | string | 调用接口提交的商户号 |
sub_mchid | string | 调用接口提交的子商户号 |
transaction_id | string | 微信支付系统生成的订单号 |
order_id | string | 微信分账/回退单号 |
out_order_no | string | 分账方系统内部的分账/回退单号 |
receiver | object | 分账接收方对象 |
type | string | 分账接收方的类型MERCHANT_ID | PERSONAL_OPENID 枚举值之一 |
account | string | 分账接收方的账号 类型是 MERCHANT_ID 时,是商户号类型是 PERSONAL_OPENID 时,是个人OpenID |
amount | number | 分账动账金额,单位为分,只能为整数 |
description | string | 分账/回退描述 |
success_time | string | 订单支付完成时间 |
appid | string | 调用接口提交的应用ID |
out_trade_no | string | 调用接口提交的商户服务订单号 |
description | string | 商户自定义字段,用户交易账单中对扣费服务的描述。 |
create_time | string | 订单成功创建时返回 |
trade_state | string | 交易状态:SUCCESS :支付成功 ACCEPT :已接收,等待扣款 PAY_FAIL :支付失败(其他原因,如银行返回失败) REFUND :转入退款 |
trade_state_description | string | 对当前订单状态的描述和下一步操作的指引。 |
bank_type | string | 银行类型,采用字符串类型的银行标识。 |
attach | string | 附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用,实际情况下只有支付完成状态才会返回该字段。 |
user_repaid | string | 枚举值:Y :用户已还款N :用户未还款注意:使用此字段前需先确认 bank_type 字段值为BPA以及 trade_state 字段值为SUCCESS 。 |
trade_scene | string | 交易场景值,目前支持PARKING :车场停车场景 |
parking_info | object | trade_scene为PARKING 时,返回停车场景信息 |
parking_id | string | 车主服务为商户分配的入场ID,商户通过入场通知接口获取入场ID |
plate_number | string | 车牌号,仅包括省份+车牌,不包括特殊字符。 |
plate_color | string | 车牌颜色BLUE | GREEN | YELLOW | BLACK | WHITE | LIMEGREEN 枚举值之一 |
start_time | string | 用户入场时间 |
end_time | string | 用户出场时间 |
parking_name | string | 所在停车位车场的名称 |
charging_duration | number | 计费的时间长,单位为秒 |
device_id | string | 停车场设备ID |
payer | object | 支付者信息,详细说明见下文 |
openid | string | 用户在服务商的标识 |
sub_openid | string | 用户在子商户的标识 |
amount | object | 订单金额信息,详细说明见下文 |
currency | string | 符合ISO 4217标准的三位字母代码,目前只支持人民币:CNY |
payer_total | number | 用户实际支付金额,单位为分,只能为整数 |
payer_currency | string | 用户支付币种 |
exchange_rate | object | 汇率信息 |
type | string | 汇率类型 |
rate | integer | 汇率值 |
promotion_detail | object[] | 优惠功能信息,详细说明见下文 |
coupon_id | string | 券或者立减优惠ID |
name | string | 优惠名称。 |
scope | string | 枚举值GLOBAL :全场代金券SINGLE :单品优惠 |
type | string | 枚举值:CASH :充值型代金券NOCASH :免充值型代金券 |
activity_id | string | 在微信商户后台配置的批次ID。 |
currency | string | CNY :人民币,境内商户号仅支持人民币。 |
sub_appid | string | 调用接口提交的子商户应用ID |
combine_appid | string | 合单应用程序APPID |
combine_mchid | string | 交易单发起方的商户号 |
combine_out_trade_no | string | 商户系统内部对交易单定义的订单号 |
combine_transaction_id | string | 微信支付系统生成的订单号 |
scene_info | object | 支付场景信息描述 |
device_id | string | 终端设备号(门店号或收银设备ID) |
sub_orders | object | 商品单列表 |
mchid | string | 商品单发起商户号 |
individual_auth_id | string | 商品单个人收款方受理授权ID |
individual_name | string | 商品单个人收款方平台昵称 |
trade_type | string | 交易类型 |
trade_state | string | 交易状态SUCCESS | REFUND | NOTPAY | CLOSED | REVOKED | USERPAYING | PAYERROR | ACCEPT 枚举值之一 |
bank_type | string | 付款银行 |
attach | string | 附加数据 |
amount | object | 订单金额 |
total_amount | number | 标价金额 |
payer_amount | number | 现金支付金额 |
currency | string | 标价币种 |
payer_currency | string | 现金支付币种 |
settlement_rate | number | 结算汇率 |
success_time | string | 支付完成时间 |
transaction_id | string | 微信支付订单号 |
out_trade_no | string | 商品单订单号 |
combine_payer_info | object | 支付者信息 |
openid | string | 用户标识 |
普通支付只有支付成功(trade_state=SUCCESS)才有通知
服务商支付成功会带上(sp_mchid及sub_mchid)字段
合单支付成功会带上(combine_mchid)字段
停车服务(trade_scene)为固定值"PARKING"
保险商户委托代扣成功通知(trade_type)为"PAP"
分账动帐通知(original_type)为"profitsharing"
跨境/全球会返回exchange_rate字典
平台收付通-个人收款场景会返回individual_auth_id字典
php
// 使用Psr标准规范,示例如何处理(取值、验签、解密)「回调通知」事件,WebServer不同,用法略有差异,供参考实现。
function webhookProcessor(\Psr\Http\Message\RequestInterface $request,
array $platformPublicKeyMap, string $apiv3Key): array {
if (!\count($platformPublicKeyMap)) {
throw new \WeChatPay\Exception\InvalidArgumentException('平台证书或者平台公钥数组不能为空');
}
if (\strlen($apiv3Key) !== 32) {
throw new \WeChatPay\Exception\InvalidArgumentException('APIV3密钥为32字节,长度不对');
}
if (!($request->hasHeader(\WeChatPay\WechatpayNonce)
&& $request->hasHeader(\WeChatPay\WechatpaySerial)
&& $request->hasHeader(\WeChatPay\WechatpaySignature)
&& $request->hasHeader(\WeChatPay\WechatpayTimestamp))) {
throw new \WeChatPay\Exception\InvalidArgumentException('通知的头参数缺失必要参数');
}
// 检查通知的时间偏移量,允许5分钟之内的偏移
[$inWechatpayTimestamp] = $request->getHeader(\WeChatPay\WechatpayTimestamp);
if (\WeChatPay\MAXIMUM_CLOCK_OFFSET < \abs(
\WeChatPay\Formatter::timestamp() - (int)$inWechatpayTimestamp)) {
throw new \WeChatPay\Exception\InvalidArgumentException('通知头参数的时间偏移量超过可信阈值');
}
// 检查通知的平台证书/平台公钥实例是否存在
[$inWechatpaySerial] = $request->getHeader(\WeChatPay\WechatpaySerial);
if (!\array_key_exists($inWechatpaySerial, $platformPublicKeyMap)) {
throw new \WeChatPay\Exception\InvalidArgumentException('通知头参数的证书序列号/公钥ID本地不存在');
}
// 验证通知的数据签名
[$inWechatpaySignature] = $request->getHeader(\WeChatPay\WechatpaySignature);
[$inWechatpayNonce] = $request->getHeader(\WeChatPay\WechatpayNonce);
$inBody = (string)$request->getBody();
if (!\WeChatPay\Crypto\Rsa::verify(
\WeChatPay\Formatter::joinedByLineFeed($inWechatpayTimestamp, $inWechatpayNonce, $inBody),
$inWechatpaySignature, $platformPublicKeyMap[$inWechatpaySerial]
)) {
throw new \WeChatPay\Exception\InvalidArgumentException('通知头参数的数据签名校验未通过');
}
// 转换通知的JSON文本消息为PHP Array数组
$inBodyArray = (array)\json_decode($inBody, true);
// 使用PHP7+的数据解构语法,从Array中解构并赋值变量
['resource' => [
'ciphertext' => $ciphertext,
'nonce' => $nonce,
'associated_data' => $aad
]] = $inBodyArray;
// 加密文本消息解密,有可能解密异常(eg: 平台探测流量)会抛 \UnexpectedValueException
$inBodyResource = \WeChatPay\Crypto\AesGcm::decrypt($ciphertext, $apiv3Key, $nonce, $aad);
// 把解密后的文本转换为PHP Array数组
$inBodyResourceArray = (array)\json_decode($inBodyResource, true);
// 把解密后的数组定义为'original_data'函数返回
$inBodyArray['resource']['original_data'] = $inBodyResourceArray;
return [
'headers' => $request->getHeaders(),
'body' => $inBodyArray
];
}
// do your business
// ...
// ...
$json = \json_encode([
'code' => 'SUCCESS',
'message' => 'OK'
]);
应答规范 | 类型 | 描述 |
---|---|---|
status | number | HTTP 状态码20X 4XX 5XX 枚举值之一 |
body | object | 应答的JSON 数据结构 |
code | string | 业务处理状态码SUCCESS | FAIL 枚举值之一 |
message | string | 业务处理附加信息 |
参阅 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档