支付中台开放接口文档(详细版)
适用对象:已在本平台开通「开放 API」应用并完成商户绑定的合作方系统。
本文档覆盖 支付、退款、入账方(分账接收方)管理、订单分账、订单分账资金归还、订单分账完结、余额分账 七大类共 13 个接口,以及配套的 签名鉴权、幂等、异步回调通知 机制。
1. 接入概览
1.1 接口清单
| 分类 | 接口 | 方法 | 路径 | 幂等要求 |
|---|---|---|---|---|
| 支付 | 支付下单 | POST | /openapi/v1/payments | 需要 |
| 支付 | 查询支付订单 | GET | /openapi/v1/payments/{outTradeNo} | — |
| 退款 | 申请退款 | POST | /openapi/v1/refunds | 需要 |
| 退款 | 查询退款 | GET | /openapi/v1/refunds/{refundRequestNo} | — |
| 入账方 | 申请入账方 | POST | /openapi/v1/receivers | — |
| 入账方 | 查询入账方申请进度 | GET | /openapi/v1/receivers/{recipientId} | — |
| 订单分账 | 发起订单分账 | POST | /openapi/v1/profit-sharing/orders | 需要 |
| 订单分账 | 查询订单分账结果 | GET | /openapi/v1/profit-sharing/orders/{businessNo} | — |
| 资金归还 | 申请订单分账资金归还 | POST | /openapi/v1/profit-sharing/order-returns | 需要 |
| 资金归还 | 查询资金归还结果 | GET | /openapi/v1/profit-sharing/order-returns/{businessNo} | — |
| 订单分账 | 发起订单分账完结 | POST | /openapi/v1/profit-sharing/order-completions | 需要 |
| 余额分账 | 发起余额分账 | POST | /openapi/v1/profit-sharing/balances | 需要 |
| 余额分账 | 查询余额分账结果 | GET | /openapi/v1/profit-sharing/balances/{businessNo} | — |
1.2 业务流程总览
- 1接入方 → 平台申请入账方,进入「入账方审核 / 签约」
- 2平台 → 接入方审核签约通过后获得
recipientId - 3接入方 → 平台支付下单(
settlementMode=DELAYED) - 4平台 → 接入方支付结果回调
- 5接入方 → 平台发起订单分账 / 余额分账
- 6平台 → 接入方分账结果回调
- 7接入方 → 平台需要时发起退款 / 资金归还
- 8平台 → 接入方退款 / 归还结果回调
1.3 接入准备
- 向平台申请开通开放 API 应用,平台将发放以下凭证:
appId:应用标识;apiSecret:请求签名密钥(≥32 字符);callbackSecret:回调验签密钥(≥32 字符,仅用于校验平台推送,不参与请求签名)。
- 将需要操作的商户商编与应用完成绑定(一个应用可绑定多个商户号)。
- 准备接收异步通知的 HTTPS 地址。
2. 通用约定
| 项目 | 约定 |
|---|---|
| 通信协议 | HTTPS(生产环境) |
| 请求格式 | Content-Type: application/json; charset=UTF-8 |
| 响应格式 | JSON,UTF-8 |
| 金额单位 | 元,Decimal 字符串/数值,最多 2 位小数 |
| 时间格式 | 请求:ISO-8601 本地时间(如 2026-10-12T10:00:00);响应:yyyy-MM-dd HH:mm:ss 或 ISO-8601(回调 occurredAt) |
| 请求体上限 | 1 MB(超出直接拒绝) |
| 单号规则 | outTradeNo / businessNo / refundRequestNo 长度 ≤64;退款请求号仅允许 [A-Za-z0-9_] |
2.1 统一响应包络
所有接口(含鉴权失败)返回统一结构:
{ "code": "00000", "msg": "成功", "data": { } }
| code | 含义 | 典型场景 |
|---|---|---|
00000 | 成功 | 业务受理成功(业务结果仍需看 data.status) |
A0001 | 参数错误 | 字段缺失、格式不合法、金额非法 |
A0002 | 未认证 | 签名不正确、时间戳过期、nonce 重放、应用被禁用 |
A0003 | 无权限 | 操作了未绑定商户、商户间越权 |
B0001 | 业务异常 | 订单状态不允许、重复提交冲突、渠道拒绝 |
C0001 | 系统异常 | 开放接口未启用、防重放服务不可用、渠道异常 |
注意:
code=00000仅代表请求处理成功。支付、退款、分账等异步业务的最终结果以data.status、异步回调或主动查询为准。
3. 请求签名(鉴权)
所有开放接口均需通过 HMAC-SHA256 签名鉴权。签名不通过时返回 HTTP 401 + code=A0002。
3.1 请求头
| 请求头 | 必填 | 格式要求 | 说明 |
|---|---|---|---|
X-App-Id | 是 | [A-Za-z0-9._-]{3,64} | 平台分配的应用标识 |
X-Timestamp | 是 | 10 位 Unix 秒级时间戳 | 与服务器时差不得超过 ±300 秒 |
X-Nonce | 是 | [A-Za-z0-9._-]{16,128} | 一次性随机串;在 TTL 内不可重复,否则判定重放 |
X-Signature | 是 | 64 位十六进制(大小写不敏感) | 按下述算法计算 |
X-Idempotency-Key | 写接口必填 | 非空 | 见 4. 幂等机制 |
3.2 签名算法
第一步:构造规范化串(各段以 \n 连接,共 6 行):
HTTP_METHOD ← 大写,如 POST
requestPath ← 如 /openapi/v1/payments(不含域名、不含 query)
rawQueryString ← 原样 query 串;无则空字符串
timestamp ← X-Timestamp 的值
nonce ← X-Nonce 的值
sha256Hex(body) ← 请求体原文的 SHA-256 十六进制;GET/无 body 时空串的 SHA-256
第二步:计算签名:
signature = hex( HmacSHA256( canonicalString, apiSecret ) ) ← 小写十六进制
第三步:将签名放入 X-Signature 请求头。
3.3 常见鉴权失败原因
| HTTP 状态 | 提示信息 | 原因与处理 |
|---|---|---|
| 503 | 开放接口未启用 | 平台侧未开放,请联系平台 |
| 401 | 开放接口鉴权失败 | 请求头缺失或格式错误;appId 未开通或已被禁用 |
| 401 | 请求时间戳已过期 | 本地时钟与服务器时差超过 ±300 秒,请校准时间后重试 |
| 401 | 开放接口签名不正确 | 规范化串构造有误或 apiSecret 不正确 |
| 401 | 请求已被处理或检测到重放 | X-Nonce 在有效期内被重复使用,请每次请求生成新随机串 |
签名注意事项
rawQueryString必须与实际请求 URL 完全一致(含编码与顺序),建议先拼签名再拼 URL;- 请求体必须以原始字节参与 SHA-256,不可重新序列化后再算;
- GET 请求 body 为空,
sha256Hex("")=e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855;- 服务器时间请使用 NTP 校准,避免时间戳过期。
4. 幂等机制
所有资金类写操作(支付下单、退款、订单分账、资金归还、订单分账完结、余额分账)均支持幂等:
- 写接口必须携带请求头
X-Idempotency-Key(建议取业务单号或其摘要),缺失时请求被拒绝; - 同一幂等键在 24 小时 内重复请求,平台直接返回首次处理结果,不会重复发起资金操作;
- 首次处理失败时幂等键自动释放,修正后可重试;
- 相同幂等键的并发请求会收到「请求处理中」提示,请稍后查询结果。
建议:
X-Idempotency-Key与业务单号保持强关联(例如取outTradeNo/refundRequestNo/businessNo的哈希),同一笔业务重试时不得更换。
5. 支付接口
5.1 支付下单
创建支付订单并提交渠道受理。USER_SCAN(扫码)模式返回收银台链接;其余模式返回渠道唤起凭证。
请求体:
| 字段 | 类型 | 必填 | 长度 | 说明 |
|---|---|---|---|---|
| merchantNo | string | 是 | ≤64 | 商户商编 |
| outTradeNo | string | 是 | ≤64 | 调用方唯一订单号,重试保持不变 |
| amount | decimal | 是 | — | 订单金额(元),必须 >0 |
| paymentMethod | string | 是 | ≤16 | 支付产品:WECHAT / ALIPAY / UNIONPAY |
| paymentMode | string | 是 | ≤32 | 支付方式,见下表 |
| businessScene | string | 否 | ≤32 | 支付场景,默认 OFFLINE |
| authorizationDirectory | string | 否 | ≤512 | 支付授权目录,完整 HTTP/HTTPS 地址 |
| goodsName | string | 是 | ≤128 | 商品名称 |
| userId | string | 条件 | ≤128 | 支付用户标识;MINI_PROGRAM / WECHAT_OFFIACCOUNT / ALIPAY_LIFE / JS_PAY 必填 |
| userIp | string | 是 | ≤64 | 用户真实公网 IP |
| notifyUrl | string | 是 | ≤512 | 支付结果通知地址(HTTPS) |
| settlementNotifyUrl | string | 否 | ≤512 | 延迟结算(清算)结果通知地址;为空沿用 notifyUrl |
| redirectUrl | string | 否 | ≤512 | 支付完成前端跳转地址;仅展示用途,不可作为支付终态依据 |
| memo | string | 否 | ≤85 | 对账备注 |
| paymentOptions | string | 否 | ≤2048 | 渠道扩展信息 JSON,仅特定支付模式需要 |
| terminalId | string | 否 | ≤64 | 线下终端 ID |
| terminalInfo | string | 否 | ≤2048 | 商家终端场景信息 JSON |
| settlementMode | string | 否 | ≤32 | IMMEDIATE(默认,实时结算)/ DELAYED(延迟结算);后续要发起订单分账必须为 DELAYED |
| expireTime | string | 否 | — | 订单过期时间,ISO-8601 本地时间 |
paymentMode 取值:
| 值 | 说明 | 特殊要求 |
|---|---|---|
USER_SCAN | 用户扫商户码(收银台模式) | 下单后返回收银台链接 paymentData,状态 PAYING |
MINI_PROGRAM | 微信小程序支付 | 必填 userId(openid) |
WECHAT_OFFIACCOUNT | 微信公众号支付 | 必填 userId |
ALIPAY_LIFE | 支付宝生活号支付 | 必填 userId |
JS_PAY | JS 支付 | 必填 userId |
SDK_PAY / H5_PAY / DIRECT_PAY / UNCONSCIOUS_PAY | SDK / H5 / 直连 / 免密 | — |
付款码(B 扫 C)等其他产品不在本接口支持范围。
响应 data:
| 字段 | 类型 | 说明 |
|---|---|---|
| outTradeNo | string | 调用方订单号 |
| paymentNo | string | 平台支付流水号 |
| amount | decimal | 订单金额(元) |
| currency | string | 币种 |
| status | string | 支付状态,见 10.1 |
| settlementStatus | string | 结算状态(延迟结算订单关注) |
| settlementAmount | decimal | 已结算金额(元) |
| unSplitAmount | decimal | 未分账余额(元),供分账参考 |
| settlementTime | string | 结算时间 |
| paymentData | string | 渠道唤起凭证:USER_SCAN 为收银台 URL;其余为渠道 prePay 凭证(如 prePayTn) |
| paidAmount | decimal | 实付金额(元) |
| refundAmount | decimal | 累计退款金额(元) |
| paidTime / expireTime | string | 支付时间 / 过期时间 |
| errorCode / errorMessage | string | PAYMENT_FAILED(支付未完成)/ PAYMENT_CLOSED(订单已关闭);无错为 null |
| syncedAt | string | 最近一次渠道状态同步时间 |
示例请求:
POST /openapi/v1/payments HTTP/1.1
Content-Type: application/json; charset=UTF-8
X-App-Id: app-20260901-001
X-Timestamp: 1760233200
X-Nonce: 8f14e45fceea167a5a36dedd4bea2543
X-Signature: 3f7a1c...(64 位 hex)
X-Idempotency-Key: 9a8b7c6d-pay-0001
{
"merchantNo": "10080000001",
"outTradeNo": "ORDER202610120001",
"amount": 1.00,
"paymentMethod": "WECHAT",
"paymentMode": "USER_SCAN",
"goodsName": "会员充值",
"userIp": "61.135.169.121",
"notifyUrl": "https://api.partner.com/callback/payment",
"settlementMode": "DELAYED"
}
示例响应:
{
"code": "00000",
"msg": "成功",
"data": {
"outTradeNo": "ORDER202610120001",
"paymentNo": "TR2026101200000001",
"amount": 1.00,
"currency": "CNY",
"status": "PAYING",
"paymentData": "https://cashier.example.com/pay?merchantNo=10080000001&tn=TR2026101200000001",
"settlementStatus": null,
"errorCode": null,
"errorMessage": null
}
}
5.2 查询支付订单
| 参数 | 位置 | 必填 | 说明 |
|---|---|---|---|
| outTradeNo | path | 是 | 调用方订单号 |
| merchantNo | query | 是 | 商户商编 |
| refresh | query | 否 | 默认 true:主动向支付机构同步最新状态并校准本地结果;false 仅查本地 |
响应结构同 5.1 响应。
6. 退款接口
6.1 申请退款
请求体:
| 字段 | 类型 | 必填 | 长度 | 说明 |
|---|---|---|---|---|
| merchantNo | string | 是 | ≤64 | 商户商编 |
| refundRequestNo | string | 是 | ≤64 | 调用方退款请求号,全局唯一,仅允许 [A-Za-z0-9_],重试保持不变 |
| outTradeNo | string | 是 | ≤64 | 原支付的调用方订单号 |
| amount | decimal | 是 | — | 退款金额(元),>0,累计不超过原订单实付金额 |
| reason | string | 否 | ≤128 | 退款原因 |
| notifyUrl | string | 是 | ≤512 | 退款状态通知地址(HTTPS) |
同步成功仅代表退款请求已受理,最终以
status、回调或查询为准。
响应 data:
| 字段 | 类型 | 说明 |
|---|---|---|
| refundRequestNo | string | 退款请求号 |
| outTradeNo | string | 原订单号 |
| refundAmount | decimal | 申请退款金额(元) |
| actualRefundAmount | decimal | 实际退款金额(元) |
| userReceivedAmount | decimal | 用户实收退款(元) |
| returnedFee | decimal | 退回手续费(元) |
| reason | string | 退款原因 |
| status | string | 退款状态,见 10.3 |
| acceptanceStatus | string | 渠道受理结果:ACCEPTED / REJECTED / UNKNOWN |
| errorCode | string | 见 12.2 退款错误码 |
| errorMessage | string | 错误描述 |
| retryable | boolean | 是否可用原请求号重试 |
| nextAction | string | NONE / QUERY / WAIT_CALLBACK / RETRY_SAME_ID / MANUAL_PROCESS |
| requestedAt / succeededAt | string | 发起 / 成功时间 |
| syncedAt | string | 最近同步时间 |
| createdAt / updatedAt | string | 记录创建 / 更新时间 |
6.2 查询退款
refresh=true(默认)时主动向支付机构同步状态。响应结构同上。
7. 入账方管理接口
入账方(分账接收方)是余额分账的资金接收主体。先申请入账方,审核签约通过后,以 recipientId(入账方申请号)参与余额分账。
7.1 申请入账方
请求体:
| 字段 | 类型 | 必填 | 长度 | 说明 |
|---|---|---|---|---|
| merchantNo | string | 是 | ≤64 | 商户商编(须已绑定) |
| receiverType | string | 是 | ≤32 | TO_PUBLIC_MANAGER(对公)/ TO_PRIVATE_MANAGER(对私) |
| receiverName | string | 是 | ≤128 | 入账方名称:对公=企业名称,对私=个人姓名 |
| licenceNo | string | 是 | ≤64 | 证件号:对公=营业执照号,对私=身份证号 |
| mobile | string | 条件 | ≤32 | 手机号,对私必填 |
| legalName | string | 条件 | ≤64 | 法人姓名,对公必填 |
| legalLicenceNo | string | 条件 | ≤64 | 法人证件号,对公必填 |
| bankCode | string | 是 | ≤32 | 银行总行编码,如 ICBC |
| bankCardNo | string | 是 | ≤64 | 银行账户号 |
| label | string | 否 | ≤64 | 入账方标签/备注 |
| settlementProduct | string | 否 | ≤16 | 结算产品:T1 / D1 / HN(定时结算);不填用商户默认 |
| settlementDate | string | 否 | ≤16 | 定时结算时间 HH:mm,仅 HN 填写,06:00~23:00 整点 |
| cnapsCode | string | 否 | ≤32 | 联行号(大额支付行号) |
响应 data:
| 字段 | 类型 | 说明 |
|---|---|---|
| recipientId | string | 入账方申请号,即后续余额分账接口的 recipientId |
| merchantNo | string | 商户商编 |
| receiverType | string | 入账方类型 |
| receiverName | string | 入账方名称 |
| bankCardMasked | string | 脱敏银行卡号 |
| bankCardLastFour | string | 银行卡后四位 |
| status | string | SUBMITTING / REVIEWING / COMPLETED / FAILED |
| auditOpinion | string | 审核意见 |
| signUrl | string | 签约链接(审核通过后返回) |
7.2 查询入账方申请进度
查询时会先同步渠道最新审核状态再返回。status=COMPLETED 且返回 signUrl 时,入账方完成签约即可用于余额分账。
8. 分账接口
8.1 发起订单分账
对本系统支付接口创建、支付成功(SUCCESS)且未退款的订单发起分账;原收款商户号由平台根据 outTradeNo 自动识别。
⚠️ 前置条件:原订单必须以
settlementMode=DELAYED创建,并已完成清算。实时结算订单发起会收到渠道OPR15035错误(平台已转译为友好提示)。
请求体:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| receiveMerchantNo | string | 是(≤32) | 分账接收商户号(收款方→接收方) |
| businessNo | string | 是(≤64) | 业务幂等号,重试保持不变 |
| outTradeNo | string | 是(≤64) | 原支付的调用方订单号 |
| notifyUrl | string | 是(≤512) | 订单分账结果通知地址(HTTPS) |
| allocationMode | string | 否 | AMOUNT(默认,按金额)/ PERCENTAGE(按比例) |
| allocationBaseAmount | decimal | 条件 | 按比例分账时的分账基准金额(元) |
| releaseRemainingAmount | boolean | 否 | 分账完成后是否释放剩余可分账金额,默认 false |
| details | array | 否 | 接收商户的分账明细,当前固定最多 1 条;不传时自动套用该商户生效中的 API_SPECIFIED 订单分账规则模板 |
details[] 明细:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| amount | decimal | 条件 | AMOUNT 模式必填,分账金额(元),>0 |
| percentage | decimal | 条件 | PERCENTAGE 模式必填,范围 (0,100],多条合计必须=100 |
| description | string | 否 | 分账说明(≤128) |
响应 data:
| 字段 | 类型 | 说明 |
|---|---|---|
| businessNo | string | 业务幂等号 |
| outTradeNo | string | 原订单号 |
| status | string | 分账状态,见 10.4 |
| divideAmount | decimal | 分账总金额(元) |
| divideRule | string | 分账规则标识(PERCENTAGE / AMOUNT) |
| createdAt / completedAt | string | 创建 / 完成时间 |
| errorCode / errorMessage | string | 失败时 PROFIT_SHARING_FAILED /「分账处理失败」 |
| nextAction | string | WAIT_CALLBACK(处理中)/ QUERY(状态未知)/ REVIEW_AND_RETRY(失败复查)/ NONE |
| details[] | array | 明细:recipientId、amount、percentage、receivedAmount(到账)、fee(手续费)、status、errorMessage |
8.2 查询订单分账结果
按业务幂等号查询,平台根据回调订阅记录自动识别分账方商户。响应同 8.1。
8.3 申请订单分账资金归还
将已分出的订单分账资金按金额归还给分账方;平台根据 outTradeNo + 分账幂等号自动关联原分账明细。
请求体:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| outTradeNo | string | 是(≤64) | 原订单号 |
| businessNo | string | 是(≤64) | 本次归还的业务幂等号 |
| amount | decimal | 是 | 归还金额(元),>0 |
| reason | string | 否 | 归还原因(≤128) |
响应 data:businessNo、outTradeNo、status、amount、reason、createdAt、completedAt、errorCode、errorMessage、nextAction。
8.4 查询订单分账资金归还结果
8.5 发起订单分账完结
释放原订单分账后剩余的冻结资金。完结分账使用独立于原分账的请求号,同一订单仅允许完结一次;重复调用时平台幂等回放已受理结果,不会重复完结。
行为说明:
- 前置条件:该订单存在一笔支付成功且分账成功的订单分账,否则报「原订单分账尚未成功,不能发起完结分账」;
- 发起订单分账时若
releaseRemainingAmount=true(分账成功自动解冻剩余金额),则无需再完结; - 订单分账确认成功后,平台默认会自动发起完结(自动解冻的除外);自动完结失败或需要指定说明时,可用本接口显式完结;
- 完结没有独立的异步回调:
PROCESSING/UNKNOWN状态请以相同参数重新调用本接口(幂等回放)或查询订单分账结果确认。
请求体:
| 字段 | 类型 | 必填 | 长度 | 说明 |
|---|---|---|---|---|
| outTradeNo | string | 是 | ≤64 | 原支付的调用方订单号 |
| businessNo | string | 否 | ≤64 | 完结业务幂等号;缺省为 COMPLETE-{outTradeNo} |
| description | string | 否 | ≤128 字节(UTF-8) | 完结分账说明 |
响应 data:
| 字段 | 类型 | 说明 |
|---|---|---|
| businessNo | string | 完结业务幂等号 |
| outTradeNo | string | 原订单号 |
| status | string | PROCESSING / SUCCESS / FAILED / UNKNOWN |
| releasedAmount | decimal | 本次完结释放的剩余冻结金额(元);渠道未返回时为 null |
| createdAt / completedAt | string | 完结申请 / 完成时间 |
| errorCode / errorMessage | string | 失败时 ORDER_COMPLETE_FAILED + 描述 |
| nextAction | string | NONE(已终态)/ QUERY(处理中或未知:重试本接口或查询)/ REVIEW_AND_RETRY(失败复查后重试) |
示例请求:
{ "outTradeNo": "ORDER202610120001", "description": "订单分账完成,释放剩余资金" }
示例响应:
{
"code": "00000",
"msg": "成功",
"data": {
"businessNo": "COMPLETE-ORDER202610120001",
"outTradeNo": "ORDER202610120001",
"status": "SUCCESS",
"releasedAmount": 7.60,
"createdAt": "2026-10-12 10:30:00",
"completedAt": "2026-10-12 10:30:01",
"errorCode": null,
"errorMessage": null,
"nextAction": "NONE"
}
}
8.6 发起余额分账
从分账方易宝资金账户余额中向入账方划拨资金。平台固定使用 accountType=YEEPAY_ACCOUNT、divideVerifyType=STANDARD_DIVIDE、接收方类型 RECEIVER。
请求体:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| merchantNo | string | 是(≤64) | 分账方商户商编 |
| businessNo | string | 是(≤64) | 业务幂等号,重试保持不变 |
| notifyUrl | string | 是(≤512) | 余额分账结果通知地址(HTTPS) |
| details | array | 是(1~30 条) | 分账明细 |
details[] 明细:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| recipientId | string | 是(≤64) | 入账方申请号(7.1 返回值) |
| amount | decimal | 是 | 分账金额(元),>0 |
| description | string | 否 | 分账明细描述(≤32) |
| feeMode | string | 否 | 手续费承担方式:DEDUCT_FROM_AMOUNT(默认,从分账金额内扣)/ CHARGE_SEPARATELY(额外收取) |
| purpose | string | 否 | 分账用途(≤40) |
响应 data:结构同 8.1 响应。
幂等与重试:同一
businessNo重试时若上次状态为PROCESSING/UNKNOWN会直接回放本地结果,不会重复划拨;仅上次FAIL的记录可沿用原号重试。
8.7 查询余额分账结果
9. 异步回调通知
9.1 事件类型
| 业务 | eventType | 触达时机 |
|---|---|---|
| 支付 | payment.status.changed | 支付状态变化(成功/失败/关闭等) |
| 结算 | payment.settlement.changed | 延迟结算订单清算状态变化(仅订阅了 settlementNotifyUrl) |
| 退款 | refund.status.changed | 退款状态变化 |
| 订单分账 | profit-sharing.order.status.changed | 订单分账状态变化 |
| 余额分账 | profit-sharing.balance.status.changed | 余额分账状态变化 |
同一业务状态未变化时不会重复推送;终态事件投递后订阅自动失效。订阅有效期默认 30 天。
9.2 通知报文
POST application/json; charset=UTF-8,body 为下单/发起时登记的 notifyUrl 接收:
{
"eventId": "3f8a2b6c-....-....",
"eventType": "payment.status.changed",
"occurredAt": "2026-10-12T10:05:30.123Z",
"appId": "app-20260901-001",
"merchantNo": "10080000001",
"businessNo": "ORDER202610120001",
"status": "SUCCESS",
"data": { }
}
| 字段 | 说明 |
|---|---|
| eventId | 投递唯一 ID,消费方据此幂等去重 |
| eventType | 事件类型,同请求头 X-Callback-Event-Type |
| occurredAt | 事件发生时间(ISO-8601 UTC) |
| businessNo | 对应业务单号:支付=outTradeNo、退款=refundRequestNo、分账=businessNo |
| status | 最新状态(同 10 状态字典) |
| data | 业务明细(支付/退款/分账的详细字段,与对应查询接口的 data 结构一致或为其子集) |
9.3 回调请求头与验签
| 请求头 | 说明 |
|---|---|
X-Callback-App-Id | 应用标识 |
X-Callback-Timestamp | 秒级时间戳 |
X-Callback-Nonce | 32 位随机串 |
X-Callback-Event-Id | 投递 ID(= body.eventId) |
X-Callback-Event-Type | 事件类型 |
X-Callback-Signature | 回调签名(64 位 hex) |
回调签名验证:
canonical = timestamp + "\n" + nonce + "\n" + eventId + "\n" + sha256Hex(rawBody)
expected = hex( HmacSHA256( canonical, callbackSecret ) )
校验:expected 与 X-Callback-Signature 小写比较一致
请务必用原始报文字节计算 SHA-256,并用
callbackSecret(不是apiSecret)验签。
9.4 应答要求
- 收到回调后请先验签、落库,再应答;
- 处理成功:响应 HTTP 2xx 且响应 body 恰为文本
SUCCESS; - 其余情况(非 2xx 或 body ≠
SUCCESS)平台视为失败进入重试; - 请保证接口幂等:同一
eventId可能被重复投递。
9.5 重试策略
| 属性 | 值 |
|---|---|
| 重试间隔 | 30 秒 → 2 分钟 → 10 分钟 → 30 分钟 → 2 小时 → 6 小时 → 24 小时 |
| 最大尝试次数 | 8 次(含首次),超过后停止重试,请联系平台人工处理 |
| 超时 | 连接 5 秒 / 读取 10 秒 |
9.6 注意事项
notifyUrl必须为 HTTPS,不得携带用户信息与#fragment;- 同一
businessNo的回调地址与商户绑定关系创建后不可变更(重复发起时保持一致即可); - 回调可能乱序:请以查询接口
refresh=true的结果为准做最终校准; - 若长时间未收到回调,主动调用对应查询接口兜底。
10. 状态字典
10.1 支付状态
| 状态 | 说明 | 终态 |
|---|---|---|
PAYING | 支付中(含 USER_SCAN 待扫码) | 否 |
SUCCESS | 支付成功 | 是 |
FAILED | 支付失败 | 是 |
CLOSED | 订单关闭(超时未付/关单) | 是 |
REFUNDED | 已全额退款 | 是 |
10.2 结算状态
延迟结算(settlementMode=DELAYED)订单的清算状态,随 payment.settlement.changed 回调或查询接口的 settlementStatus / settlementAmount / unSplitAmount / settlementTime 体现;清算完成是发起订单分账的前提。
10.3 退款状态
| 状态 | 说明 | nextAction |
|---|---|---|
PROCESSING | 退款处理中 | WAIT_CALLBACK |
SUCCESS | 退款成功 | NONE |
FAILED | 退款失败;retryable=true 时可用原请求号重试 | RETRY_SAME_ID / NONE |
ACTION_REQUIRED | 需人工介入(原 SUSPEND) | MANUAL_PROCESS |
CLOSED | 退款关闭(原 CANCEL) | NONE |
UNKNOWN | 状态未知 | QUERY |
10.4 分账状态(订单/余额通用)
| 状态 | 说明 | nextAction |
|---|---|---|
CREATED | 已创建待提交 | WAIT_CALLBACK |
PROCESSING | 渠道处理中 | WAIT_CALLBACK |
SUCCESS | 分账成功 | NONE |
FAILED | 分账失败 | REVIEW_AND_RETRY(FAIL 记录可沿用原 businessNo 重试) |
UNKNOWN | 状态未知 | QUERY |
details[].status 为单笔明细状态,枚举同上;整单 status 存在任一明细 PROCESSING 即为 PROCESSING,全部 SUCCESS 才为 SUCCESS。
10.5 入账方状态
| 状态 | 说明 |
|---|---|
SUBMITTING | 提交渠道中 |
REVIEWING | 渠道审核中 |
COMPLETED | 完成(签约通过后可用于余额分账) |
FAILED | 申请失败,见 auditOpinion |
11. 签名与调用示例
11.1 Node.js 签名与调用
const crypto = require('crypto')
const BASE_URL = 'https://api.your-platform.com'
const APP_ID = 'app-20260901-001'
const API_SECRET = 'your-api-secret-at-least-32-chars'
function sign(method, path, query, bodyStr) {
const timestamp = String(Math.floor(Date.now() / 1000))
const nonce = crypto.randomBytes(16).toString('hex') // 32 位
const bodySha256 = crypto.createHash('sha256').update(bodyStr, 'utf8').digest('hex')
const canonical = [method.toUpperCase(), path, query, timestamp, nonce, bodySha256].join('\n')
const signature = crypto.createHmac('sha256', API_SECRET).update(canonical, 'utf8').digest('hex')
return { timestamp, nonce, signature }
}
async function createPayment() {
const path = '/openapi/v1/payments'
const body = JSON.stringify({
merchantNo: '10080000001',
outTradeNo: 'ORDER202610120002',
amount: 1.00,
paymentMethod: 'WECHAT',
paymentMode: 'USER_SCAN',
goodsName: '会员充值',
userIp: '61.135.169.121',
notifyUrl: 'https://api.partner.com/callback/payment',
settlementMode: 'DELAYED'
})
const { timestamp, nonce, signature } = sign('POST', path, '', body)
const resp = await fetch(BASE_URL + path, {
method: 'POST',
headers: {
'Content-Type': 'application/json; charset=UTF-8',
'X-App-Id': APP_ID,
'X-Timestamp': timestamp,
'X-Nonce': nonce,
'X-Signature': signature,
'X-Idempotency-Key': 'idem-' + 'ORDER202610120002'
},
body
})
console.log(await resp.json())
}
11.2 Java 签名片段
String canonical = String.join("\n",
"POST", path, rawQuery, timestamp, nonce, sha256Hex(bodyBytes));
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(apiSecret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
String signature = HexFormat.of().formatHex(mac.doFinal(canonical.getBytes(StandardCharsets.UTF_8)));
11.3 回调验签示例(Node.js)
app.post('/callback/payment', express.raw({ type: '*/*' }), (req, res) => {
const timestamp = req.header('X-Callback-Timestamp')
const nonce = req.header('X-Callback-Nonce')
const eventId = req.header('X-Callback-Event-Id')
const signature = String(req.header('X-Callback-Signature') || '').toLowerCase()
const bodySha256 = crypto.createHash('sha256').update(req.body).digest('hex')
const canonical = [timestamp, nonce, eventId, bodySha256].join('\n')
const expected = crypto.createHmac('sha256', CALLBACK_SECRET)
.update(canonical, 'utf8').digest('hex')
if (expected !== signature) return res.status(401).end()
const event = JSON.parse(req.body.toString('utf8'))
// 1. 按 event.eventId 幂等落库 2. 业务处理
res.status(200).send('SUCCESS') // 固定应答文本
})
12. 错误码与常见问题
12.1 平台错误码
见 2.1 统一响应包络。鉴权类失败为 HTTP 401 + A0002,开关类失败为 HTTP 503 + C0001。
12.2 退款错误码映射
| errorCode | 说明 | retryable |
|---|---|---|
INVALID_REFUND_AMOUNT | 退款金额不符合要求(余额不足/超可退金额等) | 否 |
PAYMENT_NOT_FOUND | 原支付订单不存在 | 否 |
PAYMENT_STATUS_INVALID | 原支付订单状态不允许退款 | 否 |
TOO_MANY_REQUESTS | 渠道限频,请稍后查询退款状态 | 是 |
INSUFFICIENT_FUNDS | 退款资金不足 | 否 |
REFUND_FAILED | 其他退款处理失败 | 视渠道返回 |
12.3 常见问题(FAQ)
- Q1:发起订单分账报「原支付订单的结算方式不支持订单分账」?
- 订单创建时
settlementMode为IMMEDIATE。订单分账仅支持延迟结算订单:请使用settlementMode=DELAYED创建新订单,支付成功并完成清算后再申请。 - Q2:
code=00000但业务没成功? 00000表示接口调用成功。支付/退款/分账为异步业务,请以data.status、回调通知或查询接口为准。- Q3:签名一直失败?
- 按顺序排查:① 时间戳与服务器时差 ≤300 秒;② query 串与实际 URL 完全一致;③ body 用原始字节算 SHA-256;④
apiSecret正确且未泄;⑤ 签名头是 64 位十六进制。 - Q4:返回「请求已被处理或检测到重放」?
X-Nonce在 TTL 内重复。每次请求生成新的随机 nonce;若为业务重试请更换X-Idempotency-Key场景或先查询原单状态。- Q5:回调收不到 / 收到但状态没更新?
- ① 确认 notifyUrl 公网可达且为 HTTPS;② 应答必须是 2xx + body
SUCCESS;③ 业务状态未变化时平台不会重复推送;④ 长时间未收到可主动调用查询接口(refresh=true)兜底。 - Q6:余额分账 recipientId 从哪里来?
- 先调用「申请入账方」接口,审核签约完成(
status=COMPLETED)后返回的recipientId即可用于余额分账明细。 - Q7:同一笔余额分账想重试?
- 仅当上次状态为
FAIL时,可沿用原businessNo重新发起;PROCESSING/UNKNOWN请先查询,SUCCESS会直接回放原结果,不会重复划拨。