系统描述
本文档规定了接入支付系统使用的报文接口,包括业务处理流程、接口的通信方式、交易报文格式说明等。本文档是面向具有一定开发能力,了解相关计算机语言的接口对接开发、维护和管理人员。
相关术语
平台
平台是指提供支付能力的我司系统。
商户
商户是指接入平台的使用者。
商户号
商户号是商户在交易平台的唯一标识,是用来判断商户在平台合法性的唯一条件。
商户私钥
商户私钥是商户在进行交易请求前,对数据做加密验签使用的密钥。是平台验证商户交易请求合法性的唯一判断条件,商户应当妥善保管加密密钥,如有泄露,应及时联系平台进行处理,避免不必要的损失。
后台异步回调
系统在接收到交易数据处理完成后,交易系统主动发起通知给商户的网站,同时携带处理完成的结果信息反馈给商户网站
币种
币种是平台用来区分货币类型的字段,具体描述见接口描述。
支付类型
支付类型是平台用来区分支付产品类别的字段,具体描述见接口描述
商户订单号
商户订单号是有商户系统每生成一笔订单会产生的一个唯一标识该笔订单的号码,商户必须保证订单号的唯一性
协议规范与规则
传输方式:采用HTTPS传输
提交方式:POST
内容类型:application/json
字符编码:UTF-8
下行签名校验:HMAC(sha256,json_str, secretKey) == header(PAY-SIGN) ;用于校验平台返回的数据签名
上行鉴权校验: HMAC(sha256,json_str, secretKey) == header(PAY-ACCESS-SIGN)
交易金额:默认入参(请求)两位小数
时间参数:均使用精确到毫秒的13位时间戳,如:1622016572190。时间戳具体是指从格林尼治时间1970年01月01日00时00分00秒起至现在的毫秒数。
回调与接口响应
- 接口正常交互时,接口会在header返回
PAY-SIGN - 通知回调地址时,会在header传递
PAY-SIGN
签名对json对象原始字符串进行hmacSHA256签名,排序由调用方决定
举例:
- 接口正常响应时,只对data的json对象签名
- 返回示例:
{"code":200,"msg":"SUCCESS","data":{"certImgUrl":"","createdAt":1730785433449,"outTradeNo":"TEST_ORDER_1234567877884466","state":"SUCCESS","tradeNo":"O1853674577155104768"}} - 以上述返回为例,只对这一部分签名
{"certImgUrl":"","createdAt":1730785433449,"outTradeNo":"TEST_ORDER_1234567877884466","state":"SUCCESS","tradeNo":"O1853674577155104768"}
- 返回示例:
- 回调通知时,对请求body中整个json字符串进行hmacSHA256签名,无排序
- 校验hmac(sha256, jsonString , secretKey) == header(PAY-SIGN)
接口鉴权
签名说明
使用参数签名调用接口示例:
curl --request POST \
--url ${HOST}/mapi/v1/account/balance \
--header 'PAY-ACCESS-MCHNO: YourMchNo' \
--header 'PAY-ACCESS-TIMESTAMP: 1733712250562' \
--header 'PAY-ACCESS-SIGN: 22582bd0cff14c41edbf1ab98506286d' \
--header 'accept: application/json' \
--header 'content-type: application/json' \
--data '{"currency":"UD"}'
发起请求:
所有REST私有请求头都必须包含以下内容:
PAY-ACCESS-MCHNO 字符串类型的mchNo商户号。
PAY-ACCESS-TIMESTAMP 发起请求的毫秒时间戳(UTC时区),如: 1733712250562
PAY-ACCESS-SIGN 使用HMAC SHA256哈希函数获得哈希值,字母都小写。
所有请求都应该含有application/json类型内容,并且是有效的JSON。
签名:
PAY-ACCESS-SIGN 的请求头是对timestamp + body字符串(+表示字符串连接),使用HMAC SHA256方法配合SecretKey加密而得到的(签名字符串的字母都小写)。
如:sign=CryptoJS.HmacSHA256(timestamp + '{"currency":"UD","amount":"1.23"}', SecretKey)
没有请求body的情况示例: sign=CryptoJS.HmacSHA256(timestamp, SecretKey)
注意:json字符串排序或不排序由对接方决定,请求body传什么就拼接签名什么
其中,timestamp的值与PAY-ACCESS-TIMESTAMP请求头相同,为毫秒时间戳格式,如: 1733712250562
body是指请求主体的json字符串(json字符串排序或不排序由对接方决定),如果请求没有主体则body可省略。如: {"currency":"UD","amount":"1.23"}
账户接口
查询账户余额
请求URL:/mapi/v1/account/balance
请求方式:POST
请求类型:application/json
请求示例
{
"currency": "UD"
}
请求参数
| 参数名 | 类型 | 必传 | 描述 |
|---|---|---|---|
| currency | String | 是 | 货币编码 |
返回示例
{
"code": 200,
"msg": "SUCCESS",
"data": {
"balance": "941951.8",
"currency": "UD",
"frozenBalance": "0",
"settlementBalance": "0"
}
}
返回参数
| 参数名 | 类型 | 必传 | 描述 |
|---|---|---|---|
| balance | String | 是 | 余额 |
| frozenBalance | String | 是 | 冻结余额 |
| settlementBalance | String | 是 | 结算中余额,在支付完成、代付退款完成后一分钟内同步到余额帐户"balance" |
| currency | String | 是 | 货币代码 |
支付接口
支付下单
请求URL:/mapi/v1/charge
请求方式:POST
请求类型:application/json
请求示例
{
"amount": "1.23",
"outTradeNo": "04ceb048-02bd-4ce9-a8a4-37363dbbaacc",
"notifyUrl": "http://127.0.0.1/callback",
"currency": "UD",
"subject": "test subject",
"body": "test content body",
"expiration": 3600
}
请求参数
| 参数名 | 类型 | 必填 | 描述 |
|---|---|---|---|
| amount | String | 是 | 订单金额(小数位最多2位小数) |
| outTradeNo | String | 是 | 商户生成的订单号,回调时会返回,请确保唯一性 |
| currency | String | 是 | 货币类型(UD) |
| notifyUrl | String | 否 | 我方通知商户接收交易结果地址,需返回SUCCESS确定接收成功,传值才会发起回调 |
| subject | String | 是 | 主题描述,禁止出现标点符号,特殊符号,可能影响商户交易成功率 |
| body | String | 是 | 内容描述,禁止出现标点符号,特殊符号,可能影响商户交易成功率 |
| expiration | Int | 否 | 订单过期时间,单位秒,默认3600秒(1小时),最大604800秒(7天) |
返回示例
{
"code": 200,
"msg": "SUCCESS",
"data": {
"outTradeNo": "TEST_ORDER_1727222219547021",
"payUrl": "https://pay.fake.com?xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"qrcode": "c.pay.354.I1874748226125434880AWVsv.1735809782.10ba6bfff7cabfb29bce93b3625ce8dfe8a72789",
"state": "PENDING",
"tradeNo": "I1874748226125434880AWVsv"
}
}
返回参数
| 参数名 | 类型 | 必传 | 描述 |
|---|---|---|---|
| tradeNo | String | 是 | 平台支付订单号 |
| outTradeNo | String | 是 | 商户关联订单号 |
| state | String | 是 | 订单状态 |
| payUrl | String | 否 | 支付页,用于用户扫码支付的中间页 |
| qrcode | String | 否 | 支付代码,二维码内容 |
查询支付订单
请求URL:/mapi/v1/charge/query
请求方式:POST
请求类型:application/json
请求示例
{
"tradeNo": "I1726294720789M1726283265RJ27G",
"outTradeNo": "TEST_ORDER_123456789000004"
}
请求参数
| 参数名 | 类型 | 必传 | 描述 |
|---|---|---|---|
| tradeNo | String | 是 | 平台生成的订单号,与outTradeNo二者传一即可,两者都传只认平台订单号 |
| outTradeNo | String | 是 | 商户生成的订单号,与tradeNo二者传一即可 |
成功示例
{
"code": 200,
"msg": "SUCCESS",
"data": {
"body": "ThisIsOrderDesc",
"createdAt": 1736407313276,
"currency": "UD",
"failReason": "",
"hash": "",
"outTradeNo": "ORDER_2727545144815000036649129",
"payAmount": "50",
"payUrl": "https://payurl.fake.com?data=WEFRZEFJZ0ZOZ2F5cGdaUHNsTlFDazI5WG9zcC85UzF6VjNrSWwybUhFV2p5Q1BrcnVLR05NYVRBSFN4Zy84UnhGcXd2MWpPKzZHcTdMcGZzQWNjTGlROVAzMkZveVgxditDTUxKdUJ0Z0RseFZEZ2ZUYTQxN1I4VTBPSTRXTDFOYU40cnR5djJPN1RFM0N2bTIwVCtBckZWc1BuUnF6MWU4c1FVZzVvNk0vNUhDamFaSlV1Skg2YjRZRk5MK2huSStBL09RZ2xZNHIzMGk5QVE4YU90aTFwYUJyZjNDdi85TWcvT1dFd3Nwaz0=",
"qrcode": "c.pay.427.I1877254450159030272izcqy0mQD.1736407313276.6fdca2d2f856f6d3cf0393574b67c094821394f47146b98568cdec2be80d67f4",
"refund": null,
"state": "SUCCESS",
"subject": "ThisIsOrderTitle",
"successTime": 1736407316432,
"totalAmount": "50",
"tradeNo": "I1877254450159030272izcqy0mQD"
}
}
退款示例
{
"code": 200,
"msg": "SUCCESS",
"data": {
"body": "ThisIsOrderDesc",
"createdAt": 1736474460345,
"currency": "UD",
"failReason": "",
"hash": "",
"outTradeNo": "ORDER_272754514966201485466944",
"payAmount": "50",
"payUrl": "https://payurl.fake.com?data=WEFRZEFJZ0ZOZ2F5cGdaUHNsTlFDazI5WG9zcC85UzF6VjNrSWwybUhFV2p5Q1BrcnVLR05NYVRBSFN4Zy84UllwM0pwTmY5TEozNFFoemROSzlzeVgrejV1SlpmY0JxUWgyelVxU2EwNjVWUS9Tb0l4bU5BVXIzWjA5elJtcEcvUTJMVjhOeGt2ZnAwMW0vSzI4QjNiT0EzNFJzWDJLNW5jSnV4STRlb0xkcnc4bE5FRjM2M1krUnRnbzhraHNCM1RQblZQRm94a3J3dzV3aXJQRG1Gamx1MWdFRW12WlVWNHNwd3RGbEQyYz0=",
"qrcode": "c.pay.427.I1877536085379125248wWtBQ7c1C2.1736474460345.a3d14a16dae760c686aa707bdad17a5899fc8ac3f175621a4b92e5898220fb59",
"refund": {
"reason": "reason desc",
"refundAmount": "50",
"refundTime": 1736474467883
},
"state": "REFUND",
"subject": "ThisIsOrderTitle",
"successTime": 1736474463516,
"totalAmount": "50",
"tradeNo": "I1877536085379125248wWtBQ7c1C2"
}
}
返回参数
| 参数名 | 类型 | 必传 | 描述 |
|---|---|---|---|
| tradeNo | String | 是 | 平台支付订单号 |
| outTradeNo | String | 是 | 商户传入的订单号 |
| totalAmount | String | 是 | 订单金额 |
| payAmount | String | 是 | 实付金额 |
| currency | String | 是 | 货币代码 |
| hash | String | 否 | 交易hash |
| state | String | 是 | 订单状态 |
| subject | String | 是 | 商品标题 |
| body | String | 是 | 商品描述 |
| createdAt | Int | 是 | 订单创建时间,13位时间戳 |
| successTime | Int | 否 | 订单支付成功时间,13位时间戳 |
| refund | Object | 否 | 退款记录 |
| refund.reason | String | 否 | 退款原因 |
| refund.refundAmount | String | 否 | 退款数量 |
| refund.refundTime | Int | 否 | 退款时间 |
| payUrl | String | 是 | 支付中间页 |
| qrcode | String | 是 | 二维码内容 |
| failReason | String | 否 | 失败原因 |
state字段说明:
| state | 描述 |
|---|---|
| SUCCESS | 支付成功 |
| PENDING | 交易创建,等待付款 |
| CLOSE | 交易关闭(超时等) |
| REFUND | 退款 |
| REFUNDING | 退款中 |
支付申请退款
请求URL:/mapi/v1/charge/refund
请求方式:POST
请求类型:application/json
请求示例
{
"tradeNo": "I123456789",
"outTradeNo": "04ceb048-02bd-4ce9-a8a4-37363dbbaacc",
"reason": "test reason",
"notifyUrl": "http://127.0.0.1/callback"
}
请求参数
| 参数名 | 类型 | 必填 | 描述 |
|---|---|---|---|
| tradeNo | String | 否 | "支付下单"平台生成的订单号,与outTradeNo二者传一即可,两者都传只认平台订单号 |
| outTradeNo | String | 否 | "支付下单"商户生成的订单号,与tradeNo二者传一即可 |
| reason | String | 是 | 退款原因(最大64个字符) |
| notifyUrl | String | 否 | 我方通知商户接收退款结果地址,需返回SUCCESS确定接收成功,传值才会发起回调 |
返回示例
{
"code": 200,
"msg": "SUCCESS",
"data": {
"totalAmount": "50",
"currency": "UD",
"outTradeNo": "ZRP_ORDER_272722237936708",
"payAmount": "50",
"refundAmount": "50",
"state": "PENDING",
"tradeNo": "I1876451418919931904GzKRkcggj5"
}
}
返回参数
| 参数名 | 类型 | 必传 | 描述 |
|---|---|---|---|
| totalAmount | String | 是 | 支付订单金额 |
| currency | String | 是 | 货币代码 |
| outTradeNo | String | 是 | 商户支付订单号 |
| payAmount | String | 是 | 支付订单实付金额 |
| refundAmount | String | 是 | 退款金额 |
| state | String | 是 | 退款状态 |
| tradeNo | String | 是 | 平台支付订单号 |
支付和退款通知
请求URL:该链接是通过统一下单接口提交的参数notifyUrl设置,如果无法访问链接,商户系统将无法接收到支付中心的通知。
请求方式:POST
请求类型:application/json
签名: header : PAY-SIGN
推送及时性: 默认及时,200 QPS
通知示例
{
"mchNo": "M1729578167",
"tradeNo": "I1853326882515230720",
"outTradeNo": "TEST_ORDER_1727222253166090",
"totalAmount": "10.12",
"payAmount": "10.12",
"hash": "",
"payId": "0x00000000000000",
"currency": "UD",
"state": "SUCCESS",
"createdAt": 1726298412000,
"successTime": 1726298412000,
"ext": "",
"failReason": "",
"refund": {
"reason": "",
"refundAmount": "",
"refundTime": 0
}
}
通知参数
| 参数名 | 类型 | 必传 | 描述 |
|---|---|---|---|
| tradeNo | String | 是 | 平台支付单号 |
| mchNo | String | 是 | 平台标识编码 |
| outTradeNo | String | 是 | 商户支付单号 |
| totalAmount | String | 是 | 订单金额 |
| hash | String | 否 | 交易hash |
| payAmount | String | 是 | 订单实付金额 |
| payId | String | 是 | 付款用户支付id |
| currency | String | 是 | 币种编码 |
| state | String | 是 | 订单状态 |
| createdAt | Int | 是 | 订单创建时间,13位时间戳 |
| successTime | Int | 否 | 订单支付成功时间,13位时间戳 |
| ext | String | 是 | 根据业务单独解释 |
| failReason | String | 否 | 失败原因 |
| refund | Object | 否 | 退款记录 |
| refund.reason | String | 否 | 退款原因 |
| refund.refundAmount | String | 否 | 退款数量 |
| refund.refundTime | Int | 否 | 退款时间 |
商户返回结果
需要对这整个 JSON 字符串进行签名验签 hmac(sha256, jsonString , secretKey) == header("PAY-SIGN")
state字段说明:
| state | 描述 |
|---|---|
| SUCCESS | 支付成功 |
| PENDING | 交易创建,等待付款 |
| CLOSE | 交易关闭(超时等) |
| REFUND | 退款 |
| REFUNDING | 退款中 |
代付接口
代付下单
商户业务系统通过转账接口发起转账申请。
请求URL:/mapi/v1/transfer
请求方式:POST
请求类型:application/json
请求示例
{
"amount": "0.05",
"outTradeNo": "TEST_ORDER_12345678",
"payId": "23504509880",
"desc": "test",
"notifyUrl": "https://callback.fake.com/success",
"currency": "UD"
}
请求参数
| 参数名 | 类型 | 必填 | 描述 |
|---|---|---|---|
| outTradeNo | String | 是 | 商户生成的订单号,请确保唯一性,回调时会返回 |
| amount | String | 是 | 代付金额(2位小数)即对方收款金额,实际扣款金额包含手续费可能大于此金额 |
| currency | String | 是 | 货币编码(UD) |
| payId | String | 是 | 收款账号(收款人的支付id) |
| desc | String | 否 | 转账描述信息 |
| notifyUrl | String | 否 | 异步通知地址 |
返回示例
{
"code": 200,
"msg": "SUCCESS",
"data": {
"payId": "23504509880",
"totalAmount": "13.12",
"outTradeNo": "TEST_ORDER_12345678",
"state": "PENDING",
"tradeNo": "O1853357790068776960"
}
}
返回参数
| 参数名 | 类型 | 必传 | 描述 |
|---|---|---|---|
| payId | String | 是 | 收款账号(收款人的支付id) |
| totalAmount | String | 是 | 代付金额 |
| outTradeNo | String | 是 | 商户生成的转账订单号 |
| state | String | 是 | 转账状态 |
| tradeNo | String | 是 | 平台转账单号 |
查询代付订单
请求URL:/mapi/v1/transfer/query
请求方式:POST
请求类型:application/json
请求示例
{
"tradeNo": "O1853357790068776960",
"outTradeNo": ""
}
请求参数
| 参数名 | 类型 | 必填 | 描述 |
|---|---|---|---|
| tradeNo | String | 是 | 平台生成的代付单号,与outTradeNo二者传一即可,都传时只认tradeNo |
| outTradeNo | String | 是 | 商户生成的代付单号,与tradeNo二者传一即可 |
返回示例
{
"code": 200,
"msg": "SUCCESS",
"data": {
"createdAt": 1736840662102,
"currency": "G",
"desc": "0b0",
"failReason": "",
"hash": "",
"outTradeNo": "TEST_ORDER_12345678125127371",
"payId": "0xc14b9cd60031dc83552e3203f26bf900",
"state": "SUCCESS",
"successTime": 0,
"totalAmount": "1000",
"tradeNo": "O1879072046915260416zQAsjug5EX"
}
}
返回参数
| 参数名 | 类型 | 必传 | 描述 |
|---|---|---|---|
| outTradeNo | String | 是 | 商户生成的代付订单号 |
| tradeNo | String | 是 | 平台生成的代付订单号 |
| totalAmount | String | 是 | 转账金额 |
| currency | String | 是 | 货币编码 |
| hash | String | 否 | 交易hash |
| state | String | 是 | 转账状态 |
| payId | String | 是 | 收款帐户(收款人的支付id) |
| desc | String | 否 | 转账描述信息 |
| createdAt | Int | 是 | 订单创建时间,13位时间戳 |
| successTime | Int | 否 | 转账成功时间,13位时间戳 |
| failReason | String | 否 | 失败原因 |
state字段说明:
| state | 描述 |
|---|---|
| SUCCESS | 交易完成 |
| PENDING | 下单成功等待结算转账 |
| FAIL | 转账执行失败(余额不足、收款账号无效等) |
| CLOSE | 交易关闭 |
代付通知
请求URL:该链接是通过转账申请接口提交的参数notifyUrl设置,如果无法访问链接,商户系统将无法接收到支付中心的通知。
请求方式:POST
请求类型:application/json
签名: header: PAY-SIGN
推送及时性: 默认及时,200 QPS
通知示例数据
{
"mchNo": "88000109",
"tradeNo": "O1234567890987654321",
"outTradeNo": "TSTRORDER1234567890",
"totalAmount": "100",
"payId": "0x000000000",
"currency": "UD",
"hash": "",
"state": "SUCCESS",
"createdAt": 1726298412000,
"successTime": 1726298412000,
"ext": "",
"failReason": ""
}
通知参数
| 参数名 | 类型 | 必填 | 描述 |
|---|---|---|---|
| tradeNo | String | 是 | 平台代付订单号 |
| mchNo | String | 是 | 平台唯一标识,即商户号 |
| outTradeNo | String | 是 | 商户代付订单号 |
| totalAmount | String | 是 | 订单金额 |
| hash | String | 否 | 交易hash |
| payId | String | 是 | 收款帐户(收款用户的支付id) |
| currency | String | 是 | 货币 |
| state | String | 是 | 支付订单状态 |
| createdAt | Int | 是 | 订单创建时间,13位时间戳 |
| successTime | Int | 否 | 订单支付成功时间,13位时间戳 |
| ext | String | 否 | 扩展字段,根据业务单独解释 |
| failReason | String | 否 | 失败原因 |
商户返回结果
需要对这整个 JSON 字符串进行签名验签 hmac(sha256, jsonString, secretKey) == header("PAY-SIGN")
state字段说明:
| state | 描述 |
|---|---|
| SUCCESS | 交易完成 |
| PENDING | 下单成功等待结算转账 |
| FAIL | 转账执行失败(余额不足、收款账号无效等) |
| CLOSE | 交易关闭 |
沙箱支付
代收模拟支付
请求URL:/mapi/v1/sandbox/charge/simulatePayment
请求方式:POST、GET
请求类型:application/json
请求示例
{
"outTradeNo":"I1868917204624343040TLJRck6",
"payId":"0x1000000"
}
请求参数
| 参数名 | 类型 | 必填 | 描述 |
|---|---|---|---|
| outTradeNo | String | 是 | 商户订单号或系统订单号,建议使用系统订单号 |
| payId | String | 是 | 付款用户支付Id |
返回示例
{
"code": 200,
"msg": "SUCCESS",
"data": null
}
接口状态码
The API uses the following error codes:
| Code | 提示 | 说明 |
|---|---|---|
| 200 | SUCCESS | 业务调用成功 |
| 400 | Params error | 参数异常 |
| 405 | Invalid order no | 业务条件不满足 |
| 429 | Too many requests | 请求过快 |
| 500 | Internal service error | 内部服务错误 |
| 1000 | Merchant not exist or disabled | 商户不存在或被禁用 |
| 1001 | Invalid secret key | 密钥错误 |
| 1002 | IP is not on the whitelist | ip不在白名单 |
| 1003 | Order repeat | 商户订单号重复 |
| 1004 | Create order failed | 订单创建失败 |
| 1005 | Order not exist | 订单不存在 |
| 1006 | Insufficient Balance | 余额不足 |
| 1007 | The order is non refundable | 订单不可退款 |
| 1008 | The refund amount is not correct | 退款金额不正确 |
| 1011 | Invalid notify url | 无效回调通知url |
| 1012 | No notify URL whitelist configured | 没有配置通知url白名单 |
| 1013 | Wallet is less or disabled | 钱包被禁或不存在 |
| 1014 | The account triggers the risk control rule | 该账户触发风控规则 |
| 1015 | Account does not exist | 账户不存在(payId无效等) |
| 1016 | Invalid amount | 无效金额(金额过小等) |
| 1100 | Invalid timestamp | 请求头PAY-ACCESS-TIMESTAMP时间戳过期或为空,请传入当前UTC时区的13位毫秒时间戳数字,且该值要参与签名 |
| 1101 | Invalid mchNo | 请求头PAY-ACCESS-MCHNO无效商户号 |
| 1102 | Invalid Content-Type | 请求头Content-Type 请使用“application/JSON”格式 |
| 1103 | Invalid signature | 请求头PAY-ACCESS-SIGN无效签名 |
| 2004 | System error, please contact customer service | 系统错误,请联系客服 |