Skip to content

支付成功(TRANSACTION.SUCCESS)通知(JSON)

微信支付通过支付通知接口将用户支付成功消息通知给商户

注意:

  • 同样的通知可能会多次发送给商户系统。商户系统必须能够正确处理重复的通知。 推荐的做法是,当商户系统收到通知进行处理时,先检查对应业务数据的状态,并判断该通知是否已经处理。如果未处理,则再进行处理;如果已处理,则直接返回结果成功。在对业务数据进行状态检查和处理之前,要采用数据锁进行并发控制,以避免函数重入造成的数据混乱。
  • 如果在所有通知频率后没有收到微信侧回调,商户应调用查询订单接口确认订单状态。
  • 特别提醒:商户系统对于开启结果通知的内容一定要做签名验证,并校验通知的信息是否与商户侧的信息一致,防止数据泄露导致出现“假通知”,造成资金损失。
  • 用户支付完成后,微信会把相关支付结果和用户信息发送给商户,商户需要接收处理该消息,并返回应答。
  • 对后台通知交互时,如果微信收到商户的应答不符合规范或超时,微信认为通知失败,微信会通过一定的策略定期重新发起通知,尽可能提高通知的成功率,但微信不保证通知最终能成功。(通知频率为15s/15s/30s/3m/10m/20m/30m/30m/30m/60m/3h/3h/3h/6h/6h - 总计 24h4m)
请求参数类型描述
headersobject通知的头参数
Content-Typestringapplication/json
Request-IDstring通知的唯一标识
Wechatpay-Noncestring数据签名使用的随机串
Wechatpay-Serialstring微信支付公钥ID/平台证书序列号
Wechatpay-Signaturestring签名串
Wechatpay-Signature-Typestring签名算法
WECHATPAY2-SHA256-RSA2048 枚举值
Wechatpay-Timestampstring时间戳
bodyobject通知的JSON数据结构
idstring通知的唯一ID
create_timestring通知创建的时间
event_typestring通知的类型
TRANSACTION.SUCCESS 枚举值
resource_typestring通知的资源数据类型
summarystring回调摘要
resourceobject通知资源数据
algorithmstring对数据进行加密的加密算法
AEAD_AES_256_GCM 枚举值
associated_datastring数据加密的附加数据
noncestring加密使用的随机串
ciphertextstring加密后的密文数据
original_typestring原始回调类型
sp_mchidstring调用接口提交的商户号
sub_mchidstring调用接口提交的子商户号
transaction_idstring微信支付系统生成的订单号
order_idstring微信分账/回退单号
out_order_nostring分账方系统内部的分账/回退单号
receiverobject分账接收方对象
typestring分账接收方的类型
MERCHANT_ID | PERSONAL_OPENID 枚举值之一
accountstring分账接收方的账号
类型是MERCHANT_ID时,是商户号
类型是PERSONAL_OPENID时,是个人OpenID
amountnumber分账动账金额,单位为分,只能为整数
descriptionstring分账/回退描述
success_timestring订单支付完成时间
appidstring调用接口提交的应用ID
out_trade_nostring调用接口提交的商户服务订单号
descriptionstring商户自定义字段,用户交易账单中对扣费服务的描述。
create_timestring订单成功创建时返回
trade_statestring交易状态:
SUCCESS:支付成功
ACCEPT:已接收,等待扣款
PAY_FAIL:支付失败(其他原因,如银行返回失败)
REFUND:转入退款
trade_state_descriptionstring对当前订单状态的描述和下一步操作的指引。
bank_typestring银行类型,采用字符串类型的银行标识。
attachstring附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用,实际情况下只有支付完成状态才会返回该字段。
user_repaidstring枚举值:
Y:用户已还款
N:用户未还款
注意:使用此字段前需先确认bank_type字段值为BPA以及 trade_state字段值为SUCCESS
trade_scenestring交易场景值,目前支持PARKING:车场停车场景
parking_infoobjecttrade_scene为PARKING时,返回停车场景信息
parking_idstring车主服务为商户分配的入场ID,商户通过入场通知接口获取入场ID
plate_numberstring车牌号,仅包括省份+车牌,不包括特殊字符。
plate_colorstring车牌颜色
BLUE | GREEN | YELLOW | BLACK | WHITE | LIMEGREEN 枚举值之一
start_timestring用户入场时间
end_timestring用户出场时间
parking_namestring所在停车位车场的名称
charging_durationnumber计费的时间长,单位为秒
device_idstring停车场设备ID
payerobject支付者信息,详细说明见下文
openidstring用户在服务商的标识
sub_openidstring用户在子商户的标识
amountobject订单金额信息,详细说明见下文
currencystring符合ISO 4217标准的三位字母代码,目前只支持人民币:CNY
payer_totalnumber用户实际支付金额,单位为分,只能为整数
payer_currencystring用户支付币种
exchange_rateobject汇率信息
typestring汇率类型
rateinteger汇率值
promotion_detailobject[]优惠功能信息,详细说明见下文
coupon_idstring券或者立减优惠ID
namestring优惠名称。
scopestring枚举值
GLOBAL:全场代金券
SINGLE:单品优惠
typestring枚举值:
CASH:充值型代金券
NOCASH:免充值型代金券
activity_idstring在微信商户后台配置的批次ID。
currencystringCNY:人民币,境内商户号仅支持人民币。
sub_appidstring调用接口提交的子商户应用ID
combine_appidstring合单应用程序APPID
combine_mchidstring交易单发起方的商户号
combine_out_trade_nostring商户系统内部对交易单定义的订单号
combine_transaction_idstring微信支付系统生成的订单号
scene_infoobject支付场景信息描述
device_idstring终端设备号(门店号或收银设备ID)
sub_ordersobject商品单列表
mchidstring商品单发起商户号
individual_auth_idstring商品单个人收款方受理授权ID
individual_namestring商品单个人收款方平台昵称
trade_typestring交易类型
trade_statestring交易状态
SUCCESS | REFUND | NOTPAY | CLOSED | REVOKED | USERPAYING | PAYERROR | ACCEPT 枚举值之一
bank_typestring付款银行
attachstring附加数据
amountobject订单金额
total_amountnumber标价金额
payer_amountnumber现金支付金额
currencystring标价币种
payer_currencystring现金支付币种
settlement_ratenumber结算汇率
success_timestring支付完成时间
transaction_idstring微信支付订单号
out_trade_nostring商品单订单号
combine_payer_infoobject支付者信息
openidstring用户标识
  1. 普通支付只有支付成功(trade_state=SUCCESS)才有通知

  2. 服务商支付成功会带上(sp_mchid及sub_mchid)字段

  3. 合单支付成功会带上(combine_mchid)字段

  4. 停车服务(trade_scene)为固定值"PARKING"

  5. 保险商户委托代扣成功通知(trade_type)为"PAP"

  6. 分账动帐通知(original_type)为"profitsharing"

  7. 跨境/全球会返回exchange_rate字典

  8. 平台收付通-个人收款场景会返回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'
]);
应答规范类型描述
statusnumberHTTP状态码
20X 4XX 5XX 枚举值之一
bodyobject应答的JSON数据结构
codestring业务处理状态码
SUCCESS | FAIL 枚举值之一
messagestring业务处理附加信息

参阅 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档

Published on the GitHub by TheNorthMemory