JSAPI支付通知
适用范围说明
本支付通知接口仅适用于通过 JSAPI 接口调起支付的场景。当商户通过 JSAPI 支付接口发起支付请求并支付完成后,平台会通过本通知接口将支付结果和相关用户信息推送给商户系统。
其他支付方式(如付款码支付、扫码支付等)的通知机制可能不同,请参考对应的文档说明。
与统一交易通知的关系
- 如果在 JSAPI 支付请求中传递了
notify_url参数,支付完成后仅通知该 URL,不会再通知统一交易通知配置的default_notify_url - 如果未传递
notify_url参数,支付完成后将通知统一交易通知配置的default_notify_url - 通知优先级:
notify_url(订单级)>default_notify_url(全局级)
支付通知API
通知结果参数列表 通知URL 是请求接口中提交的参数 notify_url,支付完成后,平台会把相关支付和用户信息发送到该URL,商户需要接收处理信息。
对后台通知交互时,如果平台收到商户的应答不是纯字符串success或超过5秒后返回时,平台认为通知失败,平台会通过一定的策略(通知频率为0/15/15/30/180/1800/1800/1800/1800/3600,单位:秒)间接性重新发起通知,尽可能提高通知的成功率,但不保证通知最终能成功。 由于存在重新发送后台通知的情况, 因此同样的通知可能会多次发送给商户系统。商户系统必须能够正确处理重复的通知。 推荐的做法是, 当收到通知进行处理时, 首先检查对应业务数据的状态, 判断该通知是否已经处理过, 如果没有处理过再进行处理, 如果处理过直接返回结果成功。 在对业务数据进行状态检查和处理之前, 要采用数据锁进行并发控制, 以避免函数重入造成的数据混乱。 特别注意:商户后台接收到通知参数后,要对接收到通知参数里的订单号out_trade_sn和订单金额total_amount和自身业务系统的订单和金额做校验,校验一致后才更新数据库订单状态 后台通知通过请求中的notify_url进行, post方式给商户系统(通知参数内容为json格式的字符串)
通知参数
| 参数 | 必填 | 类型 | 说明 | 示例值 |
|---|---|---|---|---|
| code | 是 | String(32) | 支付结果 000000 FAIL | SUCCESS |
| out_trade_sn | 是 | String(32) | 商户自定义的交易订单号 | 202401010001 |
| sub_merchant_id | 是 | String(32) | 子商户的Id | 1234567890 |
| platform_trade_no | 是 | String(32) | 平台的交易号 | 123 |
| transaction_id | 是 | String(32) | 微信或者支付宝支付单号 | 420000202401010001 |
| sub_merchant_id | 是 | String(32) | 子商户的Id | 1234567890 |
| sub_appid | 是 | String(32) | 小程序的APPID | |
| sub_openid | 否 | String(32) | openid和sub_openid可以选传其中之一,如果传了sub_appid则sub_openid必传 | oABC1234567890 |
| total_amount | 是 | String(32) | 订单总金额 单位为分 | 100 |
| pay_time | 否 | String(32) | 支付时间 yyyyMMddHHmmss | 20260502012256 |
| trade_type | 是 | String(32) | 交易类型 JSAPI 公众号支付 MINIAPP公众号支付 NATIVE 扫码支付 APP APP支付 | |
| nonce_str | 是 | String(32) | 随机字符串 | |
| fee_type | 否 | String(32) | 支付货币类型 CNY | CNY |
| cash_fee | 否 | String(32) | 使用零钱金额 | 1 |
| attach | 否 | String(32) | 附加信息 | 商户附加信息,可做扩展参数 |
| sign_type | 否 | String(32) | 签名类型 | RSA2 |
| sign | 否 | String(32) | 签名 | 使用平台提供的公钥进行签名验证 |
通知示例
{
"code": "000000",
"out_trade_sn": "202401010001",
"platform_trade_no": "P202401010001",
"transaction_id": "420000202401010001",
"sub_merchant_id": "1234567890",
"sub_appid": "wx1234567890",
"sub_openid": "oABC1234567890",
"total_amount": "100",
"pay_time": "20260502012256",
"trade_type": "JSAPI",
"nonce_str": "5K8264ILTKCH16CQ2502SI8ZNMTM67VS",
"fee_type": "CNY",
"cash_fee": "100",
"attach": "商户附加信息",
"sign_type": "RSA2",
"sign": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A..."
}
通知响应
商户接收到通知后,需要返回纯字符串 success 表示接收成功,否则平台会认为通知失败并重新发送。
成功响应
success
失败响应
fail
安全注意事项
1. 签名验证(必须)
所有通知都必须进行签名验证,以确保数据来自平台且未被篡改:
- 从通知参数中获取
sign_type和sign字段 - 将除
sign外的所有参数按字母顺序排序 - 使用平台提供的公钥验证签名
- 签名验证失败的通知必须拒绝处理
2. 金额校验(必须)
必须校验通知金额与业务订单金额是否一致:
- 提取通知中的
out_trade_sn和total_amount - 从业务数据库查询对应订单的原始金额
- 金额不一致时拒绝处理并记录异常
- 金额单位为分,注意单位转换
3. 重复通知处理
平台可能会多次发送同一通知,必须实现幂等处理:
# 伪代码示例
def handle_notify(notify_data):
# 1. 验证签名
if not verify_sign(notify_data):
return "fail"
# 2. 查询订单状态
order = get_order(notify_data['out_trade_sn'])
# 3. 检查是否已处理
if order.status == 'PAID':
return "success" # 已处理,直接返回成功
# 4. 校验金额
if order.amount != notify_data['total_amount']:
log_error("金额不一致")
return "fail"
# 5. 使用数据库事务处理(防并发)
with transaction:
order = get_order_for_update(notify_data['out_trade_sn'])
if order.status == 'PAID':
return "success" # 双重检查
# 6. 更新订单状态
order.status = 'PAID'
order.transaction_id = notify_data['transaction_id']
order.pay_time = notify_data['pay_time']
order.save()
return "success"
4. 并发控制
必须使用数据库锁或分布式锁防止并发处理同一通知:
- 推荐使用数据库行锁(
SELECT ... FOR UPDATE) - 或使用 Redis 分布式锁(锁 key 为
out_trade_sn) - 锁超时时间建议设置为 10-30 秒
5. 响应要求
- 收到通知后必须尽快响应(建议 2 秒内)
- 响应必须是纯字符串
success或fail - 不要返回 JSON 或 HTML,否则平台会认为通知失败
- 业务处理可以异步进行,但响应必须同步返回