格力支付开发者平台
登录邀请注册

收款 API 接入文档

接口版本v1报文格式JSON / UTF-8认证方式RSA-SHA256

本文档定义格力支付收款接口的认证规则、请求参数、响应字段及支付结果处理方式。

01接口概述

接入方通过服务端创建收款订单,获取二维码图片,并查询客户付款结果。接口凭证和订单按账号隔离。

当前服务地址https://glzf.top
接口前缀/api/v1
传输协议公网使用 HTTPS;本机调试可使用 HTTP。
请求格式POST 使用 application/json;GET 参数放在查询字符串中。
金额单位请求字段 amount 为元,响应字段 amount_fen 为整数分。
时间单位Unix 时间戳,单位为秒。

接口列表

方法路径功能
POST/api/v1/orders创建订单
GET/api/v1/orders/{order_id}查询订单
GET/api/v1/orders/by-reference/{out_trade_no}按业务订单号查询
GET/api/v1/orders/{order_id}/qrcode获取 PNG 图片
GET/api/v1/qrcode创建或复用订单并返回图片

当前统一收款商户为豪运到。接口返回的支付成功表示客户付款已确认,不表示银行卡结算完成。当前支持 USDT 提现申请,未提供沙箱、渠道分账、退款或接入方 webhook。

02接入准备

  1. 向管理员申请邀请码,完成邀请注册并登录,在 API 凭证页面申请密钥。
  2. 保存凭证 JSON。文件包含 app_id、public_key、private_key 和 algorithm。
  3. 在接入方服务器加载私钥,为每次请求生成签名。
  4. 创建订单后获取 PNG,由接入方页面展示;每 5 秒查询支付结果。
  5. 确认订单号、金额及 paid=true 后,以幂等方式完成业务处理。

私钥仅在申请时返回一次,不可找回。重新申请会使旧私钥立即失效,App ID 和已有订单保持不变。私钥应保存在服务端,不得发送至浏览器或包含在移动端安装包中。

网站登录会话仅用于控制台。调用下列 API 时,即使已登录,也必须提供签名请求头。

03认证与签名

3.1 签名算法

使用 RSA 2048 位密钥、SHA-256 摘要和 PKCS#1 v1.5 填充。私钥格式为 PKCS#8 PEM,公钥格式为 SubjectPublicKeyInfo PEM。签名结果使用标准 Base64 编码,保留末尾填充字符 =。

3.2 公共请求头

名称必填格式 / 说明
X-App-Id是app_ 加 24 位十六进制字符,从凭证页面取得。
X-Timestamp是10 位 Unix 秒;与服务器时间差不得超过 300 秒。
X-Nonce是16–64 位随机字符串,仅允许 A-Z a-z 0-9 _ -;每次请求重新生成。
X-Signature是签名结果的 Base64 字符串,长度为 344 个字符。
Content-TypePOST 必填application/json

3.3 签名原文

按以下顺序拼接六行,以单个 LF(\n)分隔,最后一行末尾不追加换行。

签名原文格式
app_id
HTTP_METHOD
request_target
timestamp
nonce
sha256_hex(body_bytes)
组成部分处理规则
HTTP_METHOD使用大写,如 POST、GET。
request_target接口路径及原始查询字符串,不含协议、域名、端口。查询参数保留实际发送顺序及 URL 编码。
body_bytes实际发送的 UTF-8 请求体字节。JSON 序列化后计算摘要,签名后原样发送。
sha256_hexSHA-256 结果转为 64 位小写十六进制字符串。

GET 请求体为空字节,其摘要固定为:

e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855

例如,GET 创建二维码接口的 request_target 为:

/api/v1/qrcode?amount=1.00&out_trade_no=ORDER_001

3.4 签名代码

Python · 标准请求签名
import base64, hashlib, secrets, time
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import padding

timestamp = str(int(time.time()))
nonce = secrets.token_hex(16)
body = b'{"amount":"1.00","out_trade_no":"ORDER_001"}'
target = "/api/v1/orders"
message = "\n".join([
    app_id, "POST", target, timestamp, nonce,
    hashlib.sha256(body).hexdigest()
]).encode("utf-8")
key = serialization.load_pem_private_key(private_pem.encode(), password=None)
signature = base64.b64encode(
    key.sign(message, padding.PKCS1v15(), hashes.SHA256())
).decode("ascii")

查询订单、下载图片和重试请求均需重新签名。重试时保持业务订单号不变,并重新生成时间戳与 nonce。签名校验通过后的请求即使出现业务错误,也不能复用 nonce。

04创建收款订单

POST/api/v1/orders

请求参数 · JSON Body

字段类型必填约束
amountstring / number是单位元,大于 0;最多 13 位整数、2 位小数。推荐字符串,如 "1.00"。
out_trade_nostring是1–64 位字母、数字、下划线或横线;当前账号内永久唯一。
pay_typestring否支付方式偏好,默认 auto,枚举见下表。

支付方式与自动识别

pay_type支付方式说明
auto自动识别(默认)根据顾客使用的扫码应用自动适配。
wechat微信支付记录微信支付偏好。
alipay支付宝记录支付宝支付偏好。

三种选择均生成聚合收款码,实际可用支付方式以收银台为准;选择微信或支付宝不会限制其他受支持的扫码应用。返回的 pay_type 是接入方偏好,不代表最终实际付款渠道。

省略参数时默认自动识别;空字符串、null、其他值或重复的 GET 参数返回 HTTP 400。枚举区分大小写,传入参数需参与签名。同一业务单号重试沿用首次保存的偏好,省略或修改此参数不更改原订单。

请求示例 · 签名请求头按第 3 节生成
POST /api/v1/orders
Content-Type: application/json
X-App-Id: {app_id}
X-Timestamp: {timestamp}
X-Nonce: {nonce}
X-Signature: {signature}

{"amount":"1.00","out_trade_no":"ORDER_001","pay_type":"auto"}

响应结果

HTTP含义
201首次创建成功,返回订单对象。
200返回已存在的同金额订单,或返回已明确拒绝的 failed 订单。
202订单正在创建或创建结果尚未确认,状态为 creating / unknown。
409相同业务订单号对应的金额不一致,或 nonce 已使用。
响应示例 · HTTP 201
{
  "order_id": "0123456789abcdef0123456789abcdef",
  "out_trade_no": "ORDER_001",
  "pay_type": "auto",
  "amount": "1.00",
  "amount_fen": 100,
  "status": "pending",
  "paid": false,
  "message": "等待顾客扫码付款",
  "created_at": 1790553600,
  "expires_at": 1790553900,
  "confirmed_at": null,
  "receipt_id": null,
  "qrcode_url": "/api/v1/orders/0123456789abcdef0123456789abcdef/qrcode",
  "status_url": "/api/v1/orders/0123456789abcdef0123456789abcdef",
  "poll_after_ms": 5000
}

同账号、同业务订单号、同金额的重复请求返回同一订单,不延长二维码有效期。不同账号可以使用相同的业务订单号。仅 HTTP 2xx 不能说明收款码已生成,应检查订单状态和二维码地址。

实际支付限额由商户与支付渠道决定。创建请求出现超时后,按原业务订单号查询,避免更换单号重复收款。

05按平台订单号查询

GET/api/v1/orders/{order_id}
路径参数类型必填说明
order_idstring是创建接口返回的 32 位小写十六进制订单号。

请求体为空。成功返回 HTTP 200 和订单对象,订单不存在或不属于当前账号时返回 HTTP 404。同一订单最多每 5 秒刷新一次支付结果,其余请求返回已保存的状态。

Python · 查询示例
result = client.request(
    "GET", "/api/v1/orders/0123456789abcdef0123456789abcdef"
)
if (result["paid"] and result["amount_fen"] == 100
        and result["out_trade_no"] == "ORDER_001"):
    # 在业务数据库事务内幂等处理,避免重复发货。
    pass

已确认支付的订单返回保存的成功结果。查询异常返回 502,不代表付款失败。

06按业务订单号查询

GET/api/v1/orders/by-reference/{out_trade_no}
路径参数类型必填说明
out_trade_nostring是接入方创建订单时传入的业务订单号。

适用于创建请求超时、创建响应丢失或仅保留业务单号的场景。返回结构、刷新频率与按平台订单号查询一致。

请求路径示例
GET /api/v1/orders/by-reference/ORDER_001

若返回 404,可使用相同业务订单号和相同金额重新调用创建接口;每次调用仍需生成新 nonce。

07获取二维码图片

GET/api/v1/orders/{order_id}/qrcode
路径参数类型必填说明
order_idstring是平台订单号,要求与当前账号匹配。

响应

响应项说明
HTTP 200响应体为 PNG 图片的二进制内容。
Content-Typeimage/png
X-Order-Id当前平台订单号。
X-Order-Status-Url查询订单的相对路径。
X-Pay-Type首次创建保存的支付方式偏好。
HTTP 409订单尚无可用图片;响应体为 JSON 错误对象。

图片下载必须携带签名请求头。接入方应由后端下载 PNG,再通过自己的页面展示,不应将接口路径直接作为公开网页的 img 地址。

只在 status=pending 且 qrcode_url 非空时展示付款码。重复下载不重新下单;二维码内的支付收银台显示真实支付渠道与收款商户。

08GET 创建二维码

GET/api/v1/qrcode

该接口将创建订单和获取 PNG 合并为一次请求,适用于要求 GET 传参的服务端集成。

查询参数必填说明
amount是收款金额(元),规则与创建订单一致。
out_trade_no是业务订单号,规则与创建订单一致。
pay_type否auto 自动(默认)、wechat 微信、alipay 支付宝。聚合码自动适配,规则同支付方式与自动识别。
请求路径示例 · 完整查询字符串参与签名
GET /api/v1/qrcode?amount=1.00&out_trade_no=ORDER_001&pay_type=wechat

有可用图片时返回 HTTP 200 和 PNG,响应头同第 7 节。没有图片时返回 JSON 错误,应按业务单号查询当前状态。接口会复用同单号同金额的订单,包括已到期订单,接入方展示前须核对订单状态。

HEAD 不创建订单。旧路径 /api/orders、/qrcode 保留兼容,但同样要求签名与业务订单号;新接入使用 v1 路径。

09订单响应字段

字段类型可空说明
order_idstring否平台订单号,32 位小写十六进制。
out_trade_nostring否接入方业务订单号。
pay_typestring否首次创建保存的支付偏好:auto / wechat / alipay;旧订单为 auto,不代表实际付款渠道。
amountstring否订单金额,单位元,固定两位小数。
amount_feninteger否订单金额,单位分;用于金额核对。
statusstring否订单状态,枚举值见下一节。
paidboolean否付款是否已确认,只有 paid 状态为 true。
messagestring否展示文案,不应用于业务分支判断。
created_atinteger否订单创建时间,Unix 秒。
expires_atinteger否二维码到期时间,创建时间加 300 秒。
confirmed_atinteger是本平台确认付款成功的时间,Unix 秒。
receipt_idstring是支付成功后生成的平台回执号。
qrcode_urlstring是图片接口相对路径;未取得图片时为 null。
status_urlstring否订单查询接口相对路径。
poll_after_msinteger否建议查询间隔,当前为 5000 毫秒。

所有接口路径均相对于 API 服务域名。可空字段在无值时返回 JSON null。

10订单状态与支付确认

status含义处理方式
creating正在创建保留原业务单号,稍后查询。
unknown创建结果未确认继续查询原订单,不另建同笔业务订单。
pending等待付款展示可用二维码,每 5 秒查单。
paid客户付款已确认核对业务单号及金额,幂等完成业务。
expired二维码已到期且尚未确认付款停止展示,可继续核对晚到结果。
closed订单已关闭停止展示,需要核对时查询原订单。
failed创建或支付未完成不发货,需要核对时查询原订单。

接入方应通过服务端主动查单确认结果。当前没有向接入方推送的 webhook。控制台在收款窗口打开时自动轮询,关闭后停止,重新打开可继续查询。

支付成功须同时核对 paid=true、业务单号和订单金额。confirmed_at 与 receipt_id 在成功后为非空。建议为业务订单建立数据库唯一约束,在事务中处理成功状态,避免重复发货。

已确认支付的状态不会回退。到期、关闭或失败状态的订单仍可查询,以核对延迟到达的成功结果。前端跳转、付款截图及页面提示不能替代服务端查单。

11错误码与重试

11.1 错误响应

application/json
{"error":"API 签名无效","code":401}

code 为 HTTP 状态码,error 为可读原因。图片接口出错时也返回 JSON。

HTTP原因处理方式
400参数、金额或 JSON 格式错误修正参数后重新签名。
401签名错误、时间过期或密钥未启用检查 App ID、有效私钥、时间戳和原始报文。
403控制台会话验证或密码错误刷新控制台或重新登录;API 签名接口无需 CSRF。
404订单不存在或不属于当前账号核对订单号和调用账号。
409nonce 重放、金额冲突或暂无二维码按错误说明重新签名、核对金额或查询原单。
413请求体超过 64 KiB减小请求体。
415POST 内容类型错误使用 application/json。
429请求超过频率限制按 Retry-After 等待并退避。
502暂时无法核验支付结果稍后查询原订单,不视为支付失败。

11.2 请求频率

维度限制
API 请求 / 账号120 次 / 分钟
创建尝试 / 账号30 次 / 分钟,重复创建请求也计数
签名校验 / 来源 IP300 次 / 分钟
支付结果刷新 / 订单最短间隔 5 秒

11.3 异常处理

  1. 创建超时或响应丢失:先按业务订单号查询。
  2. 查到订单:继续使用该订单,按当前状态处理。
  3. 返回 404:使用原业务订单号、原金额重试创建。
  4. 查询失败或返回 502:等待后继续查单;429 时延长间隔。

每次重试使用新 nonce。网络失败不是付款失败,不能据此重复收款或提前发货。

12示例代码

以下示例调用当前真实收款接口。将凭证文件保存在服务端,使用同一业务订单号重试同一笔业务。

12.1 Python 3.10+

下载 client.py,依赖 cryptography。

命令行运行
python -m pip install cryptography
python client.py credentials.json 1.00 ORDER_001 https://glzf.top
集成到服务端
import json
from pathlib import Path
from client import PaymentClient

credentials = json.loads(Path("credentials.json").read_text(encoding="utf-8"))
client = PaymentClient("https://glzf.top", credentials)
order = client.request("POST", "/api/v1/orders", {
    "amount": "1.00", "out_trade_no": "ORDER_001", "pay_type": "wechat"
})
if order["status"] == "pending" and order["qrcode_url"]:
    image = client.request("GET", order["qrcode_url"])
    Path("payment.png").write_bytes(image)
result = client.request("GET", order["status_url"])

12.2 Node.js 20+

下载 client.mjs,无需安装 npm 依赖。

命令行运行
node client.mjs credentials.json 1.00 ORDER_002 https://glzf.top
集成到服务端
import {readFileSync, writeFileSync} from 'node:fs';
import {request} from './client.mjs';

const credentials = JSON.parse(readFileSync('credentials.json', 'utf8'));
const base = 'https://glzf.top';
const order = await request(base, credentials, 'POST', '/api/v1/orders', {
  amount: '1.00', out_trade_no: 'ORDER_002', pay_type: 'alipay'
});
if (order.status === 'pending' && order.qrcode_url) {
  const image = await request(base, credentials, 'GET', order.qrcode_url);
  writeFileSync('payment.png', image);
}
const result = await request(base, credentials, 'GET', order.status_url);

命令行示例会保存二维码 PNG,并每 5 秒查单,最多执行 180 轮。集成示例仅展示一次查询,持续确认由接入方服务端任务完成。

13接入验收

检查项预期结果
鉴权无签名、错误私钥、篡改参数、过期时间戳被拒绝。
防重放重复 nonce 返回 409。
幂等同单号同金额返回原订单;改金额返回 409。
账号隔离其他账号无法取得订单或二维码。
实际付款小额付款后,订单金额、业务单号与 paid=true 均匹配。
重复处理多次查询成功,业务仅执行一次。
异常恢复超时后能按原业务单号继续核对;到期停止展示二维码。
密钥更新旧私钥被拒绝,新私钥可查询已有订单。

14余额与 USDT 提现

以下接口均沿用第 3 节的 RSA 请求签名,按 App ID 隔离。API 密钥具有申请提现能力,只应存放在可信服务端。

14.1 费率与记账

默认收款费率 8%,例如收款 100 元,平台服务费 8 元,用户余额增加 92 元。管理员可调整专属费率,费率在创建订单时固定;调价不更改历史订单和已创建的待付款订单。服务费逐单四舍五入到人民币分,仅 paid 订单记入余额。

订单新增字段类型说明
fee_ratestring本订单费率百分数,"8.00" 为 8%。
platform_fee_feninteger收款服务费,人民币分。
net_amount_feninteger扣费后金额,人民币分;非 paid 时尚未计入余额。

14.2 接口列表

方法路径功能
GET/api/v1/balance查询当前余额。
GET/api/v1/exchange-rate查询 USDT / CNY 参考报价。
POST/api/v1/withdrawal-quotes计算并生成有效期 120 秒的提现报价。
POST/api/v1/withdrawals确认报价并冻结对应余额。
GET/api/v1/withdrawals当前账号最近 50 笔申请,倒序。
GET/api/v1/withdrawals/{withdrawal_id}查询单笔申请。

14.3 余额与报价

GET /balance 返回 HTTP 200。以下字段除费率外均为整数人民币分;fee_rate 为新订单采用的百分数。

余额响应示例
{"gross_fen":10000,"fee_fen":800,"earned_fen":9200,
 "available_fen":9200,"frozen_fen":0,"withdrawn_fen":0,"fee_rate":"8.00"}

gross_fen 为累计已确认收款,fee_fen 为累计服务费,earned_fen 为扣费后收入;可用余额 available_fen = earned_fen − frozen_fen − withdrawn_fen。

GET /exchange-rate 返回 rate(每 USDT 的人民币元)、unit(CNY/USDT)、source、updated_at(源报价 Unix 秒)、minimum_usdt(10.000000)和 fee_usdt(1.000000)。报价来自 CoinGecko,缓存 60 秒,拒绝超过 300 秒的源报价;不是场外交易买卖报价。

POST /withdrawal-quotes 请求字段:

字段类型说明
amountstring / integer必填,提现人民币元;正数,最多两位小数、13 位整数,不超过可用余额。推荐字符串。
addressstring必填,有效的 34 位 TRC20 地址,T 开头且通过 Base58Check。
HTTP 201 · 提现报价示例(汇率为示例值)
{
  "quote_id":"0123456789abcdef0123456789abcdef",
  "amount_fen":9200,"network":"TRC20","address":"实际有效的TRC20地址",
  "rate":"7.000000","rate_source":"CoinGecko","rate_updated_at":1790637500,
  "gross_usdt":"13.142857","fee_usdt":"1.000000","net_usdt":"12.142857",
  "expires_at":1790637620
}

gross_usdt 为人民币元除以汇率,向下保留六位小数,至少 10 USDT;扣除 fee_usdt 后,net_usdt 为需要转给用户的数量,最低 9 USDT。quote_id 绑定账号与全部报价字段;expires_at 为创建后 120 秒。计算报价尚未冻结余额。

14.4 确认申请与查询

POST /withdrawals 仅接收以下必填字段:

{"quote_id":"0123456789abcdef0123456789abcdef"}

首次确认成功返回 HTTP 201,相同报价重试返回 HTTP 200 与原申请。每次重试需重新签名、生成新 nonce。系统再次校验报价归属、有效期及余额,并原子冻结人民币金额;已提交申请锁定汇率和收款地址。即使原申请被退回,相同报价也只返回原申请,不再次提现。

返回提现对象:包含报价对象除 expires_at 外的全部字段,以及下列字段。查询单笔接口同样返回此对象;列表接口返回 {"withdrawals":[...]}。非本人申请返回 404。

字段类型说明
withdrawal_idstring32 位十六进制提现单号。
statusstring见下表。
created_at / updated_atinteger创建及最后处理时间,Unix 秒。
txidstring / null管理员登记的 TRON 交易哈希,已打款时非空。
notestring审核备注或退回原因,可能为空。
status含义余额处理
pending审核中冻结申请人民币金额。
processing审核通过,待打款继续冻结。
paid已登记打款转入已提现,不返还余额。
rejected申请退回释放冻结余额。

提现由管理员人工转账后登记,系统不自动发送 USDT 或核验链上 TXID。接口记录与链上到账需分别核对。

14.5 错误、重试与示例

400:金额低于门槛、超余额或地址无效;409:报价过期、不属于当前账号或确认时余额不足;503:暂时无有效汇率,未生成报价或冻结余额。创建报价每账号 20 次/分钟,确认申请每账号 10 次/10 分钟,另受公共限流约束。

Python · 复用 PaymentClient
balance = client.request("GET", "/api/v1/balance")
quote = client.request("POST", "/api/v1/withdrawal-quotes", {
    "amount": "92.00", "address": trc20_address
})
# 展示金额、网络、地址、手续费与 net_usdt,取得用户确认后提交。
withdrawal = client.request("POST", "/api/v1/withdrawals", {
    "quote_id": quote["quote_id"]
})
result = client.request("GET", "/api/v1/withdrawals/" + withdrawal["withdrawal_id"])

报价过期时重新计算并展示,勿自动确认变化后的报价。提交超时先按原 quote_id 重试或查询申请列表;不要立即新建报价重复申请。