快速開始
讀完這份指南,你會完成第一筆沙盒充值請求的簽章與發送、讀取 API 回傳的付款指示,並從系統查詢回該筆訂單。整個流程應該在 10 分鐘內走完。
本章是一份完整範例。任何涉及的主題若需要更深入內容,請循文中的交叉連結進入其他章節。
準備事項
開始前,確認以下五項都已備齊。若有任何一項缺漏,先聯絡你的客戶經理再繼續,否則下面的步驟都無法運作。
- 由客戶經理開通的沙盒
merchant_id與api_key。 - 你的來源 IP 已加入沙盒的 Cloudflare Access 允許清單。此項由客戶經理處理。關於 Cloudflare Access 來源 IP 控管的背景說明,參見 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 狀態碼的重試建議。