Files

22 KiB
Raw Permalink Blame History

Epusdt API 文档

开发者可通过 Epusdt 提供的 HTTP API 将收款能力集成到业务系统。本文档以当前代码路由为准。

旧版 POST /api/v1/order/create-transaction 已不再注册;创建订单请使用 POST /payments/gmpay/v1/order/create-transaction

接口总览

场景 方法 路径 是否需要签名
创建 GMPay 交易 POST /payments/gmpay/v1/order/create-transaction
获取公开支付配置 GET /payments/gmpay/v1/config
收银台页面 GET /pay/checkout-counter/{trade_id}
收银台初始化数据 GET /pay/checkout-counter-resp/{trade_id}
查询支付状态 GET /pay/check-status/{trade_id}
切换支付网络/通道 POST /pay/switch-network
EPay 兼容创建交易 GET/POST /payments/epay/v1/order/create-transaction/submit.php
OkPay 平台回调 POST /payments/okpay/v1/notify OkPay 签名

统一响应格式

除重定向和纯文本回调接口外,接口返回 JSON:

{
  "status_code": 200,
  "message": "success",
  "data": {},
  "request_id": "b1344d70-ff19-4543-b601-37abfb3b3686"
}

说明:

字段 类型 说明
status_code integer 业务状态码。成功为 200,错误码见文末。
message string 返回消息。
data object/null 接口数据。
request_id string 请求 ID,服务端自动生成。

签名错误会返回 HTTP 401;业务错误通常返回 HTTP 400,并在 status_code 中给出具体业务码。

签名规则

当前版本使用统一商户凭证。请求必须携带 pid,服务端用 pid 查询对应的 secret_key 作为签名密钥。默认安装会创建一个 PID 为 1000 的默认密钥。

GMPay 签名

  1. 将所有非空参数按参数名 ASCII 字典序升序排序。
  2. 使用 key=value 形式以 & 拼接。
  3. 不参与签名的字段:signature
  4. 在拼接字符串末尾直接追加 secret_key
  5. 对最终字符串做 MD5,结果转小写,作为 signature

注意:

  • pid 必须参与签名。
  • GMPay 的 payment_type 不是必填;如果请求里传了非空 payment_type,它和其他非空参数一样必须参与签名。
  • 空字符串和 null 不参与签名。
  • 参数名区分大小写。
  • JSON 数字会按服务端数字格式参与签名,例如 100.00 会被解析为 100;如果需要保留字符串格式,可使用 application/x-www-form-urlencoded

示例参数:

pid=1000
order_id=ORD202605230001
currency=cny
token=usdt
network=tron
amount=100
notify_url=https://merchant.example/notify
redirect_url=https://merchant.example/return
name=VIP

以下示例假设 secret_keyepusdt_secret_key,仅用于演示签名计算。

待签名字符串:

amount=100&currency=cny&name=VIP&network=tron&notify_url=https://merchant.example/notify&order_id=ORD202605230001&pid=1000&redirect_url=https://merchant.example/return&token=usdtepusdt_secret_key

得到:

signature=476412c422f4dd75c3d533f5c47a9cac

PHP 签名示例

GMPay 使用 signature 字段,签名时只排除 signature

function gmpaySign(array $params, string $secretKey): string
{
    unset($params['signature']);
    ksort($params, SORT_STRING);

    $pairs = [];
    foreach ($params as $key => $value) {
        if ($value === '' || $value === null) {
            continue;
        }
        $pairs[] = $key . '=' . $value;
    }

    return strtolower(md5(implode('&', $pairs) . $secretKey));
}

EPay 兼容接口使用 sign 字段,签名时排除 signsign_type

function epaySign(array $params, string $secretKey): string
{
    unset($params['sign'], $params['sign_type']);
    ksort($params, SORT_STRING);

    $pairs = [];
    foreach ($params as $key => $value) {
        if ($value === '' || $value === null) {
            continue;
        }
        $pairs[] = $key . '=' . $value;
    }

    return strtolower(md5(implode('&', $pairs) . $secretKey));
}

创建 GMPay 交易

POST /payments/gmpay/v1/order/create-transaction

支持:

  • Content-Type: application/json
  • Content-Type: application/x-www-form-urlencoded

请求示例

{
  "pid": "1000",
  "order_id": "ORD202605230001",
  "currency": "cny",
  "token": "usdt",
  "network": "tron",
  "amount": 100,
  "notify_url": "https://merchant.example/notify",
  "redirect_url": "https://merchant.example/return",
  "name": "VIP",
  "signature": "476412c422f4dd75c3d533f5c47a9cac"
}

请求参数

字段 类型 必填 说明
pid string 商户 PID,用于查找 API Key,并参与签名。
order_id string 商户订单号,最长 32 字符,不能重复。
currency string 法币币种,如 cnyusd
token string 条件必填 收款币种,如 usdttrxusdcsol。GMPay 可与 network 同时省略以创建状态 4 占位订单。
network string 条件必填 收款网络,如 tronsolanaethereumbscpolygonplasma。GMPay 可与 token 同时省略以创建状态 4 占位订单。
amount number 法币金额,必须大于 0.01
notify_url string 支付成功异步回调地址。
redirect_url string 支付完成后的同步跳转地址。
name string 商品/订单名称。
payment_type string GMPay 兼容字段,不要求必须传;如果传了非空值,必须参与 GMPay signature 计算。普通 GMPay 不传时后台会存为 Gmpay;传 Epay(大小写不敏感)会统一存为 Epay 并使用 EPay 回调格式,且 PID 必须是数字。
signature string GMPay 签名。

tokennetwork 必须同传或同缺。两者同缺时只创建包含 amount/currency 的占位订单,状态为 4,不会分配钱包、不会计算链上支付金额,也不会锁定交易金额;后续由收银台调用 /pay/switch-network 选择具体链和币种或 OkPay。只缺其中一个会返回参数错误。

建议先调用 /payments/gmpay/v1/config 获取可用的 networktoken 组合。

成功响应

{
  "status_code": 200,
  "message": "success",
  "data": {
    "trade_id": "20260523171652123456001",
    "order_id": "ORD202605230001",
    "amount": 100,
    "currency": "CNY",
    "actual_amount": 14.29,
    "receive_address": "TTestTronAddress001",
    "token": "USDT",
    "status": 1,
    "expiration_time": 1779530812,
    "payment_url": "https://pay.example.com/pay/checkout-counter/20260523171652123456001"
  },
  "request_id": "b1344d70-ff19-4543-b601-37abfb3b3686"
}
字段 类型 说明
trade_id string Epusdt 交易号。
order_id string 商户订单号。
amount number 商户提交的法币金额。
currency string 法币币种。
actual_amount number 实际需支付的加密货币数量。
receive_address string 收款地址。
token string 收款币种。
status integer 订单状态。状态 4 表示等待用户选择 token/network
expiration_time integer 订单过期时间,秒级时间戳。
payment_url string 收银台地址。该地址会跳转到前端收银台。

状态 4 占位订单的 actual_amount0receive_addresstoken 为空;过期任务或后台关闭只会把它改为状态 3,不会执行交易金额解锁。第一次成功调用 /pay/switch-network 时,如果选择普通链上 token/network,同一个父订单会原地补全链上字段并变为状态 1,此时才会创建真实交易锁;如果选择 network=okpay,同一个父订单会原地变为 OkPay 订单并返回 OkPay 托管支付链接,不创建子订单,也不会分配本系统钱包地址或链上锁。占位父单首次补全后 is_selected 仍为 false,后续同目标选择才会把父单标记为已选中;如果后续切到其它支付目标,则创建唯一一条子订单。

获取公开支付配置

GET /payments/gmpay/v1/config

返回收银台展示配置、可用链/币种、EPay 默认配置和 OkPay 公共配置。

成功响应示例

{
  "status_code": 200,
  "message": "success",
  "data": {
    "supported_assets": [
      {
        "network": "tron",
        "display_name": "TRON",
        "tokens": ["TRX", "USDT"]
      },
      {
        "network": "solana",
        "display_name": "Solana",
        "tokens": ["SOL", "USDC", "USDT"]
      }
    ],
    "site": {
      "cashier_name": "Acme Cashier",
      "logo_url": "https://cdn.example.com/logo.png",
      "website_title": "Acme Payments",
      "support_link": "https://example.com/support",
      "background_color": "#0f172a",
      "background_image_url": "https://cdn.example.com/background.png"
    },
    "epay": {
      "default_token": "",
      "default_currency": "cny",
      "default_network": ""
    },
    "okpay": {
      "enabled": false,
      "allow_tokens": ["USDT", "TRX"]
    },
    "version": "v1.0.1"
  },
  "request_id": "b1344d70-ff19-4543-b601-37abfb3b3686"
}

supported_assets 只包含同时满足以下条件的组合:

  • 链已启用。
  • 该链有可用钱包地址。
  • 该链至少有一个启用中的 token。

收银台页面

GET /pay/checkout-counter/{trade_id}

用于浏览器打开收银台。当前实现会返回 301,并跳转到:

/cashier/{trade_id}

创建交易接口返回的 payment_url 即为该地址。

收银台初始化数据

GET /pay/checkout-counter-resp/{trade_id}

用于前端收银台读取订单展示数据。该接口只确认订单存在并返回基础数据;当前支付状态请调用 /pay/check-status/{trade_id}

成功响应示例

{
  "status_code": 200,
  "message": "success",
  "data": {
    "trade_id": "20260523171652123456001",
    "amount": 100,
    "actual_amount": 14.29,
    "token": "USDT",
    "currency": "CNY",
    "receive_address": "TTestTronAddress001",
    "network": "tron",
    "status": 1,
    "payment_type": "gmpay",
    "expiration_time": 1779530812000,
    "redirect_url": "https://merchant.example/return",
    "payment_url": "",
    "created_at": 1779530212000,
    "is_selected": false
  },
  "request_id": "b1344d70-ff19-4543-b601-37abfb3b3686"
}

注意:该接口的 expiration_timecreated_at 是毫秒级时间戳。

如果订单是状态 4 占位订单,返回的仍是同一个父订单 trade_id,但链上支付字段尚未生成。该状态可能来自 GMPay 空 token/network 创建,也可能来自 EPay submit.php 在请求和数据库默认值都没有完整 token/network 时创建:

{
  "status_code": 200,
  "message": "success",
  "data": {
    "trade_id": "20260523171652123456001",
    "amount": 100,
    "actual_amount": 0,
    "token": "",
    "currency": "CNY",
    "receive_address": "",
    "network": "",
    "status": 4,
    "payment_type": "gmpay",
    "expiration_time": 1779530812000,
    "redirect_url": "https://merchant.example/return",
    "payment_url": "",
    "created_at": 1779530212000,
    "is_selected": false
  },
  "request_id": "b1344d70-ff19-4543-b601-37abfb3b3686"
}

payment_type 是归一化后的接入类型:底层订单存储为 Epay/Gmpay,该接口转为小写 epay/gmpay 返回;epay 会走 EPay 回调格式,gmpay 走默认 GMPay JSON 回调格式。

前端看到 status=4 时,应展示选择网络和币种/支付通道的界面,并在用户选择后调用 /pay/switch-network。选择链上支付成功后,该父订单会变为 status=1actual_amounttokennetworkreceive_address 会被补全,但 is_selected 保持 false,由后续同目标选择流程标记为已选中。选择 OkPay 成功后,接口返回同一个父订单 trade_id 和第三方 payment_url;父订单会变为 status=1is_selected=falsepay_provider=okpaynetwork=okpayreceive_address=OKPAY

查询支付状态

GET /pay/check-status/{trade_id}

成功响应示例

{
  "status_code": 200,
  "message": "success",
  "data": {
    "trade_id": "20260523171652123456001",
    "status": 1
  },
  "request_id": "b1344d70-ff19-4543-b601-37abfb3b3686"
}

订单状态:

说明
1 等待支付
2 支付成功
3 已过期
4 等待选择支付网络/币种

切换支付网络/通道

POST /pay/switch-network

该接口通常由收银台前端调用,用于切换到另一个链上收款地址,或切换到 OkPay 托管收银台。

请求示例

{
  "trade_id": "20260523171652123456001",
  "token": "USDT",
  "network": "solana"
}

切换到 OkPay

{
  "trade_id": "20260523171652123456001",
  "token": "USDT",
  "network": "okpay"
}

请求参数

字段 类型 必填 说明
trade_id string 父订单交易号。
token string 目标币种。
network string 目标网络,或特殊值 okpay

成功响应

返回结构与收银台初始化数据一致。链上订单的 payment_url 为空;OkPay 订单的 payment_url 是 OkPay 返回的托管支付链接。若父订单仍是 status=4,首次切换链上或 OkPay 都会原地补全父订单并返回同一个 trade_id

说明:

  • 只能对父订单切换网络,不能对子订单继续切换。
  • 父订单必须处于等待支付状态 1,或占位状态 4
  • 状态 4 第一次选择具体链和币种时,会原地补全父订单并返回同一个 trade_id,不会创建子订单。
  • 状态 4 第一次选择 network=okpay 时,不要求父订单已有链上字段;系统会原地把父订单补成 OkPay 订单并返回同一个 trade_id 与 OkPay payment_url,不会创建子订单。
  • 状态 4 补全后订单变为状态 1,但 is_selected 保持 false;之后同目标选择会返回父单并标记选中,切到其它支付目标才创建子订单。
  • 每个父订单最多创建 1 个子订单;已经创建过子订单后,不能再用该父单创建第二个新子订单。子订单本身不能继续切换网络。
  • 如果切换到同一组 token + network,会返回已有订单。

EPay 兼容创建交易

GET /payments/epay/v1/order/create-transaction/submit.php

POST /payments/epay/v1/order/create-transaction/submit.php

该接口兼容传统 EPay/易支付接入方式。成功后不会返回 JSON,而是 HTTP 302 跳转到:

/pay/checkout-counter/{trade_id}

请求参数

字段 位置 类型 必填 说明
pid query/form string 商户 PID。建议使用数字 PID;EPay 回调会按数字 PID 输出。
money query/form number 法币金额。
out_trade_no query/form string 商户订单号。
notify_url query/form string 异步回调地址。
return_url query/form string 支付完成后的同步跳转地址。
name query/form string 商品/订单名称。
type query/form string 仅支持空值、alipay,或当前已启用并可收款的 token.network selector(如 usdt.tron)。推荐使用小写 alipay
token query/form string 可选收款币种。仅在 type 不是命中的 selector 时参与解析;传了就必须参与 EPay 签名。
network query/form string 可选收款网络。仅在 type 不是命中的 selector 时参与解析;传了就必须参与 EPay 签名。
currency query/form string 可选法币币种。优先级高于后台 epay.default_currency;传了就必须参与 EPay 签名。
sign query/form string EPay 签名。
sign_type query/form string 通常为 MD5

签名规则:

  • 使用 pid 对应的 secret_key
  • 排除 signsign_type
  • 其他非空参数按 ASCII 字典序拼接后追加 secret_key 并 MD5;如果接入插件额外传了 sitename 等字段,也要一起参与签名。

示例待签名字符串:

money=100&name=VIP&notify_url=https://merchant.example/notify&out_trade_no=ORD202605230001&pid=1000&return_url=https://merchant.example/return&type=alipayepusdt_secret_key

得到:

sign=b865b0acbb2b01554c35a1bd33351452

EPay 接口解析 type/token/network/currency 的规则:

  • type 只接受三类输入:空值、alipay、命中的 token.network selector。
  • type=token.network 且命中当前已启用支付资产时,会直接确定本次订单的 token/network,并覆盖请求参数里的 token/network 以及后台 epay.default_token / epay.default_network
  • type 非空但既不是命中的 selector,也不是 alipay 时,直接返回 10009 invalid params。例如 usdt-tron、未启用的 usdc.tron 都会被拒绝。
  • type 为空或为 alipay 时,token/network 继续走原有解析:先看请求参数,再分别用数据库 epay.default_token / epay.default_network 补齐。
  • currency 解析不受 selector 影响:请求参数 currency > 数据库 epay.default_currency > cny
  • 最终解析结果里,token/network 同时有值时创建具体链上订单;同时为空时创建状态 4 占位订单;最终只缺一个时返回参数错误。
  • 这意味着“请求里只传了一个值”不一定报错;如果另一个值能被 default 补齐,仍会成功。只有最终解析后仍然只剩一个值,才返回 10009
  • 服务端会在 EPay 签名校验通过后内部注入 payment_type=Epay,该字段不参与 EPay 入站签名;但请求里显式传入的 type/token/network/currency 仍属于原始 EPay 参数,必须参与签名。

后台默认配置可通过 /payments/gmpay/v1/configepay 字段查看;新安装默认只预置 epay.default_currency=cnyepay.default_tokenepay.default_network 为空,因此 EPay 未显式传 token/network 时会创建状态 4 占位订单。已有数据库的配置不会被 seed 覆盖,删除或置空 epay.default_tokenepay.default_network 后,这两个字段会返回空字符串。

商户异步回调

订单支付成功后,Epusdt 会向订单的 notify_url 发送异步通知。目标服务器处理完成后需返回 HTTP 200,响应体为 oksuccess(大小写不敏感)。否则会按队列配置重试:首次失败后最多重试 order_notice_max_retry 次,重试间隔按 callback_retry_base_seconds 指数退避,最大 5 分钟。

GMPay 回调

普通 GMPay 订单使用 POST JSON 回调。

{
  "pid": "1000",
  "trade_id": "20260523171652123456001",
  "order_id": "ORD202605230001",
  "amount": 100,
  "actual_amount": 14.29,
  "receive_address": "TTestTronAddress001",
  "token": "USDT",
  "block_transaction_id": "0xabc123...",
  "signature": "a1b2c3d4e5f6...",
  "status": 2
}
字段 类型 说明
pid string 订单所属 API Key 的 PID。商户应使用该 PID 查本地密钥验签。
trade_id string Epusdt 交易号。
order_id string 商户订单号。
amount number 商户提交的法币金额。
actual_amount number 实际到账的加密货币数量。
receive_address string 收款地址。
token string 收款币种。
block_transaction_id string 链上交易哈希或第三方支付订单号。
signature string 回调签名。
status integer 当前仅支付成功时回调,值为 2

GMPay 回调验签方式与创建订单一致,但排除 signature 字段。

EPay 兼容回调

通过 EPay 兼容接口创建的订单,会使用 GET 请求回调 notify_url,参数如下:

EPay 回调会把 pid 输出为数字;使用 EPay 兼容接口或 payment_type=Epay 时,请确保 API Key 的 PID 是数字。

type 出站时使用订单里保存的请求值。当前主分支正常入站能保存下来的只会是 alipay 或命中的 token.network selector;如果入站请求没传 type,出站才回退为 alipay

pid=1000
trade_no=20260523171652123456001
out_trade_no=ORD202605230001
type=alipay
name=VIP
money=100.0000
trade_status=TRADE_SUCCESS
sign=a1b2c3d4...
sign_type=MD5

验签时排除 signsign_type,其余非空参数按 ASCII 字典序拼接后追加 secret_key 并 MD5。

OkPay 平台回调

POST /payments/okpay/v1/notify

这是 OkPay/OkayPay 平台通知 Epusdt 的接口,不是商户系统主动调用的接口。配置 OkPay 时,回调地址应填写该路径。

支持 JSON、application/x-www-form-urlencoded、multipart form 和原始 query-string 风格 body。成功返回纯文本:

success

失败返回 HTTP 400

fail

Epusdt 会按配置的 OkPay shop token 验证 OkPay 签名,成功后将对应 OkPay 订单标记为已支付,并触发商户回调;这个 OkPay 订单可能是由 status=4 占位父单原地补全而来,也可能是后续切换创建的子订单。

status_code 返回状态码及含义

状态码 HTTP 状态 说明
200 200 成功
400 400 系统错误,或普通参数/验证错误
401 401 签名认证错误
10001 400 钱包地址已存在
10002 400 支付交易已存在,请勿重复创建
10003 400 无可用钱包地址,无法发起支付
10004 400 支付金额有误,无法满足最小支付单位
10005 400 无可用金额通道
10006 400 汇率计算错误
10007 400 订单区块已处理
10008 400 订单不存在
10009 400 无法解析参数
10010 400 订单状态已变化
10011 400 超过子订单数量上限
10012 400 不能对子订单切换网络
10013 400 订单不是等待支付状态
10014 400 链未启用
10016 400 支持的资产不存在
10017 400 支付服务商未启用
10018 400 支付服务商配置不完整
10019 400 支付服务商不支持该币种或网络