BRPAY Merchant API 接入文档
API:商户 API · Java / PHP SDK:版本见交付包 · 更新日期:2026-11-06
1. 快速接入
本接口用于游戏充值和提现。订单、余额、流水和回调均使用 BRC。1 BRC = 1 BRL 是平台计价关系;本接口处理 BRPAY 钱包内的 BRC 划转。文中域名、账号、数量和费率均为示例。
向平台获取服务端接入参数:
| 参数 | 说明 |
|---|---|
baseUrl |
商户 API 的 HTTPS 根地址,不含路径、查询串或结尾斜线。以平台提供的地址为准。 |
merchantId |
平台分配的商户号,格式为大写 M + 8 位大写字母或数字。 |
signingSecret |
32–512 个 UTF-8 字节。只保存在商户服务端,用于 API 签名和回调验签。 |
- 准备接入资料。 先向平台领取上表三项。需要异步通知时,每笔订单都传
notifyUrl;不传就按商户单号主动查单。 - 先查一次余额。 同步服务端时间,运行签名请求示例。能拿到
availableBalance,再联调下单。 - 充值和提现各跑一笔。 充值:保存自己的订单 → 建代收单 → 打开
cashierUrl→ 查单或验签确认到账后给用户充值。提现:保存自己的订单 → 核对收款钱包 → 提交代付 → 收到明确SUCCESS即可确认到账。 - 再试三种异常。 代收过期不充值;同一回调重复到达只处理一次;下单超时先查原商户单号。
| 功能 | 路径 | SDK 方法 |
|---|---|---|
| 代收充值 | /api/merchant/orders/payin |
payin(...) |
| 代付提现 | /api/merchant/orders/payout |
payout(...) |
| 订单查询 | /api/merchant/orders/query |
queryOrder(...) / querySystemOrder(...) |
| 接单状态查询 | /api/merchant/orders/availability |
queryAvailability(...) |
| 商户余额查询 | /api/merchant/account/balance |
queryBalance(...) |
| 商户资金流水 | /api/merchant/account/transactions |
queryTransactions(...) |
| 钱包校验 | /api/merchant/wallets/query |
queryWallet(...) |
2. 公共协议
2.1 请求格式
| 项目 | 规则 |
|---|---|
| 协议 / 方法 | HTTPS / POST;仅本机开发允许回环 HTTP |
| Content-Type | application/json; charset=utf-8 |
| 字符编码 / 正文上限 | UTF-8 / 8192 字节;必须是 JSON 对象,拒绝重复键与未知字段 |
| 数量 | 正十进制字符串,最多 17 位有效整数位,必须精确到分。允许前导零和小数末尾多余的零,例如 "00030.1200" 按 "30.12" 建单;不接受 JSON 数字、科学计数法或不足一分的尾数。 |
| 时间 | UTC RFC3339,以 Z 结尾,例如 2026-11-05T04:00:00Z |
| 协议演进 | 忽略新增可选响应/回调字段,按现行回调结构校验 |
所有接口使用同一组请求头:
| 请求头 | 格式 | 说明 |
|---|---|---|
X-Merchant-Id |
大写 M 加 8 位大写字母或数字 | 平台商户号 |
X-Timestamp |
10 位 Unix 秒 | 当前时间,允许 300 秒偏差 |
X-Request-Id |
小写 UUID | 每次发送 HTTP 请求时生成一个,用于排查。同一笔订单的商户单号保留不变。 |
X-Signature |
64 位小写 hex | HMAC-SHA256;同名认证头不允许重复 |
2.2 请求签名
签名只在商户服务端做。先把 JSON 转成 UTF-8 字节,对这份正文求 SHA-256;签名和 HTTP 发送使用同一份正文。把下面 7 行用 \n(LF)连接,最后一行之后不加换行:
BP-MERCHANT
POST
<full-request-path>
<merchantId>
<epochSeconds>
<requestId>
<sha256Hex(rawBodyBytes)><full-request-path> 填实际请求路径,例如 /api/merchant/orders/payin,不带域名、查询串或额外的斜线。不要把 JSON 解析后重新排版再签,也不要把签名中的 LF 换成 Windows 的 CRLF。
可运行示例:签名并查询余额(只读)
将下方保存为 merchant-balance.mjs。使用支持内置 fetch 的 Node.js(18+),在服务端设置 BRPAY_BASE_URL、BRPAY_MERCHANT_ID、BRPAY_SIGNING_SECRET 后运行 node merchant-balance.mjs。成功时打印可用 BRC 余额。使用平台发给你的新版 API 地址;本机 8109 是 DEMO 页面端口。
import { createHash, createHmac, randomUUID } from 'node:crypto';
const { BRPAY_BASE_URL, BRPAY_MERCHANT_ID, BRPAY_SIGNING_SECRET } = process.env;
if (!BRPAY_BASE_URL || !/^M(?!0{8}$)[0-9A-Z]{8}$/.test(BRPAY_MERCHANT_ID ?? '') ||
!BRPAY_SIGNING_SECRET) {
throw new Error('Set BRPAY_BASE_URL, BRPAY_MERCHANT_ID and BRPAY_SIGNING_SECRET');
}
const origin = new URL(BRPAY_BASE_URL);
const loopback = origin.protocol === 'http:' &&
['127.0.0.1', 'localhost', '[::1]'].includes(origin.hostname);
if ((!loopback && origin.protocol !== 'https:') || origin.pathname !== '/' ||
origin.search || origin.hash || origin.username || origin.password) {
throw new Error('BRPAY_BASE_URL must be the API origin (HTTPS or loopback HTTP)');
}
const path = '/api/merchant/account/balance';
const body = Buffer.from(JSON.stringify({}), 'utf8');
const timestamp = String(Math.floor(Date.now() / 1000));
const requestId = randomUUID();
const bodyHash = createHash('sha256').update(body).digest('hex');
const text = ['BP-MERCHANT', 'POST', path, BRPAY_MERCHANT_ID,
timestamp, requestId, bodyHash].join('\n');
const signature = createHmac('sha256', BRPAY_SIGNING_SECRET).update(text, 'utf8').digest('hex');
const response = await fetch(origin.origin + path, {
method: 'POST',
headers: {
'Content-Type': 'application/json; charset=utf-8',
'X-Merchant-Id': BRPAY_MERCHANT_ID,
'X-Timestamp': timestamp,
'X-Request-Id': requestId,
'X-Signature': signature
},
body,
redirect: 'error',
signal: AbortSignal.timeout(30_000)
});
const reply = await response.json();
if (response.status !== 200 || reply?.success !== true || reply.code !== 'OK' ||
reply.requestId !== requestId || reply.data?.currency !== 'BRC' ||
typeof reply.data.availableBalance !== 'string') {
throw new Error(`HTTP ${response.status}; code=${reply?.code ?? '(missing)'}; requestId=${requestId}`);
}
console.log(reply.data.availableBalance);固定签名值: 下面两段用于比较你自己代码算出的正文哈希和签名。
signingSecret = example-only-signing-secret-32-bytes
BP-MERCHANT
POST
/api/merchant/orders/payin
M00000325
1793851200
aaaaaaaa-bbbb-4ccc-8ddd-eeeeeeeeeeee
74cbca80326f6565ae3a2f5c16607f96b8f42d13e30b7c99290ef75c8a05c07a
X-Signature: 8c5374d38568c2c84d1ca15d101b6c902f32398f83a45f9edbc30ac45b7b5c8c完整 HTTP 请求(离线验签用): 使用上面的示例密钥。请求正文是下面的单行 JSON,不含结尾换行。固定时间只用于比对签名。
POST /api/merchant/orders/payin HTTP/1.1
Host: api.example.com
Content-Type: application/json; charset=utf-8
X-Merchant-Id: M00000325
X-Timestamp: 1793851200
X-Request-Id: aaaaaaaa-bbbb-4ccc-8ddd-eeeeeeeeeeee
X-Signature: 8c5374d38568c2c84d1ca15d101b6c902f32398f83a45f9edbc30ac45b7b5c8c
Content-Length: 86
{"merchantOrderNo":"RECHARGE-001","orderAmount":"30.00","description":"Game recharge"}2.3 统一响应
成功时返回 HTTP 200、success: true 和 code: "OK"。每个接口下方都有完整响应示例;业务字段放在 data 里。对下单接口,HTTP 200 只说明请求成功处理,订单是否已完成要看 data.status。
失败时 success: false、data: null,按 HTTP 状态和 code 处理。message 给人看,不用于程序分支。requestId 回显本次请求的追踪号(未提供时可能为 null),排查时把它和商户单号一并提供给平台。timestamp 是平台返回响应的 UTC 时间。
例如签名错误:HTTP 401 / INVALID_SIGNATURE。
{
"success": false,
"code": "INVALID_SIGNATURE",
"message": "Invalid signature",
"requestId": "99999999-aaaa-4bbb-8ccc-dddddddddddd",
"timestamp": "2026-11-05T04:06:00Z",
"data": null
}3. 订单、费用与状态
下单回执、支付结果、通知状态是三件事。 代收建单成功时通常是 PENDING,此时未扣款;代付返回 SUCCESS 即已到账。notificationStatus 只表示回调送达情况,即使是 EXHAUSTED,也不改变资金结果。
| 业务 | 用户 BRC 余额 | 商户 BRC 余额 | 何时确认成功 |
|---|---|---|---|
| 代收(PAYIN) | 付款时扣除订单数量 | 入账订单数量减手续费 | 查单返回 SUCCESS,或收到验签通过的成功回调 |
| 代付(PAYOUT) | 入账订单数量 | 扣除订单数量加手续费 | 代付成功响应、查单 SUCCESS 或验签通过的成功回调 |
手续费 = 取「最小手续费」和「订单数量 × 费率 + 固定费(四舍五入保留 2 位小数)」中的较大值。代收手续费必须小于订单数量。平台在建单时保存本单费用,后续调价不影响已建订单。通过接单状态查询读取当前费率和数量范围。
商户单号用于标识一笔业务,同一商户的代收和代付不能重用该单号。收到 ORDER_EXISTS 应查询原订单,核对方向、数量及代付收款钱包;冲突回执本身不代表本次请求成功。
4. 接口详情
商户代收订单统一为 PENDING(处理中/待支付)、SUCCESS(订单付款成功)、FAILED(订单已确认失败)。failureCode 在失败时必返,当前订单过期使用 ORDER_EXPIRED;非失败为 null。只有成功或失败终态生成回调,初次结果事件为 ORDER_SUCCEEDED 或 ORDER_FAILED;后续更正使用 ORDER_CORRECTED。钱包充值成功或失败、余额不足及取消自动付款授权均不直接结束仍可付款的商户订单;请求超时不等于订单失败,须查原订单确认。建单被拒且未创建订单时仅返回接口错误,不推送订单失败回调。
4.1 代收充值
POST /api/merchant/orders/payin · SDK payin(...)
用户要为游戏账户充值时调用。接口受理后返回待支付订单;请保存两个单号,把 cashierUrl 交给用户打开(原生 App 可用 appUrl)。建单时用户和商户余额都不变,验签并核对 ORDER_SUCCEEDED,或查单确认 SUCCESS 后,再给用户充值。
只建代收单时,最小请求为 {"merchantOrderNo":"RECHARGE-001","orderAmount":"30.00"}。
回调地址: 按需在本次请求中提供 notifyUrl 和 returnUrl,不再配置到商户资料。完整规则见结果回调。
请求
| 请求字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
merchantOrderNo |
string | 是 | 商户为本笔业务生成的单号。1–64 位,匹配 [A-Za-z0-9][A-Za-z0-9._-]{0,63},区分大小写。同一商户的代收和代付共用编号空间,每笔业务只用一个单号。 |
orderAmount |
string | 是 | 正十进制字符串,最多 17 位有效整数位,必须精确到分。允许前导零和小数末尾多余的零,例如 "00030.1200" 按 "30.12" 建单;不接受 JSON 数字、科学计数法或不足一分的尾数。 |
description |
string / null | 否 | 业务说明。可省略或传空字符串;省略时保存为空字符串。最多 160 个 UTF-16 代码单元,不含 ASCII 控制字符;null 同省略。 |
notifyUrl |
string / null | 否 | 异步通知地址;省略不发送通知。完整 HTTPS URL,最多 512 字符。 |
returnUrl |
string / null | 否 | 同步返回商户页面;省略不展示返回入口。完整 HTTPS URL,最多 512 字符。 |
{
"merchantOrderNo": "RECHARGE-001",
"orderAmount": "30.00",
"description": "Game recharge",
"notifyUrl": "https://merchant.example.com/webhooks/brpay",
"returnUrl": "https://merchant.example.com/orders/RECHARGE-001"
}成功响应 data
初次建单通常返回 PENDING,示例费率为 1% + 0.10 BRC。以 expiresAt 为准,勿在商户端写死有效期。查询发现订单已过期时停止引导付款。
| 响应字段 | 类型 | 说明 |
|---|---|---|
orderNo |
string | 平台代收单号,RC + 14 位数字。 |
merchantOrderNo |
string | 原请求的商户单号。 |
direction |
string | 固定 PAYIN。 |
currency |
string | 固定 BRC。 |
orderAmount |
string | 用户应支付的 BRC 数量。 |
feeAmount |
string | 建单时确定的手续费,单位 BRC,可为 "0.00"。 |
merchantAmount |
string | 成功后商户净入账数量:orderAmount - feeAmount,单位 BRC。 |
actualAmount |
string / null | 待支付时为 null;付款成功后为用户实际扣除的 BRC 数量。 |
status |
string | 建单后为 PENDING(待支付)。最终结果看查单或回调。 |
failureCode |
string / null | 失败原因;FAILED 时必返,当前过期为 ORDER_EXPIRED;其他状态为 null。 |
walletId |
string / null | 待支付时为 null;付款成功后为实际付款的钱包账号(W + 8 位大写字母或数字)。 |
createdAt |
string | 建单时间,UTC。 |
expiresAt |
string | 付款截止时间,UTC。到期后不能支付。 |
completedAt |
string / null | 待支付时为 null;付款或过期后可查到终态时间。 |
orderRevision |
integer | 订单结果修订号;初始为 0,更正后递增。 |
notificationStatus |
string / null | 待支付时为 null,尚无结果回调。 |
cashierUrl |
string | BRPAY 收银页,用于 Web 付款、打开 App 或在 App 内扫码。 |
appUrl |
string | 原生 App 付款链接,brazilpay://cashier/<token>,凭证为 22 个 Base64URL 字符,与 cashierUrl 一致。 |
完整响应示例 · HTTP 200
{
"success": true,
"code": "OK",
"message": "",
"requestId": "aaaaaaaa-bbbb-4ccc-8ddd-eeeeeeeeeeee",
"timestamp": "2026-11-05T04:00:00Z",
"data": {
"orderNo": "RC00000000128364",
"merchantOrderNo": "RECHARGE-001",
"direction": "PAYIN",
"currency": "BRC",
"orderAmount": "30.00",
"feeAmount": "0.40",
"merchantAmount": "29.60",
"actualAmount": null,
"status": "PENDING",
"walletId": null,
"createdAt": "2026-11-05T04:00:00Z",
"expiresAt": "2026-11-05T04:30:00Z",
"completedAt": null,
"orderRevision": 0,
"notificationStatus": null,
"cashierUrl": "https://payments.example.com/pay/aaaaaaaaaaaaaaaaaaaaaA",
"appUrl": "brazilpay://cashier/aaaaaaaaaaaaaaaaaaaaaA",
"failureCode": null
}
}异常处理: ORDER_EXISTS、超时或无有效回执时先按原商户单号查单;数量不合规时核对接单范围和费用。
4.2 代付提现
POST /api/merchant/orders/payout · SDK payout(...)
用户从游戏提现到 BRPAY 钱包时调用。先用钱包校验确认 exists=true、canReceive=true,并核对用户选定的收款账号。HTTP 200 表示订单已受理,初始状态为 PENDING,同时占用本金和手续费。只有查单或回调的 SUCCESS 才表示到账;FAILED 表示失败且占用已释放。请求被拒且未建单不会占用资金。
回调地址: 按需在本次请求中提供 notifyUrl 和 returnUrl,不再配置到商户资料。完整规则见结果回调。
请求
| 请求字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
merchantOrderNo |
string | 是 | 商户为本笔业务生成的单号。1–64 位,匹配 [A-Za-z0-9][A-Za-z0-9._-]{0,63},区分大小写。同一商户的代收和代付共用编号空间,每笔业务只用一个单号。 |
orderAmount |
string | 是 | 正十进制字符串,最多 17 位有效整数位,必须精确到分。允许前导零和小数末尾多余的零,例如 "00030.1200" 按 "30.12" 建单;不接受 JSON 数字、科学计数法或不足一分的尾数。 |
recipientWalletId |
string | 是 | 钱包校验返回的 walletId,格式为 W + 8 位大写字母或数字。 |
description |
string / null | 否 | 业务说明。可省略或传空字符串;省略时保存为空字符串。最多 160 个 UTF-16 代码单元,不含 ASCII 控制字符;null 同省略。 |
notifyUrl |
string / null | 否 | 异步通知地址;省略不发送通知。完整 HTTPS URL,最多 512 字符。 |
returnUrl |
string / null | 否 | 同步返回商户页面;省略不展示返回入口。完整 HTTPS URL,最多 512 字符。 |
{
"merchantOrderNo": "WITHDRAW-001",
"orderAmount": "20.00",
"description": "Game withdrawal",
"recipientWalletId": "W00012836",
"notifyUrl": "https://merchant.example.com/webhooks/brpay",
"returnUrl": "https://merchant.example.com/orders/WITHDRAW-001"
}成功响应 data
示例费率为 2% + 0.20 BRC。受理返回 PENDING;下方是最终查单成功的示例。确认 SUCCESS 即表示已到账,不需要再等待回调。代付不返回 cashierUrl 或 appUrl。
| 响应字段 | 类型 | 说明 |
|---|---|---|
orderNo |
string | 平台代付单号,PY + 14 位数字。 |
merchantOrderNo |
string | 原请求的商户单号。 |
direction |
string | 固定 PAYOUT。 |
currency |
string | 固定 BRC。 |
orderAmount |
string | 用户应收到的 BRC 数量。 |
feeAmount |
string | 建单时确定的手续费,单位 BRC,可为 "0.00"。 |
merchantAmount |
string | 商户需占用及结算的数量:orderAmount + feeAmount,单位 BRC。 |
actualAmount |
string / null | 仅 SUCCESS 为用户实际到账数量;PENDING / FAILED 为 null。 |
status |
string | PENDING 已受理处理中、SUCCESS 已到账、FAILED 已失败且占用已释放。 |
failureCode |
string / null | FAILED 的明确失败原因;其余状态为 null。 |
walletId |
string | 本单指定的收款钱包(W + 8 位大写字母或数字),各状态均返回并对应 recipientWalletId;只有 SUCCESS 才表示到账。 |
createdAt |
string | 建单时间,UTC。 |
expiresAt |
null | 固定 null,代付没有付款期限。 |
completedAt |
string / null | 终态完成时间,UTC;PENDING 为 null。 |
orderRevision |
integer | 订单结果修订号;初始为 0,更正后递增。 |
notificationStatus |
string / null | PENDING 待发送或重试、DELIVERED 已收到 2xx、EXHAUSTED 自动尝试耗尽。与资金状态独立。 |
完整响应示例 · HTTP 200
{
"success": true,
"code": "OK",
"message": "",
"requestId": "22222222-3333-4444-8555-666666666666",
"timestamp": "2026-11-05T04:05:00Z",
"data": {
"orderNo": "PY00000000128365",
"merchantOrderNo": "WITHDRAW-001",
"direction": "PAYOUT",
"currency": "BRC",
"orderAmount": "20.00",
"feeAmount": "0.60",
"merchantAmount": "20.60",
"actualAmount": "20.00",
"status": "SUCCESS",
"walletId": "W00012836",
"createdAt": "2026-11-05T04:05:00Z",
"expiresAt": null,
"completedAt": "2026-11-05T04:05:00Z",
"failureCode": null,
"orderRevision": 0,
"notificationStatus": "PENDING"
}
}异常处理: WALLET_NOT_FOUND、ACCOUNT_DISABLED 或 WALLET_LIMIT_EXCEEDED 时核对收款钱包;MERCHANT_INSUFFICIENT_BALANCE 时查余额。ORDER_EXISTS、超时或无有效回执时先用原商户单号查单,核对方向、数量及收款钱包。
4.3 订单查询
POST /api/merchant/orders/query · SDK queryOrder(...)(商户单号)/querySystemOrder(...)(平台单号)
用商户单号或平台单号查询当前商户的订单。用于处理回调未到、下单超时或对账。以 status 判断资金结果,notificationStatus 只说明回调投递进度。
请求
| 请求字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
merchantOrderNo |
string | 二选一 | 原商户单号,1–64 位,匹配 [A-Za-z0-9][A-Za-z0-9._-]{0,63},区分大小写。 |
orderNo |
string | 二选一 | BRPAY 平台单号:代收 RC 或代付 PY + 14 位数字。 |
两种请求示例,每次只发送其中一种:
{
"merchantOrderNo": "RECHARGE-001"
}{
"orderNo": "RC00000000128364"
}成功响应 data
示例为已付款且回调已确认的代收订单。表内字段均返回;付款链接仅代收返回,即使订单已终态也可能带有链接,只有 PENDING 时才应引导付款。
| 响应字段 | 类型 | 说明 |
|---|---|---|
orderNo |
string | 平台单号,代收 RC、代付 PY,前缀后接 14 位数字。 |
merchantOrderNo |
string | 原请求的商户单号。 |
direction |
string | PAYIN 代收或 PAYOUT 代付。 |
currency |
string | 固定 BRC。 |
orderAmount |
string | 原订单的 BRC 数量。 |
feeAmount |
string | 建单时确定的手续费,单位 BRC,可为 "0.00"。 |
merchantAmount |
string | 正数,单位 BRC:代收为成功后商户净入账,代付为商户含手续费支出。 |
actualAmount |
string / null | 仅 SUCCESS 时返回实际从用户扣除(代收)或转给用户(代付)的 BRC 数量;其余状态为 null。 |
status |
string | 代收为 PENDING(待支付)、SUCCESS(已付款)或 FAILED(已失败,原因见 failureCode);代付为 PENDING(处理中)、SUCCESS(已到账)或 FAILED(已失败且占用已释放)。 |
failureCode |
string / null | 失败原因;FAILED 时必返,当前过期为 ORDER_EXPIRED;其他状态为 null。 |
walletId |
string / null | 代付各状态均为收款钱包账号;代收返回实际付款钱包;未付款为 null,更正为失败时可能保留原钱包。格式为 W + 8 位大写字母或数字。 |
createdAt |
string | 建单时间,UTC。 |
expiresAt |
string / null | 代收付款截止时间(UTC);代付为 null。 |
completedAt |
string / null | 成功时为完成时间(UTC);代收过期时等于 expiresAt,待支付时为 null。 |
orderRevision |
integer | 订单结果修订号;初始为 0,更正后递增。 |
notificationStatus |
string / null | PENDING 待发送或重试、DELIVERED 已收到 2xx、EXHAUSTED 自动尝试耗尽;null 表示尚无通知记录(如待支付或刚过期)。 |
cashierUrl |
string | 仅代收返回。BRPAY 收银页;只有待支付时使用。 |
appUrl |
string | 仅代收返回。原生 App 付款链接,凭证为 22 个 Base64URL 字符,与 cashierUrl 一致。 |
完整响应示例 · HTTP 200
{
"success": true,
"code": "OK",
"message": "",
"requestId": "33333333-4444-4555-8666-777777777777",
"timestamp": "2026-11-05T04:03:00Z",
"data": {
"orderNo": "RC00000000128364",
"merchantOrderNo": "RECHARGE-001",
"direction": "PAYIN",
"currency": "BRC",
"orderAmount": "30.00",
"feeAmount": "0.40",
"merchantAmount": "29.60",
"actualAmount": "30.00",
"status": "SUCCESS",
"walletId": "W00012836",
"createdAt": "2026-11-05T04:00:00Z",
"expiresAt": "2026-11-05T04:30:00Z",
"completedAt": "2026-11-05T04:02:00Z",
"orderRevision": 0,
"notificationStatus": "DELIVERED",
"cashierUrl": "https://payments.example.com/pay/aaaaaaaaaaaaaaaaaaaaaA",
"appUrl": "brazilpay://cashier/aaaaaaaaaaaaaaaaaaaaaA",
"failureCode": null
}
}未查到: 返回 HTTP 404 / ORDER_NOT_FOUND。若正在处理下单超时,按结果未确认处理;一次 404 不代表仍在处理的原请求一定不会提交。
查不到订单的完整响应 · HTTP 404
{
"success": false,
"code": "ORDER_NOT_FOUND",
"message": "Record not found",
"requestId": "33333333-4444-4555-8666-888888888888",
"timestamp": "2026-11-05T04:06:10Z",
"data": null
}4.4 接单状态查询
POST /api/merchant/orders/availability · SDK queryAvailability(...)
下单前可查当前是否接单、允许的 BRC 数量范围和费率。结果不预留额度,也不检查实际商户余额或收款钱包容量。维护期间不接新单,既有订单及账户仍可查询。
允许范围取手续费、存储容量、当前商户单笔上下限及当日剩余额度的交集。代收、代付分别按圣保罗自然日累计已受理订单的原始 BRC 金额;待支付、过期订单仍计入创建当天,创建失败并回滚的订单不计。商户单笔限额和单日限额配置为 0 表示不加该项限制,新订单仍须满足正数金额、手续费和余额要求。
请求
| 请求字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
direction |
string | 否 | PAYIN 查代收,PAYOUT 查代付;省略则查两个方向。 |
{}只查代付时,发送:
{
"direction": "PAYOUT"
}成功响应 data
请求 {} 返回 payin 和 payout 两个对象;指定 direction 时只返回对应对象。两个对象内的字段如下:
| 响应字段 | 类型 | 说明 |
|---|---|---|
acceptingOrders |
boolean | 当前是否接单,受维护、当前商户方向开关、单笔范围、当日剩余额度及手续费要求影响。 |
minimumAmount |
string / null | 订单 BRC 数量下限,含端点;无有效范围时为 null。 |
maximumAmount |
string / null | 订单 BRC 数量上限,含端点;无有效范围时与下限同时为 null。 |
feeRate |
string | 小数费率,范围 0–1,最多 6 位小数;"0.01" 表示 1%。 |
feeFixed |
string | 每笔订单的固定手续费,单位 BRC,可为 "0.00"。 |
feeMin |
string | 每笔订单的最低手续费,单位 BRC。 |
方向关闭时仍可能返回数量范围;能否提交以 acceptingOrders 为准。即使为 true,平台仍会在下单时重新检查。
完整响应示例 · HTTP 200
{
"success": true,
"code": "OK",
"message": "",
"requestId": "44444444-5555-4666-8777-999999999999",
"timestamp": "2026-11-05T03:59:00Z",
"data": {
"payin": {
"acceptingOrders": true,
"minimumAmount": "1.00",
"maximumAmount": "10000.00",
"feeRate": "0.01",
"feeFixed": "0.10",
"feeMin": "0.00"
},
"payout": {
"acceptingOrders": true,
"minimumAmount": "1.00",
"maximumAmount": "10000.00",
"feeRate": "0.02",
"feeFixed": "0.20",
"feeMin": "0.00"
}
}
}只查代付的完整响应 · HTTP 200
{
"success": true,
"code": "OK",
"message": "",
"requestId": "44444444-5555-4666-8777-aaaaaaaaaaaa",
"timestamp": "2026-11-05T03:59:10Z",
"data": {
"payout": {
"acceptingOrders": true,
"minimumAmount": "1.00",
"maximumAmount": "10000.00",
"feeRate": "0.02",
"feeFixed": "0.20",
"feeMin": "0.00"
}
}
}4.5 商户余额查询
POST /api/merchant/account/balance · SDK queryBalance(...)
同一次读取返回账面余额、占用余额和可用余额,均为 BRC 十进制字符串。代付受理时,订单本金加手续费必须不超过可用余额,并在同一事务预留。heldBalance 只累计实际占用,不累计代收待支付金额或通知待送达金额。
请求
无业务字段,发送空 JSON 对象 {},不要留空正文。
{}成功响应 data
| 响应字段 | 类型 | 说明 |
|---|---|---|
currency |
string | 固定 BRC。 |
balance |
string | 账面总余额,包含占用资金,单位 BRC。 |
heldBalance |
string | 通用占用余额,暂不可使用;代付受理时占用本金和手续费,成功结算、失败释放。不限定业务类型。 |
availableBalance |
string | 可用于新业务的 BRC 余额:balance - heldBalance。 |
asOf |
string | 余额读取时间,UTC。 |
完整响应示例 · HTTP 200
{
"success": true,
"code": "OK",
"message": "",
"requestId": "55555555-6666-4777-8888-aaaaaaaaaaaa",
"timestamp": "2026-11-05T04:04:00Z",
"data": {
"currency": "BRC",
"balance": "29.60",
"heldBalance": "5.30",
"availableBalance": "24.30",
"asOf": "2026-11-05T04:04:00Z"
}
}查询失败: 保留错误提示,不要将查询失败显示为零余额。
4.6 商户资金流水
POST /api/merchant/account/transactions · SDK queryTransactions(...)
查询已入账的商户余额变动,可通过系统单号或商户单号关联业务。每页最多 50 条,按 createdAt 倒序,同一时间按 id 升序。实时分页可能因新流水而重复,按 id 识别同一条记录。
请求
| 请求字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
page |
integer | 否 | 从 0 开始,省略默认为 0,最大 2147483647。传 JSON 整数。 |
{
"page": 0
}成功响应 data
| 响应字段 | 类型 | 说明 |
|---|---|---|
page |
integer | 当前页号。 |
hasMore |
boolean | 为 true 时可继续请求 page + 1。 |
rows |
array | 本页流水。无记录时为 []。 |
rows[] 的字段
| 响应字段 | 类型 | 说明 |
|---|---|---|
id |
string | 流水记录的 UUID,用来识别同一条记录。 |
orderNo |
string | 关联的平台单号:RC 或 PY + 14 位数字。 |
merchantOrderNo |
string | 关联的商户单号。 |
direction |
string | PAYIN 代收或 PAYOUT 代付。 |
amount |
string | 带符号的商户账面余额变动。更正冲回与原流水符号相反;对账按本字段的正负,不按订单方向推算。 |
feeAmount |
string | 此订单的手续费,单位 BRC;已反映在 amount 中。 |
balanceAfter |
string | 本笔变动入账后的商户 BRC 余额。 |
createdAt |
string | 入账记录时间,UTC。 |
完整响应示例 · HTTP 200
{
"success": true,
"code": "OK",
"message": "",
"requestId": "66666666-7777-4888-8999-bbbbbbbbbbbb",
"timestamp": "2026-11-05T04:04:30Z",
"data": {
"page": 0,
"hasMore": false,
"rows": [
{
"id": "66666666-7777-4888-8999-aaaaaaaaaaaa",
"orderNo": "RC00000000128364",
"merchantOrderNo": "RECHARGE-001",
"direction": "PAYIN",
"amount": "29.60",
"feeAmount": "0.40",
"balanceAfter": "29.60",
"createdAt": "2026-11-05T04:02:00Z"
}
]
}
}这是实时流水查询;逐页读取不能视为同一时点的冻结对账单。
4.7 钱包校验
POST /api/merchant/wallets/query · SDK queryWallet(...)
代付前用用户提供的 BRPAY 钱包账号检查是否可收款。接口不返回用户姓名、余额或其他资料,商户仍需在自己的业务流程里确认收款人。
请求
| 请求字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
account |
string | 是 | 大写 W + 8 位大写字母或数字,例如 W00012836。 |
{
"account": "W00012836"
}成功响应 data
| 响应字段 | 类型 | 说明 |
|---|---|---|
exists |
boolean | 钱包是否存在。false 时只返回此字段。 |
walletId |
string | 仅钱包存在时返回。将此值传给代付的 recipientWalletId。 |
account |
string | 仅钱包存在时返回。匹配的钱包账号(W + 8 位大写字母或数字)。 |
canReceive |
boolean | 仅钱包存在时返回。true 表示当前允许收款;代付时仍检查账户和收款容量。 |
完整响应示例 · HTTP 200
{
"success": true,
"code": "OK",
"message": "",
"requestId": "77777777-8888-4999-8aaa-bbbbbbbbbbbb",
"timestamp": "2026-11-05T04:04:45Z",
"data": {
"exists": true,
"walletId": "W00012836",
"account": "W00012836",
"canReceive": true
}
}钱包不存在也返回 HTTP 200,此时请用户核对账号:
{
"success": true,
"code": "OK",
"message": "",
"requestId": "88888888-9999-4aaa-8bbb-cccccccccccc",
"timestamp": "2026-11-05T04:04:50Z",
"data": {
"exists": false
}
}5. 结果回调
5.1 接收与验签
建单请求选填 notifyUrl(异步结果通知)和 returnUrl(同步返回商户页面),各随订单保存,最多 512 个 ASCII 字符;中文等非 ASCII 内容须按 URL 百分号编码。必须是完整 HTTPS URL,不含用户凭据、片段或控制字符;仅本地验证允许 http://127.0.0.1。省略 notifyUrl 不生成结果通知,notificationStatus 为 null;省略 returnUrl 不展示返回商户入口。显式 null 或空字符串等同省略;非空的无效 URL 被拒绝。两个方向均接受这两个字段;代付没有收银台,因此 returnUrl 仅保存,不触发跳转。代收终态结果页通过“返回商户”按钮打开原样保存的地址,不自动跳转、不追加结果参数。返回本身不代表成功,商户必须验签通知或服务端查单。
地址按订单生效。 代收、代付分别使用各次请求的地址;新订单地址不影响旧订单及其补发。平台仍须配置允许发送的 HTTPS 源地址。
BRPAY 使用 HTTP POST 和 UTF-8 JSON 投递。接收端应直接返回状态码,不要重定向。
| 请求头 | 说明 |
|---|---|
X-BrazilPay-Event-Id |
小写 UUID,与正文 eventId 相同 |
X-BrazilPay-Timestamp |
本次发送时 UTC RFC3339,以 Z 结尾;不是请求签名的 Unix 秒 |
X-BrazilPay-Signature |
64 位小写 HMAC-SHA256 hex |
签名输入为以下字节拼接,正文必须使用收到的原始 UTF-8 字节,末尾不追加换行:
canonicalBytes = utf8(timestamp + "\n" + eventId + "\n") + rawBodyBytes
signature = hmacSha256Hex(utf8(signingSecret), canonicalBytes)使用常量时间比较;SDK 验证签名、头/正文事件号、订单字段和 300 秒发送时间窗口。occurredAt 可以是较早的业务事件时间,不能用它替代发送时间验证。
5.2 事件格式
代收付款成功、代付到账时发送 ORDER_SUCCEEDED。代收过期或代付明确失败时发送 ORDER_FAILED;PENDING 不发送终态通知。通知发送失败不改变资金状态,也不会重新占用或释放资金。
| 回调字段 | 类型 | 说明 |
|---|---|---|
eventId |
string | 事件 UUID,与 X-BrazilPay-Event-Id 相同;重投保持不变。 |
eventType |
string | ORDER_SUCCEEDED、ORDER_FAILED 或 ORDER_CORRECTED(订单更正)。 |
occurredAt |
string | 订单进入终态的时间,等于 data.completedAt(UTC)。 |
data |
object | 订单结果,字段见下表。 |
data 的字段
| 回调字段 | 类型 | 说明 |
|---|---|---|
orderNo |
string | 平台单号,代收 RC、代付 PY,前缀后接 14 位数字。 |
merchantOrderNo |
string | 原请求的商户单号。 |
direction |
string | PAYIN 代收或 PAYOUT 代付。 |
currency |
string | 固定 BRC。 |
orderAmount |
string | 原订单的 BRC 数量。 |
feeAmount |
string | 建单时确定的手续费,单位 BRC,可为 "0.00"。 |
merchantAmount |
string | 正数,单位 BRC:代收为商户净入账,代付为商户含手续费支出。代收过期不会入账。 |
actualAmount |
string / null | 成功时为实际从用户扣除(代收)或转给用户(代付)的 BRC 数量;过期为 null。 |
status |
string | SUCCESS 表示已付款或到账;FAILED 表示明确失败终态,适用于代收过期或代付处理失败;原因见 failureCode。 |
failureCode |
string / null | 失败原因;FAILED 时必返,当前过期为 ORDER_EXPIRED;其他状态为 null。 |
walletId |
string / null | 代付各终态均为指定收款钱包;代收仅 SUCCESS 为实际付款钱包,其余为 null。钱包标识本身不代表到账。 |
createdAt |
string | 建单时间,UTC。 |
expiresAt |
string / null | 代收付款截止时间(UTC);代付为 null。 |
completedAt |
string | 订单终态时间(UTC)。成功为完成时间;过期等于 expiresAt。 |
以下给出三种完整的回调正文。回调是平台发给商户的请求,外层直接是 eventId、eventType、occurredAt、data。
代收已付款 · ORDER_SUCCEEDED
{
"eventId": "44444444-5555-4666-8777-888888888888",
"eventType": "ORDER_SUCCEEDED",
"occurredAt": "2026-11-05T04:02:00Z",
"data": {
"orderNo": "RC00000000128364",
"merchantOrderNo": "RECHARGE-001",
"direction": "PAYIN",
"currency": "BRC",
"orderAmount": "30.00",
"feeAmount": "0.40",
"merchantAmount": "29.60",
"actualAmount": "30.00",
"status": "SUCCESS",
"walletId": "W00012836",
"createdAt": "2026-11-05T04:00:00Z",
"expiresAt": "2026-11-05T04:30:00Z",
"completedAt": "2026-11-05T04:02:00Z",
"failureCode": null
}
}代收成功的 HTTP 请求头示例(离线验签)。使用 §2.2 的示例密钥和上面代收成功的 JSON(UTF-8、LF、正文无结尾换行),可以复算下面的签名。验时间窗口时,把测试时钟固定到 X-BrazilPay-Timestamp;真实接收端用当前时间。
POST /webhooks/br-pay HTTP/1.1
Host: merchant.example.com
Content-Type: application/json; charset=utf-8
X-BrazilPay-Event-Id: 44444444-5555-4666-8777-888888888888
X-BrazilPay-Timestamp: 2026-11-05T04:02:01Z
X-BrazilPay-Signature: be9644982cae18aead3d900226ca585e2d30e6ab62370147e6f049404ce858a0
Content-Length: 585代付已到账 · ORDER_SUCCEEDED
{
"eventId": "44444444-5555-4666-8777-bbbbbbbbbbbb",
"eventType": "ORDER_SUCCEEDED",
"occurredAt": "2026-11-05T04:05:00Z",
"data": {
"orderNo": "PY00000000128365",
"merchantOrderNo": "WITHDRAW-001",
"direction": "PAYOUT",
"currency": "BRC",
"orderAmount": "20.00",
"feeAmount": "0.60",
"merchantAmount": "20.60",
"actualAmount": "20.00",
"status": "SUCCESS",
"walletId": "W00012836",
"createdAt": "2026-11-05T04:05:00Z",
"expiresAt": null,
"completedAt": "2026-11-05T04:05:00Z"
}
}代收未付款到期 · ORDER_FAILED
{
"eventId": "44444444-5555-4666-8777-aaaaaaaaaaaa",
"eventType": "ORDER_FAILED",
"occurredAt": "2026-11-05T04:30:00Z",
"data": {
"orderNo": "RC00000000128366",
"merchantOrderNo": "RECHARGE-002",
"direction": "PAYIN",
"currency": "BRC",
"orderAmount": "30.00",
"feeAmount": "0.40",
"merchantAmount": "29.60",
"actualAmount": null,
"status": "FAILED",
"walletId": null,
"createdAt": "2026-11-05T04:00:00Z",
"expiresAt": "2026-11-05T04:30:00Z",
"completedAt": "2026-11-05T04:30:00Z",
"failureCode": "ORDER_EXPIRED"
}
}5.3 确认与重复通知
- 先验签。 保留收到的原始正文,用三个回调头验签。验签失败时不更新订单、不返回 2xx。
- 找到自己的订单。 用
data.merchantOrderNo查本地记录,核对方向、BRC数量、已保存的平台单号,以及代付收款账号。若回调比下单响应先到、还没保存平台单号,先按原商户单号查单核实。 - 按结果处理。
ORDER_SUCCEEDED:已付款或已到账,只对尚未处理的本地订单做一次充值或确认提现。ORDER_FAILED:代收失败不充值,代付失败不确认到账;ORDER_CORRECTED 按订单修订处理。 - 提交后再确认。 本地订单和必要的业务记录保存成功后,返回
204 No Content;其他 2xx 也可以,无需响应正文。收到已处理的同一事件并核对无误时,也返回 2xx。
平台会在订单结果保存后发送回调。重复投递使用相同的 eventId 和正文,但会重新生成发送时间和签名。收不到回调时可主动查单;通知失败不会撤销付款或到账。当前工程基线:最多 10 次自动尝试、5 秒 HTTP 超时、5 秒起指数退避,最高 300 秒;这些是可调工程值。应在 5 秒内持久化必要业务,再把耗时工作交给商户自己的后台。自动重试用完后,仍可查单,也可联系平台补发。
未配置发送范围时,平台保留待通知并记录 DELIVERY_DISABLED,不消耗 HTTP 发送次数。不要据此重新创建资金订单。
6. 错误处理与结果未确认
| HTTP | code | 处理方式 |
|---|---|---|
| 400 | MERCHANT_REQUEST_INVALID |
字段/JSON/UUID 格式错误,修正输入 |
| 401 | MERCHANT_AUTH_REQUIRED / INVALID_SIGNATURE / SIGNATURE_EXPIRED |
检查商户 ID、密钥、原始字节、路径和服务器时间 |
| 403 | MERCHANT_DIRECTION_DISABLED / ORIGIN_REJECTED |
商户关闭方向时查询接单状态;Origin 拒绝时改由商户服务端调用 |
| 403 | ACCOUNT_DISABLED |
收款账户已禁用,停止向该账户代付并联系平台核对;重新校验钱包 canReceive,不靠更换商户单号重试 |
| 404 | ORDER_NOT_FOUND / WALLET_NOT_FOUND |
核对商户单号或钱包;钱包查询不存在通常返回 exists=false |
| 405 | METHOD_NOT_ALLOWED |
改用 POST;响应带 Allow: POST |
| 409 | ORDER_EXISTS |
查原单并核对业务,不把冲突当成功 |
| 409 | MERCHANT_INSUFFICIENT_BALANCE / WALLET_LIMIT_EXCEEDED |
商户余额不足或用户余额容量不足 |
| 409 | CONFIG_CHANGED / MERCHANT_DAILY_LIMIT |
配置变化或已用完日额度,重新核对接单状态 |
| 413 | BODY_TOO_LARGE |
正文超过 8192 字节 |
| 415 | JSON_REQUIRED |
发送 application/json |
| 422 | INVALID_AMOUNT / INVALID_FEE / MERCHANT_AMOUNT_OUT_OF_RANGE |
核对数量字符串、费用和实际允许范围 |
| 503 | MAINTENANCE_CONFIG_UNAVAILABLE |
无法确认维护配置,服务拒绝接新单;联系平台恢复配置。资金回执按结果未确认处理,先查原单 |
| 503 | CLIENT_MAINTENANCE |
全站维护,不接新单;已有订单/账户仍可查询 |
| 503 | MERCHANT_SCHEMA_REQUIRED / CLIENT_MAINTENANCE / SERVICE_UNAVAILABLE |
服务或配置暂不可用,联系平台;资金结果未确认需先查单 |
HTTP 5xx、断线、超时、读不到响应,或响应里的单号对不上,都不能判断资金是否已经划转。先查原商户单号。错误码区分大小写。
例:代付 WITHDRAW-001 超时了。
- 保存原请求、商户单号
WITHDRAW-001和请求追踪号;先不要改余额,也不要换一个单号再代付。 - 用
{"merchantOrderNo":"WITHDRAW-001"}查原单。查到SUCCESS时核对方向、BRC 数量和收款钱包,核对一致再确认已到账。查到PENDING时仍在处理且资金继续占用;FAILED时确认失败并已释放占用。查到代收单须核对方向,不能当作代付结果。 - 如果查询本身也超时或返回 5xx,等待后继续查原单。只有明确返回
404 / ORDER_NOT_FOUND时,才可用原商户单号和原业务参数重新提交;原请求仍可能在处理,若收到409 / ORDER_EXISTS,继续查同一单。
Java / PHP SDK 用 MerchantApiException 返回明确的业务拒绝,用 UnknownOutcomeException 表示结果未确认(含 HTTP 5xx)。默认请求总期限 30 秒,包含连接、响应头及正文,最多接收 256 KiB;可按实际环境调整期限。SDK 不自动跟随重定向。
7. SDK 使用
7.1 Java
Java 17+。使用交付包的 merchant-sdk.jar 及独立 POM,或构建 sdk/java。依赖 tools.jackson.core:jackson-databind:3.1.4。下例展示独立调用方式,数量与收款人须来自商户自己的业务校验,不能作为连续自动执行脚本:
import java.time.Duration;
import tech.brazilpay.merchant.sdk.MerchantClient;
import tech.brazilpay.merchant.sdk.MerchantWebhookVerifier;
// Load settings from your server-side configuration / secret store.
var client = new MerchantClient(baseUrl, merchantId, signingSecret, Duration.ofSeconds(30));
var balance = client.queryBalance();
String availableBalance = balance.body().path("data").path("availableBalance").asText();
var availability = client.queryAvailability("PAYOUT");
var wallet = client.queryWallet("W00012836");
// Check exists / canReceive and confirm the actual recipient before payout.
var payin = client.payin(new MerchantClient.PayinRequest("RECHARGE-001", "30.00", "Game recharge", notifyUrl, returnUrl));
var payout = client.payout(new MerchantClient.PayoutRequest("WITHDRAW-001", "20.00", "Game withdrawal", recipientWalletId, notifyUrl, returnUrl));
var order = client.queryOrder("RECHARGE-001");
var orderByPlatformNo = client.querySystemOrder("RC00000000128364");
var transactions = client.queryTransactions(0);
// Inside your HTTP callback receiver; rawBodyBytes must be the untouched body.
var event = new MerchantWebhookVerifier(signingSecret)
.verifyAndParse(timestampHeader, eventIdHeader, signatureHeader, rawBodyBytes);
// Verify your business order, persist under its state conditions, then return 2xx.7.2 PHP
PHP 8.1+,启用 cURL 和 JSON;Composer PSR-4 命名空间 BrazilPay\Merchant\,或加载 src/*.php。命令行 demo.php 默认只读余额,不自动下单。
use BrazilPay\Merchant\MerchantClient;
use BrazilPay\Merchant\MerchantWebhookVerifier;
// Require Composer autoload.php, or the supplied src/*.php classes first.
$client = new MerchantClient($baseUrl, $merchantId, $signingSecret, 30);
$balance = $client->queryBalance();
$availableBalance = $balance['data']['availableBalance'];
$availability = $client->queryAvailability('PAYOUT');
$wallet = $client->queryWallet('W00012836');
// Check exists / canReceive and confirm the actual recipient before payout.
$payin = $client->payin(['merchantOrderNo'=>'RECHARGE-001', 'orderAmount'=>'30.00', 'description'=>'Game recharge', 'notifyUrl'=>$notifyUrl, 'returnUrl'=>$returnUrl]);
$payout = $client->payout(['merchantOrderNo'=>'WITHDRAW-001', 'orderAmount'=>'20.00', 'description'=>'Game withdrawal', 'recipientWalletId'=>$recipientWalletId, 'notifyUrl'=>$notifyUrl, 'returnUrl'=>$returnUrl]);
$order = $client->queryOrder('RECHARGE-001');
$orderByPlatformNo = $client->querySystemOrder('RC00000000128364');
$transactions = $client->queryTransactions(0);
// Inside your HTTP callback receiver; reject failures before business changes.
$rawBody = file_get_contents('php://input');
$event = (new MerchantWebhookVerifier($signingSecret))->verifyAndParse(
$_SERVER['HTTP_X_BRAZILPAY_TIMESTAMP'] ?? '',
$_SERVER['HTTP_X_BRAZILPAY_EVENT_ID'] ?? '',
$_SERVER['HTTP_X_BRAZILPAY_SIGNATURE'] ?? '',
$rawBody
);
// Verify your business order, persist under its state conditions, then return 2xx.8. App 付款与网页引导
- 商户服务端创建代收单,客户端打开 cashierUrl,或使用 Android ACTION_VIEW 打开 appUrl。
- 有有效 Web 会话时直达原单确认页;其余用户可选择网页支付、打开 App 或在 App 内扫描收银页的二维码。确认安装关联 App 时会优先提示打开。链接只携带随机订单凭证,不授权扣款。
- 登录/注册、基础资料和支付密码补齐后返回原订单。网页关闭时可从商户原订单再次打开。
- 以服务端 expiresAt 为准,安装不延长期限;以签名回调或服务端查单确认 SUCCESS,再完成商户业务。
App 拉起不是付款成功。正式 HTTPS App Links 域名关联、正式包签名和真机支付属于独立验收。配置的付款 origin 必须实际承载 /pay/*,不能仅指向没有付款入口的官网;SDK 仅接受当前 /pay/ 路径。
9. 本地 SDK DEMO
打开本机 DEMO。它是可选的调试工作台,网页上的 /api/* 是 DEMO 自己的地址,不应填进商户系统。接入请使用平台发给你的 baseUrl;要用 DEMO 联调新版交易,先确认它连接的是同版商户 API。OpenAPI 从本页顶部下载。
在测试环境联调回调前,请与平台确认该测试商户的回调地址及通知发送已启用。只读查询通过不等于回调送达。
10. 接入验收
- 核对 baseUrl、merchantId 和密钥;用只读请求验证签名。确认建单已提供 notifyUrl,且平台能从允许的 HTTPS 源投递到该接收端。
- 按允许的测试环境验证代收成功/到期、代付成功/明确拒绝,以及费用与双侧余额变化。
- 验证篡改正文/错误签名/过期时间被拒绝,成功回调持久化后返回 2xx,重复通知不重复完成业务。
- 验证超时和 5xx 按结果未确认处理,使用原商户单号查询和恢复;不能创建新业务号掩盖未知结果。
- 真实设备 App 付款、HTTPS、正式回调出站范围及发布由对应环境单独验收。本地构建与页面检查不能替代这些结果。
后台订单更正通知
管理员在核验实际资金后,可将未完成订单改为成功/失败,也可更正原终态。更正沿用原本金和原手续费,通过追加账变完成;订单号及原始金额不变。
更正通知事件为 ORDER_CORRECTED,包含 correctionId(更正记录 UUID)、previousStatus(PENDING/SUCCESS/FAILED)和 data.orderRevision(递增正整数)。data 是更正后的完整终态快照;status 为 SUCCESS 或 FAILED,失败时 failureCode=MANUAL_CORRECTION。失败的代收订单可以保留原付款用户的 W 账号。查单也返回 orderRevision,原始结果为 0。
每次更正有新的事件 ID。该事件自身的重复投递保持原事件 ID 和正文;旧事件保留原正文和尝试审计,不被改写为新结果。商户须在自己的订单事务中保存最近已处理的终态修订:尚未处理终态时,接收第一次正常成功/失败事件(修订 0);已处理终态后,只处理更高修订。PENDING 查单中的修订 0 不表示已处理终态,不能因此丢弃随后到达的正常终态事件。旧修订晚到不能覆盖新结果,同一事件重复送达不能再次执行业务效果。已产生的游戏充值、发货等外部效果必须按更正事件对账,不能把更正当成第二次充值。
Java/PHP SDK 验签器识别该事件;Demo 展示更正后的快照与原状态。Demo 的内存通知列表不替代商户自身的持久业务账。