https://openplatform.gateapi.io
- 登录商户后台,商户后台地址
https://www.gate.com/zh/merchant#/get-started
- 填写基本资料,申请成为商户
- 进入应用配置页面,配置新应用
- 查看已配置应用列表,获取应用
ClientId
- 进入开发者页面,生成"支付API秘钥",用于计算支付接口签名验签
- 进入开发者页面,生成"授权秘钥",用于计算授权接口签名验签
- 支付API秘钥,⽤于请求签名
ClientId
,用于识别身份,配合支付API秘钥进行验签
所有接口响应返回JSON
格式
字段名 | 类型 | 说明 |
---|---|---|
status | string | 接口响应结果,SUCESS 成功,FAIL 失败 |
code | string | 响应错误码 |
label | string | 响应错误名称 |
errorMessage | string | 错误描述 |
data | string | JSON 格式业务响应数据 |
请求成功响应示例:
{
"status": "SUCCESS",
"code": "000000",
"errorMessage": "",
"data": {
"prepayId": "43013197477711872",
"merchantId": 10002,
"merchantTradeNo": "13683379532935164644",
"currency": "USDT",
"totalFee": "1.6",
"merchant_name": "MINIAPP PAYMENT TEST",
"goods_name": "NFT",
"status": "PENDING",
"qrcode": "http://openplatform.gate.io/qr/P_6uSR4icI56VUdM2lbYdVihLxR_SsrcNfbdzNzfgp0=",
"create_time": 1672216745425,
"expire_time": 1672220345420
}
}
失败响应示例:
{
"status": "FAIL",
"code": "400002",
"label": "INVALID_SIGNATURE",
"errorMessage": "Incorrect signature result",
"data": {}
}
商户支付的订单号由商户自定义生成,仅支持使用字母、数字、中划线-、下划线_这些英文半角字符的组合,请勿使用汉字或全角等特殊字符。GatePay要求商户订单号保持唯一性(建议根据当前系统时间加随机序列来生成订单号)。重新发起一笔支付要使用原订单号,避免重复支付。
所有金额参数都采用字符串传输,精度精确到小数点后6位,单笔交易金额最小单位为0.000001
,最大为5000000
。
GatePay支持的币种请参考支持币种章节
所有时间字段,如果没有额外说明,格式都是毫秒级的Unix
时间戳
为确定GatePay API收到的请求一定来自于被授权的合法第三方平台,GatePay API接口会检查HTTP请求头部的必须字段对请求进行签名验证。
新版协议头,使用在商户后台生成的秘钥计算签名
Head字段 | 为保证交易安全性,采用HTTPS传输 |
---|---|
X-GatePay-Certificate-ClientId | 商户在Gate商户后台注册应用时分配的clientId |
X-GatePay-Timestamp | 请求生成时的UTC 时间戳,milliseconds 。请注意,GatePay不处理收到请求时间与这个时间戳差距大于10秒钟的请求 |
X-GatePay-Nonce | 随机字符串,字符符合HTTP Header 头部的规范,建议长度在32个字符以内,字符串组成为数字和字母 |
X-GatePay-Signature | 请求签名。GatePay通过此签名来确定此请求是否合法 |
构造签名串
我们希望商户的技术开发人员按照当前文档约定的规则构造签名串。 GatePay会使用同样的方式构造签名串。如果商户构造签名串的方式错误,将导致签名验证不通过。下面先说明签名串的具体格式。
每一行为一个参数。行尾以\n
(换行符,ASCII编码值为0x0A)结束。如果参数本身以\n
结束,也需要附加一个\n
。
请求时间戳\n请求随机串\n请求报文主体\n
签名算法示例见右侧
GO语言
import
(
"crypto/hmac"
"crypto/sha512"
"encoding/hex"
"fmt"
)
// GenerateSignature 生成请求签名
// timestamp: 转换成字符串的UTC时间戳,精度是millisecond
// nonce: 随机字符串
// body: 请求体
// secretKey: Gate提供的api_secret
// return: 字符串签名
func GenerateSignature(timestamp string, nonce string, body string, secretKey string) string {
payload := fmt.Sprintf("%s\n%s\n%s\n", timestamp, nonce, body)
mac := hmac.New(sha512.New, []byte(secretKey))
mac.Write([]byte(payload))
signature := mac.Sum(nil)
return hex.EncodeToString(signature)
}
JAVA语言
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Formatter;
public class Main {
private static final String HMAC_SHA512 = "HmacSHA512";
private static String toHexString(byte[] bytes) {
Formatter formatter = new Formatter();
for (byte b : bytes) {
formatter.format("%02x", b);
}
return formatter.toString();
}
public static String calculateHMAC(String data, String key)
throws InvalidKeyException, NoSuchAlgorithmException {
SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), HMAC_SHA512);
Mac mac = Mac.getInstance(HMAC_SHA512);
mac.init(secretKeySpec);
return toHexString(mac.doFinal(data.getBytes()));
}
public static void main(String[] args) throws Exception {
String timeStamp = "1673613945439";
String nonce = "3133420233";
String body = "{\"code\":\"ac8B7Pl7C-XgfH6zxtd3SidYt7XIfWKU\",\"grant_type\":\"authorization_code\",\"redirect_uri\":\"https://gate.bytetopup.com\",\"client_id\":\"2Ugf9YGMCFRk85Yy\"}";
String data = String.format("%s\n%s\n%s\n", timeStamp, nonce, body);
String key = "zgsN5DntmQ2NCQiyJ4kJLyyEO25ewdDHydOSFIHdGrM=";
String hmac = calculateHMAC(data, key);
System.out.println(hmac);
}
}
Python语言
import hashlib
import hmac
def generate_signature(timestamp: string, nonce: string, body string, secret: string) -> string:
'''
生成请求签名
:param timestamp: 转换成字符串的UTC时间戳,精度是millisecond
:param nonce: 随机字符串
:param body: 请求体
:param secret: GatePay提供的api_secret
:return: 返回字符串签名
'''
payload = '%s\n%s\n%s\n' % (timestamp, nonce, body)
signed = hmac.new(secret.encode(), payload.encode(), hashlib.sha512)
return signed.digest().hex()
PHP语言
<?php
function generateSignature($timestamp, $nonce, $body, $secretKey) {
$payload = "$timestamp\n$nonce\n$body\n";
$signature = hash_hmac('sha512', $payload, $secretKey, true);
return bin2hex($signature);
}
$timestamp = "1631257823000";
$nonce = "abcd1234";
$body = 'the post request body content';
$secretKey = "your_secret_key";
$signature = generateSignature($timestamp, $nonce, $body, $secretKey);
echo "Signature: " . $signature;
JSON (content-type:application/json)
GET
/v1/pay/balance/query
请求示例:
curl --location 'https://openplatform.gateapi.io/v1/pay/balance/query' \
--header 'Content-Type: application/json' \
--header 'X-GatePay-Certificate-ClientId: mZ96D37oKk-HrWJc' \
--header 'X-GatePay-Timestamp: 1695611256106' \
--header 'x-GatePay-Nonce: 1260554069' \
--header 'x-GatePay-Signature: bae293c2575ccea15592fe4cec2efa2629ea37c04fc8d856060ce76dc3cebdea9382a1088c43e14a33301a320b4a2aefc029b399c337459581220bcdc17de526'
请求响应:
{
"status": "SUCCESS",
"code": "000000",
"errorMessage": "",
"data": {
"balance_list": [
{
"currency": "DOGE",
"available": "1843.32095"
},
{
"currency": "FORG",
"available": "3.02"
}
]
}
}
字段名 | 类型 | 是否必须 | 说明 |
---|---|---|---|
status | string | 是 | SUCCESS 或者 FAIL |
code | string | 是 | 出错代码 |
data | []*BalanceItem | 否 | 退款单信息 |
errorMessage | string | 否 | 错误信息 |
BalanceItem
:
字段名 | 类型 | 是否必须 | 说明 |
---|---|---|---|
currency | string | 是 | 币种 |
available | string | 是 | 币种在商户该币种在现货账户中的余额,余额保留六位小数,向下取整,末尾为0时省略末尾 |
字段名 | 类型 | 说明 |
---|---|---|
bizType | string | 描述通知类别, 见表BizType |
bizId | string | 订单id |
bizStatus | string | 订单状态,见表BizStatus |
client_id | string | 创建订单的商户client_id |
data | string | 消息内容,根据bizType不同而不同 |
消息结构示例:
{
"bizType":"PAY",
"bizId":"6948484859590",
"bizStatus":"PAY_SUCCESS",
"client_id":"cdhu-fgrfg44-5ggd-cdvsa",
"data":"{...Json格式数据...}"
}
商户在收到异步回调消息后,需响应成功收到消息,消息结构示例:
{
"returnCode": "SUCCESS",
"returnMessage": ""
}
值 | 解释 |
---|---|
PAY | 非地址支付订单状态改变通知支付成功PAY_SUCCESS ,超时或商户关闭订单 PAY_CLOSE ,支付错误 PAY_ERROR |
PAY_REFUND | 退款单状态改变通知,退款成功或失败 |
PAY_BATCH | 批量奖励订单状态改变通知 |
TRANSFER_ADDRESS | 地址支付订单资金到账通知 |
RECEIVED_CONVERT_DELAY_ADDRESS | 地址支付的延迟支付订单处理通知 |
PAY_ACTUALLY | 这是一个属于商户指定营收币种支付单的通知 |
值 | 解释 |
---|---|
PAY_SUCCESS | 订单支付成功 |
PAY_ERROR | 订单支付遇到错误 |
PAY_CLOSE | 订单被商户关闭或订单超时 |
REFUND_SUCCESS | 退款成功 |
REFUND_REJECTED | 退款被拒绝 |
PAY_EXPIRED_IN_PROCESS | 地址支付订单进入PROCESS 状态通知 |
PAY_EXPIRED_IN_EXCHANGE_FLUCTUATION | 地址支付闪因闪兑支付汇率波动导致支付失败 |
TRANSFERRED_ADDRESS_IN_TERM | 非闪兑地址支付单检测到成功支付记录后,即时将对应金额充值到商户支付账户中。(有效期内到账转账) |
TRANSFERRED_ADDRESS_DELAY | 延迟到账转账 |
CONVERT_ADDRESS_PAY_DELAY | 闪兑延迟支付,但未转账 |
TRANSFERRED_ADDRESS_BLOCK | 资金Gate成功接收,但资金有风险,不会入账 |
bizType
为PAY
,TRANSFER_ADDRESS
,RECEIVED_CONVERT_DELAY_ADDRESS一种
字段名 | 类型 | 说明 |
---|---|---|
merchantTradeNo | string | 商户交易号 |
productType | string | 创建订单时候的goodsType |
productName | string | 创建订单时候的GoodsName |
tradeType | string | 创建订单时候的terminalType |
goodsName | string | 创建订单时候的GoodsName |
terminalType | string | 创建订单时候的terminalType |
currency | string | 订单币种 |
orderAmount | string | 订单金额 |
payCurrency | string | 用户实际支付币种 |
payAmount | string | 用户实际支付金额 |
expectCurrency | string | 商户指定营收币种 |
actualCurrency | string | Gate平台向商户结算的实际币种,若Gate将订单币种转换为商户要求币种成功,则actualCurrency 与expectCurrency 相同,否则actualCurrency 等于currency |
actualAmount | string | 对应actualCurrency 币种的金额 |
payerId | int64 | 支付用户的UID |
createTime | int64 | 订单创建时间 |
channelId | string | 客户名称 |
chain | string | 网络 |
address | string | 收款地址 |
**地址支付 收到风险资金 bizStatus = TRANSFERRED_ADDRESS_BLOCK
{
"bizType": "TRANSFER_ADDRESS",
"bizId": "355736614742863872",
"bizStatus": "TRANSFERRED_ADDRESS_BLOCK",
"client_id": "gvnOrRLCqLPZVLut",
"data": {
"merchantTradeNo": "kt40t9i3t34kt0k09f5449343333",
"productType": "",
"productName": "Sipariş Ödemesi - 177",
"clientId": "gvnOrRLCqLPZVLut",
"tradeType": "APP",
"goodsName": "Sipariş Ödemesi - 177",
"terminalType": "APP",
"currency": "USDT",
"orderAmount": "10",
"payerId": 0,
"createTime": 1746775818221,
"transferAmount": "100000000",
"tx_hash": "kt40t9i3t34kt0k09t54393332223111222",
"channelId": "",
"address": "0x0410084a4c1a8fC8f6Ca67aF168Bc2ceB5ee8A31",
"chain": "ETH"
}
}
非地址支付订单状态通知bizType=PAY
{
"bizType":"PAY",
"bizId":"6948484859590",
"bizStatus":"PAY_SUCCESS",
"client_id":"cdhu-fgrfg44-5ggd-cdvsa",
"data":{
"merchantTradeNo":"gateio_withdraw6331782520222",
"productType":"NFT",
"productName":"ka",
"tradeType":"APP",
"goodsName":"ka",
"terminalType":"APP",
"currency":"USDT",
"totalFee":"1.2",
"orderAmount":"1.2",
"createTime":1664123708000,
"transactionId":"24344545",
"channelId":"123456"
}
}
地址支付订单资金到账通知bizType=TRANSFER_ADDRESS
{
"bizType": "TRANSFER_ADDRESS",
"bizId": "316518004856401920",
"bizStatus": "TRANSFERRED_ADDRESS_IN_TERM",
"client_id": "mZ96D37oKk-HrWJc",
"data": {
"merchantTradeNo": "2025012110092945520120735194",
"productType": "",
"productName": "测试订单0005",
"clientId": "mZ96D37oKk-HrWJc",
"tradeType": "MINIAPP",
"goodsName": "测试订单0005",
"terminalType": "MINIAPP",
"currency": "USDT",
"orderAmount": "1",
"payerId": 0,
"createTime": 1737425372977,
"transactionId": "316518169102520320",
"channelId": "test",
"transferAmount": "1",
"tx_hash": "2025012110093850928633404431",
"address": "TKoWkE1DfBACQTD5hsdUbj5Bn",
"chain": "TRX"
}
}
过期地址单转账bizType=TRANSFERRED_ADDRESS_DELAY
{
"bizType": "TRANSFER_ADDRESS",
"bizId": "316518004856401920",
"bizStatus": "TRANSFERRED_ADDRESS_DELAY",
"client_id": "mZ96D37oKk-HrWJc",
"data": {
"merchantTradeNo": "2025012110092945520120735194",
"productType": "",
"productName": "测试订单0005",
"clientId": "mZ96D37oKk-HrWJc",
"tradeType": "MINIAPP",
"goodsName": "测试订单0005",
"terminalType": "MINIAPP",
"currency": "USDT",
"orderAmount": "1",
"payerId": 0,
"createTime": 1737425372977,
"transactionId": "316518169102520320",
"channelId": "test",
"transferAmount": "1",
"tx_hash": "2025012110093850928633404431",
"address": "TKoWkE1DfBACQTD5hsdUbj5Bn",
"chain": "TRX"
}
}
地址支付订单状态通知bizType=RECEIVED_CONVERT_DELAY_ADDRESS
{
"bizType":"RECEIVED_CONVERT_DELAY_ADDRESS",
"bizId":"6948484859598",
"bizStatus":"TRANSFERRED_ADDRESS_PAID",
"client_id":"cdhu-fgrfg44-5ggd-cdvsa",
"data":{
"merchantTradeNo":"gateio_withdraw6331782520222",
"productType":"NFT",
"productName":"ka",
"tradeType":"APP",
"goodsName":"ka",
"terminalType":"APP",
"currency":"USDT",
"totalFee":"1.2",
"orderAmount":"1.2",
"createTime":1664123708000,
"transactionId":"24344545",
"transferAmount":"0.8",
"channelId":"123456"
}
}
bizType=PAY_REFUND
{
"bizType":"PAY_REFUND",
"bizId":123289163323899904,
"bizStatus":"REFUND_SUCCESS",
"data":{
"merchantTradeNo":"56236",
"orderAmount":"1.91",
"refundInfo":{
"orderAmount":"1.91",
"prepayId":"1647438500687506",
"refundRequestId":"156123911",
"refundAmount":"0.8"
},
"currency":"BTC",
"productName":"NFT",
"terminalType":"MINIAPP"
}
}
bizType=PAY_BATCH
{
"bizType":"PAY_BATCH",
"bizId":"1234567999800",
"bizStatus":"REFUND_SUCCESS",
"client_id":"JaBxopuhY",
"data":{
"merchant_batch_no":"6678554A99000",
"currency":"",
"order_list":[
{
"receiver_id":10000,
"amount":"1.3",
"currency":"USDT",
"status":"PAID",
"reward_id":"50888456789213330",
"create_time":1676336326072
},
{
"receiver_id":10001,
"amount":"5.7",
"currency":"USDT",
"status":"PAID",
"reward_id":"50888456789215557",
"create_time":1676336326072
}
]
}
}
bizType=PAY_ACTUALLY
{
"bizType":"RECEIVED_CONVERT_DELAY_ADDRESS",
"bizId":"577886948403339870",
"bizStatus":"CONVERT_ADDRESS_PAY_DELAY",
"client_id":"cdhu-fgrfg44-5ggd-cdvsa",
"data":{
"merchantTradeNo":"2345677666545556",
"productType":"NFT",
"productName":"NFT",
"tradeType":"WEB",
"goodsName":"NFT2",
"terminalType":"APP",
"currency":"USDT",
"totalFee":"2.35",
"orderAmount":"2.35",
"payCurrency":"USDT",
"payAmount":"2.36",
"expectCurrency":"",
"actualCurrency":"",
"actualAmount":"",
"payerId":10000,
"createTime":1676343810430,
"transactionId":"59847585498494",
"channelId":"123456"
}
}
http状态码 | 错误码 | 描述 | 解决方案 |
---|---|---|---|
500 | 300000 | 系统错误 | 系统异常,请用相同参数重新调用 |
500 | 300001 | 内部错误 | 系统异常,请用相同参数重新调用 |
500 | 400000 | 未知错误 | 系统异常,请用相同参数重新调用 |
200 | 400001 | 请求参数格式错误 | 检查请求数据参数和格式 |
200 | 400002 | 签名校验失败 | 检查商户签名是否正确 |
200 | 400003 | 请求时间戳超时 | 检查请求head里的时间戳字段 |
200 | 400007 | 不支持的media type | 查看接口设置的media type |
200 | 400020 | 签名随机数错误 | 请检查随机数是否为空 |
200 | 400201 | 商户订单号重复 | 请核实商户订单号是否重复提交 |
200 | 400202 | 订单不存在 | 请检查订单是否发起过交易或订单号是否正确 |
200 | 400203 | 商户号不存在 | 请检查商户号是否正确 |
200 | 400204 | 订单状态不正确 | 检查订单是否过期,取消被关闭状态,可调查询接口 |
200 | 400205 | 无效币种 | 请检查下单币种类型 |
200 | 400304 | 退款单ID不存在 | 检查请求的退款单ID |
200 | 400603 | 订单超时 | 请核实订单是否过期 |
200 | 400604 | 退款关联交易单无效 | 请检查退款交易单是否为完成状态 |
200 | 400605 | 支付账户余额不足 | 支付账户余额不足 |
200 | 400607 | 退款次数太多 | 退款次数大于限制 |
200 | 400608 | 退款金额异常 | 请检查退款金额 |
200 | 400620 | 订单重复支付 | 请核实商户订单号是否重复提交 |
200 | 400621 | 错误的支付金额 | 检查请求金额 |
200 | 400622 | 汇率波动导致币种兑换失败 | 可以重试在次申请 |
200 | 400623 | 不支持币种支付 | 请检查支付币种 |
200 | 400624 | 无效订单状态通知地址 | 检查商户提供回调地址是否有效 |
200 | 500008 | 未找到对应商户 | 检查请求商户ID是否正确 |
200 | 500100 | 支付二维码过期 | 重新下单生成新二维码 |
200 | 500101 | 二维码重复支付 | 请核实订单状态 |
200 | 500103 | 地址支付闪兑币种错误 | 闪兑收汇率影响 |
200 | 500203 | 地址支付无法查询到订单详情 | 请检查地址是否正确 |
200 | 500204 | 退款单的退款接收人ID无效 | 请确认退款接受人为Gate用户 |
200 | 500205 | 退款单币种币匹配 | 请保证退款币种为订单币种或用户支付币种之一 |
200 | 500206 | 退款金额超过上限 | 请检查订单退款金额 |
200 | 500207 | 地址支付无法查询到退款单 | 请求确认是否发起退款成功或查询地址是否正确 |
200 | 500208 | 不能以转换地址的方式退还无转换地址的订单 | 请确认退款订单类型 |
背景
由于网络异常或者系统的波动,可能会导致用户支付成功,但是商户侧未能成功接收到支付结果通知,进而显示订单未支付的情况。商户侧的订单状态更新不及时,容易造成用户投诉,甚至是重复支付的情况发生。
目标
商户在未能收到支付结果通知时,也能及时、准确地获取到订单的支付状态,提升商户系统的健壮性,减少因为订单状态不同步导致的用户投诉。
商户后端服务处理
商户后台需要准确、高效地处理GatePay发送的异步支付结果通知,并按接口规范把处理结果返回给GatePay服务。
定时轮询查单
如果长时间没有收到支付结果通知,商户后台应该定时轮询调用(订单状态查询接口/v1/pay/order/query
)去核实订单状态。
方案一
以订单下单成功时间为基准,每隔5秒/10秒/30秒/1分钟/3分钟/5分钟/10分钟/30分钟调用订单状态查询接口(/v1/pay/order/query
) 查询一次,最后一次查询还是未返回支付成功状态,则停止后续查询 。并调用关单接口(/v1/pay/order/close)关闭订单。(轮询时间间隔和次数,商户可以根据自身业务场景灵活设置)
方案二
定时任务每隔30秒启动一次,找出最近10分钟内创建并且未支付的订单,调用订单状态查询接口(/v1/pay/order/query
)核实订单状态。系统记录订单查询的次数,在10次查询之后状态还是未支付成功,则停止后续查询,并调用并调用关单接口(/v1/pay/order/close)关闭订单。轮询时间间隔和次数,商户可以根据自身业务场景灵活设置)
注意:支付完成后,GatePay会把相关支付结果通过数据流的形式发送给商户,商户需要接收处理,并按文档规范返回应答。
1.商户侧调用退款接口(/v1/pay/order/refund
)创建退款单,
注意退款是异步流程,该接口成功并不代表实际退款结果。
2.等待退款回调通知,如果收到GatePay异步回调通知获取到退款结果,不用在调用查询接口。
3.如果在10s内没有收到退款状态通知,调用(/v1/pay/order/refund/query
)接口查询退款单状态,具体可参考支付回调和查单实现指引章节
使用HTTPS确保网络传输安全性。
禁用SSL等不安全协议和算法,建议使用TLS1.2
及以上。
不要轻易的尝试设计和实现自己的加密传输算法,几乎都会存在问题。
敏感信息禁止出现在日志中,如确实需要,需进行脱敏处理。
缓存和DB中的敏感数据需进行加密
密码等关键认证必须采用加盐加密方式保存。
外部请求数据访问必须进行鉴权操作。
对于内部的数据访问要严加控制,降低用户信息泄露风险
支付成功回调通知必须验证GatePay签名,避免被恶意攻击。
在后台进行商户价格的判断逻辑,避免客户端篡改价格导致商户损失。
避免在App或者网站页面里面出现商户Securekey
。