API接口文档
首页
支付业务
首页
支付业务
  • 说明

    • 概述
    • 接口说明
  • JSAPI支付

    • 二维码支付
    • JSAPI支付
    • JSAPI调起支付
    • JSAPI支付通知
    • 付款码支付
  • 交易管理

    • 交易查询
    • 申请退款
    • 退款查询
    • 统一交易通知
  • 商户进件
    • 进件概述
    • 图片上传
    • 进件申请
    • 进件状态查询
    • 进件信息修改
    • 认证状态查询
    • MCC行业编码
    • 创建自定义表单定义
    • 申请固定二维码

统一交易通知

统一交易通知 default_notify_url

通知结果参数列表 通知URL 是默认配置的全局通知URL,交易完成时平台会把相关交易和表单信息发送到该URL,需要接收处理信息。

通知优先级:notify_url(订单级)> default_notify_url(全局级)

  • 如果订单请求中传递了 notify_url,则仅通知该URL,不会通知 default_notify_url
  • 如果订单请求中未传递 notify_url,则通知 default_notify_url

对后台通知交互时,如果平台收到服务商的应答不是纯字符串success或超过5秒后返回时,平台认为通知失败,平台会通过一定的策略(通知频率为0/15/15/30/180/1800/1800/1800/1800/3600,单位:秒)间歇性重新发起通知,尽可能提高通知的成功率,但不保证通知最终能成功。 由于存在重新发送后台通知的情况, 因此同样的通知可能会多次发送给服务商系统。服务商系统必须能够正确处理重复的通知。 推荐的做法是, 当收到通知进行处理时, 首先检查对应业务数据的状态, 判断该通知是否已经处理过, 如果没有处理过再进行处理, 如果处理过直接返回结果成功。 在对业务数据进行状态检查和处理之前, 要采用数据锁进行并发控制, 以避免函数重入造成的数据混乱。 特别注意:服务商后台接收到通知参数后,要对接收到通知参数里的订单号pay_trade_no和订单金额total_amount和自身业务系统的订单和金额做校验,校验一致后才更新数据库订单状态 后台通知通过请求中的notify_url进行, post方式给服务商系统(通知参数内容为json格式的字符串)

通知参数

参数必填类型说明示例值
code是String(32)交易结果 000000 表示成功000000
pay_trade_no是String(32)支付凭证中的商户单号202401010001
transaction_id是String(32)支付凭证中交易单号420000202401010001
merchant_no是String(32)商户号1234567890
payment_method是String(32)支付方式 WECHAT-微信 ALIPAY-支付宝 UNIONPAY-云闪付WECHAT
openid否String(32)微信用户openid 或者支付宝buyer_idoABC1234567890
trade_state是String(32)交易状态 SUCCESS-支付成功 REFUND-转入退款 NOTPAY-未支付 CLOSED-已关闭SUCCESS
total_amount是String(32)订单总金额 单位为分100
pay_time否String(32)支付时间 yyyyMMddHHmmss20260502012256
remark否String(256)备注信息交易备注
form_info否Object表单信息 {"name":"姓名", "mobile":"手机号"}{"name":"张三", "mobile":"13800138000"}
nonce_str是String(32)随机字符串5K8264ILTKCH16CQ2502SI8ZNMTM67VS
timestamp是String(32)时间戳 Unix时间戳(秒)1704067200
sign_type否String(32)签名类型RSA2
sign否String(512)签名使用平台提供的公钥进行签名验证

通知示例

支付成功通知(未启用自定义表单)

{
  "code": "000000",
  "pay_trade_no": "202401010001",
  "transaction_id": "420000202401010001",
  "merchant_no": "1234567890",
  "payment_method": "WECHAT",
  "openid": "oABC1234567890",
  "trade_state": "SUCCESS",
  "total_amount": "100",
  "pay_time": "20260502012256",
  "remark": "交易备注",
  "nonce_str": "5K8264ILTKCH16CQ2502SI8ZNMTM67VS",
  "timestamp": "1704067200",
  "sign_type": "RSA2",
  "sign": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A..."
}

支付成功通知(启用自定义表单)

{
  "code": "000000",
  "pay_trade_no": "202401010002",
  "transaction_id": "420000202401010002",
  "merchant_no": "1234567890",
  "payment_method": "ALIPAY",
  "openid": "20881021234567890",
  "trade_state": "SUCCESS",
  "total_amount": "200",
  "pay_time": "20260502013045",
  "form_info": {"name":"张三", "mobile":"13800138000", "project":"项目A"},
  "remark": "扫码支付",
  "nonce_str": "7N9375JLMEID27DR3613TJ9ZOMUN78WT",
  "timestamp": "1704067800",
  "sign_type": "RSA2",
  "sign": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8B..."
}

说明:

  • form_info 字段仅在启用自定义表单时返回,包含用户扫码时填写的信息
  • form_info 为 JSON 对象,字段由商户在申请二维码时自定义

通知响应

服务商接收到通知后,需要返回纯字符串 success 表示接收成功,否则平台会认为通知失败并重新发送。

成功响应

success

失败响应

fail

安全注意事项

1. 签名验证(必须)

所有通知都必须进行签名验证,以确保数据来自平台且未被篡改:

  1. 从通知参数中获取 sign_type 和 sign 字段
  2. 将除 sign 外的所有参数按字母顺序排序
  3. 使用平台提供的公钥验证签名
  4. 签名验证失败的通知必须拒绝处理

2. 金额校验(必须)

必须校验通知金额与业务订单金额是否一致:

  1. 提取通知中的 pay_trade_no 和 total_amount
  2. 从业务数据库查询对应订单的原始金额
  3. 金额不一致时拒绝处理并记录异常
  4. 金额单位为分,注意单位转换

3. 重复通知处理

平台可能会多次发送同一通知,必须实现幂等处理:

# 伪代码示例
def handle_notify(notify_data):
    # 1. 验证签名
    if not verify_sign(notify_data):
        return "fail"
    
    # 2. 查询订单状态
    order = get_order(notify_data['pay_trade_no'])
    
    # 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['pay_trade_no'])
        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 为 pay_trade_no)
  • 锁超时时间建议设置为 10-30 秒

5. 响应要求

  • 收到通知后必须尽快响应(建议 2 秒内)
  • 响应必须是纯字符串 success 或 fail
  • 不要返回 JSON 或 HTML,否则平台会认为通知失败
  • 业务处理可以异步进行,但响应必须同步返回

最佳实践

1. 日志记录

记录所有通知的完整信息,便于问题排查:

log.info(f"收到通知: {notify_data}")
log.info(f"订单号: {pay_trade_no}, 金额: {total_amount}")
log.info(f"签名验证: {'成功' if sign_valid else '失败'}")
log.info(f"处理结果: {result}")

2. 异常处理

  • 通知处理失败时返回 fail,平台会重新发送
  • 记录详细的错误信息到日志
  • 设置告警机制,监控通知处理失败率

3. 自定义表单数据处理

如果启用了自定义表单(enable_custom_form=true):

  1. form_info 中的字段由商户自定义,可能是用户信息、项目名称等
  2. 根据业务需要存储 form_info 数据
  3. 建议将 form_info 存储为 JSON 格式,便于后续查询

4. 测试建议

  • 上线前使用沙箱环境充分测试
  • 测试各种场景:支付成功、失败、重复通知、金额不一致等
  • 验证签名验证逻辑的正确性
  • 测试并发场景下的幂等性

5. 常见问题

Q: 为什么收到重复通知?
A: 如果响应超时或未返回 success,平台会重试。检查响应逻辑是否正确。

Q: 签名验证失败怎么办?
A: 确认使用了正确的公钥,参数排序是否正确,不要遗漏任何参数。

Q: 如何处理异步业务逻辑?
A: 先快速返回 success,然后将业务处理放入消息队列异步执行。

Prev
退款查询