版本:v1.13.0 · 更新:2026-10-27。本文面向接入 HissPay 的下游商户,依据当前生产接口源码和产品配置编写;各版本改动见第 17 节「变更记录」。示例中的应用、订单和账户均为占位值。产品配置可能变化;下单前以商户实际签约及平台通知为准。
1. 开始接入
生产网关:https://transaction.hisspay.com;沙箱网关:https://sandbox-api.hisspay.com。使用前请登录商户后台(mch.hisspay.com)→「应用密钥」核对环境和应用ID(appId),生产与沙箱凭据不得混用。下文通用示例以 {GATEWAY} 代替。
所有接口为 HTTPS POST,使用 Content-Type: application/json。交易请求由商户服务端发起,不要把密钥放在网页、App 安装包或公开仓库中。
接入前向平台确认:
- 商户已审核并启用;取得商户号。
- 创建并启用 API 应用,取得该应用的
appId和appSecret。商户号不能替代 appId。 - 开通交易币种和账户,分别签约代收、代付产品,取得对应产品编码。
- 平台已配置可用交易路由、金额范围、费率和结算周期。应用可用不代表所有产品可交易。
- 代付申请需配置商户服务器的公网出口 IP 白名单;多出口机器应提供完整清单。回调来源 IP 另向平台确认,不能用上游服务商 IP 替代平台通知来源。
- 提供公网 HTTPS 回调地址;准备正确验签、事务幂等和订单核对逻辑。
示例使用 INR。其他币种和支付方式以商户实际开通结果为准,不能仅改变 currency 就跨币种交易。名称包含 test 的商户或应用不等于沙箱;生产请求涉及真实业务。
当前币种及产品目录(2026-10-27 生产快照)
产品在目录中出现,不等于该商户可交易。服务端还会检查应用、商户币种、有效协议、金额及可用路由/渠道。下表由产品清单自动生成,状态含义如下:
LIVE:可用。仍须商户签约、开通币种并有可用路由才能交易;不保证任一商户都已开通,也不保证实际交易成功。PLANNED:即将开放,不可下单。BLOCKED:暂不可用,不可下单。
「说明」列出该产品在通用字段之外对下单请求的要求,具体字段见第 6 节「按支付方式的下单要求」。标为 PLANNED 或 BLOCKED 的产品,开放时间以平台通知为准。
共 30 个目录产品:LIVE 7 个,PLANNED 13 个,BLOCKED 10 个。
| 状态 | 币种 | 方向 | 产品编码 | 名称 | 支付方式 | 说明 |
|---|---|---|---|---|---|---|
| LIVE(可用) | AUD | 代收 | AUD-PAYPLUS-APPLE-M85 | 澳元 Apple Pay(AUD Apple Pay) | 钱包 | 已向部分商户开放;此编码仅适用于实际签约该产品的应用。请以后台签约产品为准,其他商户不要直接复用。 |
| LIVE(可用) | AUD | 代收 | AUD-PAYPLUS-GOOGLE-M85 | 澳元 Google Pay(AUD Google Pay) | 钱包 | 已向部分商户开放;此编码仅适用于实际签约该产品的应用。请以后台签约产品为准,其他商户不要直接复用。 |
| LIVE(可用) | AUD | 代收 | AUD-VKDPWMRT | AU-PAYIN(Australia AUD pay-in) | 银行卡 | 澳洲银行卡托管支付:须传 payType=CARD、clientIp(真实付款人 IP,键名区分大小写)、cardExtra(JSON 字符串,格式见第 6 节「澳洲银行卡(AUD-VKDPWMRT)」)和 https 的 returnUrl;金额 1–500 AUD,整数或 .99(如 20、20.00、19.99);仅支持澳洲手机号与澳洲账单信息;返回支付链接 payUrl,结果以回调或查单为准。 |
| LIVE(可用) | INR | 代收 | INR-HBGMMNKI | 印度卢比-收款T0(India INR pay-in (T+0)) | 未细分 | — |
| LIVE(可用) | INR | 代付 | INR-NPLMSUEX | 印度卢比-默认代付(India INR default payout) | 未细分 | — |
| LIVE(可用) | THB | 代收 | THB-PROMPTPAY-PAYIN | 泰铢-PromptPay 扫码代收(Thailand THB PromptPay QR pay-in) | 未细分 | 按商户逐个开通,以开通通知为准。不传 payType;cardExtra 须为含 payerAccountNo、payerBankCode 的 JSON 字符串;默认单笔 500.00–49999.00 THB;返回收银台 payUrl,结果以回调或查单为准(见第 16 节)。 |
| LIVE(可用) | USD | 代收 | USD-EKCBBONA | 美元-国际卡收款(USD international card pay-in) | 银行卡 | 银行卡托管收银台:下单返回支付链接 payUrl,引导付款人跳转付款;结果以回调或查单为准。 |
| PLANNED(即将开放,不可下单) | EUR | 代收 | EUR-CARD-PAYIN | 欧元-银行卡代收(托管收银台,需购物车/购物者信息)(EUR card pay-in (hosted checkout, cart & shopper required)) | 银行卡 | 银行卡代收须在下单请求中提供 cardExtra(购物车和购物者信息,见第 6 节「按支付方式的下单要求」);金额两位小数;返回支付链接 payUrl,结果以回调或查单为准。 |
| PLANNED(即将开放,不可下单) | GBP | 代收 | GBP-CARD-PAYIN | 英镑-银行卡代收(托管收银台,需购物车/购物者信息)(GBP card pay-in (hosted checkout, cart & shopper required)) | 银行卡 | 银行卡代收须在下单请求中提供 cardExtra(购物车和购物者信息,见第 6 节「按支付方式的下单要求」);金额两位小数;返回支付链接 payUrl,结果以回调或查单为准。 |
| PLANNED(即将开放,不可下单) | JPY | 代收 | JPY-CARD-PAYIN | 日元-银行卡代收(托管收银台,需购物车/购物者信息)(JPY card pay-in (hosted checkout, cart & shopper required)) | 银行卡 | 银行卡代收须在下单请求中提供 cardExtra(购物车和购物者信息,见第 6 节「按支付方式的下单要求」);JPY 金额须为整数(否则 10006);返回支付链接 payUrl,结果以回调或查单为准。 |
| PLANNED(即将开放,不可下单) | THB | 代付 | THB-BANK-PAYOUT | 泰铢-银行转账代付(Thailand THB bank transfer payout) | 银行转账 | 需单独开通,收到开通通知(含可用银行、户名长度上限、单笔范围与费率)前不可下单;bizCode 固定 BANK(见第 16 节)。 |
| PLANNED(即将开放,不可下单) | USD | 代收 | USD-APPLEPAY-PAYIN | 美元-Apple Pay 托管代收(USD Apple Pay hosted pay-in) | 钱包 | Apple Pay 托管页,仅 USD:须提供付款人公网 IPv4(clientIp);单笔限额待确认;返回支付链接 payUrl,结果以回调或查单为准。 |
| PLANNED(即将开放,不可下单) | USD | 代收 | USD-CARD-PAYIN | 美元-银行卡代收(托管收银台,需购物车/购物者信息)(USD card pay-in (hosted checkout, cart & shopper required)) | 银行卡 | 银行卡代收须在下单请求中提供 cardExtra(购物车和购物者信息,见第 6 节「按支付方式的下单要求」);金额两位小数;返回支付链接 payUrl,结果以回调或查单为准。 |
| PLANNED(即将开放,不可下单) | USD | 代收 | USD-CASHAPP-PAYIN | 美元-Cash App 代收(USD Cash App pay-in) | 钱包 | Cash App 代收,仅 USD,单笔 1–99:须提供付款人公网 IPv4(clientIp);返回支付链接 payUrl;实付可能比订单金额少 0.01–0.20,按通知中的 realAmount 入账。 |
| PLANNED(即将开放,不可下单) | USD | 代收 | USD-HOSTED-PAYIN | 美元-托管收银台代收(USD hosted checkout pay-in) | 托管收银台 | 托管收银台:须提供格式有效的付款人 email;返回支付链接 payUrl(有有效期),结果以回调或查单为准;不能指定只收银行卡。 |
| PLANNED(即将开放,不可下单) | USD | 代付 | USD-LOCAL-PAYOUT | 美国-代付(待配置)(USD payout (draft)) | 钱包 | Cash App 代付,仅 USD:收款人 Cash App 账户名填入 account(字段映射待确认;格式规则待确认:1–64 位字母或数字,不能是纯数字,不含 $ 或 @),accountName 必填;处理可能长达约 1 小时,期间为处理中,不是失败。 |
| PLANNED(即将开放,不可下单) | USDT | 代收 | USDT-BSC | USDT-BSC 网络代收(USDT pay-in on BSC) | 加密资产 | USDT 代收,金额最多 4 位小数;下单返回 payUrl 与收款信息 paymentInfo;结果以回调或查单为准,链上确认后查单返回 txHash。 |
| PLANNED(即将开放,不可下单) | USDT | 代收 | USDT-ETHEREUM | USDT-Ethereum 网络代收(USDT pay-in on Ethereum) | 加密资产 | USDT 代收,金额最多 4 位小数;下单返回 payUrl 与收款信息 paymentInfo;结果以回调或查单为准,链上确认后查单返回 txHash。 |
| PLANNED(即将开放,不可下单) | USDT | 代收 | USDT-SOLANA | USDT-Solana 网络代收(USDT pay-in on Solana) | 加密资产 | USDT 代收,金额最多 4 位小数;下单返回 payUrl 与收款信息 paymentInfo;结果以回调或查单为准,链上确认后查单返回 txHash。 |
| PLANNED(即将开放,不可下单) | USDT | 代收 | USDT-TRON | USDT-Tron 网络代收(USDT pay-in on Tron) | 加密资产 | USDT 代收,金额最多 4 位小数;下单返回 payUrl 与收款信息 paymentInfo;结果以回调或查单为准,链上确认后查单返回 txHash。 |
| BLOCKED(暂不可用,不可下单) | AUD | 代收 | AUD-CARD | 澳洲-银行卡代收(待开通)(Australia card pay-in (pending)) | 银行卡 | — |
| BLOCKED(暂不可用,不可下单) | AUD | 代付 | AUD-LOCAL-PAYOUT | 澳大利亚-代付(待配置)(Australia payout (draft)) | 未细分 | — |
| BLOCKED(暂不可用,不可下单) | BDT | 代收 | BDT-LOCAL-PAYIN | 孟加拉-代收(待配置)(Bangladesh pay-in (draft)) | 未细分 | — |
| BLOCKED(暂不可用,不可下单) | BDT | 代付 | BDT-LOCAL-PAYOUT | 孟加拉-代付(待配置)(Bangladesh payout (draft)) | 未细分 | — |
| BLOCKED(暂不可用,不可下单) | IDR | 代收 | IDR-LOCAL-PAYIN | 印度尼西亚-代收(待配置)(Indonesia pay-in (draft)) | 未细分 | — |
| BLOCKED(暂不可用,不可下单) | IDR | 代付 | IDR-LOCAL-PAYOUT | 印度尼西亚-代付(待配置)(Indonesia payout (draft)) | 未细分 | — |
| BLOCKED(暂不可用,不可下单) | PHP | 代收 | PHP-LOCAL-PAYIN | 菲律宾-代收(待配置)(Philippines pay-in (draft)) | 未细分 | — |
| BLOCKED(暂不可用,不可下单) | PHP | 代付 | PHP-LOCAL-PAYOUT | 菲律宾-代付(待配置)(Philippines payout (draft)) | 未细分 | — |
| BLOCKED(暂不可用,不可下单) | PKR | 代收 | PKR-LOCAL-PAYIN | 巴基斯坦-代收(待配置)(Pakistan pay-in (draft)) | 未细分 | — |
| BLOCKED(暂不可用,不可下单) | PKR | 代付 | PKR-LOCAL-PAYOUT | 巴基斯坦-代付(待配置)(Pakistan payout (draft)) | 未细分 | — |
生产产品状态 0 为正常(对应 LIVE)、1 为锁定(本表记为 PLANNED 或 BLOCKED);以上仅供识别目录,不构成交易授权。平台还有更广的支付方式编码(CARD、PAYPAL、CASHAPP、GOOGLE_PAY 等,见第 6 节「支付方式编码」),但编码并不代表已经签约或有可用通道。没有明确开通的组合不能交易。现金类产品编码以平台分配为准,目录中暂无现金类产品行。币种余额查询还要求商户有相应账户。
2. 接口清单
| 操作 | 路径 | 说明 |
|---|---|---|
| 创建代收订单 | /transaction/payin/v1/apply | 返回支付页面及平台订单号 |
| 查询代收订单 | /transaction/payin/v1/query | 查询本应用的订单 |
| 创建代付订单 | /transaction/payout/v1/apply | 受理成功后异步处理,不代表已到账 |
| 查询代付订单 | /transaction/payout/v1/query | 查询最终付款状态 |
| 查询账户余额 | /transaction/payout/v1/balance | 返回账面、可用和冻结余额 |
| 创建收款人 | /transaction/payout/v1/beneficiaries/create | 仅已开通 USD 银行卡代付的应用,见第 7 节「收款人管理」 |
| 查询收款人 | /transaction/payout/v1/beneficiaries/query | 同上 |
| 更新收款人 | /transaction/payout/v1/beneficiaries/update | 同上 |
| 沙箱:查看来源 IP | /transaction/sandbox/v1/network | 仅沙箱。返回 environment 与平台看到的来源 IP sourceIp,用于配置代付白名单(沙箱默认不校验白名单,开启「模拟生产白名单」后才需要) |
| 沙箱:查询 / 修改沙箱设置 | /transaction/sandbox/v1/settings/query、/transaction/sandbox/v1/settings/update | 仅沙箱。payoutIpWhitelistMode:SANDBOX_OPEN(默认,不校验代付来源 IP)或 PRODUCTION(模拟生产白名单,名单外返回 10010) |
| 沙箱:通知投递记录 | /transaction/sandbox/v1/notify-log/query | 仅沙箱。本商户最近的通知投递结果(限速顺延、熔断、拒绝非公网目标等),见沙箱测试指南「回调规则」 |
| 沙箱:查询模拟单 | /transaction/sandbox/v1/query | 仅沙箱。查看模拟渠道状态与投递记录,见沙箱测试指南 |
| 沙箱:手动终态 | /transaction/sandbox/v1/trigger | 仅沙箱。对可手动场景的模拟单触发 PAID / FAILED |
| 沙箱:重发通知 | /transaction/sandbox/v1/resend | 仅沙箱。请平台重发最近终态通知 |
所有接口都使用第 3 节的请求签名。上述沙箱接口只在沙箱网关存在,生产网关返回 404。生产代付始终校验来源 IP 白名单。
代收查单、代付查单、余额查询当前分别按 appId 限制 每秒 5 次。建议订单轮询间隔至少 3–5 秒,并在失败时退避。下单 TPS、日额度和路由限额需单独确认;不要把查询限频当作下单容量承诺。
3. 请求签名
必填请求头
| 请求头 | 类型 | 说明 |
|---|---|---|
appId | string | 应用标识 |
timestamp | string | Unix 时间戳,毫秒,与平台时间差不得超过 30 秒 |
nonce | string | 至少 10 个字符;推荐随机 UUID,每次请求重新生成 |
sign | string | SHA-256 小写十六进制,64 位 |
服务器应同步时间。相同 appId 下的 nonce 在防重放窗口(当前 60 秒,即时间戳允许误差的两倍)内不能重复:重复使用已成功验签的 nonce 返回业务码 400(msg 形如 nonce has been used);两个并发请求同时使用同一 nonce 时,后到者返回 900(Request repeated)。重试查询时更换 nonce、timestamp、sign,但保留原订单标识。
签名相关的常见拒绝(均为业务码 400,msg 仅供诊断):缺少请求头(如 AppId cannot be null)、nonce 少于 10 个字符(nonce cannot be null)、时间戳不是整数或超出 30 秒(timestamp invalid / timestamp has expired)、请求体不是 JSON 对象或含 null 值(Signed JSON body invalid)、签名不一致(Sign invalid)。签名校验服务暂时不可用时返回 500(Signature verification temporarily unavailable),换新 nonce 稍后重试。代付类接口(第 7 节)把这些 msg 统一改写为 Payout request rejected,业务码不变;但代付下单缺少签名请求头或 Content-Type 不是 application/json 时返回业务码 500(Payout request not confirmed; query original order),请先确认请求头齐全再按第 11 节处理。
计算规则
- 合并请求体字段与请求头
appId、timestamp、nonce;不要加入sign。 - 字段名按字典序升序排序,拼成
key=value,各项以&连接。 - 在拼接结果末尾直接追加 appSecret,不加分隔符。
- 对完整字符串按 UTF-8 编码计算 SHA-256,输出小写十六进制。
sign = lowercase_hex(SHA256(UTF8(sorted_key_value_pairs + appSecret)))
下游签名不是 HMAC,也不需要 RSA;不要套用上游通道文档的签名算法。无需对待签名值做 URL 编码,不把整个 JSON 原文当成待签名串。HTTP 请求中无需传 appSecret。
请求体使用扁平 JSON 对象。推荐字段值使用字符串,金额统一写为 "100.00";省略无值的可选字段:不能发送 null(任何字段为 null 都会被拒绝,业务码 400),也不要发送数组或嵌套对象。若发送空字符串,应将该字段的 key= 纳入签名。不要签名后再更改金额格式或字段值。
例如,以下为余额查询的固定签名演示,密钥完全虚构:
body: {"currency":"INR"}
appId: demo-app
timestamp: 1700000000000
nonce: demo-nonce-0001
appSecret: demo-secret
待签名串:
appId=demo-app¤cy=INR&nonce=demo-nonce-0001×tamp=1700000000000demo-secret
此时间戳只用于核对算法,不能直接请求生产接口。可下载固定测试向量、Python 示例和 Node.js 示例。两个示例默认只做只读余额查询,从服务器环境变量读取网关 HISSPAY_GATEWAY 与凭据 HISSPAY_APP_ID、HISSPAY_APP_SECRET;旧变量名 PAYPLAT_APP_ID、PAYPLAT_APP_SECRET 暂时仍可使用,但会提示已弃用,请改名。
4. 金额与订单号
- 下游接口金额单位是币种主单位。100 INR 传
"100.00",不要传上游 paise 值10000。 - 使用十进制定点计算,避免二进制浮点金额误差。精度按币种:
- 一般法币(INR、USD、AUD、THB 等)最多两位小数;
- 零小数位币种(JPY、KRW、VND、CLP、UGX 等)必须为整数,否则返回 10006;
- 三位小数币种(BHD、JOD、KWD、OMR、TND)当前不支持,下单返回 10006;
- USDT 最多 4 位小数(四个网络一致,见第 6 节「USDT 代收」)。
- 超出精度的金额会被拒绝(10006);实际通道可要求整数金额或更严格的范围。
- 代收金额必须大于 0。
- 代付金额必须大于 0,且不低于代付最低金额。通道下限由平台为代付通道配置;未配置时为该币种默认值:AUD、USD 为 1.00;INR、PKR、BDT 为 100.00;其他币种为 100.00(平台可为通道设置其他值,可高于或低于默认值)。签约下限为商户签约中配置的最低金额,未配置时不额外限制。实际下限取通道下限与签约下限中的较高者(签约下限低于通道下限时按通道下限),并仍需满足签约路由的上下限。低于下限返回
10006;代付接口当前把 msg 统一改写为Payout request rejected(见第 11 节),不会显示具体下限,请以签约与开通通知中的金额范围自查。 - 银行账户、商户号、appId、平台订单号均作为字符串处理,保留前导零。
merchantOrderNo由商户生成;建议使用商户前缀+时间+随机串,控制在 64 个 ASCII 字符以内,使用字母、数字、_、-。接口上限为 255 个字符,超过会被拒绝(业务码 400;代收 msg 形如「请求参数不正确:merchantOrderNo too long」,代付 msg 统一为Invalid payout request,见第 11 节)。代收、代付上限相同。product为平台分配的产品编码,不要自行拼接或改写。代收下单最长 64 个字符,代付下单最长 20 个字符;超长返回业务码 400(product too long)。- 每笔业务使用唯一订单号。同一应用下,用已存在的
merchantOrderNo再次下单,无论参数是否相同,通常返回10020(商户订单号重复),不会返回原受理结果。平台可为个别产品单独启用业务幂等(目前仅可能用于 USD 银行卡代付):启用后同一单号、同一参数返回原受理结果,参数改变返回409。是否启用以开通通知为准;未说明时按 10020 处理。 - 无论是否启用幂等,下单超时或结果不确定时都先用原单号查单,不要换单号重试未知付款,也不要把 10020 当作下单失败。
实际到账金额规则
amount始终为创建订单时的原订单金额,用于核对订单,不能被回调改写。realAmount为平台核验后的实际收款总额(主币种单位、扣手续费前),是代收成功后下游入账的金额依据。它可能小于或大于amount,尤其是孟加拉实际付款金额通道。商户账户结算净额另按签约费率及固定费用计算,不能把realAmount当作扣费后的可用余额。- 不要要求
realAmount == amount才接受成功通知,也不要收到成功就按amount入账。 例如 BDT 原订单 100.00、实际收到 95.00,则按 95.00 记实际收款;实际收到 105.00,则按 105.00 记实际收款。商品交付、少付补款或多付处理由商户业务规则决定,不能伪造平台收款金额。 - 仅在成功状态、验签及订单归属核验通过、
realAmount为有效正数时执行幂等入账。未成功、金额缺失/非法、同一订单重复通知却金额不同、查询与通知矛盾时,保留待核对状态;不能回退使用原订单金额补记账。 - 平台仅对已完成实际金额验证的渠道自动处理差额;其他金额异常进入待核对。本文不表示尚未开通的国家或渠道已经可交易。
5. 通用响应
{"code":0,"msg":"Success","data":{}}
同时检查 HTTP 状态和 JSON code。HTTP 200 也可能包含业务错误。code=0 表示接口操作成功;是否支付成功要看订单状态。msg 供诊断展示,不作为稳定的程序判断条件。金额在响应中是 JSON 数字,小数位数不固定(可能是 100、100.0 或 100.0000,平台账务按 4 位小数存储),商户应使用支持 Decimal 的解析方式并按数值比较,不要按字符串比较。尚未成功的代收订单查单时 realAmount 可能为 0 或 0.0,不是实际收款。
接口响应通过 HTTPS 返回;当前交易接口没有统一的响应签名字段。异步回调另有签名,必须验签。
6. 代收下单
POST /transaction/payin/v1/apply
| 字段 | 必填 | 说明 |
|---|---|---|
merchantOrderNo | 是 | 商户唯一订单号,最长 255 个字符 |
amount | 是 | 主币种金额,如 "100.00";法币最多两位小数,零小数位币种(如 JPY)须为整数,USDT 最多 4 位小数(如 "12.3456");不符合返回 10006 |
currency | 是 | 如 INR,须已开通;USDT 代收固定为 USDT。四字母代码只支持 USDT,其他返回 10006 |
product | 是 | 平台分配、该商户已签约的代收产品编码,最长 64 个字符 |
name | 是 | 付款人姓名;具体格式受支付通道要求约束。AUD-VKDPWMRT 仍须填写但不使用,名/姓以 cardExtra 为准(见下文「澳洲银行卡(AUD-VKDPWMRT)」) |
phone | 是 | 付款人手机号;按通道要求填写,不能留空。AUD-VKDPWMRT 只能是 5–15 位数字,不带 +、空格、横线和国家码(见下文「澳洲银行卡(AUD-VKDPWMRT)」) |
email | 是 | 付款人邮箱,不能留空 |
userId | 是 | 商户侧付款用户的唯一标识,字符串,最长 32 个字符;同一付款人保持不变,参与签名。超过 32 个字符返回业务码 400 |
clientIp | 按渠道要求 | 真实付款人的公网 IPv4/IPv6 地址;部分渠道(包括印度原生 UPI)必填,按签约产品要求传入。值非空白(不是空串或全空白)时必须是合法的 IPv4 或 IPv6 文本,否则返回 10021(超过 45 个字符也返回 10021);空串或全空白在这一步按未传处理。此字段必须参与签名,不要使用商户服务器 IP 代替。键名区分大小写,必须写作 clientIp。AUD-VKDPWMRT 必填,见下文「澳洲银行卡(AUD-VKDPWMRT)」 |
notifyUrl | 否 | 本单公网 HTTPS 通知地址;未填时使用商户默认通知地址。非空时(空字符串视为未填)必须是合法的 http(s) URL,否则返回业务码 400 |
returnUrl | 否 | 支付后浏览器跳转地址;非空时(空字符串视为未填)必须是合法的 http(s) URL,否则返回业务码 400;部分通道要求必填的 HTTPS 地址。AUD-VKDPWMRT 必填 HTTPS,见下文「澳洲银行卡(AUD-VKDPWMRT)」 |
addr | 否 | 付款人地址,视通道要求。AUD-VKDPWMRT 不使用 |
payType | 否 | 支付方式编码,见下文「支付方式编码」表;显式传入时须与币种匹配,否则返回 10012;不传时按产品路由。不使用时请省略字段:传空字符串也返回 10012。编码不代表已开放。AUD-VKDPWMRT 必须显式传 CARD,见下文「澳洲银行卡(AUD-VKDPWMRT)」 |
otherExtend | 否 | 扩展字符串;当前下单响应可返回,不保证查单/回调携带 |
cardExtra | 按产品要求 | 通道扩展信息,字符串。AUD-VKDPWMRT 必填,格式见下文「澳洲银行卡(AUD-VKDPWMRT)」。部分即将开放的银行卡代收产品也须提交购物车和购物者信息,见下文「银行卡代收(需提交购物车和购物者信息)」;这些产品使用前向平台确认格式 |
{
"merchantOrderNo":"DEMO_IN_20260912_001",
"amount":"100.00",
"currency":"INR",
"product":"INR-HBGMMNKI",
"name":"Test User",
"phone":"YOUR_TEST_PHONE",
"email":"payer@example.com",
"notifyUrl":"https://merchant.example.com/payments/notify",
"returnUrl":"https://merchant.example.com/payments/result"
}
示例响应:
{"code":0,"msg":"Success","data":{"appId":"YOUR_APP_ID","merchantOrderNo":"DEMO_IN_20260912_001","orderNo":"PLATFORM_ORDER_NO","currency":"INR","product":"INR-HBGMMNKI","amount":100.00,"payUrl":"https://cashier.example.com/example","payStatus":0,"otherExtend":null}}
响应中的 amount 为 JSON 数字,请用 Decimal 解析;所有代收下单响应都返回 product。法币订单不返回 paymentInfo 键(仅 USDT 产品返回)。
记录平台 orderNo,再让付款人访问 payUrl。浏览器跳转到 returnUrl 不能作为到账凭证;以验签通过的回调或查单结果确认付款。
支付方式与实际开通
payType 是可选字段;不传时由已签约产品及路由决定。显式传入时平台只校验编码与币种的组合(不匹配返回 10012),不按 payType 选择通道,也不证明相应通道已在生产开通。当前已配置产品见上表;任何支付方式均须向平台确认适用产品、商户协议和通道要求。returnUrl 推荐公网 HTTPS,非空时须为合法 http(s) URL,不强制 HTTPS;部分上游可要求其必填。不要向本接口提交卡号或 CVV。例外:AUD-VKDPWMRT 必须显式传 payType=CARD,且 returnUrl 必填并必须为 HTTPS(见下文「澳洲银行卡(AUD-VKDPWMRT)」)。
支付方式编码
payType | 支付方式 | 可用币种 | 说明 |
|---|---|---|---|
CARD | 银行卡 | 24 个币种(含 USD、EUR、GBP、JPY、AUD、INR 等) | 卡支付/托管收银台 |
PAYPAL | PayPal | 20 个币种 | PayPal 钱包 |
CASHAPP | Cash App Pay | USD | Cash App 钱包支付;不是现金交付 |
GOOGLE_PAY | Google Pay(GPay) | USD、EUR、GBP、JPY、AUD | Google Pay 钱包支付;不设独立产品编码,配合银行卡、托管收银台或钱包类产品使用 |
APPLE_PAY | Apple Pay | USD、AUD | Apple Pay 托管页;AUD 见第 15 节(沙箱暂未同步 AUD + APPLE_PAY,当前返回 10012) |
CASH | 现金 | USD、AUD、EUR、GBP、INR、BDT、PKR、PHP、IDR | 现金类代收;现金类产品编码以平台分配为准 |
HOSTED | 托管收银台 | USD、AUD、EUR、GBP | 托管支付页,由付款人在页面选择方式 |
CRYPTO | 加密资产 | USDT | USDT 代收,见下文「USDT 代收」 |
WALLET | 钱包 | INR | 印度钱包/UPI |
JAZZCASH、EASYPAISA | 钱包 | PKR | 巴基斯坦钱包 |
BKASH、NAGAD | 钱包 | BDT | 孟加拉钱包 |
币种列是编码级校验范围,不是商户开通范围;不在范围内的组合返回 10012。具体某个产品能否使用某编码,以平台开通结果为准。商户只传标准 payType;上游通道字段由平台映射。
按支付方式的下单要求
平台将陆续开放银行卡、钱包和托管收银台等支付方式。本节说明这些支付方式对本接口下单请求的额外要求。对应产品在上方目录中标为 LIVE 之前不可下单。
- 商户只对接 HissPay 接口,只传本文列出的字段。上游所需的参数由平台配置,商户无需也不能传入。
- 标为「待确认」的内容尚未定稿。请勿自行猜测格式,接入前向平台索取该产品的字段说明。
- 请求签名、nonce 和通用字段规则不变(第 3、4 节)。新增要求只通过已有字段表达,不需要新的请求头。
通用要求
- 币种与金额:订单币种必须是该产品的币种。金额为主币种单位,最多两位小数;平台按支付方式换算金额时不做舍入,超出精度的金额会被拒绝(10006)。JPY 等零小数位币种的请求金额必须为整数,否则返回 10006。
- 付款人信息:
name、email、phone请填写付款人真实信息,不要使用占位值。部分支付方式会在平台侧校验邮箱格式、付款人 IP 等;不合格的请求会被拒绝,具体响应形式待确认。AUD-VKDPWMRT 的情况已经确定:请求先过通用校验,不合规时返回各自原有的业务码(400、10006、10012、10021 等);通过通用校验后,在本产品校验中不合规的,当前返回 9999(见下文「澳洲银行卡(AUD-VKDPWMRT)」与第 11 节「9999 自查清单」)。无法判断结果时,按下文「下单结果不确定时」处理。 returnUrl:使用公网 HTTPS。部分托管收银台产品付款后不会跳回returnUrl。无论是否跳回,都不能据此判断付款结果。- 不要向本接口提交卡号、CVV 或钱包凭据。这些支付方式都由托管支付页收集支付信息。
- 付款结果只以验签通过的平台回调或查单为准(第 8、10 节)。部分通道会在付款明确失败时发送失败通知(
status="2",见第 10 节「代收失败通知」);其他通道的失败、放弃或过期订单不发送通知,可能长期保持payStatus=0,需要联系平台核对。
银行卡代收(需提交购物车和购物者信息)
AUD-VKDPWMRT 不适用本小节,见下文「澳洲银行卡(AUD-VKDPWMRT)」。
部分银行卡代收产品要求商户在 cardExtra 中提交购物车和购物者信息,产品目录的「说明」列会注明;目录中暂无需要 cardExtra 的钱包产品。cardExtra 缺失或信息不全时,下单会被拒绝。
cardExtra 是字符串字段,与其他字段一样原样参与签名;请求体仍须是扁平 JSON,不要把 cardExtra 写成 JSON 对象或数组。cardExtra 内部的键名和编码格式待平台确认,下表只列需要提供的信息项和示例值:
| 信息项 | 是否必填 | 示例值 | 说明 |
|---|---|---|---|
| 购物车商品列表 | 是 | 编号 SKU-1,名称 Demo item,单价 25.00,数量 1 | 至少一项;每项包含商品编号、名称、单价、数量,单价按主币种两位小数。各项合计是否必须等于 amount,待平台确认 |
| 购物者名(first name) | 是 | Test | 与姓分开提供 |
| 购物者姓(last name) | 是 | User | 与名分开提供 |
| 付款人公网 IP | 是 | 203.0.113.10 | 真实付款人 IP。由顶层 clientIp 提供还是须写入 cardExtra,待平台确认 |
| 邮箱 | 是 | payer@example.com | 是否须在 cardExtra 中重复顶层 email,待平台确认 |
| 电话 | 按产品要求 | YOUR_TEST_PHONE | 产品要求账单信息时必填(见产品目录「说明」) |
| 账单地址、城市、邮编 | 按产品要求 | Example Street 1,Example City,00000 | 同上 |
| 国家 | 按产品要求 | US | 同上;取值格式(是否为两位国家代码)待确认 |
| 州或省 | 按产品要求 | Example State | 同上 |
| 收货地址 | 否 | — | 可选 |
| 付款人浏览器 User-Agent、语言 | 否 | Mozilla/5.0 …,en-US | 可选 |
| 商户侧客户编号 | 否 | CUST0001 | 可选 |
下单请求示例(cardExtra 的值是占位符,实际内容按平台提供的格式填写):
{
"merchantOrderNo":"DEMO_CARD_20260930_001",
"amount":"25.00",
"currency":"USD",
"product":"YOUR_CARD_PAYIN_PRODUCT",
"name":"Test User",
"phone":"YOUR_TEST_PHONE",
"email":"payer@example.com",
"clientIp":"203.0.113.10",
"notifyUrl":"https://merchant.example.com/payments/notify",
"returnUrl":"https://merchant.example.com/payments/result",
"cardExtra":"CART_AND_SHOPPER_INFO_IN_PLATFORM_FORMAT"
}
- 成功时响应与普通代收相同。
payUrl是托管支付页的 HTTPS 地址,可能不在 HissPay 域名下;请直接引导付款人打开,不要按域名白名单拦截或改写链接。之后按下文「托管收银台」的方式等待结果。 - 支付方式由所签约的产品决定,商户无需传入其他参数;可用币种以商户实际开通为准。
澳洲银行卡(AUD-VKDPWMRT)
AUD-VKDPWMRT 是澳元银行卡托管支付:下单成功返回托管支付页 payUrl,付款人在支付页填写卡信息,商户不接触、也不得提交卡号或 CVV。除通用字段规则(第 3、4 节及上方字段表)外,下列要求须全部满足。错误码按校验顺序返回:请求先经过通用校验,不合规时返回原有业务码,例如必填字段为空、URL 非法返回 400,金额 ≤0 或精度超限返回 10006,payType 与币种不匹配或为空字符串返回 10012,clientIp 非空白但格式非法返回 10021,产品、协议、路由问题返回 10004、10005 等;只有通过这些通用校验之后,在本产品自身的校验中不满足下列要求时,当前才返回业务码 9999,平台正在改为具体业务码。收到 9999 时按第 11 节「9999 自查清单」处理。
当前仅支持澳洲手机号(area_code 固定 61)与澳洲账单信息(country=AU、state 取下表所列、4 位邮编);海外手机号/海外账单地址暂不支持(平台正在向通道确认)。
顶层字段
| 字段 | 本产品要求 |
|---|---|
payType | 必填,必须显式传 CARD。传空字符串或与 AUD 不匹配的编码返回 10012;不传时平台按 WALLET 记录订单,或传了其他与 AUD 匹配的编码(如 PAYPAL),都会被本产品拒绝(当前 9999) |
amount | AUD 1–500(含两端),只能是整数或 .99 结尾,最多两位小数表示:"20"、"20.00"、"19.99"、"500" 合规;"20.50"、"0.99"、"500.99" 不合规。金额 ≤0 或超过两位小数(如 "19.999")返回 10006;超出签约路由金额范围按路由规则返回(如 10005);其余不合规当前返回 9999 |
clientIp | 必填。键名必须精确为 clientIp(区分大小写);写成 client_ip、clientIP、ip 等能通过验签,但平台不读取该键(未知字段被忽略),等同未传,下单被拒。值为真实付款人 IP 的字面量:IPv4 点分形式(如 203.0.113.10)或 IPv6;不能是域名,不能带端口或方括号,IPv4 不要写成 ::ffff:203.0.113.10 这类映射形式。不要传商户服务器 IP;不要放在请求头或 URL 查询参数里(同样不会被读取)。值非空白但不是合法 IPv4/IPv6(含超过 45 个字符)返回 10021;不传、null、空字符串、全空白或键名错误都视为缺失,本产品当前返回 9999;能通过通用格式检查、但不是本产品接受的字面量(如 ::ffff: 映射形式)当前也返回 9999 |
returnUrl | 必填。必须以 https:// 开头;主机名须含点(如 merchant.example.com),不能是 localhost;不含用户信息(如 user@);端口省略或为 443;总长不超过 512 个字符。非空且不是合法 http(s) URL 返回 400;不传、空字符串,或是合法 URL 但不满足上述要求(如 http://、localhost、端口 8443)当前返回 9999 |
phone | 必填,只能是 5–15 位数字:不带 +、空格、横线或括号,也不要加国家码 61(国家码由 cardExtra 中 account_info.area_code 提供)。为空返回 400;非空但不符合上述格式当前返回 9999 |
email | 必填,形如 payer@example.com:不含空白,只有一个 @,@ 之后含点;不超过 254 个字符。为空返回 400;非空但不符合上述格式当前返回 9999 |
name | 仍必填(不能为空),但本产品不使用;付款人名、姓以 cardExtra 中的 first_name、last_name 为准 |
addr | 本产品不使用,可省略 |
cardExtra | 必填,JSON 字符串,格式见下 |
cardExtra 格式
cardExtra 的值是一段字符串:先把下面描述的 JSON 对象序列化成文本,再把这段文本作为 cardExtra 的字符串值放进请求体。顶层请求体仍是扁平 JSON,不要把 cardExtra 作为嵌套对象或数组发送。平台把该字符串解析为 JSON 后按以下规则校验:
- 顶层必须恰好是
connection_info、account_info两个对象,不允许任何其他键。 - 两个对象中只能出现下表列出的键;出现未列出的键(包括拼写或大小写错误)即被拒。必填键缺失也被拒。
- 除
create_time外,所有值都必须是 JSON 字符串:不能为空或全空白,不能含控制字符(换行、制表符等),长度和格式须完全符合下表。 - 任何文本值都不得含
cvv、cvc、card number、card no、security code字样(不区分大小写,cardnumber、card_no、security-code等写法同样禁止),不得含疑似卡号(13–19 位数字串,含以空格或-分隔的写法,且通过 Luhn 校验)。 - 整个
cardExtra字符串按 UTF-8 编码不超过 8192 字节。 - 禁止放入卡号、CVV 等任何卡信息;卡信息只在托管支付页填写。
connection_info:
| 键 | 必填 | 类型 | 规则 |
|---|---|---|---|
country | 是 | 字符串 | 固定为 "AU" |
state | 是 | 字符串 | NSW、VIC、QLD、ACT、NT、SA、TAS、WA 之一(大写) |
zip_code | 是 | 字符串 | 4 位数字,如 "2000" |
media_source | 是 | 字符串 | 来源标识,由商户自定:1–64 个字符,只能是字母、数字、_、-([A-Za-z0-9_-]{1,64}) |
user_agent | 否 | 字符串 | 付款人浏览器 User-Agent,不超过 1024 个字符,不含控制字符 |
uuid | 否 | 字符串 | 不超过 64 个字符,只能是字母、数字、.、_、:、-([A-Za-z0-9._:-]+) |
language | 否 | 字符串 | 不超过 16 个字符,形如 en、en-AU([A-Za-z]{2,3}(-[A-Za-z0-9]{2,8})?) |
account_info:
| 键 | 必填 | 类型 | 规则 |
|---|---|---|---|
merchant_user_id | 是 | 字符串 | 付款人在商户侧的稳定用户 ID;不超过 64 个字符,只能是字母、数字、.、_、:、@、-([A-Za-z0-9._:@-]+);与 email、phone 一一绑定,见下 |
create_time | 是 | 整数 | 该用户在商户侧的注册时间,Unix 毫秒时间戳,必须是 JSON 整数(不能加引号写成字符串,不能带小数);不小于 1000000000000,且不晚于当前时间 |
role | 是 | 字符串 | GUEST 或 PRIVATE(大写) |
area_code | 是 | 字符串 | 固定为 "61"(字符串,不是数字) |
first_name | 是 | 字符串 | 付款人名:不超过 64 个字符,只能是英文字母、空格、'、- |
last_name | 是 | 字符串 | 付款人姓:规则同 first_name |
用户身份绑定:merchant_user_id 与 email、phone 一一绑定。同一 merchant_user_id 必须始终使用同一 email 和同一 phone;同一 email 或同一 phone 也不能出现在另一个 merchant_user_id 下(email 比较不区分大小写)。任一组合与之前的订单冲突时下单被拒。请使用商户侧稳定的用户 ID,不要每单随机生成,也不要多个用户共用一个 ID;用户更换邮箱或手机号前请联系平台。
cardExtra 示例(为便于阅读而换行;实际发送时用下方请求示例中的单行文本):
{
"connection_info":{
"country":"AU",
"state":"NSW",
"zip_code":"2000",
"media_source":"demo_site",
"user_agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64)",
"language":"en-AU"
},
"account_info":{
"merchant_user_id":"demo-user-0001",
"create_time":1767225600000,
"role":"PRIVATE",
"area_code":"61",
"first_name":"Test",
"last_name":"User"
}
}
完整请求示例
以下全部为合成数据(IP 为文档保留地址段,邮箱与域名为 example.com,手机号为虚构值):
{
"merchantOrderNo":"DEMO_AUD_20261003_001",
"amount":"20.00",
"currency":"AUD",
"product":"AUD-VKDPWMRT",
"payType":"CARD",
"name":"Test User",
"phone":"0491570156",
"email":"payer@example.com",
"clientIp":"203.0.113.10",
"notifyUrl":"https://merchant.example.com/payments/notify",
"returnUrl":"https://merchant.example.com/payments/result",
"cardExtra":"{\"connection_info\":{\"country\":\"AU\",\"state\":\"NSW\",\"zip_code\":\"2000\",\"media_source\":\"demo_site\",\"user_agent\":\"Mozilla/5.0 (Windows NT 10.0; Win64; x64)\",\"language\":\"en-AU\"},\"account_info\":{\"merchant_user_id\":\"demo-user-0001\",\"create_time\":1767225600000,\"role\":\"PRIVATE\",\"area_code\":\"61\",\"first_name\":\"Test\",\"last_name\":\"User\"}}"
}
该示例的待签名串
签名规则与第 3 节相同。以虚构的 appId=demo-app、timestamp=1700000000000、nonce=demo-nonce-0001、appSecret=demo-secret 为例,待签名串为(实际是一整行,此处未换行):
amount=20.00&appId=demo-app&cardExtra={"connection_info":{"country":"AU","state":"NSW","zip_code":"2000","media_source":"demo_site","user_agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64)","language":"en-AU"},"account_info":{"merchant_user_id":"demo-user-0001","create_time":1767225600000,"role":"PRIVATE","area_code":"61","first_name":"Test","last_name":"User"}}&clientIp=203.0.113.10¤cy=AUD&email=payer@example.com&merchantOrderNo=DEMO_AUD_20261003_001&name=Test User&nonce=demo-nonce-0001¬ifyUrl=https://merchant.example.com/payments/notify&payType=CARD&phone=0491570156&product=AUD-VKDPWMRT&returnUrl=https://merchant.example.com/payments/result×tamp=1700000000000demo-secret
cardExtra作为字符串原样参与签名:取的是请求体 JSON 解码后的字符串值本身。请求体里的\"在待签名串中就是";JSON 转义只是传输写法,不改变待签名值。- 先生成一次
cardExtra文本,并用同一段文本既放进请求体、又参与签名。签名后不要再重新序列化或格式化cardExtra:键顺序或空格的任何变化都会改变字符串内容,导致验签失败。 - 值中的空格(如
Test User、User-Agent)原样保留,不做 URL 编码。 - 本例时间戳已过期、密钥虚构,不能请求生产接口,因此不给出 sign 值;请用Python 示例或 Node.js 示例按第 3 节算法自行计算并核对。
结果
成功时响应与普通代收相同:返回平台 orderNo 和 payUrl(托管支付页 HTTPS 地址,可能不在 HissPay 域名下),payStatus=0 只表示已创建。之后按下文「托管收银台」的方式引导付款人打开 payUrl,以验签通过的回调或查单结果为准,按 realAmount 入账;跳回 returnUrl 不代表已付款。
托管收银台(hosted checkout)
托管收银台产品下单后返回托管支付页链接,付款人在该页面选择方式并付款,商户不接触支付信息。
- 调用代收下单,保存平台
orderNo,把payUrl交给付款人:网页端整页跳转或在新窗口打开。能否嵌入 iframe,待确认。 - 支付页有有效期,因支付方式而异(例如 15 分钟或 1 小时)。不要缓存或重复发放旧链接。
- 付款后付款人可能跳回
returnUrl,也可能停留在支付页。跳回不代表已付款,未跳回也不代表未付款。 - 等待平台代收成功通知(
status="1"),或低频查单直到payStatus=1;按realAmount入账(第 4 节)。 - 付款人放弃或链接过期时,平台通常不发送失败通知,查单可能一直是
payStatus=0;只有通道明确确认付款失败时才可能收到失败通知或查到payStatus=2(第 8、10 节)。如需让付款人重新付款,先联系平台确认原单已关闭,再用新的merchantOrderNo创建订单;不要用原订单号重新下单。 - 收银台页面展示哪些支付方式由产品配置决定;商户不能通过本接口要求只收银行卡。
USDT 代收(加密资产)
USDT 代收使用与法币相同的下单、查单和异步通知接口,currency 固定为 USDT,通过 product 选择网络;商户不传链编号或网络字段。对应产品在上方目录中标为 LIVE 之前不可下单(当前为 PLANNED,即将开放,不可下单)。
| 产品编码 | 资产 | 网络 | 金额精度 |
|---|---|---|---|
USDT-TRON | USDT | Tron | 最多 4 位小数 |
USDT-ETHEREUM | USDT | Ethereum | 最多 4 位小数 |
USDT-BSC | USDT | BSC | 最多 4 位小数 |
USDT-SOLANA | USDT | Solana | 最多 4 位小数 |
- 金额使用十进制字符串,四个网络统一最多 4 位小数(最小单位
0.0001USDT),且须满足签约路由的金额下限;超出精度返回 10006。后续如放宽精度会另行通知。 payType可省略;显式传入时为CRYPTO。clientIp、cardExtra沿用通用规则。
下单请求示例:
{
"merchantOrderNo":"DEMO_USDT_20261001_001",
"amount":"12.3456",
"currency":"USDT",
"product":"USDT-TRON",
"name":"Test User",
"phone":"YOUR_TEST_PHONE",
"email":"payer@example.com",
"notifyUrl":"https://merchant.example.com/payments/notify",
"returnUrl":"https://merchant.example.com/payments/result"
}
下单成功时响应比法币多返回收款信息 paymentInfo(product 所有代收均返回);商户可跳转 payUrl,也可按自身页面规范展示地址和二维码:
{"code":0,"msg":"Success","data":{"appId":"YOUR_APP_ID","merchantOrderNo":"DEMO_USDT_20261001_001","orderNo":"PLATFORM_ORDER_NO","currency":"USDT","product":"USDT-TRON","amount":12.3456,"payUrl":"https://cashier.example.com/example","payStatus":0,"paymentInfo":{"asset":"USDT","network":"TRON","address":"NETWORK_RECEIVING_ADDRESS","amount":"12.3456","qrCodeImage":null,"expiresAt":"2026-10-01T12:00:00Z"},"otherExtend":null}}
paymentInfo仅 USDT 产品且平台已分配收款地址时返回,否则不返回该键;返回时 6 个字段全部输出;network为TRON、ETHEREUM、BSC或SOLANA;qrCodeImage为预留字段,当前版本恒为 null,请按address与amount自行生成二维码;expiresAt为 UTC ISO 8601 时间,由平台给出,可能为 null;有值时过期后不要继续引导付款。响应中的amount为 JSON 数字,paymentInfo.amount为字符串,均最多 4 位小数,请用 Decimal 解析。- 付款人须按
paymentInfo.amount足额转账;payStatus=0只表示待付款,不代表链上到账。 - 查单(第 8 节)额外返回
product和txHash;txHash仅在链上确认后返回,确认前不返回该键。建议轮询间隔 10–15 秒并退避。 - 成功通知(第 10 节)在有
txHash时追加该字段,参与签名;USDT 通知金额保留 4 位小数。 - 按通知或查单中的
realAmount幂等入账;不要用订单amount替代缺失的realAmount。
订阅与定期扣款
平台目前没有订阅或定期扣款接口。不要用代收下单模拟订阅或周期扣款;有需求请联系平台。
下单结果不确定时
下单请求超时、断线、HTTP 5xx、返回非 JSON 或无法判断的结果时,订单可能已经创建。平台遇到不确定结果时不会自动重新提交,商户也不要重新提交。
- 保留原
merchantOrderNo,不要再次调用代收下单:既不要换新订单号重下,也不要用同一订单号重下。 - 用原
merchantOrderNo低频查单,并等待验签通过的成功通知;以回调或查单的payStatus=1为准。 - 不要用同一订单号修改金额或其他参数后重下:同一订单号再次下单返回 10020(启用业务幂等的产品参数不同返回 409,第 4 节)。
- 长时间保持
payStatus=0时联系平台核对,不要自行判定失败,也不要让付款人再次付款。
16.3 THB 代收(PromptPay 扫码)
POST /transaction/payin/v1/apply,字段在第 6 节字段表基础上按下表填写。下表是统一契约:同时满足所有付款通道的要求。不同付款通道实际做的校验不完全相同,部分不合规的值可能被某些通道接受、或被替换为默认值(如不合规的 returnUrl 可能被替换为平台默认结果页),因此平台不保证对不符合下表的请求一律拒绝;请按下表在提交前自行校验,不要以平台是否拒绝作为判断依据。
| 字段 | 必填 | THB 代收要求 |
|---|---|---|
currency | 是 | 固定 THB |
product | 是 | 平台分配的 THB 代收产品编码 |
amount | 是 | 见 16.2,默认 500.00–49999.00 |
name | 是 | 付款银行账户的户名,须与付款账户户名一致。去掉首尾空格后 1–50 个字符;只能使用泰文或英文字母(含泰文元音及声调符号)、空格、.、'、-,首字符须为字母;不能含数字或其他符号 |
phone | 是 | 付款人手机号:6–15 位数字,可带一个前导 +(如 0812345678、+66812345678),不含空格或 - |
email | 是 | 必填。付款人邮箱,形如 payer@example.com,不含空格,不超过 64 个字符。不传或传空字符串时返回 400(msg 形如「请求参数不正确:email invalid」);格式不符或超长返回 9999(见下方自查清单) |
cardExtra | 是 | JSON 字符串,格式见下 |
payType | 否 | 请勿传。付款方式由 THB 代收产品决定(PromptPay 扫码),目前没有对应的 payType 编码。平台编码表中的 THB CARD、PAYPAL 组合能通过编码校验,但与本产品无关,传入也不会改变付款方式 |
returnUrl | 否 | 付款后浏览器跳转地址。填写时必须是 https:// 绝对地址,不能包含 ? 查询参数或 # 片段,不含空白字符,不超过 128 个字符;开通通知给出更短的上限时以通知为准。不填时付款后显示平台的默认结果页。开通通知未确认长度上限前,建议不传 |
notifyUrl | 否 | 与第 6 节相同;平台通知只发往这里或商户默认通知地址 |
clientIp、addr | 否 | 本产品不使用,可省略 |
cardExtra 的值是一段字符串:把下面的 JSON 对象序列化成文本,再作为 cardExtra 的字符串值放进请求体(与第 6 节其他产品相同,不要作为嵌套对象发送)。对象必须恰好包含以下两个键,不允许其他键,值都必须是 JSON 字符串,序列化后的文本不超过 512 个字符:
| 键 | 必填 | 规则 |
|---|---|---|
payerAccountNo | 是 | 付款人的泰国银行账号(不是银行卡号,也不是手机号或 PromptPay ID),6–20 位纯数字,保留前导零,不含空格或 - |
payerBankCode | 是 | 付款银行,取 16.1 表中「代收」为「支持」的编码 |
说明:本产品要求提交付款人的银行账号与户名,用于让付款人使用该账户完成扫码付款;这是第 6 节「不要提交卡号」规则的唯一例外,仍然不得提交银行卡号、CVV、密码或任何验证码。付款人使用其他账户或其他户名付款时,可能无法自动确认到账,订单会进入人工核对。
完整请求示例:
{
"merchantOrderNo":"DEMO_THB_20261026_001",
"amount":"1500.50",
"currency":"THB",
"product":"YOUR_THB_PAYIN_PRODUCT",
"name":"Somchai Jaidee",
"phone":"0812345678",
"email":"payer@example.com",
"notifyUrl":"https://merchant.example.com/payments/notify",
"cardExtra":"{\"payerAccountNo\":\"1234567890\",\"payerBankCode\":\"SCB\"}"
}
该示例的待签名串(虚构 appId=demo-app、timestamp=1700000000000、nonce=demo-nonce-0001、appSecret=demo-secret;实际是一整行):
amount=1500.50&appId=demo-app&cardExtra={"payerAccountNo":"1234567890","payerBankCode":"SCB"}¤cy=THB&email=payer@example.com&merchantOrderNo=DEMO_THB_20261026_001&name=Somchai Jaidee&nonce=demo-nonce-0001¬ifyUrl=https://merchant.example.com/payments/notify&phone=0812345678&product=YOUR_THB_PAYIN_PRODUCT×tamp=1700000000000demo-secret
对应 sign(SHA-256 小写十六进制):2857fc38acbfdcbaec806757360be995c4fa32349ffb5b6a273e14f6c3304bd4
cardExtra 按解码后的字符串原样参与签名,规则同第 6 节「澳洲银行卡(AUD-VKDPWMRT)」中的说明:先生成一次文本,同一段文本既放进请求体又参与签名。
成功响应与普通代收相同(第 6 节),例如:
{"code":0,"msg":"Success","data":{"appId":"YOUR_APP_ID","merchantOrderNo":"DEMO_THB_20261026_001","orderNo":"PLATFORM_ORDER_NO","currency":"THB","product":"YOUR_THB_PAYIN_PRODUCT","amount":1500.50,"payUrl":"https://cashier.example.com/example","payStatus":0,"otherExtend":null}}
payUrl是收银台页面(含 PromptPay 二维码)的地址,域名可能是第三方收银台,不在 HissPay 域名下,协议可能是https或http。请把付款人直接跳转到payUrl:不要校验或白名单限制其域名,不要改写、截断或追加参数,也不要自行重新生成二维码。payStatus=0只表示已创建。- 引导付款人用
cardExtra中声明的银行账户扫码付款。付款页的有效期由付款页面显示;过期后不要让付款人重复付款,按下文处理。
下单结果与处理:
| 下单结果 | 订单状态 | 处理 |
|---|---|---|
code=0 且有 payUrl | 处理中 | 引导付款,等待成功通知或查单 |
| 400、10006、10012 等通用校验错误 | 未创建或未提交 | 按第 11 节修正后用新单号下单 |
9999,且原单查单 paymentStatus=CANCELLED 或查无此单 | 本节要求不满足,订单未提交付款通道,没有 payUrl | 按下方自查清单修正后用新的 merchantOrderNo 下单 |
9999,且原单查单 paymentStatus=PROCESSING | 结果未知:请求可能已到达付款通道 | 按第 6 节「下单结果不确定时」处理,不要换单号重下 |
| 10009 | 付款通道明确未受理,没有 payUrl;当前订单仍显示 PROCESSING | 不要用原单号重试,不要引导付款;联系平台核对后再决定是否用新单号下单 |
| HTTP 5xx、超时、非 JSON | 结果未知 | 同第 6 节「下单结果不确定时」 |
9999 自查清单(THB 代收):cardExtra 是字符串且恰好含两个键;payerAccountNo 为 6–20 位数字;payerBankCode 是 16.1 的 6 个代收编码之一;name 符合户名规则且不超过 50 个字符;phone、email 都已填写且符合上表格式(不传会先返回 400);returnUrl 不传,或为不含 ?/#、不超过上限的 https:// 地址;金额在单笔范围内;没有传 payType。
结果确认:
- 平台核实付款状态与金额后才把订单置为成功,并发送成功通知(第 10 节,
status="1",realAmount等于订单金额)。THB 代收只发送成功通知。 - 付款失败、放弃或过期的订单不发送通知。生产环境中这类订单一般长期保持
PROCESSING(旧字段payStatus=0),平台目前不会把 THB 代收自动置为FAILED;沙箱失败场景(尾数 .01、.07)会显示paymentStatus=FAILED、isFinal=true,仅用于演练。不要把长期处理中当作失败,也不要换单号让付款人再付一次。 - 创建超过 3 天仍未确认成功的订单,平台可能无法再自动核实,请联系平台人工核对;不要换单号重复收款。
7. 代付下单
POST /transaction/payout/v1/apply
| 字段 | 必填 | 说明 |
|---|---|---|
merchantOrderNo | 是 | 商户唯一代付订单号,最长 255 个字符 |
amount | 是 | 主币种金额,须大于 0 且不低于代付最低金额(通道下限与签约下限中的较高者,见第 4 节);低于最低金额返回 10006 |
currency | 是 | 已开通币种 |
product | 是 | 已签约的代付产品编码,与代收产品分开,最长 20 个字符 |
accountName | 是 | 收款账户姓名 |
account | 是 | 收款银行账号或适用支付账户,字符串 |
phone | 是 | 收款人手机号 |
email | 是 | 收款人邮箱 |
bizCode | 条件必填 | 业务类型编码,按平台提供的编码填写。仅下文「免填 bizCode / branchCode 的条件」三项同时满足时可不传;否则缺失或为空返回业务码 400 |
payCode | 是 | 支付方式编码;由平台按产品提供 |
branchCode | 条件必填 | 分行或银行标识;印度银行代付通常填写收款账户 IFSC,THB 填银行编码(第 16 节)。免填条件同 bizCode |
bankName | 否 | 银行名称,视通道要求;最长 255 个字符,超长返回业务码 400 |
notifyUrl | 否 | 本单 HTTPS 回调地址;可回退到商户默认通知地址 |
address | 否 | 收款人地址 |
accountId | 是 | 收款人在商户系统中的唯一标识,同一收款人保持不变;平台按通道要求作为收款用户标识上送 |
swiftCode | 否 | SWIFT 标识,视通道要求 |
extra | 否 | 扩展信息,使用前确认格式 |
免填 bizCode / branchCode 的条件(三项同时满足才可省略,否则两者都必填):
payCode为CARD;product为USD-PAYPLUS-PAYOUT、USD-LOCAL-PAYOUT或AUD-LOCAL-PAYOUT之一;currency与产品匹配:前两个产品为USD,AUD-LOCAL-PAYOUT为AUD。
例如上述产品传 payCode=BANK,或币种不匹配时,缺少 bizCode / branchCode 会返回业务码 400。
{
"merchantOrderNo":"DEMO_OUT_20260912_001",
"amount":"100.00",
"currency":"INR",
"product":"INR-NPLMSUEX",
"accountName":"RECIPIENT_NAME",
"account":"RECIPIENT_ACCOUNT",
"phone":"RECIPIENT_PHONE",
"email":"recipient@example.com",
"bizCode":"ASSIGNED_BIZ_CODE",
"payCode":"ASSIGNED_PAY_CODE",
"branchCode":"RECIPIENT_IFSC",
"notifyUrl":"https://merchant.example.com/payments/notify"
}
成功受理返回 appId、merchantOrderNo、orderNo、currency、amount、payStatus:0。平台会校验可用余额,并按签约费用及账务规则占用资金,异步完成付款。不要收到 code=0 就向用户宣称“已到账”,也不要因为尚未回调再次提交新代付。
代付下单返回非 0 业务码时,订单通常未创建、未占用资金;返回 500、超时或非 JSON 时结果未确认,须先查原单。代付接口的 msg 当前统一改写、不指出具体字段,各类拒绝的返回形式与处置见第 11 节「代付下单的拒绝与 msg」。
USD 银行卡代付接入(按商户开通)
本节描述 USD 银行卡代付的接口格式,不代表该产品已向您的应用开放。先由平台确认产品编码、USD 账户、额度、费用、来源 IP 白名单及收款人确认,再使用生产应用。当前只支持单笔 CARD,不支持该产品批量代付或自动重付;收款人通过下方独立接口创建、查询和更新。请勿复用 USD 钱包代付产品编码。
| 字段 | USD 银行卡要求 |
|---|---|
currency / payCode | USD / CARD |
product | 平台为该应用开通的银行卡代付产品编码;不是渠道名称或代收产品 |
account | 收款卡号,13–19 位数字,通过卡号校验;不要提交 CVV |
extra | JSON 字符串,仅含 beneficiary_id(正整数)和 card_valid(MM/YYYY);有效期不能已过期 |
extra.beneficiary_id | 收款人接口查询 READY 后返回的 beneficiaryId;不可猜测,不可跨应用、产品或环境使用 |
accountName / phone / email | 必填,填写该收款人的合法信息 |
bizCode / branchCode | 此美国卡代付产品不需要,新订单省略这两个字段;不填写 IMPS、IFSC 或占位值。历史订单重试保留首次请求原值,避免幂等冲突 |
notifyUrl | 您自己的 HTTPS 接收地址。接收的是本平台商户通知,不是资金服务商的通知格式 |
以下为结构示例。替换所有占位值后使用,金额须符合实际签约范围;编号 12345 仅用于展示类型。
{
"merchantOrderNo":"MERCHANT_OUT_UNIQUE_001",
"amount":"100.00",
"currency":"USD",
"product":"USD-PAYPLUS-PAYOUT",
"accountName":"RECIPIENT_NAME",
"account":"RECIPIENT_CARD_NUMBER",
"phone":"RECIPIENT_PHONE",
"email":"recipient@example.com",
"payCode":"CARD",
"extra":"{\"beneficiary_id\":12345,\"card_valid\":\"12/2030\"}",
"notifyUrl":"https://merchant.example.com/payout/notify"
}
签名沿用第 3 节:请求体平面字段与 appId、timestamp、nonce 合并,按键排序,拼接 key=value(& 分隔),末尾直接追加 appSecret,再计算 UTF-8 SHA-256 小写十六进制。extra 是字符串,以传输时相同的字符串内容参与签名;不要把 JSON 内部字段单独展开或改空格。timestamp 为毫秒,时间误差不超过 30 秒,每次 HTTP 请求使用新 nonce。商户只使用本平台 appId/appSecret。
可下载 Python 请求示例、Java 请求示例 和 JSON 配置模板、Java 配置模板。Python 默认只预检,Java 默认只预检;均需显式 --send 才请求。先用 query 或 balance 验证鉴权,单笔提交时保存原单号。两种示例都不会自动重试或跟随重定向;通信中断后用原单号查单,不换号重付。示例配置可能含卡资料,仅保存在受限服务器,不提交 Git。
python3 payout_request.py balance payout-request.json
python3 payout_request.py query payout-request.json --send
javac PayoutRequest.java
java PayoutRequest balance payout-request.properties
java PayoutRequest query payout-request.properties --send
生产请求地址由 HISSPAY_GATEWAY 指定(https://transaction.hisspay.com);商户模拟测试使用 https://sandbox-api.hisspay.com 和独立沙箱凭据。HISSPAY_APP_ID、HISSPAY_APP_SECRET 只在服务器环境变量配置。
结果判定与幂等:下单 code=0 / payStatus=0 只是受理。同一应用与 merchantOrderNo 唯一绑定原请求:相同单号再次提交返回 10020;若开通通知说明本产品已启用业务幂等,则相同参数返回原受理结果、改变金额或收款资料返回 409(第 4 节)。出现超时、5xx、无回调、查单暂未找到时均不能推断付款失败。先查原单;paymentStatus=SUCCEEDED / isFinal=true 表示成功,FAILED / true 表示失败,其他状态继续核对。退款或异常资金调整由平台核对,不能据此自动重付。
异步通知:JSON 字段为 merchantOrderNo、orderNo、currency、product、amount、status、statusDes,utr 可能为空。status="1" 成功、status="2" 失败;这与查询的 paymentStatus 不同。HTTP 头 appId/timestamp/nonce/sign 按第 10 节验签(空值不参与签名)。核对订单归属、币种、产品和原金额,在数据库事务中按应用和订单去重并保存结果后,返回 HTTP 200、纯文本 SUCCESS 或 OK。同一状态重复通知应正常 ACK,冲突终态进入人工核对,不能再次付款。成功响应正文不要包 JSON 或 HTML。失败说明仅用于诊断,不据 statusDes 文本判断资金终态。
常见错误:400 参数或签名错误;10006 金额限制;10008 可用余额不足;10010 代付来源 IP 未加白;10011 产品无效;10005 无可用路由;10020 商户订单号重复;409 同单号参数冲突(仅启用业务幂等的产品);429 限流。代付接口的 msg 统一改写(见第 11 节「代付下单的拒绝与 msg」)。500、超时及非 JSON 错误均视为结果尚未确认,查原单。完整错误码见第 11 节,HTTP 200 本身不能代表业务成功。
商户可先完成 沙箱代付完整自测,包括成功、失败、未知结果查询、手动终态、真实沙箱通知与本地重复通知去重。该测试使用模拟资金,不证明生产银行卡到账或服务商沙箱验收通过。
美国与澳洲:按支付方式阅读
按“适用范围 → 收款人 → 请求路径与方法 → Header → Body → 返回结果 → 查询与回调 → 测试”顺序接入。下表区分服务商规范与本平台接口,不能混用域名、签名或状态码。
| 项目 | 美国 USD 卡代付 | 澳洲 AUD |
|---|---|---|
| 正式支持 | 已签约并开通 USD-PAYPLUS-PAYOUT 的应用;美国收款人,借记卡出款 | 尚未开放;已核实的正式卡代付规范未列出 AUD,不提供推测的正式请求 |
| 平台沙箱 | USD-LOCAL-PAYOUT;模拟收款账户,不发生真实银行卡出款 | AUD-LOCAL-PAYOUT;模拟收款账户,不证明真实 AUD 出款已支持 |
| 收款人 | 创建 → 查询 READY → 使用返回编号付款;可复用同一已确认收款人 | 平台模拟使用 SANDBOX-SUCCESS 等场景账户;正式受益人规则待官方规范确认 |
| bizCode / branchCode | 新订单省略;不需要平台另发出款或分行编码 | AUD-LOCAL-PAYOUT + AUD + CARD 的模拟订单省略;不可据此构造正式请求 |
| 金额 | 以本应用签约限额为准,不从沙箱最低额推断正式限额 | 模拟最低 100 AUD;正式范围不作承诺 |
美国字段对应关系:平台 extra.beneficiary_id 对应服务商 beneficiary_id;account 对应 card_number;extra.card_valid 对应 card_valid(MM/YYYY);currency、amount 含义一致。平台生成服务商 reference_id,商户使用自己的 merchantOrderNo 查单和对账。收款人 firstName/lastName/payeeId 是平台字段,平台负责转换和隔离,商户不能直接提交其他账户的服务商编号。extra 保持 JSON 字符串格式参与签名。
平台差异:商户请求本平台 /transaction/payout/v1/...,使用本平台 appId/timestamp/nonce/sign;不要使用服务商的 AppId/Authorization 或服务商密钥。product/payCode/notifyUrl 是本平台路由和商户通知字段。平台下单受理 code=0 不等于到账;查询终态和验签回调按本节说明处理,不直接套用服务商返回码。已有订单重试时保持首次完整请求,包括原 bizCode/branchCode;不要因新版将字段改为空、删掉或换商户单号再次付款。
收款人管理:创建 → 查询确认 → 代付
本节为新增接口接入说明,须在平台通知接口及您签约产品已开放后使用;文档中的格式示例不代表正式代付已开通。当前实际银行卡流程仅支持美国收款人(country=US)及 USD。AUD 沙箱模拟仍可测试,但不表示真实 AUD 银行卡代付已获支持。
商户可为不同收款人创建记录,无需平台提前绑定一个固定收款人。为每位收款人在自己的系统中生成稳定的 payeeId,相同应用内不可换绑其他人;不要使用邮箱或卡号作为该标识。不同应用、产品及环境的记录不可混用。资料和卡号仅在商户服务端安全保存,不出现在前端、日志或代码仓库。
| 操作 | HTTPS POST 路径 | operation |
|---|---|---|
| 创建收款人 | /transaction/payout/v1/beneficiaries/create | CREATE |
| 查询收款人 | /transaction/payout/v1/beneficiaries/query | QUERY |
| 更新资料 | /transaction/payout/v1/beneficiaries/update | UPDATE |
三种操作均用本平台 appId / timestamp / nonce / sign 签名,规则与代付一致;operation 是请求体必填字段,也参与签名,必须匹配路径。资料为平面字符串,不嵌套 JSON;更新时 version 必须是 JSON 整数,不能加引号。
| 字段 | 创建 | 查询 | 更新 | 填写方法 |
|---|---|---|---|---|
operation | 必填 | 必填 | 必填 | 分别为 CREATE、QUERY、UPDATE |
product | 必填 | 必填 | 必填 | 您已签约的 USD 银行卡代付产品编码 |
payeeId | 必填 | 必填 | 必填 | 商户不可变收款人标识,3–60 位字母、数字、下划线或连字符 |
language / country | 必填 | 不传 | 必填 | 语言与国家,例如 en / US |
phone / email | 必填 | 不传 | 必填 | 收款人真实联系电话与邮箱 |
birthdate | 必填 | 不传 | 必填 | 真实出生日期,YYYY-MM-DD |
firstName / lastName | 必填 | 不传 | 必填 | 收款人真实名与姓 |
zip / city / state / address | 必填 | 不传 | 必填 | 收款人真实邮编、城市、州及地址 |
version | 不传 | 不传 | 必填 | 最近查询返回的版本,JSON 非负整数;不要猜测或固定为 0 |
requestId | 不传 | 不传 | 必填 | 本次修改的唯一标识,3–60 位字母、数字、下划线或连字符 |
产品编码最多 20 字符,所有资料不能含控制字符。语言为两个英文字母;电话为 4–12 位数字,或 +国家码-号码(国家码 1–3 位,例如 +1-2025550123)。名、姓各 3–50 字符,支持拉丁字母、撇号、连字符及内部空格。城市和州为 2–50 字符,支持拉丁字母及撇号、&、括号、连字符、内部空格;美国邮编为 5 位、5+4 位或 5–10 位数字。地址 5–100 字符,支持字母、数字、空格及 # ' ( ) , - . / : ; °;邮箱最多 120 字符。出生日期须在 1900-01-01 至当前 UTC 日期之间。更新版本须为 JSON 整数,范围 0–9223372036854775806。
查询仅传 operation / product / payeeId,不传个人资料,也不传第三方受益人编号。更新须提交完整资料,不是局部补丁;payeeId 不可改名或转给另一人。
响应沿用 { "code": 0, "msg": "", "data": ... }。data 包含 payeeId、可为空的 beneficiaryId、status 和 version。必须同时检查业务 code 和状态;HTTP 200 本身不表示收款人可用于付款。
status | 商户处理 |
|---|---|
READY | 资料已确认;有有效 beneficiaryId 时才可申请代付 |
UNKNOWN | 创建或更新结果未确认;查询同一 payeeId,不要换号重建 |
NOT_FOUND | 查询暂未确认记录;不能据此自动重复创建,先核对原请求或联系平台 |
REVIEW | 资料或结果需要核对;暂停对此记录出款 |
推荐流程:
- 新收款人调用 create,并保存原
payeeId和响应。连接中断或结果未知时改查同一payeeId,不换号重复创建。 - 调用 query,确认
READY,保存返回的beneficiaryId和version。相同资料的已确认收款人可复用。 - 以该
beneficiaryId填写原代付请求extra的beneficiary_id,同时填写真实卡号和card_valid;然后用唯一merchantOrderNo申请一次代付。收款人创建成功不表示付款已完成。 - 需要修改尚未用于出款的记录时,使用最近
version、新的requestId和完整资料调用 update,再查询确认。版本冲突返回 409 时先查最新记录,不盲目递增或重放更新。 - 已被任意出款冻结上下文使用的收款人不能再更新;合法资料变化需要新的
payeeId,重新创建并确认。新记录不能替代对旧代付订单的查询,也不能作为重复付款理由。
格式错误通常为 400,未开放或无权限为 403,身份、版本或已使用冲突为 409;500、超时及非 JSON 响应按结果未知处理。所有请求使用新的时间戳和 nonce,但不能把新的签名当成可重复创建、更新或付款的许可。
Python / Java 收款人请求示例
下载 Python 示例、公共签名客户端、Java 示例,以及创建、查询、更新模板:创建 JSON / Properties、查询 JSON / Properties、更新 JSON / Properties。示例资料全部为虚构,发送前替换产品及真实收款人资料。配置文件执行 chmod 600。
# 默认只预检字段名称,不打印收款人资料,也不请求网络。
python3 beneficiary_request.py create beneficiary-create.json
python3 beneficiary_request.py query beneficiary-query.json
python3 beneficiary_request.py update beneficiary-update.json
javac BeneficiaryRequest.java
java BeneficiaryRequest create beneficiary-create.properties
java BeneficiaryRequest query beneficiary-query.properties
java BeneficiaryRequest update beneficiary-update.properties
环境变量沿用 HISSPAY_GATEWAY、HISSPAY_APP_ID、HISSPAY_APP_SECRET,须选对应环境的凭据。正式环境会创建或修改真实收款人;确认服务开放和资料准确后,才为所需命令加 --send。不要一次批量执行创建、更新和付款。示例不自动重试,不跟随重定向;query 确认 READY 后,将返回编号放入 代付 Python 示例 或 Java 示例 的配置,再单独提交付款。
平台 USD/AUD 沙箱代付合成标记无需真实收款人资料,仍使用原有沙箱完整自测工具。本节新增收款人接口的沙箱可用性须以平台开放通知为准,不能将合成银行卡标记用到正式网关。
USD / AUD 代付沙箱示例
美国USD与澳洲AUD均可按沙箱测试指南使用平台模拟资金完成下单、查单、成功/失败通知和重复通知验收。使用对应币种的独立沙箱应用、已开通代付产品与路由,默认金额100。模拟结果不证明真实银行卡到账或正式产品开通。
下载完整自测工具,其中包含Python与Java,以及USD JSON、USD Properties、AUD JSON、AUD Properties。合成收款标记仅限沙箱,未知结果查询原单,不重复提交。
16.4 THB 代付(银行转账)
THB 代付需单独开通,以开通通知为准。 未收到开通通知(含代付产品编码、可用银行、户名长度上限、单笔范围与费率)前请勿调用;下文是开通后的接口格式。
POST /transaction/payout/v1/apply,字段在第 7 节字段表基础上按下表填写:
| 字段 | 必填 | THB 代付要求 |
|---|---|---|
currency | 是 | 固定 THB |
product | 是 | 平台分配的 THB 代付产品编码(第 1 节目录,以开通通知为准) |
payCode | 是 | 不能为空;填 BANK。平台不按 payCode 选择 THB 付款通道,开通通知另有说明时以通知为准 |
amount | 是 | 收款人应收金额,见 16.2,默认 500.00–49999.00 |
bizCode | 是 | 固定为 BANK |
branchCode | 是 | 收款银行编码,取 16.1 表中的值,且须在开通通知列出的可用银行内(可用银行按付款通道配置) |
account | 是 | 收款人泰国银行账号,6–20 位纯数字,保留前导零,不含空格或 -;必须真实有效 |
accountName | 是 | 收款账户户名,须与收款账户一致:泰文或英文字母(含泰文元音及声调符号)、空格、.、'、-,首字符须为字母,去掉首尾空格后按字符计算长度。长度上限按付款通道不同,以开通通知为准:部分通道默认最多 10 个字符(付款通道自身限制,平台可在通道确认后放宽,最多 50),部分通道最多 64 个字符。开通通知未写明时按 10 个字符准备;户名超长时不要自行截断,联系平台确认 |
phone | 是 | 收款人手机号:6–15 位数字,可带一个前导 +(如 0812345678 或 +66812345678),不含空格或 - |
email | 是 | 必填。收款人邮箱,形如 recipient@example.com,不含空格,不超过 64 个字符。不传或传空字符串时返回业务码 400(代付 msg 见第 11 节) |
notifyUrl | 否 | 与第 7 节相同 |
bankName、swiftCode、address、accountId、extra | 否 | 本产品不使用,可省略 |
完整请求示例:
{
"merchantOrderNo":"DEMO_THB_OUT_20261026_001",
"amount":"2000.00",
"currency":"THB",
"product":"YOUR_THB_PAYOUT_PRODUCT",
"payCode":"BANK",
"bizCode":"BANK",
"branchCode":"KBANK",
"account":"1234567890",
"accountName":"Somchai J",
"phone":"0812345678",
"email":"recipient@example.com",
"notifyUrl":"https://merchant.example.com/payments/notify"
}
该示例的待签名串(虚构值同上;实际是一整行):
account=1234567890&accountName=Somchai J&amount=2000.00&appId=demo-app&bizCode=BANK&branchCode=KBANK¤cy=THB&email=recipient@example.com&merchantOrderNo=DEMO_THB_OUT_20261026_001&nonce=demo-nonce-0001¬ifyUrl=https://merchant.example.com/payments/notify&payCode=BANK&phone=0812345678&product=YOUR_THB_PAYOUT_PRODUCT×tamp=1700000000000demo-secret
对应 sign:d5e969c9734479c732d7f3a8e74b9e273e9334ccc7f805eb3d067ab663236f4b
结果与状态:
- 下单
code=0只表示平台已受理并占用资金(16.2),付款异步完成;不要据此宣称已到账。 - 本节字段不合规时同步返回参数校验错误、本次请求未建单、不占用资金:msg 形如
Invalid payout request: <field> <rule>,指出字段与规则(不回显字段值),具体返回码与各字段的 msg 见第 11 节「代付下单的拒绝与 msg」。按 msg 修正后重新提交(可沿用原merchantOrderNo)。 - 平台可能按权重把 THB 代付分配到多个付款通道;校验按本次可能分配到的所有通道规则的交集执行(户名取最严上限、可用银行取交集等),与实际分配到哪个通道无关。
- 下单返回 500(
Payout request not confirmed; query original order)表示平台或上游出现未知异常、结果未确认:保留原单号查单,不要换单号重付(第 11 节)。 - 自查清单:
bizCode=BANK;branchCode在 16.1 表且在开通通知的可用银行内;account6–20 位数字;accountName只含允许的字符且不超过开通通知的长度上限;phone6–15 位数字可带前导+;email格式正确且不超过 64 个字符;金额在单笔范围内。 - 平台核实付款通道的处理结果和金额后才给出终态:成功通知
status="1"(utr为银行流水号,可能为 null),失败通知status="2"。statusDes只用于展示和排查,不要用它做程序判断。 - 超时不等于失败。 结果无法确认(如付款通道超时、处理中、返回不一致或无法验证)时,订单保持处理中(查单
payStatus=0),资金继续占用,由平台核对后再给出终态。不要因为尚未收到通知或查单仍为 0 就判定失败、退款给用户,或用新的商户订单号重复代付;超过您内部跟踪时限时联系平台人工核对(第 12 节)。
| 场景 | 查单 payStatus | 通知 status | 含义 |
|---|---|---|---|
| 已受理/处理中/结果待核实 | 0 | 不发送 | 继续等待,不要重复提交 |
| 付款成功 | 1 | "1" | 已核实成功,utr 可能为 null |
| 付款失败或被拒(含部分通道的提交前校验未通过) | 2 | "2" | 未出款,按账务规则释放占用资金 |
8. 查单与状态
代收:POST /transaction/payin/v1/query
代付:POST /transaction/payout/v1/query
{"merchantOrderNo":"YOUR_ORIGINAL_MERCHANT_ORDER_NO"}
或使用平台订单号:
{"orderNo":"PLATFORM_ORDER_NO"}
两者至少提供一个,建议只传一个。两者同时提供时必须指向同一笔订单,否则返回 30001。使用创建订单的同一个 appId;不存在或不属于当前应用的订单也可能返回 30001。
返回字段:appId、merchantOrderNo、orderNo、currency、amount、payStatus,以及第 14 节的 paymentStatus、isFinal、可选 failureReason。代收额外返回 realAmount、product(当前订单均返回,历史归档订单可能为 null)、txHash(仅 USDT 产品在链上确认后返回,法币或确认前不返回该键)和可选 failureCode(通道明确确认付款失败时给出,取值如 PAYMENT_DECLINED、PAYMENT_RISK_REJECTED、PAYMENT_FAILED);金额字段为 JSON 数字;代付额外返回 payStatusDes。未知的新字段请忽略。
| 场景 | 状态字段 | 值 | 含义 |
|---|---|---|---|
| 代收下单 | payStatus | 0 | 创建/待支付 |
| 代收查单 | payStatus | 1 | 已收款 |
| 代收查单 | payStatus | 0 | 未报告成功:创建、处理中、待核对、已取消或结果未知,用 paymentStatus 细分 |
| 代收查单 | payStatus | 2 | 通道明确确认付款失败(目前仅部分通道),paymentStatus=FAILED、isFinal=true |
| 代付下单 | payStatus | 0 | 已受理、异步处理中 |
| 代付查单 | payStatus | 0 | 处理中或尚未得出终态 |
| 代付查单 | payStatus | 1 | 付款成功 |
| 代付查单 | payStatus | 2 | 付款失败 |
不能把代收查单的 0 当作“确定失败”;代收失败只在 payStatus=2 或 paymentStatus=FAILED(isFinal=true)时确认。成功代收的原金额与实际金额可以不同,按上述实际到账规则处理。金额缺失/非法、长时间未决、查询与回调矛盾时保留订单并联系平台核对。
9. 余额查询
POST /transaction/payout/v1/balance
{"currency":"INR"}
{"code":0,"msg":"Success","data":{"appId":"YOUR_APP_ID","accountNo":"ACCOUNT_NUMBER","currency":"INR","balance":1000.00,"balanceActive":800.00,"balanceFreeze":200.00}}
balance:账面余额。balanceActive:可用余额,是否可代付还要考虑手续费及其他校验。balanceFreeze:冻结/占用金额,不能再次使用。
页面旧余额不能替代实时查询。扣款、代付或审核中的申请可能已占用资金;应处理原申请,不要重复提交来尝试“清零”。
10. 异步通知与验签
平台以 POST JSON 请求商户 notifyUrl,头部携带 appId、timestamp、nonce、sign。
通知体
代收成功示例(字段值当前以字符串发送):
{"currency":"INR","merchantOrderNo":"DEMO_IN_20260912_001","orderNo":"PLATFORM_ORDER_NO","product":"YOUR_PAYIN_PRODUCT","amount":"100.00","realAmount":"100.00","status":"1"}
代付成功示例:
{"currency":"INR","merchantOrderNo":"DEMO_OUT_20260912_001","orderNo":"PLATFORM_ORDER_NO","product":"YOUR_PAYOUT_PRODUCT","amount":"100.00","utr":"BANK_REFERENCE_IF_AVAILABLE","status":"1","statusDes":"Success"}
代付失败:status="2",statusDes 提供原因(仅供展示)。utr 可能为 null;不能假设每次都有。法币通知金额字符串固定两位小数,JPY 等零小数币种的通知也如此;USDT 通知金额固定 4 位小数。不要混淆通知体的 status 与查单的 payStatus。
代收失败通知
代收成功一定发送通知;失败通知只在部分通道、且通道明确确认付款失败时发送,其余失败、放弃、过期或取消的订单不发送通知,须查单或联系平台核对。失败通知 status="2"、realAmount="0.00",有两种形态,验签规则相同(所有非 null 字段参与签名):
- 基本形态:字段与成功通知相同,仅
status="2"、realAmount="0.00"。 - 扩展形态:另含
eventId(事件唯一标识,可用于去重)、eventType="PAYIN_FAILED"、paymentStatus="FAILED"、isFinal="true"、failureCode(PAYMENT_DECLINED、PAYMENT_RISK_REJECTED或PAYMENT_FAILED)与failureReason(英文说明,仅供展示)。
{"amount":"100.00","currency":"USD","eventId":"EVENT_ID","eventType":"PAYIN_FAILED","failureCode":"PAYMENT_DECLINED","failureReason":"Payment declined","isFinal":"true","merchantOrderNo":"DEMO_IN_20260912_001","orderNo":"PLATFORM_ORDER_NO","paymentStatus":"FAILED","product":"YOUR_PAYIN_PRODUCT","realAmount":"0.00","status":"2"}
收到失败通知时:验签并核对订单归属后,把订单置为失败、不入账,返回 SUCCESS;不要按 failureReason 文本做程序判断。已确认成功的订单若再收到矛盾的失败通知,进入人工核对,不要冲正。
USDT 代收成功示例(有链上交易哈希时追加 txHash,并参与签名;未知的新字段同样参与验签):
{"currency":"USDT","merchantOrderNo":"DEMO_USDT_20261001_001","orderNo":"PLATFORM_ORDER_NO","product":"USDT-TRON","amount":"12.3456","realAmount":"12.3456","status":"1","txHash":"CHAIN_TRANSACTION_HASH"}
验签与入账顺序
- 按不区分大小写的 HTTP 头名读取四个签名头,核对 appId 属于接收方应用。
- 取通知体所有非 null字段,加上头部 appId、timestamp、nonce,按请求签名同一排序方式计算 SHA-256。不要把 sign 本身加入;未知的新字段也应参与计算。
- 用恒定时间比较校验签名。核对时间戳;建议允许不超过 5 分钟的时钟偏差并同步服务器时间。重试通知会重新生成签名头。
- 核对商户订单号、平台订单号、币种、产品,确认
amount等于本地原订单金额。代收成功以realAmount作为实际收款依据,允许其与amount不同;验签、归属、金额合法性或已入账金额存在矛盾时进入人工核对,不自动重复记账。 - 在商户数据库事务中持久化通知和状态,以“应用+业务类型+商户订单号”的唯一约束/行锁保证同一笔只入账一次。
- 数据提交成功后,返回 HTTP 2xx,响应体为纯文本
SUCCESS。重复通知验签并确认已处理后,也返回 SUCCESS。
响应不能包在 JSON 或 HTML 中,不附加换行或空格。当前兼容纯文本 OK 或 SUCCESS,大小写不敏感;统一使用 SUCCESS 最稳妥。失败或未能持久化时不要提前确认成功。
实际金额不同的成功通知
以下 BDT 仅用于说明金额语义;该币种目前没有完整生产交易配置,不能据此下单。
以下业务体仍使用上文签名头和验签规则,不能仅凭 JSON 内容入账:
{"currency":"BDT","merchantOrderNo":"DEMO_BD_001","orderNo":"PLATFORM_ORDER_NO","product":"YOUR_BDT_PAYIN_PRODUCT","amount":"100.00","realAmount":"95.00","status":"1"}
查单成功时对应 payStatus=1,amount=100.00、realAmount=95.00,语义与通知一致。通知使用 status,查单使用 payStatus,不要混淆字段。同一订单的查询与通知应共用一个事务幂等入账记录,不分别入账。
重试
常规通知任务每笔最多投递 5 次(含首次),未收到有效 ACK 时按分钟级递增间隔重试(间隔约 5、10、30、60 分钟),实际执行时间受队列及网络影响,不应依赖固定送达时刻。超过次数后不再自动重试,可联系平台人工重发;人工重发、沙箱 resend 都可能带来额外通知。每次投递都会重新生成 timestamp、nonce 和 sign,不能仅用 nonce 去重,应按订单(或扩展失败通知的 eventId)幂等处理。
单次投递的规则:
- 目标:可公开访问的
http://或https://地址,推荐 HTTPS;不得含用户名/密码或#片段。平台代码不限制端口,但出口网络可能只放行常用端口,建议使用 443。内网、回环、链路本地、保留地址会被拦截,不能将 localhost 作为通知地址。沙箱更严格:只允许 80 / 443 端口,解析到非公网地址的通知直接判失败、不再重试,并有每商户 / 每目标主机每分钟上限与连续失败熔断,规则与投递记录查询见沙箱测试指南「回调规则」。 - 来源 IP:回调来源 IP 按环境不同:沙箱
3.109.43.0(固定);生产 IP 在开通通知中提供。接收端如有入站白名单,请按环境加入对应地址。 - 不跟随重定向:返回 3xx 视为投递失败,请直接配置最终地址。
- 超时:连接约 5 秒,整个请求约 10 秒;请先持久化再快速返回,耗时处理放到异步任务。
- ACK:HTTP 2xx 且响应体恰好为
SUCCESS或OK(大小写不敏感,不 trim:前后不能有空格或换行);响应体超过 64 KB 也视为失败。
若未配置本单或默认通知地址,平台不会发送通知,应先补齐配置。
11. 错误码与处理
| code / 情况 | 含义 | 建议 |
|---|---|---|
| 0 | 接口操作成功 | 继续按支付状态判断 |
| 400 | 签名、时间戳、nonce或输入不合法等(HTTP 200、JSON code=400;代收接口字段超长或 URL 非法时 msg 形如「请求参数不正确:merchantOrderNo too long」;签名类 msg 见第 3 节;代付类接口 msg 统一改写,见下文「代付下单的拒绝与 msg」) | 看 msg,校时并检查必填字段;不要盲重试下单 |
| 404 / 405 | 请求路径不存在 / 请求方法不正确(五个接口都只接受 POST) | 核对 {GATEWAY} 与第 2 节路径、使用 POST |
| 409 | 启用业务幂等的产品:同业务单号的请求参数与原单不一致;收款人接口:身份、版本冲突或已被出款使用(Beneficiary request conflicts with existing state),或创建/更新结果未确认(Beneficiary not confirmed; query the original payeeId. Do not recreate.) | 查原单或原 payeeId,不改参数重复提交,不换号重建 |
| 500 | 系统异常(HTTP 200、JSON code=500,msg 多为「系统异常」):例如请求体无法按字段类型解析(如把 cardExtra 写成 JSON 对象或数组)、签名校验服务暂时不可用(Signature verification temporarily unavailable)。代付接口的 500 msg 为 Payout request not confirmed; query original order,含义见下文「代付下单的拒绝与 msg」 | 按结果不确定处理:保留原单号查单,不要换单号重付;修正请求格式后再处理。代付请先查询余额与原单 |
| 900 | 并发重复请求:两个请求同时使用同一 nonce(Request repeated)。已验签过的 nonce 在 60 秒内再次使用返回 400 nonce has been used(第 3 节) | 每次请求使用新的 nonce、timestamp、sign |
| 999 | 功能未实现:本单路由到的支付通道不支持该操作(平台配置问题,仅代收下单可能返回);订单可能已创建,但未提交支付通道 | 用原 merchantOrderNo 查单并联系平台;不要换单号重付 |
| 9999 | 系统异常(An error has occurred. Please try again later.)。当前部分下单参数不合规也可能返回 9999:请求已通过通用校验(400、10006、10012、10021 等不会以 9999 返回),但在产品自身的校验中不合规。例如 AUD-VKDPWMRT 的 cardExtra 缺失或格式不对;clientIp 缺失(不传、空串、全空白、键名错误);payType 未传或不是 CARD;phone/email 非空但格式不符;returnUrl 缺失或不是符合要求的 HTTPS;金额不在 1–500 或不是整数/.99。平台正在改为具体业务码 | 不要按 msg 字面「稍后再试」直接重发,也不要换单号重付;按下文「9999 自查清单」处理 |
| 10000 | 应用不存在 | 核对 appId,勿填商户号 |
| 10001 | 应用不可用 | 联系平台核对应用状态 |
| 10002 | 币种不可用 | 确认币种开通 |
| 10003 | 商户不可用 | 核对商户状态 |
| 10004 | 产品未开通或协议过期 | 同码可能有不同原因,结合 msg 核对签约 |
| 10005 | 无匹配渠道 | 核对产品路由、通道状态、金额范围 |
| 10006 | 金额不允许(Amount not allowed:超出路由金额范围或币种精度;代收低于路由最低金额时 msg 形如 The minimum payable amount is 500.00.;含代付金额低于最低金额(通道下限与签约下限中的较高者),代付接口 msg 为 Payout request rejected) | 以实际产品/路由范围、代付最低金额(第 4 节)和币种精度为准(第 4 节),不能只依赖英文提示 |
| 10007 | 账户不可用 | 核对币种账户 |
| 10008 | 可用余额不足 | 查询可用与冻结余额,预留手续费 |
| 10009 | 通道暂不可用 | 原单查单;不要自动换单重付 |
| 10010 | 来源 IP 不在白名单 | 提供真实服务器公网出口 IP(沙箱默认不校验,仅开启「模拟生产白名单」后出现) |
| 10011 | 产品无效 | 核对代收/代付产品编码 |
| 10012 | 支付方式无效(payType 与币种不匹配,或传了空字符串) | 对照第 6 节「支付方式编码」表,或省略 payType |
| 10013 | 通道尚未接入:产品已配置但通道尚未接入,订单未创建 | 视为该产品暂不可用,不要重试;联系平台确认开放时间 |
| 10020 | 商户订单号重复:同一应用下该 merchantOrderNo 已存在(无论参数是否相同,第 4 节) | 查询原单,不生成新号重付 |
| 10021 | 付款人 IP 不合规:clientIp 非空白但不是合法的 IPv4/IPv6 文本(含超过 45 个字符);或部分要求必填的产品(如印度原生 UPI)缺少可用的付款人 IP。注意:AUD-VKDPWMRT 缺失 clientIp(不传、null、空字符串、全空白、键名写错)当前返回 9999,不是 10021 | 填真实付款人公网 IPv4/IPv6 字面量,键名写作 clientIp |
| 10022 | 代收准入未通过 | 联系平台确认当前准入规则 |
| 10023 / 10024 | 代收 / 代付费率配置不完整 | 联系平台确认签约费用 |
| 30001 | 订单未找到或不可访问 | 核对 appId 及订单号。代付下单返回 500 后原单号持续查单 30001,表示该笔未建单(见下文「代付下单的拒绝与 msg」);其他未知代付结果须人工核对 |
| 30002 | 币种参数无效或账户未开通 | 同码多义,结合 msg 核对 |
| 48001 | 仅沙箱:模拟订单当前状态不允许手动终态或重发(Sandbox order state does not allow this operation) | 先查询模拟单状态,只对可手动场景操作 |
| 47001 / 47014 / 47015 | 交易条款未配置 / 条款冲突 / 条款不支持(平台侧签约条款配置问题,仅在平台为商户启用新结算条款后可能由代收下单返回);订单未创建 | 不要重试;联系平台核对签约条款 |
| 429 / 限流 | 请求过快:查询接口按 appId 每秒超过 5 次(当前为 HTTP 200、JSON code=429,msg 形如 Query limit 5 times in 1s) | 降低频率,使用退避 |
| HTTP 5xx、超时、非 JSON | 结果不确定 | 保存原单号并查单,禁止盲目重复付款 |
错误表为常见交易错误,不包含平台内部管理接口。不要依赖返回的英文文本完全一致。
代付下单的拒绝与 msg
代付类接口(代付下单、代付查单、余额查询、收款人接口)为避免回显收款人资料,会改写错误 msg。下单被拒时按下表判断订单是否已创建:
自 平台代付参数校验新版本上线(上线时间以平台通知为准) 起,代付请求参数校验失败同步返回、不建单:HTTP 状态 仍为 200(与所有商户接口一致,结果看响应体),JSON code=400,msg 格式 Invalid payout request: <field> <rule>[; <field> <rule>...]——<field> 为本接口字段名,<rule> 为违反的规则(规则中的数字是平台或通道配置的上下限),不回显字段值。适用范围:所有币种的代付下单 POST /transaction/payout/v1/apply(商户后台代付相同);THB 代付另加 16.4 的付款通道字段规则。多个字段同时出错时全部列出,用 ; 分隔,通用字段按字段名字母序,THB 字段按 bizCode、branchCode、account、accountName、phone、email、amount 顺序;同一字段只报一条。
| 校验项 | msg(<field> <rule> 部分) |
|---|---|
必填字段缺失或为空(merchantOrderNo、amount、currency、product、accountName、account、phone、email、payCode) | <field> required(如 email required、payCode required);amount 不大于 0 时为 amount must be greater than 0 |
字段超长(merchantOrderNo > 255、product > 20、bankName > 255) | merchantOrderNo too long (max 255)、product too long (max 20)、bankName too long (max 255) |
bizCode / branchCode 不满足免填条件(第 7 节) | bizCode required、branchCode required |
THB:bizCode 不是 BANK | bizCode must be BANK |
THB:branchCode 不在 16.1 表 | branchCode not supported (allowed: BAY, BBL, ...),allowed 按字母序列出当前可用银行 |
THB:branchCode 不在开通通知列出的可用银行内 | 同上:branchCode not supported (allowed: ...),allowed 只含开通通知列出的可用银行(多个付款通道时取交集) |
THB:account 不是 6–20 位数字 | account must be 6-20 digits |
THB:accountName 含不允许的字符或超过开通通知的长度上限 | accountName required、accountName too long (max 10)(上限为通道配置,默认 10)、accountName invalid characters (letters, spaces and . ' - only, starting with a letter) |
THB:phone 格式不符 | phone required、phone must be 6-15 digits with an optional leading + |
THB:email 格式不符或超长 | email required、email too long (max 64)、email invalid format(部分付款通道要求,按交集执行,请始终按 16.4 填写) |
THB:amount 超出付款通道单笔范围 | amount out of range (500.00-49999.00)(范围为通道配置) |
| 情况 | 返回 | 订单 | 处理 |
|---|---|---|---|
| 上表参数校验失败 | code=400,msg Invalid payout request: <field> <rule> | 确定未创建,未冻结资金,merchantOrderNo 未被占用 | 按 msg 指出的字段修正后重新提交(可沿用原 merchantOrderNo,也可用新单号) |
请求体不是 JSON 对象或字段类型错误(如 amount 不是数字) | code=400,msg Invalid payout request: body must be a JSON object with correctly typed fields | 未创建 | 修正请求格式后重提 |
| THB:本次可能路由到的付款通道未开通或配置不完整 | 10005,msg Payout request rejected | 未创建 | 联系平台 |
| 业务规则拒绝:10004、10005、10006、10008、10010、10011、10013、10020 等 | 对应业务码,msg 仍为 Payout request rejected | 未创建 | 按第 11 节错误表处理 |
平台内部异常(数据库、提交阶段异常、超时等),以及缺少签名请求头或 Content-Type 不是 application/json | 业务码 500,msg Payout request not confirmed; query original order | 可能已建单 | 先补齐请求头;保留原单号查单,查到订单按查单结果处理,持续 30001 时联系平台确认后再处理,不要直接换单号重提 |
| 超时、断线、HTTP 5xx、非 JSON | — | 不确定 | 同第 12 节,只查原单 |
说明:code=400 表示本次请求未建单;同号旧单可能已存在(重复单号仍按 10020),返回 500 时须先用原单号查单。THB 代付的金额精度与最低金额仍按原规则返回 10006(在 THB 字段规则之后检查)。已启用业务幂等的产品,同单号同参数重放直接返回原订单、不再校验;其他重复单号仍返回 10020。批量代付和平台人工改派不经过同步校验,仍可能以失败终态结束。以前参数错误的 msg 固定为 Invalid payout request,现在其后追加 : <详情>:按 code 或该前缀判断的客户端不受影响,按整串相等判断的请改为前缀匹配。上游付款通道的处理结果仍为异步,超时等未知结果按 16.4 「超时不等于失败」处理,不会变成 400。
9999 自查清单
收到 9999 时:
- 先用原
merchantOrderNo查单(第 8 节),不要换单号重付。查单返回 30001(持续查无)说明本次未建单;返回payStatus=0说明订单已创建。对下面清单中的参数问题,平台在提交支付通道之前就会取消该订单,不会生成支付链接(9999 响应中也没有payUrl),付款人无法对该单付款;查单接口目前不区分“已取消”与“待支付”,两者都显示payStatus=0。 - 按下表逐项核对请求。下表只列通过通用校验后才会检查的项目;不满足通用校验的请求返回 400、10006、10012、10021 等原有业务码,不会是 9999(以 AUD-VKDPWMRT 为例,格式详见第 6 节「澳洲银行卡(AUD-VKDPWMRT)」)。
- 确认属于清单中的参数问题并修正后,如需重新下单,使用新的
merchantOrderNo:原单号已被占用,用同一单号、同样参数再次提交可能返回code=0但不含可用payUrl的原单受理结果,参数改动后再提交返回 409(第 4 节)。 - 清单全部核对无误仍收到 9999,或无法确定原因时,按第 6 节「下单结果不确定时」处理:保留原单号、联系平台核对原单,不要自行换单号重付。
| 自查项 | 合规要求 |
|---|---|
cardExtra 是否存在且为字符串 | 必填;值是序列化后的 JSON 文本,不是嵌套对象或数组(写成对象或数组可能返回 500) |
cardExtra 顶层键 | 恰好 connection_info、account_info 两个对象,无其他键 |
cardExtra 内部键与值 | 只用第 6 节表中的键,必填键齐全;country="AU"、state 为 8 个州/领地缩写之一、zip_code 4 位数字、area_code="61"、role 为 GUEST 或 PRIVATE;create_time 为 JSON 整数毫秒时间戳且不晚于当前时间;名/姓只含英文字母、空格、'、- |
cardExtra 内容限制 | 无空值、无控制字符、无卡号/CVV 字样或疑似卡号;总长不超过 8192 字节 |
clientIp | 键名精确为 clientIp;值不是空串或全空白,是真实付款人 IP 字面量(IPv4 直接写点分形式);放在请求体中 |
payType | 显式传 CARD |
phone | 5–15 位纯数字,不带 +、空格、横线、国家码 |
email | 形如 payer@example.com,不超过 254 个字符 |
returnUrl | 必填,https://,主机名含点且非 localhost,端口省略或 443,不超过 512 个字符 |
| 金额 | 1–500 AUD,整数或 .99 结尾(如 "20"、"20.00"、"19.99") |
| 用户身份绑定 | 同一 merchant_user_id 始终对应同一 email 和 phone,email、phone 也未被其他 merchant_user_id 使用 |
16.5 THB 错误码速查
| 情况 | 返回 | 处理 |
|---|---|---|
缺少 name、phone、email 等必填字段,或传空字符串 | 400:代收 msg 形如「请求参数不正确:email invalid」;代付 msg 见第 11 节「代付下单的拒绝与 msg」 | 补齐字段后用新单号下单 |
| 金额超出单笔范围或签约路由范围、超过两位小数 | 10006 | 按 16.2 修正金额;不要只依赖 msg |
| THB 未开通、产品未签约 | 10002 / 10004 / 10011 | 核对开通通知与签约产品 |
| 没有可用的 THB 路由 | 10005 | 联系平台 |
代收传了 payType 空字符串或与币种不匹配的编码 | 10012 | 省略 payType |
代收本节字段不合规(cardExtra、户名、手机号、邮箱、returnUrl、单笔范围) | 9999(订单未提交) | 按 16.3 自查清单修正,用新单号下单 |
| 代收结果未知 | 9999(订单处理中)或 HTTP 5xx/超时 | 原单查单,不要重下 |
| 代收付款通道明确未受理 | 10009 | 不要重试原单,联系平台 |
| 代付可用余额不足(含手续费) | 10008 | 查询 THB 余额后再处理 |
| 代付来源 IP 不在白名单 | 10010 | 提供真实服务器公网出口 IP |
| 代付本节字段不合规 | 参数校验错误(见第 11 节「代付下单的拒绝与 msg」),本次请求未建单 | 按 msg 修正后重提(可沿用原单号) |
| THB 付款通道未开通或配置不完整 | 10005 | 联系平台 |
| 代付 500 | 结果未知,可能已建单 | 原单查单,不要换单号重付 |
| 代付结果未知 | payStatus=0 持续 | 等待平台核对,不要重复代付 |
其他错误码含义见第 11 节。
12. 重复请求、超时和对账
代付超时是结果未知,不是付款失败。 商户请求超时、HTTP 5xx、断线、非 JSON,或上游超时后平台仍返回处理中时,下游应保留待查状态。已受理订单在未确认终态前保留相应资金占用,不应因等待时间长就退款、解冻、重发或改走其他渠道。
- 保存原
merchantOrderNo、已取得的orderNo、请求金额和时间,使用原订单标识查单。每次查询生成新的签名头。 payStatus=0表示仍待查;按退避间隔查询,等待验签通过的终态通知,超过内部跟踪时限转人工核对。- 一次“查无此单”(例如 30001)不能证明上游没有受理;先确认 appId 和订单号,持续核查或联系平台,禁止立即换号重发。
payStatus=1确认成功,停止重发;仅在原单明确终态失败并完成资金核对后,才可按新的业务审批另建新单。不能对失败单或未知单使用原商户订单号重新提交。- 重复通知和查询结果共用同一幂等处理流程;后到的处理中或超时结果不能覆盖已经确认的成功状态。
创建本地业务订单 → 持久化商户单号 → 提交平台
├─ 受理成功 → 保存平台单号 → 等回调 / 低频查单
├─ 超时或未知 → 保留同一商户单号 → 查单 / 人工核对
└─ 明确参数拒绝 → 修正配置;确认原单未执行后再处理
回调验签 → 校验归属/金额 → 数据库事务幂等更新 → 返回 SUCCESS
商户至少保存:appId、业务类型、商户单号、平台单号、币种、订单金额、实际金额、状态、请求/回调时间及验签结果。日志不要记录 appSecret、完整银行账户或包含密钥的待签名串。
每日核对本地订单与平台订单,分别核对代收实际到账、代付金额及签约费用。差异单、未决单、冻结金额单独跟踪。历史记录不能因停用或删除商户而丢失;资金未处理完成时不应直接清账删除。
13. 联调与上线清单
- 使用平台分配的应用和产品,先运行示例默认的余额查询,验证网络、时间戳及签名。
- 校验必填手机号、邮箱等信息。使用受控的小额业务,金额仍需符合产品/接口最低要求。
- 验证代收创建、付款、查单、成功回调、重复回调幂等,以及不正确签名被拒绝。
- 单独验证代付来源 IP、余额及费用、受理/成功/失败/未知状态;生产代付测试须明确收款账户和资金用途。
- 验证通知失败后的重试、服务重启后的订单恢复,以及对账差异处理。
- 对需要
cardExtra等扩展信息的产品,逐项核对产品目录「说明」和第 6 节;托管收银台还要验证付款后未跳回、链接过期和下单结果不确定时的处理。 - USDT 代收开放后,验证
paymentInfo展示、地址过期、4 位小数金额、含txHash的通知验签,以及按realAmount入账。 - THB 代收或代付开通后,按第 16.6 节补充检查。
- 只有完成以上验收,才能把“接口可以访问”提升为“业务链路已验证”。
下载 OpenAPI 描述 · Python 签名与验签示例 · Node.js 签名与验签示例
14. 查询状态扩展与沙箱自测
查询返回 paymentStatus、isFinal 和可选 failureReason(代收另有可选 failureCode,见第 8 节);原 payStatus 的值和含义不变。failureReason 仅供展示和排查,可能是大写代码(如 PAYOUT_FAILED)也可能是英文短句,不要据此做程序判断。客户端应忽略未知响应字段,金额用Decimal解析;这次不改变请求或回调签名规则。下单返回code=0只表示受理。
| paymentStatus | isFinal | 含义 |
|---|---|---|
| CREATED | false | 已创建,未确认支付结果 |
| PROCESSING | false | 处理中 |
| PENDING_CONFIRMATION | false | 代收金额等信息待核对,不自动入账 |
| SUCCEEDED | true | 已确认成功,仍需核对订单、币种和realAmount |
| CANCELLED | false | 平台侧已取消。一般情况下本地取消不能证明上游未扣款,failureReason=CANCELLATION_NOT_PAYMENT_PROOF,保留待核对;例外:下单时返回 9999、且订单在提交付款通道前就因参数不合规被取消(如 THB 代收,见 16.3),此时付款人无法付款,可修正后用新单号下单 |
| FAILED | true | 明确失败:代付 failureReason=PAYOUT_FAILED;代收在通道明确确认失败时给出(沙箱为 SANDBOX_SIMULATED_FAILURE) |
| UNKNOWN | false | 未知状态,保留待核对,不自动重付 |
代收旧payStatus成功为1,部分通道明确失败为2,其余为0(包括部分 FAILED 订单,请以 paymentStatus/isFinal 为准);代付成功1、失败2、其他0。不要把所有payStatus=0判为失败,也不要将所有取消判为支付最终失败。代收失败通知只在部分通道发送(第 10 节「代收失败通知」)。沙箱100.01失败场景可通过FAILED/isFinal=true与100.02持续处理区分;正式渠道未确认失败仍保留待核对状态。
生产与沙箱共用本开发文档和签名示例。沙箱测试指南、商户后台沙箱说明从同一份版本化契约生成。沙箱产品和余额独立,不代表正式产品开通;代付金额范围以指南的环境差异说明为准。
下载可运行沙箱工具:余额、服务器出口IP、下单、查单、回调验签、SQLite幂等入账演示、本地重复通知测试和结果报告。按指南配置独立沙箱凭据与自己的公网HTTPS通知地址;不使用生产密钥、不连接真实钱包。
15. AUD Google Pay 与 Apple Pay 完整请求示例
本节用于已签约这两种 AUD 钱包产品的商户。沿用当前应用的 appId/appSecret;应用必须已绑定对应产品。产品编码中的字符属于完整编码,请原样传递。本文示例不会替商户开通产品,也不代表所有商户都已开通。
| 支付方式 | product | payType |
|---|---|---|
| Google Pay | AUD-PAYPLUS-GOOGLE-M85 | GOOGLE_PAY |
| Apple Pay | AUD-PAYPLUS-APPLE-M85 | APPLE_PAY |
正式接口:POST https://transaction.hisspay.com/transaction/payin/v1/apply,Content-Type 为 application/json。请求头需 appId、timestamp、nonce、sign。timestamp 为当前13位毫秒时间戳,允许时间差30秒;nonce 每次重新生成且至少10字符。
金额为1–500 AUD,目前仅整数或 .99,例如1.00、1.99、19.99。示例金额使用字符串,避免签名时浮点格式变化。具体商户费率与结算周期以签约协议为准。
必须替换示例中的姓名、邮箱、电话、真实付款人公网IP、客户ID、注册时间、州、邮编、订单号及回调/跳转URL。clientIp不是商户服务器IP。澳大利亚资料country=AU、area_code=61,州为NSW/VIC/QLD/ACT/NT/SA/TAS/WA,邮编四位;电话仅数字。create_time为客户真实注册时间的13位毫秒整数,不晚于当前时间;注册用户role=PRIVATE,游客GUEST。
cardExtra 是 JSON 字符串,不是嵌套对象;只序列化一次,签名后不重新格式化。不要传银行卡号、CVV或钱包密码。姓名、邮箱、电话通过顶层字段传递,cardExtra.account_info中不要重复添加email/phone。
Google Pay 完整请求体
{
"merchantOrderNo": "WALLET_GOOGLE_REPLACE_UNIQUE",
"amount": "1.00",
"currency": "AUD",
"product": "AUD-PAYPLUS-GOOGLE-M85",
"payType": "GOOGLE_PAY",
"name": "John Smith",
"phone": "412345678",
"email": "john@example.com",
"clientIp": "<PAYER_PUBLIC_IP>",
"notifyUrl": "https://your-domain.com/payment/notify",
"returnUrl": "https://your-domain.com/payment/result",
"cardExtra": "{\"connection_info\":{\"country\":\"AU\",\"state\":\"NSW\",\"zip_code\":\"2000\",\"media_source\":\"website\"},\"account_info\":{\"merchant_user_id\":\"customer_10001\",\"create_time\":1790812800000,\"role\":\"PRIVATE\",\"area_code\":\"61\",\"first_name\":\"John\",\"last_name\":\"Smith\"}}"
}
Apple Pay 完整请求体
{
"merchantOrderNo": "WALLET_APPLE_REPLACE_UNIQUE",
"amount": "1.00",
"currency": "AUD",
"product": "AUD-PAYPLUS-APPLE-M85",
"payType": "APPLE_PAY",
"name": "John Smith",
"phone": "412345678",
"email": "john@example.com",
"clientIp": "<PAYER_PUBLIC_IP>",
"notifyUrl": "https://your-domain.com/payment/notify",
"returnUrl": "https://your-domain.com/payment/result",
"cardExtra": "{\"connection_info\":{\"country\":\"AU\",\"state\":\"NSW\",\"zip_code\":\"2000\",\"media_source\":\"website\"},\"account_info\":{\"merchant_user_id\":\"customer_10001\",\"create_time\":1790812800000,\"role\":\"PRIVATE\",\"area_code\":\"61\",\"first_name\":\"John\",\"last_name\":\"Smith\"}}"
}
Python 3 请求示例(标准库,无额外依赖)
下载 Python 示例。先下载任一请求JSON并替换示例资料。密钥从服务端环境变量HISSPAY_APP_ID、HISSPAY_APP_SECRET读取,不填入前端代码或仓库。
默认仅打印请求体,不发送订单:
python3 wallet_request.py google-pay-request.json
python3 wallet_request.py apple-pay-request.json
准备好真实资料与唯一订单号后,选择一种方式发送一次正式请求。--send会创建真实支付订单:
python3 wallet_request.py google-pay-request.json --send
# 或:python3 wallet_request.py apple-pay-request.json --send
#!/usr/bin/env python3
"""Python 3, standard library. Dry run by default; --send creates ONE real order."""
import hashlib
import json
import os
import secrets
import sys
import time
import urllib.request
from pathlib import Path
URL = "https://transaction.hisspay.com/transaction/payin/v1/apply"
def sign(body, app_id, secret, timestamp, nonce):
headers = {"appId": app_id, "timestamp": timestamp, "nonce": nonce}
values = {**body, **headers}
if any(not isinstance(v, str) for v in values.values()):
raise ValueError("Use string top-level fields; cardExtra must be JSON text")
canonical = "&".join(k + "=" + values[k] for k in sorted(values))
headers["sign"] = hashlib.sha256((canonical + secret).encode("utf-8")).hexdigest()
return headers
class NoRedirect(urllib.request.HTTPRedirectHandler):
def redirect_request(self, *args, **kwargs):
return None
def main():
if len(sys.argv) not in (2, 3) or (len(sys.argv) == 3 and sys.argv[2] != "--send"):
raise SystemExit("Usage: python3 wallet_request.py request.json [--send]")
body = json.loads(Path(sys.argv[1]).read_text(encoding="utf-8"))
if len(sys.argv) == 2:
print(json.dumps(body, ensure_ascii=False, indent=2))
return
# Replace ALL example customer data, URLs and order number before --send.
if body.get("clientIp") == "<PAYER_PUBLIC_IP>" or "REPLACE_UNIQUE" in body.get("merchantOrderNo", ""):
raise SystemExit("Replace example payer IP and unique order number first")
headers = sign(body, os.environ["HISSPAY_APP_ID"], os.environ["HISSPAY_APP_SECRET"],
str(int(time.time() * 1000)), secrets.token_hex(16))
headers["Content-Type"] = "application/json"
request = urllib.request.Request(URL,
data=json.dumps(body, ensure_ascii=False, separators=(",", ":")).encode("utf-8"),
headers=headers, method="POST")
# No automatic application retry or redirects. On timeout, query the original order.
with urllib.request.build_opener(NoRedirect).open(request, timeout=30) as response:
print(response.read().decode("utf-8"))
if __name__ == "__main__":
main()
Java 17+ 请求示例(JDK标准库,无额外依赖)
下载 Java 示例 · 下载付款人参数模板。将文件放在同一目录,编辑模板中的全部示例数据;两种钱包分别使用不同的唯一订单号。环境变量HISSPAY_APP_ID、HISSPAY_APP_SECRET与Python一致。
javac WalletRequest.java
# 以下仅打印JSON,不请求网络:
java WalletRequest google wallet-payer.properties
java WalletRequest apple wallet-payer.properties
# 准备好真实资料后,选择一种方式发送一次正式请求:
java WalletRequest google wallet-payer.properties --send
# 或:java WalletRequest apple wallet-payer.properties --send
import java.net.URI;
import java.net.http.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.security.MessageDigest;
import java.time.Duration;
import java.util.*;
// Java 17+, no external dependencies. Default: print request only.
public class WalletRequest {
static final String URL = "https://transaction.hisspay.com/transaction/payin/v1/apply";
static String quote(String s) {
StringBuilder b = new StringBuilder("\"");
for (char c : s.toCharArray()) {
switch (c) {
case '"': b.append("\\\""); break;
case '\\': b.append("\\\\"); break;
case '\n': b.append("\\n"); break;
case '\r': b.append("\\r"); break;
case '\t': b.append("\\t"); break;
default: if (c < 32) b.append(String.format("\\u%04x", (int)c)); else b.append(c);
}
}
return b.append('"').toString();
}
static String json(Map<String, ?> values) {
StringJoiner j = new StringJoiner(",", "{", "}");
values.forEach((k, v) -> j.add(quote(k) + ":" +
(v instanceof Map ? json((Map<String, ?>)v) : v instanceof Long ? v.toString() : quote((String)v))));
return j.toString();
}
static String required(Properties p, String k) {
String v = p.getProperty(k);
if (v == null || v.isBlank()) throw new IllegalArgumentException("Missing " + k);
return v;
}
static Map<String, String> body(String wallet, Properties p) {
if (!wallet.equals("google") && !wallet.equals("apple")) throw new IllegalArgumentException("Use google or apple");
Map<String, Object> connection = new LinkedHashMap<>();
connection.put("country", "AU");
connection.put("state", required(p, "state"));
connection.put("zip_code", required(p, "zip"));
connection.put("media_source", "website");
Map<String, Object> account = new LinkedHashMap<>();
account.put("merchant_user_id", required(p, "userId"));
account.put("create_time", Long.parseLong(required(p, "created")));
account.put("role", required(p, "role"));
account.put("area_code", "61");
account.put("first_name", required(p, "first"));
account.put("last_name", required(p, "last"));
Map<String, Object> context = new LinkedHashMap<>();
context.put("connection_info", connection); context.put("account_info", account);
Map<String, String> b = new LinkedHashMap<>();
for (String k : List.of("merchantOrderNo", "amount", "name", "phone", "email", "clientIp", "notifyUrl", "returnUrl"))
b.put(k, required(p, k));
b.put("currency", "AUD");
b.put("product", wallet.equals("google") ? "AUD-PAYPLUS-GOOGLE-M85" : "AUD-PAYPLUS-APPLE-M85");
b.put("payType", wallet.equals("google") ? "GOOGLE_PAY" : "APPLE_PAY");
b.put("cardExtra", json(context)); // String, NOT a nested JSON object.
return b;
}
static Map<String, String> sign(Map<String, String> body, String appId, String secret, String timestamp, String nonce) throws Exception {
Map<String, String> headers = new LinkedHashMap<>();
headers.put("appId", appId); headers.put("timestamp", timestamp); headers.put("nonce", nonce);
TreeMap<String, String> values = new TreeMap<>(body); values.putAll(headers);
StringJoiner canonical = new StringJoiner("&");
values.forEach((k, v) -> canonical.add(k + "=" + v));
byte[] digest = MessageDigest.getInstance("SHA-256").digest((canonical.toString() + secret).getBytes(StandardCharsets.UTF_8));
headers.put("sign", HexFormat.of().formatHex(digest));
return headers;
}
public static void main(String[] args) throws Exception {
if (args.length < 2 || args.length > 3 || (args.length == 3 && !args[2].equals("--send")))
throw new IllegalArgumentException("Usage: java WalletRequest google|apple payer.properties [--send]");
Properties p = new Properties();
try (var reader = Files.newBufferedReader(Path.of(args[1]), StandardCharsets.UTF_8)) { p.load(reader); }
Map<String, String> b = body(args[0], p);
String payload = json(b);
if (args.length == 2) { System.out.println(payload); return; }
if (b.get("clientIp").equals("<PAYER_PUBLIC_IP>") || b.get("merchantOrderNo").contains("REPLACE_UNIQUE"))
throw new IllegalArgumentException("Replace example payer IP and unique order number first");
String appId = Objects.requireNonNull(System.getenv("HISSPAY_APP_ID"), "HISSPAY_APP_ID");
String secret = Objects.requireNonNull(System.getenv("HISSPAY_APP_SECRET"), "HISSPAY_APP_SECRET");
Map<String, String> headers = sign(b, appId, secret, Long.toString(System.currentTimeMillis()), UUID.randomUUID().toString().replace("-", ""));
HttpRequest.Builder req = HttpRequest.newBuilder(URI.create(URL)).timeout(Duration.ofSeconds(30))
.header("Content-Type", "application/json").POST(HttpRequest.BodyPublishers.ofString(payload, StandardCharsets.UTF_8));
headers.forEach(req::header);
HttpClient client = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(10)).followRedirects(HttpClient.Redirect.NEVER).build();
// Send once. On timeout/error, query the original order; do not automatically resubmit.
HttpResponse<String> response = client.send(req.build(), HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
System.out.println("HTTP " + response.statusCode()); System.out.println(response.body());
}
}
签名与结果处理
两个示例都将请求体字段与appId、timestamp、nonce合并,按字段名升序拼接key=value,以&连接,末尾直接追加appSecret,不加分隔符、不做URL编码;UTF-8 SHA-256输出小写十六进制。cardExtra使用原字符串值参与签名,而不是外层JSON转义后的文本。
下单响应code=0且data.payUrl有效时,打开data.payUrl托管收银台,不使用iframe。下单受理不等于付款成功,returnUrl仅用于页面跳转;最终按服务端通知或查单确认。按第10节验证下游通知签名、订单号、金额和币种,再幂等入账。
超时、网络断开或结果不确定时保留原merchantOrderNo,先查询原单,不换单号重复下单。查询接口为POST /transaction/payin/v1/query,请求体例如 {"merchantOrderNo":"原商户订单号"},使用同样签名规则和新的timestamp、nonce。成功创建订单也不能证明真实钱包付款与入账验收完成。
16. 泰国(THB)代收与代付
开通状态(2026-10-27):THB 代收(PromptPay 扫码)已按商户逐个开通,收到开通通知(含产品编码)后即可接入,产品编码见第 1 节目录;THB 代付需单独开通,以开通通知为准,未收到开通通知前不要调用 THB 代付(未开通时下单会被拒绝)。
本节是 THB 代收、代付在本接口上的统一接入要求:平台为您开通的所有 THB 代收产品使用同一套字段规则,所有 THB 代付产品也一样,平台调整或增加付款通道时您不需要改代码。本节不表示该产品已向您的应用开放:产品编码、单笔金额范围、费率、结算周期以及可用银行以平台开通通知为准,未开通前不要下单。签名、请求头、nonce、通用字段、查单、余额和通知验签规则不变,见第 3–5 节、第 8–10 节和第 14 节。下文示例中的姓名、账号、手机号和域名均为虚构值。
16.1 银行编码
THB 代收的 cardExtra.payerBankCode 与 THB 代付的 branchCode 使用同一套编码,只能取下表的值(区分大小写,不加前缀或后缀)。表外的值或其他写法都会被拒绝。
| 银行编码 | 银行 | 代收 | 代付 |
|---|---|---|---|
SCB | Siam Commercial Bank(汇商银行) | 支持 | 支持 |
KBANK | Kasikornbank(开泰银行) | 支持 | 支持 |
KTB | Krungthai Bank(泰京银行) | 支持 | 支持 |
BAY | Bank of Ayudhya / Krungsri(大城银行) | 支持 | 支持 |
GSB | Government Savings Bank(政府储蓄银行) | 支持 | 支持 |
TTB | TMBThanachart Bank(TTB 银行) | 支持 | 支持 |
BBL | Bangkok Bank(盘谷银行) | 不支持 | 支持 |
代收付款银行只能是表中「代收」为「支持」的 6 家。代付实际可用的收款银行按付款通道配置,以开通通知列出的为准:平台只对已与付款通道逐家确认的银行出款,可能少于 7 家;通道未确认任何银行时代付不可用。使用未列出的银行编码时不会付款(返回形式见 16.4)。
16.2 金额、范围与费用
currency固定为THB,订单币种必须与签约的 THB 产品一致。amount为泰铢主币种金额,必须大于 0,最多两位小数,如"1500"、"1500.00"、"1500.50"。超过两位小数返回 10006。- 单笔金额范围:代收与代付默认均为 500.00–49999.00 THB(含两端)。开通通知给出更窄的范围时以通知为准。超出签约路由范围的请求同步返回 10006:代收 msg 形如
The minimum payable amount is 500.00.(即使是超过上限也可能显示这条最低金额文案,不要依赖 msg 判断上下限);代付 msg 统一为Payout request rejected(第 11 节)。平台在提交付款通道前还会再核对一次通道的单笔范围:代收不满足时返回 9999 且订单不会提交(见 16.3),代付不满足时的返回形式见 16.4。 - 平台按两位小数精确核对金额:付款金额或付款通道确认金额与订单金额不一致的订单,不会自动入账或出款,保留待人工核对(查单
paymentStatus为PENDING_CONFIRMATION或保持处理中)。THB 代收成功时realAmount等于amount(沙箱尾数 .06 场景故意返回不同的realAmount,用于演练第 4 节的差额处理,生产 THB 不会出现)。 - 代收费用:
realAmount是扣费前的实际收款金额;商户 THB 账户的结算净额按您签约的 THB 代收费率与固定费用计算(第 4 节「实际到账金额规则」)。 - 代付费用:收款人应收金额就是请求中的
amount,手续费不从amount中扣减。受理时平台从您的 THB 可用余额中占用amount + amount × 代付费率(向上保留 4 位小数)+ 单笔代付费用,费率与单笔费用以您的 THB 代付签约为准;成功后按此扣款,失败按账务规则释放。可用余额不足时返回 10008,下单前请用第 9 节查询 THB 可用余额并预留手续费。
16.6 THB 余额、联调与上线检查
- 余额查询使用第 9 节接口,传
{"currency":"THB"};返回的是商户在平台的 THB 账户余额。 - 沙箱提供 THB 代收与代付模拟产品,字段校验按本节统一契约(代付户名上限、可用银行及不合规时的返回形式与生产当前默认一致,见沙箱指南),按金额尾数模拟成功、失败、处理中、超时、重复通知等场景,详见沙箱测试指南「泰国 THB 模拟」。沙箱模拟不连接银行,不产生真实付款。
- 上线前按第 13 节清单验证,并额外确认:代收
cardExtra字符串与签名一致、户名与付款账号一致、16.1 编码映射正确、不传payType;代付金额、手续费与可用余额核对正确,使用真实收款账户的小额测试并确认成功与失败两种通知的处理;代付超时或长期处理中时不重复出款。