代付接入
用开放 API 把 PKR 可结算余额付给第三方收款人:开通条件、资金规则、创建与查询、收款信息规则、幂等与重试、状态与失败码、Webhook、测试与上线检查。
最后更新于 2026-11-02 · 契约 v1.0.0
代付是什么
代付是按你的指令,把某币种的可结算余额付给你指定的第三方收款人。它和打款是两回事:
| 项目 | 代付(本页) | 打款 |
|---|---|---|
| 谁发起 | 你:开放 API,或商户后台单笔、批量 | 平台:按结算周期生成结算单,审核后执行 |
| 钱付给谁 | 你指定的第三方收款人:EasyPaisa、JazzCash 钱包或巴基斯坦银行账户 | 你自己登记的收款账户 |
| 资金来源 | 该币种的可结算余额 | 结算单占用的可结算余额 |
| 对象与接口 | Disbursement(dsb_),POST / GET /v1/disbursements | Settlement、Payout(po_),只读查询 |
代付不经人工审核,通过受理检查就提交。代付也不是提现:平台不提供把任意金额转到你自己账户的自助提现,你自己的资金只能经结算和打款到账。首期只支持 PKR;不支持跨币种代付;平台不保存收款人,每笔都提交完整收款信息;代付没有撤销。
开通与准备
| 准备项 | 由谁完成 | 没准备好时 |
|---|---|---|
| 代付能力:测试、生产分别开通,默认关闭 | 平台(联系你的平台对接人) | 403 capability_disabled |
| PKR 代付通道与代付限额(单笔下限、单笔上限、日累计金额、日笔数) | 平台 | 422 currency_not_supported |
| 代付费率(比例 + 固定费),写在你的费率版本里 | 平台按合同配置 | 422 currency_not_supported;配好后每笔手续费见响应 fee |
| 足够的 PKR 可结算余额 | 来自你的 PKR 代收,过了锁定周期后转入(见资金规则) | 422 insufficient_balance |
API Key:发起代付用带 disbursements:write 的 Key,只查询用 disbursements:read;查可结算和流水另要 finance:read | 所有者或管理员创建(生产 Key 需二次认证);开发者只能在自己的“可授予 API 范围”内创建,默认不含代付范围 | 403 forbidden |
Webhook:测试、生产端点分别订阅 4 个 disbursement.* 事件 | 你自己配置 | 收不到结果通知,只能靠查询 |
- 管理 API Key:商户后台 API Key(在新标签页打开商户后台)(测试环境;需要所有者、管理员或开发者)(开发者需已被授予代付范围)。
- 订阅代付事件:配置 Webhook(在新标签页打开商户后台)(测试环境;需要所有者、管理员或开发者)。
资金规则
| 时点 | 可结算(available) | 代付占用(disbursement_pending) | 说明 |
|---|---|---|---|
| 受理(201) | 减 金额 + 手续费 | 加 金额 + 手续费 | 不够时 422 insufficient_balance,error.details.shortfall 是差额,不建单 |
| 成功 | 实付少于金额时,加 金额 − paid_amount | 减 金额 + 手续费 | 正式扣减;手续费照收 |
| 失败 | 加 金额 + 手续费 | 减 金额 + 手续费 | 手续费不收 |
| 冲正 | 加 paid_amount + 手续费 | — | 收款人没有收到钱;原成功记录保留 |
- 算例:可结算 PKR 1,000.00,发起金额 300.00、手续费 6.00 的代付。受理后可结算 694.00、代付占用 306.00;成功后代付占用归零;如果失败,可结算回到 1,000.00。
- 退回可结算的钱(失败、冲正、少付差额)立即可用,不再等锁定周期。
- 查询:
GET /v1/balances的available与disbursement_pending;GET /v1/balance-transactions中代付流水的source_type为disbursement,记账类型有disbursement_reserved(冻结)、disbursement_succeeded(成功扣减,含少付差额退回)、reservation_released(失败退回)、disbursement_reversed(冲正退回)。两个接口都需要finance:read。
说明:可结算从哪里来
代收的资金先进待结算,满足结算条款的 T+N 天,再满足最短锁定小时数(从支付成功时刻算),两项都满足才转入可结算。
创建代付
POST/v1/disbursements,必须带 Idempotency-Key,scope disbursements:write。请求里没有商户号、手续费或通道字段:商户与环境只由 API Key 决定,手续费由平台计算。
| 字段 | 类型 | 必填 | 规则 |
|---|---|---|---|
merchant_disbursement_no | string | 是 | 商户代付单号:1–64 个字母、数字、点、下划线、冒号或横线;同一商户同一环境内唯一,区分大小写;发起前生成并保存 |
amount | integer | 是 | 最小货币单位。PKR 必须是 100 的倍数:30000(300.00)可以,30050 不行 |
currency | string | 是 | 大写三字母;首期只有 PKR |
method | string | 是 | easypaisa、jazzcash、bank |
payee.account | string | 是 | 收款账号,规则见收款信息规则;响应只返回后 4 位 |
payee.name | string | 是 | 收款人户名,2–64 个字符,必须与账户真实户名一致 |
payee.bank_code | string | method=bank 时必填 | 银行代码;其他方式不能传 |
customer_ip | string | 否 | 收款人(你的用户)在你的系统里提交这笔付款申请时的 IP,IPv4 或 IPv6,≤ 45 字符;按受信代理链解析,不用你服务器的 IP;用于风控检查 |
description | string | 否 | ≤ 256 字符,供你自己对账 |
metadata | object | 否 | 同 Payment:最多 20 个键,键 ≤ 40 字符,值为 ≤ 200 字符的字符串 |
完整字段与约束见 API 参考:创建代付。占位符 <TEST_SERVER_API_KEY> 换成带 disbursements:write 的测试 Key;示例里的手续费只作演示,以你的费率为准。
curl https://api.fluxan.example/v1/disbursements \
-H "Authorization: Bearer <TEST_SERVER_API_KEY>" \
-H "Idempotency-Key: disbursement.create:DSB-DEMO-001:1" \
-H "Content-Type: application/json" \
-d '{
"merchant_disbursement_no": "DSB-DEMO-001",
"amount": 30000,
"currency": "PKR",
"method": "easypaisa",
"payee": { "account": "03001234567", "name": "Test Payee" },
"customer_ip": "203.0.113.10",
"description": "示例代付",
"metadata": { "customer_ref": "CUSTOMER-DEMO-001" }
}'需要替换的占位符
<TEST_SERVER_API_KEY>:测试环境服务端 API Key:在商户后台「API Key」页面创建
201 响应(同键重放返回 200 和同一代付的最新状态):
{
"id": "dsb_demo_001",
"version": 1,
"merchant_disbursement_no": "DSB-DEMO-001",
"environment": "test",
"amount": 30000,
"currency": "PKR",
"fee": 600,
"method": "easypaisa",
"payee": {
"account_last4": "4567",
"name": "Test Payee",
"bank_code": null,
"bank_name": null,
"utr": null
},
"customer_ip": "203.0.113.10",
"status": "processing",
"resolution_status": "known",
"failure": null,
"paid_amount": null,
"succeeded_at": null,
"failed_at": null,
"reversed_at": null,
"description": "示例代付",
"metadata": { "customer_ref": "CUSTOMER-DEMO-001" },
"source": "api",
"batch_id": null,
"created_at": "2026-10-29T08:00:00Z",
"updated_at": "2026-10-29T08:00:00Z",
"request_id": "req_demo_101"
}注意:HTTP 2xx 只代表受理
HTTP 2xx 只代表平台受理了这笔代付,钱是否付出要看 status 或经验签的 disbursement.* 事件。
银行账户代付的请求体差异:"method": "bank","payee": { "account": "012345678901", "name": "Test Payee", "bank_code": "MEEZAN" }。
Disbursement 对象
| 字段 | 说明 |
|---|---|
id、environment | 代付号(dsb_ 前缀);环境 test / live |
version | 对象版本,每次有效变更递增;处理事件时比较它,不按状态名排序 |
merchant_disbursement_no、amount、currency、method、payee.name、payee.bank_code、customer_ip、description、metadata | 与请求一致;不是 bank 时 bank_code 为 null;商户后台发起的代付 customer_ip 为 null |
fee | 代付手续费,受理时按当时的费率版本计算并冻结,之后调整费率不影响这笔 |
payee.account_last4、payee.bank_name、payee.utr | 收款账号后 4 位(完整账号不出现在任何响应和事件里);收款银行名称,由代付通道回传,可能为 null;收款方钱包或银行的流水号,成功后才有,收款人凭它向钱包或银行查账 |
status、resolution_status | 含义见状态与结果 |
failure | status=failed 时为 { code, message },否则为 null |
paid_amount | 实付金额,成功后才有;可能小于 amount,差额已退回可结算 |
succeeded_at、failed_at、reversed_at、created_at、updated_at | 进入对应状态的时间、受理时间、最近一次变更时间;冲正后 succeeded_at、paid_amount、payee.utr 保留 |
source、batch_id | 来源:api 开放 API、portal 商户后台单笔、batch 商户后台批量;batch_id 是批次号(dsbb_ 前缀),只有批量的才有 |
逐字段的类型与可空性见 API 参考:Disbursement 对象。
查询代付
GET/v1/disbursements/{disbursement_id} 按代付号查询,GET/v1/disbursements 按条件列出,都需要 disbursements:read。
# 按代付号查询(scope disbursements:read)
curl https://api.fluxan.example/v1/disbursements/dsb_demo_001 \
-H "Authorization: Bearer <TEST_SERVER_API_KEY>"
# 按商户代付单号查询:超时后确认这一单是否已受理
curl "https://api.fluxan.example/v1/disbursements?merchant_disbursement_no=DSB-DEMO-001" \
-H "Authorization: Bearer <TEST_SERVER_API_KEY>"
# 对账:某天仍在处理中的代付
curl "https://api.fluxan.example/v1/disbursements?status=processing¤cy=PKR&created_gte=2026-10-29T00:00:00Z&created_lt=2026-10-30T00:00:00Z&limit=100" \
-H "Authorization: Bearer <TEST_SERVER_API_KEY>"需要替换的占位符
<TEST_SERVER_API_KEY>:测试环境服务端 API Key:在商户后台「API Key」页面创建
- 列表筛选:
status、currency、created_gte(含)、created_lt(不含)、merchant_disbursement_no(精确匹配、区分大小写)、batch_id;limit默认 20、最大 100;cursor用上一页的next_cursor;按创建时间倒序,同一时间按id倒序(同分页)。返回data、has_more、next_cursor、request_id。 - 只返回当前商户、当前环境的代付;其他商户或其他环境的代付号返回 404,不透露是否存在。受理时被拒的请求没有代付记录,查不到。
- 商户后台单笔、批量发起的代付也能查到(看
source):查看代付记录(在新标签页打开商户后台)(测试环境;需要所有者、管理员、财务或开发者)。
收款信息规则
| 方式 | payee.account 规则 | 示例 |
|---|---|---|
easypaisa、jazzcash | 03 开头的 11 位数字;开放 API 不做任何规整 | 03001234567 |
bank(本地账号) | 去掉空格、转大写后为 9–24 位字母和数字 | 012345678901 |
bank(IBAN) | 以 PK 开头时按 IBAN 校验:24 位,mod 97 校验位正确 | PK29HABB0001234567890123 |
- 不通过时返回 400
invalid_request,field为payee.account、payee.name或payee.bank_code。 - 钱包号由你的系统先规整再提交:去掉空格和横线;
+92或92开头加 10 位数字的,换成0加 10 位;10 位且以3开头的(表格软件吃掉了开头的 0),前面补0。商户后台的表单与批量文件会自动规整,开放 API 不会。 payee.name不能含 13–19 位连续数字(卡号样式)。户名与账户真实户名不一致时,受理成功,之后代付失败(payee_account_invalid)。平台不对收款账号做卡号(Luhn)检查;格式正确但账号不存在的,同样受理后失败。- 完整收款账号只在创建请求里出现,之后 API 响应、事件、商户后台和导出都只显示后 4 位;你的日志、错误上报、工单里也不要记录完整账号。
警告:代付付出后不能撤回
平台按你提交的账号付款,账号写错而户名恰好对上时,钱会进入那个账户。提交前核对账号与户名。
银行代码
method=bank 时 payee.bank_code 取下表的代码(取自契约,区分大小写):
| 代码 | 银行名称 |
|---|---|
| HBL | Habib Bank Limited |
| UBL | United Bank Limited |
| MCB | MCB Bank Limited |
| MCB_ARIF_HABIB | MCB Arif Habib |
| NBP | National Bank of Pakistan |
| ALFALAH | Bank Alfalah Limited |
| ALLIED | Allied Bank Limited |
| ASKARI | Askari Bank Limited |
| FAYSAL | Faysal Bank Limited |
| MEEZAN | Meezan Bank |
| AL_HABIB | Bank Al Habib Limited |
| HABIB_METRO | Habib Metropolitan Bank |
| BANK_ISLAMI | Bank Islami Pakistan Limited |
| DUBAI_ISLAMIC | Dubai Islamic Bank Pakistan |
| PUNJAB_BANK | The Bank of Punjab |
| SINDH_BANK | Sindh Bank Limited |
| SONERI | Soneri Bank Limited |
| SUMMIT | Summit Bank Limited |
| SILK | Silk Bank Limited |
| SAMBA | Samba Bank Limited |
| JS | JS Bank Limited |
| NIB | NIB Bank Limited |
| APNA | Apna Microfinance Bank |
| CITI | Citi Bank NA |
| NRSP | NRSP Microfinance Bank |
| CDNS | Central Directorate of National Savings |
幂等与重试
| 情况 | 平台行为 | 你该做什么 |
|---|---|---|
| 同键、请求体相同 | 返回同一代付的最新状态(200),不会再付一次 | 按返回的 status 处理 |
| 同键、请求体不同(任一字段,包括收款信息) | 409 idempotency_conflict,原代付不变;related_resource_id 是用这个键受理的代付号 | 集成缺陷:修复键的生成逻辑;先查询那笔代付再决定是否还要付 |
| 同键并发 | 只有一个请求执行;其他等它完成后返回同一代付,或 409 operation_in_progress | 稍后用同一个键重试 |
| 换键、同一商户代付单号 | 409 disbursement_no_conflict,related_resource_id 是已有代付号 | 查询已有代付;这个单号已经付过或正在付 |
| 被 400、401、403、422 拒绝 | 没有建单,不占用幂等键和单号 | 修正后用新键提交,单号可以沿用 |
503 temporarily_unavailable、429 rate_limited | 本次未受理,没有建单 | 稍后(429 按 Retry-After)用同一个键重试 |
超时、500、502 / 503 upstream_unavailable | 可能已受理 | 用同一个键重试,或按单号查询(见下图) |
- 幂等键由单号确定:
disbursement.create:<单号>:<序号>,序号只在被 4xx 拒绝、修正后重新提交时加 1。用随机 UUID 时必须先写库再发送,重试时从库里读,不能重新生成。 - 参与比较的是全部请求字段;幂等记录保留 7 天,单号唯一一直有效。
- 重试封装沿用重试参考实现的
postWithRetry。
警告:超时或 5xx 后不要换单号或换幂等键重发
请求超时或返回 5xx 时,代付可能已经受理,钱可能正在付给收款人。这时换一个新的幂等键、或换一个新的商户代付单号重新发起,平台会把它当作一笔新的代付——收款人可能收到两次钱,而且付出的钱不能撤回。正确做法只有两种:用原来的幂等键重试,或按商户代付单号查询。详见不要换键猜测重扣。
状态与结果
status | resolution_status | 含义 | 你该做什么 |
|---|---|---|---|
processing | known | 已受理,正在付款 | 等事件;不要当作失败 |
processing | needs_query | 结果未知,平台正在向代付通道查证 | 同上;不要另建 |
processing | manual_review | 自动查证 72 小时仍未知,已转人工核实;平台仍每 24 小时查一次,不会自动判失败 | 保持“处理中”;结果以事件通知,需要催办时联系平台并提供代付号 |
succeeded | known | 代付成功 | 按 paid_amount 记账;把 payee.utr 告诉收款人 |
failed | known | 代付失败,金额和手续费已退回可结算 | 按失败码处理;要再付就用新单号另建 |
reversed | known | 成功后被收款方退回,收款人没有收到钱;本金和手续费已退回可结算 | 先核实,再决定是否用新单号另建;不要自动重发 |
| 不认识的值 | — | 平台新增的状态 | 当作“未完成”,查询并告警,绝不当作成功 |
- 没有取消、撤回或重试接口;
failed、reversed是终态。只有代付通道明确失败、连续 30 分钟提交不出去、或平台人工核实后,才会变成failed。 - 冲正可能在成功之后才出现:平台在成功后第 1 天、第 3 天各再核对一次,也随时处理冲正通知。
- 查询频率:以 Webhook 为主。受理 10 分钟后仍是
processing且没收到事件时,在 30 分钟、2 小时、6 小时、24 小时各查一次,之后每天一次直到终态;同一代付两次查询间隔不少于 10 秒。查询只读平台当前状态,查得再勤也不会更早得到结果。 - 单笔查看与导出:代付记录(在新标签页打开商户后台)(需要所有者、管理员、财务或开发者)。
失败码
status=failed 时 failure.code 取下表的值(取自契约):
| failure.code | 含义 | 你该做什么 |
|---|---|---|
| payee_account_invalid | 收款账号无效或不存在、户名不符、银行代码错 | 让收款人核对收款信息,用新的商户代付单号另建 |
| payee_account_restricted | 收款账户受限、冻结、超过收款额度或暂停收款 | 收款人解除限制后另建,或换账号 |
| upstream_rejected | 代付通道拒绝(业务拒绝、人工驳回、通道额度用尽等) | 稍后另建;持续出现时联系平台 |
| upstream_unavailable | 连续 30 分钟提交不出去,平台判失败;与 HTTP 错误码 upstream_unavailable 同名不同义 | 稍后另建 |
| upstream_failed | 其他失败,包括平台人工核实后判定失败;不认识的失败码按它处理 | 联系平台 |
- 每种失败都已把金额和手续费全额退回可结算。
- 客户端须容忍新增的失败码,不认识的按
upstream_failed处理。
注意:两个 upstream_unavailable 不是一回事
HTTP 错误码 upstream_unavailable(创建请求返回 502 / 503,请求结果未知,用原键重试)和失败码 upstream_unavailable(代付已失败、资金已退回)不是一回事。前者看 error.code,后者看 failure.code(失败原因与错误码的区别)。
Webhook 事件
| 事件 | 触发条件 | 你通常要做什么 |
|---|---|---|
disbursement.processing | 受理后;开放 API、商户后台单笔与批量发起的都发 | 记录代付号,把你的业务单标为“代付处理中” |
disbursement.succeeded | 确认成功,带 paid_amount、payee.utr | 按实付标记已付 |
disbursement.failed | 明确失败,带 failure | 标记失败;资金已退回可结算 |
disbursement.reversed | 成功后被退回,带 reversed_at | 把已付改回未付并通知你的运营;不要自动重发 |
data.object 是事件发生时的 Disbursement 快照(字段同 Disbursement 对象);用 GET/v1/events/{event_id} 补查代付事件需要 disbursements:read。订阅事件:配置 Webhook(在新标签页打开商户后台)(需要所有者、管理员或开发者)。
示例载荷(生产环境的冲正事件;测试环境无法触发冲正):
{
"id": "evt_demo_203",
"type": "disbursement.reversed",
"api_version": "v1",
"created_at": "2026-10-30T02:10:00Z",
"environment": "live",
"merchant_id": "mer_demo_001",
"data": {
"object": {
"id": "dsb_demo_009",
"version": 3,
"merchant_disbursement_no": "DSB-DEMO-009",
"environment": "live",
"amount": 30000,
"currency": "PKR",
"fee": 600,
"method": "easypaisa",
"payee": {
"account_last4": "4567",
"name": "Test Payee",
"bank_code": null,
"bank_name": null,
"utr": "UTR-DEMO-001"
},
"customer_ip": "203.0.113.10",
"status": "reversed",
"resolution_status": "known",
"failure": null,
"paid_amount": 30000,
"succeeded_at": "2026-10-29T08:01:04Z",
"failed_at": null,
"reversed_at": "2026-10-30T02:09:58Z",
"description": "示例代付",
"metadata": { "customer_ref": "CUSTOMER-DEMO-001" },
"source": "api",
"batch_id": null,
"created_at": "2026-10-29T08:00:00Z",
"updated_at": "2026-10-30T02:09:58Z"
}
}
}处理代付事件(先按验签校验签名与环境、按事件 ID 去重并返回 2xx):
// 先按 Webhook 文档验签、校验环境、按事件 ID 去重并返回 2xx,再异步调用本函数
export async function onDisbursementEvent(event) {
const d = event.data.object;
const local = await findDisbursement(d.id); // 你保存的代付快照;后台发起的代付(source 为 portal、batch)本地可能没有
if (local && local.version >= d.version) return; // 旧版本事件:丢弃
await saveDisbursementSnapshot(d);
if (d.status === 'succeeded')
return markPaid(d.merchant_disbursement_no, d.paid_amount, d.payee.utr); // 按实付记账
if (d.status === 'failed') return markFailed(d.merchant_disbursement_no, d.failure.code); // 资金已退回可结算
if (d.status === 'reversed') return markReversed(d.merchant_disbursement_no); // 已付改回未付、通知运营;不要自动重发
// processing 或不认识的状态:继续等待,绝不当作成功
}同一代付的事件可能乱序、重复到达:reversed(version 3)可能先于 succeeded(version 2)到达,按 version 处理后晚到的 succeeded 被丢弃,不会把已冲正的单改回已付(乱序与版本比较)。冲正后要再付,用新单号另建;后台发起的代付不填单号时,merchant_disbursement_no 等于代付号。
错误码与限流
创建代付按下表顺序检查,前一项不通过就返回,不再往下检查;除 201 外都不建单、不冻结资金。
| 顺序 | 检查 | 不通过时 | 你该做什么 |
|---|---|---|---|
| 1 | 请求格式:缺 Idempotency-Key、缺字段、类型或长度不对;description、metadata 含卡号样式数字 | 400 invalid_request(field 指出字段);400 sensitive_data_rejected | 修正后用新键提交 |
| 2 | 幂等:同键不同请求体;同键请求仍在执行 | 409 idempotency_conflict;409 operation_in_progress | 前者修复键的生成;后者稍后用同一个键重试 |
| 3 | 商户状态与代付能力 | 403 merchant_not_active;403 capability_disabled | 联系平台 |
| 4 | 币种已为你开通、有可用的代付通道,且配了限额和代付费率 | 422 currency_not_supported | 首期只用 PKR;联系平台 |
| 5 | 整数金额;收款信息与 customer_ip 格式 | 400 invalid_request(field 为 amount、payee.* 或 customer_ip) | 修正后用新键提交 |
| 6 | 单号唯一 | 409 disbursement_no_conflict(related_resource_id 为已有代付) | 查询已有代付,不要换单号重发 |
| 7 | 手续费小于金额 | 422 fee_exceeds_amount | 提高金额 |
| 8 | 限额:单笔下限、单笔上限、日累计金额、日笔数 | 422 amount_out_of_range,不回传阈值 | 调整金额,或等下一个业务日再发;限额向平台了解 |
| 9 | 平台暂停代付 | 503 temporarily_unavailable,不说明原因 | 稍后用同一个键重试 |
| 10 | 风控名单 | 422 risk_rejected | 不要尝试绕过;有疑问联系平台 |
| 11 | 冻结可结算 | 422 insufficient_balance,error.details 带 shortfall、currency | 等可结算足够后,用同一单号、新的幂等键重新提交 |
| — | 全部通过 | 201 processing,随后推送 disbursement.processing | 保存代付号,等结果 |
{
"error": {
"code": "insufficient_balance",
"message": "可结算余额不足以支付代付金额与手续费,差额 PKR 56.00",
"field": null,
"related_resource_id": null,
"retryable": false,
"details": { "shortfall": 5600, "currency": "PKR" }
},
"request_id": "req_demo_102"
}通用错误:401 unauthenticated;403 forbidden(Key 缺少所需 scope)、ip_not_allowed;查询不存在或不属于当前商户、环境的代付 404 not_found;429 rate_limited;500 internal_error、502 / 503 upstream_unavailable(结果未知,按幂等与重试处理)。全部错误码见错误码字典。
限流
代付创建按商户单独计算频率限制,不占用支付、退款的额度,换 Key 不能绕过;超过时返回 429,本次未执行,等 Retry-After 秒后用原幂等键重试。一次要付很多笔时控制并发,或使用商户后台的批量代付。查询接口的限流同 API 参考的限流说明。
在测试环境验证
- 前提与生产相同(见开通与准备):平台为测试环境开通代付能力并配好 PKR 通道与限额,你用带代付范围的测试 Key。测试环境不产生真实资金。
- 测试可结算同样来自 PKR 代收,过了测试环境的锁定周期才转入;需要尽快演练时,联系平台缩短测试环境的锁定周期。
- 金额小于 500.00 PKR(
amount< 50000)的代付成功;500.00 PKR 及以上的失败,失败码以failure.code为准,不固定为某一个码。 - 银行账号长度不在 9–20 位之间的,受理后直接失败,所以 24 位的 IBAN 在测试环境总是失败,测试银行代付用 9–20 位的本地账号;生产环境没有这条限制。
- 冲正与结果未知(
needs_query、manual_review)暂不支持模拟。自测方法:用单元测试把示例载荷交给事件处理函数,确认已付改回未付、不自动重发;把processing+needs_query的响应交给你的代码,确认不当作失败、不另建;在 Webhook 配置(在新标签页打开商户后台)(测试环境;需要所有者、管理员或开发者) 用“发送测试事件”发一条disbursement.reversed,只核对接收与验签(测试事件不对应真实代付,不要据此改业务数据)。
| # | 场景 | 操作 | 预期 |
|---|---|---|---|
| T1 | 钱包代付成功 | 创建示例,amount 30000 | 201 processing → disbursement.succeeded,paid_amount、payee.utr 有值;可结算减少金额加手续费 |
| T2 | 代付失败 | amount 60000 | 201 processing → disbursement.failed;金额加手续费回到可结算 |
| T3 | 银行账户代付 | method=bank、bank_code、12 位本地账号 | 同 T1 |
| T4 | IBAN(测试限制) | 24 位 IBAN | 受理后直接失败 |
| T5 | 可结算不足 | 金额加手续费大于可结算 | 422 insufficient_balance,带 details.shortfall;查不到代付记录 |
| T6 | 格式错误 | amount 30050;或 payee.account 为 +923001234567 | 400 invalid_request,field 分别为 amount、payee.account |
| T7 | 同键重放 | 用 T1 的键和请求体再发一次 | 200,同一个代付 |
| T8 | 换键同单号 | 新键、T1 的单号 | 409 disbursement_no_conflict,related_resource_id 为 T1 的代付号 |
| T9 | 超时后同键重试 | 客户端超时设为 100 毫秒发起,再用同一个键重试或按单号查询 | 只有一个代付 |
| T10 | 只读 Key 创建 | 只有 disbursements:read 的 Key 调创建示例 | 403 forbidden |
通用的测试环境说明见测试指南。
上线检查
上线代付前逐项确认(勾选只保存在本机,可复制为 Markdown);通用的上线检查见上线检查清单。
开通与权限 0/2幂等与结果 0/3资金与收款信息 0/2自测 0/1
开通与权限
DC-02发起代付的生产 Key 由所有者或管理员创建,只给代付用得到的范围:disbursements:write、查询结果的 disbursements:read,按 DC-06 核对可结算时再加 finance:read;并配置 IP 白名单在哪里:API Key(在新标签页打开商户后台)(需要所有者、管理员或开发者)开通与权限 · 所需角色:所有者或管理员幂等与结果
DC-04生产端点订阅 4 个 disbursement.* 事件;按事件 ID 去重、按 version 处理在哪里:Webhook(在新标签页打开商户后台)(需要所有者、管理员或开发者)、乱序与版本比较幂等与结果 · 所需角色:所有者、管理员或开发者DC-05按 paid_amount 记账;收到 failed 按失败码处理,不认识的按 upstream_failed 处理;收到 reversed 时把已付改回未付并通知运营,不自动重发在哪里:失败码幂等与结果资金与收款信息
自测