Skip to content

快速開始

讀完這份指南,你會完成第一筆沙盒充值請求的簽章與發送、讀取 API 回傳的付款指示,並從系統查詢回該筆訂單。整個流程應該在 10 分鐘內走完。

本章是一份完整範例。任何涉及的主題若需要更深入內容,請循文中的交叉連結進入其他章節。

準備事項

開始前,確認以下五項都已備齊。若有任何一項缺漏,先聯絡你的客戶經理再繼續,否則下面的步驟都無法運作。

  • 由客戶經理開通的沙盒 merchant_idapi_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-KeyX-TimestampX-Signature。簽章是把排序後的請求參數附上 api_key 之後,取小寫 hex 的 MD5。簽章的參考實作放在 docs-site/shared/code-samples/

先決定要送出的 payload。充值建單端點接受以下欄位,定義位於 backend/app/schemas/order.pyDepositOrderCreate

欄位型別必填說明
merchant_order_nostring你自己的訂單識別碼。1–64 字元。每個商戶內必須唯一。
amountdecimal充值金額。必須 > 0。以字串傳送以保留精度。
currency_codestringISO 4217 三字母代碼,例如 THBVNDIDR
callback_urlstring閘道用以 POST 非同步狀態更新的 HTTPS URL。
user_idstring你的終端使用者識別碼(如需附帶)。最多 64 字元。
extra_datastring不透明的中繼資料,會在 callback 中原樣回傳。

本範例使用以下 payload:

json
{
  "merchant_order_no": "QUICKSTART-0001",
  "amount": "100.00",
  "currency_code": "THB",
  "callback_url": "https://your-domain.example/callbacks/deposit"
}

接著計算簽章。Python 參考輔助工具長這樣:

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:

bash
API_KEY="pre_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
TIMESTAMP=$(date +%s)

PARAMS="amount=100.00&callback_url=https://your-domain.example/callbacks/deposit&currency_code=THB&merchant_order_no=QUICKSTART-0001&timestamp=${TIMESTAMP}"

SIGNATURE=$(python sign.py --api-key "$API_KEY" --params "$PARAMS")
echo "$SIGNATURE"

相同的呼叫形式也適用於 sign.shsign.phpsign.ts,差別只在直譯器。四份實作透過 CI parity test 綁定,相同輸入會產出完全一致的 digest。

底層演算法、完整 digest 範例與四份完整實作,參見 Authentication & Signing

步驟 2:發送請求

拿到新的簽章後,把 JSON 主體 POST 到充值建單端點。路徑是 /api/v1/merchant/orders/deposit,其中 /orders 區段來自商戶路由前綴,很容易漏掉。

bash
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&currency_code=THB&merchant_order_no=QUICKSTART-0001&timestamp=${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 有兩種型態,取決於訂單被導向支付渠道或導向我方帳上的銀行帳戶:

json
{
  "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_codebank_nameaccount_noaccount_holderremark:帳戶導向訂單要顯示給付款人的付款指示。
  • payment_urlpayment_info:渠道導向訂單會改為帶這兩個欄位。將付款人導向 payment_url,或由你自行渲染 payment_info

完整的信封合約,包含你應該分支處理的錯誤碼清單,參見 Errors。每個端點的每個欄位定義,參見 API Reference

步驟 4:查詢訂單

要確認訂單已落入系統,用 order_no 反查。查詢端點僅支援 GET,沒有主體要簽,因此簽章只覆蓋 timestamp。

bash
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_atcompleted_at

json
{
  "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 串接與充值狀態機(PENDINGMATCHEDCOMPLETED 及其失敗路徑)。
  • Callbacks:閘道如何送達非同步狀態更新、callback 的簽章方案,以及你這側的重試語意。
  • Sandbox:完整的沙盒與正式環境對照、Cloudflare Access 存取控制、mock-channel 行為,以及常見沙盒陷阱。
  • Errors:信封合約、你應該分支處理的 code 值清單,以及 HTTP 狀態碼的重試建議。