快速开始
读完这份指南,你会完成第一笔沙盒充值请求的签名与发送、读取 API 回传的付款指示,并从系统查询回该笔订单。整个流程应该在 10 分钟内走完。
本章是一份完整示例。任何涉及的主题若需要更深入内容,请循文中的交叉链接进入其他章节。
准备事项
开始前,确认以下五项都已备齐。若有任何一项缺漏,先联络你的客户经理再继续,否则下面的步骤都无法运作。
- 由客户经理开通的沙盒
merchant_id与api_key。 - 你的来源 IP 已加入沙盒的 Cloudflare Access 允许清单。此项由客户经理处理。关于 Cloudflare Access 访问控制的背景说明,参见 Sandbox。
- 对应你开发语言的签名辅助工具:
sign.sh(bash)、sign.py(Python)、sign.php(PHP)或sign.ts(Node.js)。四份实作 byte-for-byte 等价,挑最接近你技术栈的那一份即可。 - 任一 HTTP 客户端:
curl、Postman、HTTPie 或你语言的 HTTP 函式库。 - 与 NTP 误差在 ±5 分钟以内的时钟。服务器会拒绝超出此区间的请求。
下面的示例片段使用 Python,但每个步骤在其他三种语言中完全一致。
步骤 1:签署充值请求
每一个商户 API 呼叫都带三个标头:X-API-Key、X-Timestamp 与 X-Signature。签名是把排序后的请求参数附上 api_key 之后,取小写 hex 的 MD5。签名的参考实作放在 docs-site/shared/code-samples/。
先决定要送出的 payload。充值建单端点接受以下字段,定义位于 backend/app/schemas/order.py 的 DepositOrderCreate:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
merchant_order_no | string | 是 | 你自己的订单识别码。1–64 字符。每个商户内必须唯一。 |
amount | decimal | 是 | 充值金额。必须 > 0。以字符串传送以保留精度。 |
currency_code | string | 是 | ISO 4217 三字母代码,例如 THB、VND、IDR。 |
callback_url | string | 是 | 网关用以 POST 异步状态更新的 HTTPS URL。 |
user_id | string | 否 | 你的终端用户识别码(如需附带)。最多 64 字符。 |
extra_data | string | 否 | 不透明的中继数据,会在 callback 中原样回传。 |
本示例使用以下 payload:
{
"merchant_order_no": "QUICKSTART-0001",
"amount": "100.00",
"currency_code": "THB",
"callback_url": "https://your-domain.example/callbacks/deposit"
}接着计算签名。Python 参考辅助工具长这样:
#!/usr/bin/env python3
"""Reference implementation: MD5 signature for Payment Gateway merchant API.
Usage:
python sign.py --api-key <key> --params 'k1=v1&k2=v2&...'
Algorithm:
1. Parse params into a dict.
2. Sort keys alphabetically (ASCII order).
3. Build query string 'k=v&k=v', skipping None values.
4. Append api_key directly (no '&').
5. MD5 the UTF-8 bytes; output lowercase hex.
"""
import argparse
import hashlib
import sys
from urllib.parse import parse_qsl
def create_signature(params: dict[str, str], api_key: str) -> str:
sorted_items = sorted(params.items())
query_string = "&".join(f"{k}={v}" for k, v in sorted_items if v is not None)
return hashlib.md5((query_string + api_key).encode("utf-8")).hexdigest()
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("--api-key", required=True)
parser.add_argument("--params", required=True)
args = parser.parse_args()
params = dict(parse_qsl(args.params, keep_blank_values=True))
print(create_signature(params, args.api_key))
return 0
if __name__ == "__main__":
sys.exit(main())使用方式:把每个请求参数(包含当前的 Unix timestamp)以 k=v&k=v 字符串传入,然后附上你的 API key:
API_KEY="pre_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
TIMESTAMP=$(date +%s)
PARAMS="amount=100.00&callback_url=https://your-domain.example/callbacks/deposit¤cy_code=THB&merchant_order_no=QUICKSTART-0001×tamp=${TIMESTAMP}"
SIGNATURE=$(python sign.py --api-key "$API_KEY" --params "$PARAMS")
echo "$SIGNATURE"相同的呼叫形式也适用于 sign.sh、sign.php 与 sign.ts,差别只在直译器。四份实作透过 CI parity test 绑定,相同输入会产出完全一致的 digest。
底层算法、完整 digest 示例与四份完整实作,参见 Authentication & Signing。
步骤 2:发送请求
拿到新的签名后,把 JSON 主体 POST 到充值建单端点。路径是 /api/v1/merchant/orders/deposit,其中 /orders 区段来自商户路由前缀,很容易漏掉。
API_KEY="pre_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
BASE_URL="https://pre-api.open-pay.co"
TIMESTAMP=$(date +%s)
PARAMS="amount=100.00&callback_url=https://your-domain.example/callbacks/deposit¤cy_code=THB&merchant_order_no=QUICKSTART-0001×tamp=${TIMESTAMP}"
SIGNATURE=$(python sign.py --api-key "$API_KEY" --params "$PARAMS")
curl -X POST "${BASE_URL}/api/v1/merchant/orders/deposit" \
-H "Content-Type: application/json" \
-H "X-API-Key: ${API_KEY}" \
-H "X-Timestamp: ${TIMESTAMP}" \
-H "X-Signature: ${SIGNATURE}" \
-d '{
"merchant_order_no": "QUICKSTART-0001",
"amount": "100.00",
"currency_code": "THB",
"callback_url": "https://your-domain.example/callbacks/deposit"
}'执行前需要替换的几个占位符:
API_KEY:客户经理给你的沙盒api_key。沙盒密钥通常以pre_为前缀。callback_url:你能控制且可公开连线的 HTTPS 端点。首次测试用 request bin 或打到开发机的 ngrok tunnel 都可以。merchant_order_no:每个商户内必须唯一。若重复带相同值,第二次呼叫会以 duplicate-order 错误失败。
JSON 主体承载的参数与进入签名的那一组相同,差别在于少了 timestamp(它透过 X-Timestamp 标头传递)。注意:签名不在乎传输时的 JSON key 顺序,只在乎你计算 digest 时 key 是依字母排序的。
步骤 3:读取响应
成功的请求会回传 HTTP 201 Created 与标准信封。data 区块的结构由 backend/app/schemas/order.py 中的 DepositPaymentInfo 定义。data 有两种型态,取决于订单被导向支付渠道或导向我方账上的银行账户:
{
"success": true,
"code": "SUCCESS",
"message": "OK",
"data": {
"order_no": "DEP20260520000001",
"requested_amount": "100.00",
"amount": "100.00",
"currency_code": "THB",
"bank_code": "KBANK",
"bank_name": "ธนาคารกสิกรไทย (Kasikorn Bank)",
"bank_branch": null,
"account_no": "1234567890",
"account_holder": "Mock Payment Co., Ltd.",
"remark": "DEP20260520000001",
"expire_at": "2026-05-20T08:15:00Z",
"payment_url": null,
"payment_info": null
}
}你最常用到的字段:
order_no:本笔充值在网关侧的识别码。保存它。你会用它来查询状态、并对应异步 callback。amount:付款人实际应转账的金额。若渠道为了让订单可区分而套用浮动金额,这个值可能与requested_amount不同;显示给付款人的应是amount。expire_at:超过此时间后订单不再接受付款。bank_code、bank_name、account_no、account_holder、remark:账户导向订单要显示给付款人的付款指示。payment_url、payment_info:渠道导向订单会改为带这两个字段。将付款人导向payment_url,或由你自行渲染payment_info。
完整的信封合约,包含你应该分支处理的错误码清单,参见 Errors。每个端点的每个字段定义,参见 API Reference。
步骤 4:查询订单
要确认订单已落入系统,用 order_no 反查。查询端点仅支持 GET,没有主体要签,因此签名只覆盖 timestamp。
ORDER_NO="DEP20260520000001"
API_KEY="pre_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
TIMESTAMP=$(date +%s)
PARAMS="timestamp=${TIMESTAMP}"
SIGNATURE=$(python sign.py --api-key "$API_KEY" --params "$PARAMS")
curl -X GET "https://pre-api.open-pay.co/api/v1/merchant/query/deposit/${ORDER_NO}" \
-H "X-API-Key: ${API_KEY}" \
-H "X-Timestamp: ${TIMESTAMP}" \
-H "X-Signature: ${SIGNATURE}"刚建立的订单会回 status: "pending",且尚未有 matched_at 或 completed_at:
{
"success": true,
"code": "SUCCESS",
"message": "OK",
"data": {
"id": "01J...",
"order_no": "DEP20260520000001",
"merchant_order_no": "QUICKSTART-0001",
"merchant_id": "01J...",
"amount": "100.00",
"currency_code": "THB",
"status": "pending",
"expire_at": "2026-05-20T08:15:00Z",
"matched_at": null,
"completed_at": null,
"callback_url": "https://your-domain.example/callbacks/deposit",
"callback_status": null,
"callback_count": 0,
"created_at": "2026-05-20T08:00:00Z",
"updated_at": "2026-05-20T08:00:00Z"
}
}看到 status: "pending" 就是成功判准。订单已进入网关、签名已被接受,正在等待付款人转账。从此刻起,后续状态转换会透过你提供的 callback_url 异步送达。
后续步骤
你已经把整条对接从头到尾跑通。以下章节对每个环节有更深入的内容:
- Authentication:完整签名参考、5 分钟 timestamp 容忍区间、完整 digest 示例,以及四种语言辅助工具的并列对照。
- Deposits:完整充值指南,包含 cashier-mode 对接与充值状态机(
PENDING→MATCHED→COMPLETED及其失败路径)。 - Callbacks:网关如何送达异步状态更新、callback 的签名方案,以及你这侧的重试语义。
- Sandbox:完整的沙盒与生产环境对照、Cloudflare Access 访问控制、mock-channel 行为,以及常见沙盒陷阱。
- Errors:信封合约、你应该分支处理的
code值清单,以及 HTTP 状态码的重试建议。