ManWei — 商戶 API 介接文件 (菲律賓)
| 項目 | 內容 |
|---|---|
| 文件版本 | v2.0(線上版) |
| 更新日期 | 2026-10-05 |
| 適用對象 | 商戶端開發人員 |
| API 版本 | v1 |
目錄
1. 快速開始
1.1 環境與網址
| 環境 | Base URL |
|---|---|
| 正式環境 | https://api.kawinpay-ph.com/api/v1 |
| 測試環境 | 請洽您的客戶經理索取 |
⚠️ 注意路徑中的
/api不可省略。完整範例:https://api.kawinpay-ph.com/api/v1/collection/create少了/api會得到 404,而非認證錯誤。
1.2 介接前準備
請向平台申請以下資訊:
| 項目 | 說明 |
|---|---|
| API Key | 識別您身分的金鑰,放在 X-API-Key 標頭 |
| API Secret | 用於計算簽章,請妥善保管、絕不可外洩或寫在前端程式碼 |
| IP 白名單 | 您呼叫 API 的伺服器對外 IP。若您經過 NAT/Proxy,請提供實際的出口 IP |
| Webhook URL | 接收交易結果通知的端點(需 HTTPS) |
| Webhook Secret | 驗證回調來源的金鑰。僅在您於商戶後台設定 Webhook 時才有;若您是建單時帶 callbackUrl(per-order 回調),則改用 API Secret 驗章,見 8.3 |
API Secret 只在建立時顯示一次。若遺失,需請平台重新產生(舊的會立即失效)。
1.3 權限說明
每組 API Key 有其權限範圍:
| 權限 | 可呼叫的端點 |
|---|---|
READ |
所有查詢類端點(query / list / balance / channels / institutions / statement) |
WRITE |
建立與取消類端點(create / cancel) |
ALL |
全部 |
權限不足時回 HTTP 403、errorCode = 0001010。
1.4 支援的交易類型
| 交易 | 是否支援 API |
|---|---|
| 代收(Collection) | ✅ 支援 |
| 代付(Payout) | ✅ 支援 |
| 下發(Withdrawal) | ❌ 不提供 API,請於商戶後台建立 |
| 轉帳(Transfer) | ❌ 不提供 API,請於商戶後台建立 |
2. 認證機制
每一次請求都需要 API Key + HMAC-SHA256 簽章。
2.1 必要標頭
| 標頭 | 說明 | 範例 |
|---|---|---|
X-API-Key |
您的 API Key | sk_live_xxxxxxxxxxxxxxxxxxxxxxxx |
X-Timestamp |
Unix 時間戳,單位為「秒」 | 1704067200 |
X-Nonce |
每次請求唯一的隨機字串(建議 UUID) | 3f2a9c1e... |
X-Signature |
HMAC-SHA256 簽章,Base64 編碼 | a1b2c3d4...= |
Content-Type |
application/json(POST 時) |
⚠️ 最常見的三個錯誤:
X-Timestamp用了毫秒(應為秒)- 簽章輸出用了 hex(應為 Base64)
- 簽章串接用了
|或,(應為.)
2.2 簽章演算法
待簽字串 = timestamp + "." + nonce + "." + payload
簽章 = Base64( HMAC-SHA256( 金鑰 = API Secret, 資料 = 待簽字串 ) )
關於 payload:
| 請求方法 | payload 內容 |
|---|---|
POST |
您實際送出的 request body 原始字串(逐位元組一致) |
GET |
空字串 ""(待簽字串會是 timestamp + "." + nonce + ".",結尾有一個點) |
⚠️ 關鍵:必須簽「您實際送出的那串位元組」。 若您先用某種方式序列化來簽章、送出時又重新序列化(空白、欄位順序、浮點數格式不同), 簽章就會對不上,回
INVALID_SIGNATURE。 建議做法:先產生 JSON 字串,用該字串簽章,再把同一個字串當 body 送出。
2.3 時間戳與防重放
| 規則 | 說明 |
|---|---|
| 時間窗 | 伺服器時間 ±300 秒(5 分鐘) |
| Nonce | 5 分鐘內不可重複使用,重複會回 INVALID_NONCE |
請確保您的伺服器時間有做 NTP 校時。時間偏移超過 5 分鐘會導致所有請求失敗。 時間戳過期與簽章錯誤都回
INVALID_SIGNATURE,請先檢查時鐘。
2.4 完整範例
Python
import hmac, hashlib, base64, json, time, uuid, requests
API_KEY = "sk_live_xxxxxxxx"
API_SECRET = "your_api_secret"
BASE_URL = "https://api.kawinpay-ph.com/api/v1"
def call_api(path, body=None, method="POST"):
payload = json.dumps(body, separators=(',', ':'), ensure_ascii=False) if body else ""
ts = str(int(time.time())) # 秒
nonce = uuid.uuid4().hex
message = f"{ts}.{nonce}.{payload}"
signature = base64.b64encode(
hmac.new(API_SECRET.encode(), message.encode(), hashlib.sha256).digest()
).decode()
headers = {
"Content-Type": "application/json",
"X-API-Key": API_KEY,
"X-Timestamp": ts,
"X-Nonce": nonce,
"X-Signature": signature,
}
if method == "POST":
# 注意:送出的是與簽章時「完全相同」的字串
return requests.post(BASE_URL + path, data=payload.encode(), headers=headers)
return requests.get(BASE_URL + path, headers=headers)
resp = call_api("/collection/create", {
"merchantOrderNo": "M202608080001",
"amount": 1000.00,
"currency": "PHP",
})
print(resp.json())
PHP
<?php
$apiKey = 'sk_live_xxxxxxxx';
$apiSecret = 'your_api_secret';
$baseUrl = 'https://api.kawinpay-ph.com/api/v1';
function callApi($path, $body = null) {
global $apiKey, $apiSecret, $baseUrl;
$payload = $body ? json_encode($body, JSON_UNESCAPED_UNICODE) : '';
$ts = (string) time(); // 秒
$nonce = bin2hex(random_bytes(16));
$message = $ts . '.' . $nonce . '.' . $payload;
$signature = base64_encode(hash_hmac('sha256', $message, $apiSecret, true));
$ch = curl_init($baseUrl . $path);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $payload, // 與簽章用的字串相同
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'X-API-Key: ' . $apiKey,
'X-Timestamp: ' . $ts,
'X-Nonce: ' . $nonce,
'X-Signature: ' . $signature,
],
]);
return json_decode(curl_exec($ch), true);
}
2.5 IP 白名單
若您的 API Key 啟用了 IP 白名單,只有名單內的 IP 可呼叫。支援格式:
| 格式 | 範例 |
|---|---|
| 精確 IP | 203.0.113.10 |
| CIDR 網段 | 203.0.113.0/24 |
| 萬用字元 | 203.0.113.* |
不在名單內回 HTTP 403、errorCode = IP_NOT_ALLOWED。
提醒:請提供您伺服器的對外出口 IP。若經過 NAT、雲端 NAT Gateway 或 Proxy, 實際出口 IP 可能與伺服器本機 IP 不同。
2.6 流量限制
| 項目 | 限制 |
|---|---|
| 頻率 | 每個 API Key 每 60 秒 1000 次(涵蓋所有 /v1/* 端點合計) |
每次回應都會帶以下標頭:
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 997
X-RateLimit-Reset: 1704067260000
超過限制回 HTTP 429,並帶 Retry-After: 60 標頭:
{ "success": false, "errorCode": "RATE_LIMIT_EXCEEDED", "message": "請求過於頻繁,請稍後再試", "retryAfter": 60 }
請依
X-RateLimit-Remaining主動節流,並在收到 429 時依Retry-After退避重試。
3. 通用約定
3.1 回應格式
所有 /v1/* 端點都使用相同的封裝:
成功
{
"success": true,
"data": { },
"timestamp": 1704067200000
}
失敗
{
"success": false,
"errorCode": "0001012",
"message": "訂單不存在",
"timestamp": 1704067200000
}
⚠️ 極重要:請務必檢查 body 中的
success欄位,不要只看 HTTP 狀態碼。 部分業務失敗(例如查無訂單)會回 HTTP 200 但success: false。
| 欄位 | 說明 |
|---|---|
success |
true / false,判斷成敗的唯一依據 |
data |
成功時的資料內容;無資料時此欄位不出現 |
errorCode |
失敗時才有。請視為不透明字串(格式不一,有數字碼也有英文碼) |
message |
失敗原因說明(中文) |
timestamp |
伺服器時間,Unix 毫秒 |
3.1.1 失敗回應的三種結構(重要)
平台的失敗回應不只一種結構,取決於錯誤發生在哪一層。請務必三種都能解析。
結構 A — 業務失敗(最常見)
通過認證、進到業務邏輯後才失敗。/v1/* 端點的絕大多數錯誤都是這種。
{
"success": false,
"errorCode": "0001011",
"message": "餘額不足或凍結失敗: 可用餘額不足",
"timestamp": 1704067200000
}
結構 B — 欄位格式驗證失敗
請求欄位不符規格(必填漏帶、超長、金額 ≤ 0 等),在進入業務邏輯之前就被擋下。
{
"code": 400,
"errorCode": "0001002",
"message": "商戶訂單號不能為空, 金額必須大於0",
"timestamp": "2026-08-08T12:00:00"
}
⚠️ 注意:這個結構沒有
success欄位,且timestamp是 ISO 字串而非毫秒數。 多個欄位同時有誤時,message會以,串接全部錯誤。 解析時請先判斷success是否存在,不存在時改看code。
結構 C — 認證/流量限制失敗
在認證層被擋下,請求不會進到業務邏輯。
{
"success": false,
"errorCode": "INVALID_SIGNATURE",
"message": "簽名驗證失敗",
"timestamp": 1704067200000
}
流量限制(HTTP 429)會多一個 retryAfter 欄位(秒),並在回應標頭帶 Retry-After:
{
"success": false,
"errorCode": "RATE_LIMIT_EXCEEDED",
"message": "請求過於頻繁,請稍後再試",
"retryAfter": 60,
"timestamp": 1704067200000
}
建議的解析順序
1. body 有 success 欄位?
├─ 有 → success === false 即失敗,讀 errorCode / message
└─ 沒有 → 屬結構 B(欄位驗證失敗),讀 code / errorCode / message
2. 永遠不要只用 HTTP 狀態碼判斷成敗
3.1.2 關於 message 與 errorCode
| 用途 | |
|---|---|
errorCode |
程式判斷請用這個。值域穩定,不會隨版本變動措辭 |
message |
僅供人工排查與記錄。內容是動態文字(例如 閘道呼叫失敗: ... 後面接實際通道回傳的原因),會隨情境與版本改變,請勿用字串比對來做邏輯判斷 |
特別是
0001011(業務規則錯誤):餘額不足、通道不可用、超出限額、風控攔截等都回這個碼, 實際原因只在message。建議完整記錄message以利與平台對帳排查。
3.2 金額與時間格式
| 項目 | 格式 |
|---|---|
| 金額 | 數字,最多 2 位小數。整數部分上限 12 位 |
| 幣別 | ISO 4217,如 PHP、HKD |
| 時間 | ISO 8601,如 2026-08-08T12:00:00(無時區位移,為伺服器當地時間) |
建議:
currency請明確帶值,不要依賴系統預設。
3.3 訂單狀態
代收(Collection)
| 狀態 | 說明 | 是否終態 |
|---|---|---|
PENDING |
已建立,等待付款 | ❌ |
PROCESSING |
處理中 | ❌ |
SUCCESS |
收款成功 | ✅ |
FAILED |
收款失敗 | ✅ |
EXPIRED |
逾期未付 | ✅ |
CANCELLED |
已取消 | ✅ |
代付(Payout)
| 狀態 | 說明 | 是否終態 |
|---|---|---|
PENDING |
待處理 | ❌ |
PROCESSING |
處理中 | ❌ |
SUCCESS |
付款成功 | ✅ |
FAILED |
付款失敗 | ✅ |
REJECTED |
已駁回 | ✅ |
REVERSED |
已沖正 | ✅ |
CANCELLED |
已取消 | ✅ |
3.4 冪等機制(重要)
兩個建單端點都以 merchantOrderNo 做冪等控制:
重送相同
merchantOrderNo時,系統會回傳「原本那筆訂單的當前狀態」, 並且success: true——不會建立第二筆,也不會回錯誤。
因此:
- ✅ 網路逾時後可安全重試,不會重複扣款
- ⚠️ 系統不會告訴您「這是重複請求」,請比對回傳的
systemTransactionNo與amount判斷 - ⚠️ 若
merchantOrderNo留空,系統會自動產生,此時冪等失效
強烈建議:每筆訂單都帶上您自己系統的唯一訂單號。
4. 代收 API
4.1 建立代收訂單
POST /v1/collection/create 權限:WRITE
請求欄位
| 欄位 | 型別 | 必填 | 限制 | 說明 |
|---|---|---|---|---|
merchantOrderNo |
string | ✅ | ≤64 | 您的訂單號,冪等依據 |
amount |
decimal | ✅ | ≥0.01,最多 2 位小數 | 訂單金額 |
currency |
string | ≤10 | 幣別,建議明確帶值。可帶 USDT 等外幣,系統依平台匯率換算成 PHP 收款,詳見 §11 |
|
payerName |
string | ≤100 | 付款人姓名,菲律賓站選填(通道回調可能覆寫) | |
payerAccount |
string | ≤100 | 付款人帳號(通道回調可能覆寫) | |
merchantPayerAccount |
string | ≤100 | 您的自訂識別(如會員 ID),不會被覆寫 | |
channelCode |
string | ✅ | ≤50 | 指定支付方式,必填。PH1001 GCash QR/PH1002 QRPH。可用值請呼叫 /v1/channels 取得 |
remark |
string | ≤500 | 備註 | |
expireMinutes |
int | 1–10080 | 訂單有效期,預設 15 分鐘;實際到期以回應的 expireAt 為準 |
|
invoiceNo |
string | ≤64 | 發票號 | |
callbackUrl |
string | ≤500 | 本筆訂單專用的回調網址,覆蓋您在後台設定的 Webhook URL;留空則使用後台設定 | |
returnUrl |
string | ≤500 | 付款完成後的返回網址(2026-10-02 起)。須為 http / https 絕對網址,格式不符回 0001002。詳見下方說明 |
|
extraData |
string | ≤1000 | 自訂資料,會原樣回傳,也會出現在 Webhook |
回應欄位
| 欄位 | 型別 | 說明 |
|---|---|---|
merchantOrderNo |
string | 您的訂單號 |
systemTransactionNo |
string | 平台訂單號,請保存供後續查詢 |
amount |
decimal | 訂單金額 |
fee |
decimal | 手續費 |
netAmount |
decimal | 實際入帳金額 = amount − fee |
currency |
string | 幣別 |
status |
string | 訂單狀態 |
cashierUrl |
string | 收銀台網址,請導向您的客戶前往付款 |
expireAt |
string | 過期時間 |
createdAt |
string | 建立時間 |
extraData |
string | 原樣回傳 |
failureReason / failureCode |
string | 失敗時才有 |
範例
// 請求
{
"merchantOrderNo": "M202608080001",
"amount": 1000.00,
"currency": "PHP",
"expireMinutes": 30,
"returnUrl": "https://merchant.com/payment/result",
"extraData": "{\"userId\":8891}"
}
// 回應
{
"success": true,
"data": {
"merchantOrderNo": "M202608080001",
"systemTransactionNo": "COL202608080001",
"amount": 1000.00,
"fee": 15.00,
"netAmount": 985.00,
"currency": "PHP",
"status": "PENDING",
"cashierUrl": "https://cashier.kawinpay-ph.com/pay/abcd1234",
"expireAt": "2026-08-08T12:30:00",
"createdAt": "2026-08-08T12:00:00",
"extraData": "{\"userId\":8891}"
},
"timestamp": 1786000000000
}
代收手續費為內扣:訂單 1000、手續費 15 → 您實際入帳 985。
失敗回應範例
// 業務失敗(HTTP 400)— 例如商戶可入帳餘額已達上限
{
"success": false,
"errorCode": "0001011",
"message": "商戶餘額將達上限,無法建立此筆代收。上限 1000000.00,目前可入帳上限剩餘 320.00",
"timestamp": 1786000000000
}
// 欄位驗證失敗(HTTP 400)— 注意此結構沒有 success 欄位
{
"code": 400,
"errorCode": "0001002",
"message": "商戶訂單號不能為空, 金額必須大於0",
"timestamp": "2026-08-08T12:00:00"
}
本端點可能的失敗
| HTTP | errorCode |
說明 |
|---|---|---|
| 400 | 0001002 |
欄位驗證失敗(結構 B)、callbackUrl 未通過安全檢查,或 returnUrl 不是 http / https 絕對網址 |
| 403 | 0001010 |
API Key 沒有寫入權限(需 WRITE) |
| 400 | 0001011 |
業務規則錯誤,實際原因見 message。常見:餘額上限、單筆金額超限、代收功能已關閉、付款人黑名單、風控攔截、閘道呼叫失敗: ...(所有候選通道都送單失敗) |
| 500 | 0001001 |
系統異常,退避後重試 |
payerName(付款人姓名)在菲律賓站為選填。
returnUrl付款後返回商戶頁面(2026-10-02 起)訂單成功、失敗或逾時後,收銀台會顯示倒數,約 3 秒後將付款人轉回
returnUrl, 並附加以下查詢參數(已 URL 編碼;原網址已有?時以&接續):
參數 說明 statussuccess(成功)/failed(失敗或已取消)/expired(逾時)merchantOrderNo您的訂單號 systemTransactionNo平台訂單號 範例:
https://merchant.com/payment/result?status=success&merchantOrderNo=M202608080001&systemTransactionNo=COL202608080001⚠️
returnUrl僅供頁面跳轉,不可作為入帳依據:網址參數可被付款人竄改。 訂單結果請一律以 Webhook 回調(§8)或查詢 API(§4.2)為準。 未帶returnUrl時,收銀台停留在結果頁,行為與先前相同。
重送同一
merchantOrderNo不會重複建單:平台以商戶訂單號做冪等, 重送會直接回傳既有訂單的成功回應(見 §3.4)。
4.2 查詢代收訂單
POST /v1/collection/query 權限:READ
請求:merchantOrderNo 與 systemTransactionNo 至少擇一(兩者皆有時以後者優先)。
{ "merchantOrderNo": "M202608080001" }
回應:同 4.1 的訂單欄位。
| 情況 | HTTP | errorCode |
|---|---|---|
| 兩個訂單號都沒帶 | 400 | 0001002 |
| 查無訂單 | 200 | 0001012(success: false) |
| 訂單屬於其他商戶 | 403 | 1001004 |
| 系統異常 | 500 | 0001001 |
失敗回應範例
// 查無訂單 —— 注意這是 HTTP 200
{
"success": false,
"errorCode": "0001012",
"message": "訂單不存在",
"timestamp": 1786000000000
}
// 訂單屬於其他商戶(HTTP 403)
{
"success": false,
"errorCode": "1001004",
"message": "無權訪問此訂單",
"timestamp": 1786000000000
}
⚠️ 查無訂單回的是 HTTP 200,不是 404。若您的程式只檢查 HTTP 狀態碼, 會把「查無此單」誤判為查詢成功。請務必檢查
success欄位。
4.3 查詢代收列表
POST /v1/collection/list 權限:READ
請求欄位
| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
status |
string | 訂單狀態;帶入無效值會造成錯誤,請使用 3.3 節的合法值 | |
startTime |
string | ISO 8601,如 2026-08-01T00:00:00 |
|
endTime |
string | ISO 8601 | |
page |
int | 從 1 起算,預設 1 | |
pageSize |
int | 1–100,預設 20 |
回應
{
"success": true,
"data": {
"orders": [ /* 訂單陣列 */ ],
"pagination": {
"page": 1, "pageSize": 20, "totalCount": 150,
"totalPages": 8, "hasNext": true, "hasPrevious": false
}
},
"timestamp": 1786000000000
}
失敗回應範例
// 分頁參數超出範圍(HTTP 400,結構 B)
{
"code": 400,
"errorCode": "0001002",
"message": "每頁筆數最大為 100",
"timestamp": "2026-08-08T12:00:00"
}
| HTTP | errorCode |
說明 |
|---|---|---|
| 400 | 0001002 |
page < 1 或 size 不在 1~100 |
| 403 | 0001010 |
API Key 沒有讀取權限 |
| 500 | 0001001 |
系統異常 |
⚠️ 請先確認參數格式:
status傳入非法值、或startTime/endTime非YYYY-MM-DDTHH:mm:ss格式時,目前會回 HTTP 5000001001而非參數錯誤。 查無資料不是失敗,會回success: true且列表為空陣列。
4.4 取消代收訂單
POST /v1/collection/cancel 權限:WRITE
請求:merchantOrderNo 或 systemTransactionNo(擇一)+ reason(選填,≤200)。
僅
PENDING或PROCESSING狀態可取消,其他狀態回 HTTP 400、errorCode = 0001013。
失敗回應範例
// 狀態不允許取消(HTTP 400)
{
"success": false,
"errorCode": "0001013",
"message": "當前狀態不允許取消",
"timestamp": 1786000000000
}
// 查無訂單 —— HTTP 200
{
"success": false,
"errorCode": "0001012",
"message": "訂單不存在",
"timestamp": 1786000000000
}
| HTTP | errorCode |
說明 |
|---|---|---|
| 400 | 0001002 |
兩個訂單號都沒帶 |
| 200 | 0001012 |
查無訂單 |
| 403 | 1001004 |
訂單屬於其他商戶 |
| 403 | 0001010 |
API Key 沒有寫入權限 |
| 400 | 0001013 |
僅 PENDING / PROCESSING 可取消 |
| 500 | 0001001 |
系統異常 |
4.5 產生 QR Code
POST /v1/collection/qr/generate 權限:WRITE
此端點會同時建立一筆代收訂單並回傳 QR 相關資訊。
請求欄位:merchantOrderNo(必填)、amount(必填)、channelCode(必填)、currency、expireMinutes、remark(≤200)、extraData。
回應欄位:merchantOrderNo、systemTransactionNo、amount、currency、qrCodeUrl、qrCodeData(EMVCo 原始字串)、cashierUrl、deepLinkUrl、expireAt、createdAt、extraData。
注意:
qrCodeUrl/qrCodeData/deepLinkUrl是否有值,取決於實際路由到的支付通道。 部分通道不產生 QR,此時這些欄位為空,請改用cashierUrl。
失敗回應範例
// 業務失敗(HTTP 400)
{
"success": false,
"errorCode": "0001011",
"message": "閘道呼叫失敗: [通道選擇失敗] 無可用的代收通道,請檢查金額範圍和通道配置",
"timestamp": 1786000000000
}
| HTTP | errorCode |
說明 |
|---|---|---|
| 400 | 0001002 |
欄位驗證失敗(結構 B) |
| 403 | 0001010 |
API Key 沒有寫入權限 |
| 400 | 0001011 |
業務規則錯誤,與 §4.1 相同(本端點會實際建立一筆代收訂單) |
| 500 | 0001001 |
系統異常 |
HK 站注意:本端點不傳
payerName,而 HK 站該欄為必填, 因此在 HK 站呼叫會回0001011+付款人姓名不能為空。HK 站請改用 §4.1 建單。
4.6 查詢 QR Code
POST /v1/collection/qr/inquire 權限:READ
用於驗證一組 QR Code 是否有效、以及對應訂單的目前狀態。
請求
| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
qrCode |
string | ✅ | QR Code 的原始字串(EMVCo 格式) |
回應欄位
| 欄位 | 說明 |
|---|---|
qrId |
QR 的關聯識別碼 |
merchantOrderNo / systemTransactionNo |
對應的訂單號 |
merchantName |
QR 上的收款方顯示名稱 |
amount |
應付金額 |
currency |
幣別 |
status |
見下表 |
isValid |
是否仍可付款 |
expireAt / createdAt |
時間 |
errorMessage |
無法解析或查無訂單時的說明 |
status 對照
| 值 | 意義 | isValid |
|---|---|---|
VALID |
有效,可付款 | true |
PAID |
已完成付款 | false |
EXPIRED |
已過期 | false |
CANCELLED |
訂單已取消 | false |
FAILED |
訂單已失敗 | false |
NOT_FOUND |
查無對應訂單 | false |
INVALID |
無法解析,或非本平台產生的 QR | false |
// 請求
{ "qrCode": "00020101021228...6304A1B2" }
// 回應
{
"success": true,
"data": {
"qrId": "REF20260808001",
"merchantOrderNo": "M202608080001",
"systemTransactionNo": "COL202608080001",
"amount": 1000.00,
"currency": "PHP",
"status": "VALID",
"isValid": true,
"expireAt": "2026-08-08T12:30:00",
"createdAt": "2026-08-08T12:00:00"
},
"timestamp": 1786000000000
}
查詢他人的訂單會回 HTTP 403、
errorCode = 1001004。
失敗回應範例
// QR 無法解析或非本平台產生 —— 注意 success 是 true
{
"success": true,
"data": {
"status": "INVALID",
"isValid": false,
"errorMessage": "無法解析此 QR Code,或該 QR Code 非本平台產生"
},
"timestamp": 1786000000000
}
// 查無對應訂單 —— success 同樣是 true
{
"success": true,
"data": {
"qrId": "REF123456",
"status": "NOT_FOUND",
"isValid": false,
"errorMessage": "查無此 QR Code 對應的訂單"
},
"timestamp": 1786000000000
}
⚠️ 本端點是例外:QR 解析不出、或查無對應訂單時,回的是
success: true, 沒有errorCode,錯誤說明在data.errorMessage。 請改以data.isValid與data.status(INVALID/NOT_FOUND)判斷。
| HTTP | errorCode |
說明 |
|---|---|---|
| 403 | 0001010 |
API Key 沒有讀取權限 |
| 403 | 1001004 |
QR 對應的訂單屬於其他商戶 |
| 500 | 0001001 |
系統異常(含 QR 字串完全非 EMVCo 格式) |
5. 代付 API
5.1 建立代付訂單
POST /v1/payout/create 權限:WRITE
⚠️ 重要:透過 API 建立的代付「不經過審核」,直接送出執行。 請在您自己的系統內做好風控與覆核,避免誤付。
請求欄位
| 欄位 | 型別 | 必填 | 限制 | 說明 |
|---|---|---|---|---|
merchantOrderNo |
string | ✅ | ≤64 | 您的訂單號,冪等依據 |
amount |
decimal | ✅ | ≥0.01,最多 2 位小數 | 付款金額 |
currency |
string | ≤10 | 幣別,建議明確帶值。可帶 USDT 等外幣,系統依平台匯率換算成 PHP 收款,詳見 §11 |
|
beneficiaryName |
string | ✅ | ≤100 | 收款人姓名,須與帳戶登記名一致 |
beneficiaryAccount |
string | ✅ | ≤100 | 收款帳號 |
beneficiaryBank |
string | ≤100 | 不需傳入;系統會由 institutionCode 自動帶出銀行名稱 |
|
institutionCode |
string | ✅ | ≤50 | 收款機構代碼,必填,請由 /v1/institutions 取得,勿自行拼湊 |
channelCode |
string | ✅ | ≤50 | 指定通道,必填。PH2001 InstaPay/PH2002 PESONet。可用值請呼叫 /v1/channels?productType=PAYOUT 取得 |
remark |
string | ≤500 | 備註 | |
callbackUrl |
string | ≤500 | 本筆訂單專用的回調網址,覆蓋後台設定;留空則使用後台設定 | |
extraData |
string | ≤1000 | 自訂資料,原樣回傳 |
回應欄位
| 欄位 | 型別 | 說明 |
|---|---|---|
merchantOrderNo |
string | 您的訂單號 |
systemTransactionNo |
string | 平台訂單號 |
amount |
decimal | 付款金額(收款人實收金額) |
fee |
decimal | 手續費 |
totalAmount |
decimal | 您實際被扣款的金額 = amount + fee |
currency |
string | 幣別 |
beneficiaryName |
string | 收款人姓名 |
beneficiaryAccount |
string | 收款帳號(已遮罩,如 6228****6789) |
status |
string | 訂單狀態 |
completedAt / createdAt |
string | 時間 |
failureReason |
string | 失敗時才有 |
extraData |
string | 原樣回傳 |
範例
// 請求
{
"merchantOrderNo": "P202608080001",
"amount": 5000.00,
"currency": "PHP",
"beneficiaryName": "Juan Dela Cruz",
"beneficiaryAccount": "6228480123456789",
"institutionCode": "BNORPHMM_TRIXON_INSTAPAY"
}
// 回應
{
"success": true,
"data": {
"merchantOrderNo": "P202608080001",
"systemTransactionNo": "APP202608080001",
"amount": 5000.00,
"fee": 25.00,
"totalAmount": 5025.00,
"currency": "PHP",
"beneficiaryName": "Juan Dela Cruz",
"beneficiaryAccount": "6228****6789",
"status": "PENDING",
"createdAt": "2026-08-08T12:00:00"
},
"timestamp": 1786000000000
}
代付手續費為外扣:收款人收到 5,000,您的餘額扣 5,025。 建單時系統會先凍結
totalAmount,餘額不足會建單失敗。
失敗回應範例
// 餘額不足(HTTP 400)
{
"success": false,
"errorCode": "0001011",
"message": "餘額不足或凍結失敗: 可用餘額不足",
"timestamp": 1786000000000
}
// 收款銀行未帶(HTTP 400)
{
"success": false,
"errorCode": "0001002",
"message": "收款銀行不能為空",
"timestamp": 1786000000000
}
本端點可能的失敗
| HTTP | errorCode |
說明 |
|---|---|---|
| 400 | 0001002 |
欄位驗證失敗(結構 B)、callbackUrl 未通過安全檢查、或未帶 institutionCode(FPS 通道除外) |
| 403 | 0001010 |
API Key 沒有寫入權限(需 WRITE) |
| 400 | 0001011 |
業務規則錯誤,常見:餘額不足、單筆金額超限、代付功能已關閉、風控攔截、無效的通道代碼、商戶未配置該平台產品的供應商偏好 |
| 400 | 2001001 |
商戶不存在 |
| 500 | 0001001 |
系統異常 |
⚠️ 建單成功 ≠ 出款成功:本端點回傳成功只代表訂單已受理。 實際送往銀行/通道是非同步進行的,路由或閘道失敗不會反映在這個回應裡。 請務必透過 Webhook 回調(§8)或 §5.2 查詢代付訂單確認最終結果。
channelCode常見誤用:此欄要填平台產品代碼(如PH2001)或通道產品代碼, 不是銀行代碼。要指定收款銀行請用institutionCode。
5.2 查詢代付訂單
POST /v1/payout/query 權限:READ
請求與行為同 4.2。
失敗回應範例
// 查無訂單 —— HTTP 200
{
"success": false,
"errorCode": "0001012",
"message": "訂單不存在",
"timestamp": 1786000000000
}
| HTTP | errorCode |
說明 |
|---|---|---|
| 400 | 0001002 |
兩個訂單號都沒帶 |
| 200 | 0001012 |
查無訂單 |
| 403 | 1001004 |
訂單屬於其他商戶 |
| 403 | 0001010 |
API Key 沒有讀取權限 |
| 500 | 0001001 |
系統異常 |
⚠️ 同 §4.2:查無訂單回 HTTP 200,請檢查
success欄位。
5.3 查詢代付列表
POST /v1/payout/list 權限:READ
請求與回應格式同 4.3。
失敗回應範例
// 分頁參數超出範圍(HTTP 400,結構 B)
{
"code": 400,
"errorCode": "0001002",
"message": "每頁筆數最大為 100",
"timestamp": "2026-08-08T12:00:00"
}
| HTTP | errorCode |
說明 |
|---|---|---|
| 400 | 0001002 |
page < 1 或 size 不在 1~100 |
| 403 | 0001010 |
API Key 沒有讀取權限 |
| 500 | 0001001 |
系統異常 |
⚠️ 同 §4.3:
status非法值或時間格式錯誤目前會回 HTTP 500。 查無資料不是失敗,會回success: true與空陣列。
5.4 取消代付訂單
POST /v1/payout/cancel 權限:WRITE
請求:merchantOrderNo 或 systemTransactionNo + reason(選填)。
僅未執行的訂單可取消。取消成功後狀態為
REJECTED。
失敗回應範例
// 狀態不允許取消(HTTP 400)
{
"success": false,
"errorCode": "0001013",
"message": "當前狀態不允許取消",
"timestamp": 1786000000000
}
| HTTP | errorCode |
說明 |
|---|---|---|
| 400 | 0001002 |
兩個訂單號都沒帶 |
| 200 | 0001012 |
查無訂單 |
| 403 | 1001004 |
訂單屬於其他商戶 |
| 403 | 0001010 |
API Key 沒有寫入權限 |
| 400 | 0001013 |
當前狀態不允許取消 |
| 500 | 0001001 |
系統異常 |
⚠️ API 建立的代付單無法用本端點取消:本端點僅接受
PENDING_APPROVAL/APPROVED狀態,而透過 API 建立的代付單起始狀態為PROCESSING, 呼叫必定回0001013。如需中止,請聯繫平台人工處理。
6. 查詢類 API
6.1 查詢餘額
GET /v1/balance 權限:READ
{
"success": true,
"data": {
"merchantCode": "merchant01",
"balances": [
{
"currency": "PHP",
"availableBalance": 100000.00,
"frozenBalance": 5000.00,
"pendingBalance": 20000.00,
"totalBalance": 125000.00
}
]
},
"timestamp": 1786000000000
}
| 欄位 | 說明 |
|---|---|
availableBalance |
可用餘額,可用於建立新交易 |
frozenBalance |
鎖定中金額(代付/下發處理中) |
pendingBalance |
待結算金額(T+N 尚未到期) |
totalBalance |
總餘額 |
失敗回應範例
{
"success": false,
"errorCode": "2001001",
"message": "商戶不存在",
"timestamp": 1786000000000
}
| HTTP | errorCode |
說明 |
|---|---|---|
| 403 | 0001010 |
API Key 沒有讀取權限 |
| 400 | 2001001 |
商戶不存在(注意是 400,不是 404) |
| 500 | 0001001 |
系統異常 |
6.2 查詢可用支付方式
GET /v1/channels?productType=COLLECTION 權限:READ
productType 可帶 COLLECTION 或 PAYOUT,留空回全部。
{
"success": true,
"data": {
"channels": [
{
"channelCode": "PH1001",
"channelName": "GCash QR",
"channelType": "COLLECTION",
"supportedCurrencies": ["PHP"],
"minAmount": 1.00,
"maxAmount": 50000.00,
"available": true
}
]
},
"timestamp": 1786000000000
}
只會回傳已為您開通的支付方式。
channelCode可直接用於建單的channelCode欄位。
失敗回應範例
{
"success": false,
"errorCode": "0001010",
"message": "API Key 沒有讀取權限",
"timestamp": 1786000000000
}
| HTTP | errorCode |
說明 |
|---|---|---|
| 403 | 0001010 |
API Key 沒有讀取權限 |
| 500 | 0001001 |
系統異常 |
查無可用通道不是失敗:會回
success: true,data.channels為空陣列。
6.3 查詢收款機構清單
GET /v1/institutions 權限:READ
{
"success": true,
"data": [
{
"institutionCode": "BNORPHMM_TRIXON_INSTAPAY",
"institutionName": "BDO Unibank",
"institutionType": "BANK",
"channelProductId": 12
}
],
"timestamp": 1786000000000
}
代付建單時的
institutionCode請由此端點取得,不要自行拼湊。
失敗回應範例
{
"success": false,
"errorCode": "0001010",
"message": "API Key 沒有讀取權限",
"timestamp": 1786000000000
}
| HTTP | errorCode |
說明 |
|---|---|---|
| 403 | 0001010 |
API Key 沒有讀取權限 |
| 500 | 0001001 |
系統異常 |
查無機構不是失敗:會回
success: true與空陣列。
6.4 查詢收付款帳號
POST /v1/payment-accounts/list 權限:READ
回傳您已設定的收付款帳號清單(id、accountName、accountType、status、currency、collectEnabled、payoutEnabled)。
失敗回應範例
{
"success": false,
"errorCode": "0001010",
"message": "API Key 沒有讀取權限",
"timestamp": 1786000000000
}
| HTTP | errorCode |
說明 |
|---|---|---|
| 403 | 0001010 |
API Key 沒有讀取權限 |
| 500 | 0001001 |
系統異常 |
7. 對帳單 API
7.1 查詢日結對帳單
POST /v1/statement/daily 權限:READ
請求:date(必填,YYYY-MM-DD)。
{
"success": true,
"data": {
"date": "2026-08-07",
"merchantCode": "merchant01",
"currency": "PHP",
"summary": {
"collectionCount": 100, "collectionAmount": 50000.00, "collectionFee": 750.00,
"payoutCount": 50, "payoutAmount": 30000.00, "payoutFee": 150.00,
"withdrawalCount": 5, "withdrawalAmount": 10000.00, "withdrawalFee": 50.00,
"netSettlement": 19050.00,
"openingBalance": 120000.00,
"closingBalance": 139050.00
},
"details": [
{
"systemTransactionNo": "COL202608070001",
"merchantOrderNo": "M202608070001",
"transactionType": "COLLECTION",
"channelCode": "PH1002",
"amount": 1000.00,
"fee": 15.00,
"netAmount": 985.00,
"status": "SUCCESS",
"completedAt": "2026-08-07T09:15:22"
}
]
},
"timestamp": 1786000000000
}
summary 欄位說明
| 欄位 | 說明 |
|---|---|
openingBalance |
期初餘額(前一日收盤總餘額) |
closingBalance |
期末餘額(當日收盤總餘額) |
netSettlement |
淨結算 = 代收淨額 − 代付金額 − 下發金額 |
⚠️ 餘額欄位可能為
null:若該日尚無餘額快照(例如商戶當日才建立, 或查詢日期早於平台啟用),會回null而非0,以便您分辨「餘額為零」與「無資料」。
details 欄位說明
| 欄位 | 說明 |
|---|---|
transactionType |
COLLECTION / PAYOUT / WITHDRAWAL |
amount |
訂單金額 |
fee |
手續費 |
netAmount |
代收 = 實際入帳(amount − fee);代付/下發 = 實際扣款(amount + fee) |
completedAt |
完成時間,明細依此由早到晚排序 |
統計與明細範圍皆僅含「成功」的交易。
⚠️ 明細筆數上限 1,000 筆。若當日成功交易超過此數量,
details會被截斷, 請改用 7.2 的 CSV 下載 取得完整明細。
失敗回應範例
{
"success": false,
"errorCode": "2001001",
"message": "商戶不存在",
"timestamp": 1786000000000
}
| HTTP | errorCode |
說明 |
|---|---|---|
| 403 | 0001010 |
API Key 沒有讀取權限 |
| 400 | 2001001 |
商戶不存在 |
| 500 | 0001001 |
系統異常 |
⚠️
date格式必須是YYYY-MM-DD:格式錯誤目前會回 HTTP 5000001001, 而非參數錯誤,請於送出前自行校驗。
明細上限 1000 筆:超過時會靜默截斷只回前 1000 筆,回應中沒有截斷標記。 交易量大的日期請改用 §7.2 下載 CSV。
7.2 下載對帳單 CSV
GET /v1/statement/download?date=2026-08-07 權限:READ
回傳 text/csv(UTF-8),欄位依序為:
Transaction No, Merchant Order No, Type, Channel, Amount, Fee, Net Amount, Status, Completed At
僅含當日成功的代收與代付紀錄。
失敗回應範例
// 注意:本端點成功時回的是 CSV 純文字,只有失敗時才是 JSON
{
"success": false,
"errorCode": "0001010",
"message": "API Key 沒有讀取權限",
"timestamp": 1786000000000
}
| HTTP | errorCode |
說明 |
|---|---|---|
| 403 | 0001010 |
API Key 沒有讀取權限 |
| 500 | 0001001 |
系統異常(含 date 格式錯誤、或未帶 date) |
⚠️ 本端點的回應有兩種型別: 成功回
text/csv純文字(帶Content-Disposition: attachment), 失敗才回 JSON。請先檢查Content-Type再決定用哪種方式解析, 不要一律套用 JSON parser。
8. Webhook 回調
當訂單狀態變更時,平台會主動 POST 通知您設定的 Webhook URL。
8.0 指定單筆訂單的回調網址
建單時可帶 callbackUrl,只有這一筆訂單的通知會送到該網址,覆蓋您在後台設定的 Webhook URL。
適合「不同業務線用不同接收端點」的情境。留空則沿用後台設定。
限制(不符會回 HTTP 400、errorCode = 0001002):
| 規則 | 說明 |
|---|---|
| 協定 | 必須是 http 或 https 的絕對網址(建議用 https) |
| 長度 | ≤ 500 字元 |
| 禁止 | 不可指向 localhost、127.0.0.1 等本機位址 |
| 禁止 | 不可指向內部網段(10.x、192.168.x、172.16~31.x、169.254.x) |
這些限制是為了防止回調被導向內部服務,屬平台安全機制。 請提供可從公網存取的網址。
相同規則也適用於商戶後台的 Webhook 設定(新增與修改皆會驗證)。 若您在後台儲存 Webhook URL 時收到「不可指向本機位址/內部網段」的提示,原因即在此。
8.1 事件類型
event |
觸發時機 |
|---|---|
payment.success |
代收成功 |
payment.failed |
代收失敗 |
refund.success / refund.failed |
退款結果 |
withdrawal.success / withdrawal.failed |
代付結果/下發結果 |
8.2 回調格式
{
"event": "payment.success",
"timestamp": 1786000000,
"data": {
"merchantOrderNo": "M202608080001",
"systemTransactionNo": "COL202608080001",
"status": "SUCCESS",
"amount": 1000.00,
"fee": 15.00,
"netAmount": 985.00,
"currency": "PHP",
"completedAt": "2026-08-08T12:05:30",
"extraData": "{\"userId\":8891}"
}
}
| 欄位 | 說明 |
|---|---|
event |
事件名稱 |
timestamp |
Unix 時間戳,單位為「秒」 |
data |
事件內容,欄位依事件類型而異 |
⚠️
data內的欄位可能隨版本增加。請以「取用您需要的欄位」的方式解析, 不要用嚴格 schema 驗證,以免平台新增欄位時您的系統報錯。 請以merchantOrderNo/systemTransactionNo定位訂單,以status判斷結果。
代付/下發回調
代付(withdrawal.success / withdrawal.failed)的 data 範例:
{
"event": "withdrawal.success",
"timestamp": 1786000000,
"data": {
"merchantOrderNo": "P202608080001",
"systemTransactionNo": "APP202608080001",
"applicationNo": "APP202608080001",
"status": "SUCCESS",
"amount": 1000.00,
"currency": "PHP",
"beneficiaryName": "Juan Dela Cruz",
"beneficiaryAccount": "09171234567",
"beneficiaryBank": "GCASH",
"completedAt": "2026-08-08T12:05:30"
}
}
| 欄位 | 說明 |
|---|---|
merchantOrderNo |
您的訂單號(建議以此定位訂單) |
systemTransactionNo |
平台訂單號,三種交易皆有此欄位,代付即為 applicationNo |
applicationNo |
代付申請單號(與 systemTransactionNo 同值) |
💡
merchantOrderNo與systemTransactionNo三種交易(代收/代付/下發)皆有, 若您想用同一套程式處理所有回調,直接取這兩個欄位即可。
相容欄位說明
為相容早期介接的商戶,部分回調的 data 內會同時出現蛇形命名的重複欄位
(例如 merchant_order_id 之於 merchantOrderNo、completed_at 之於 completedAt),
兩者值完全相同。
新介接請一律使用本文件列出的駝峰欄位;蛇形欄位屬過渡相容,未來版本會移除。
8.3 驗證回調簽章(必做)
回調請求會帶以下標頭:
X-Timestamp: 1786000000 (秒)
X-Nonce: 3f2a9c1e...
X-Signature: a1b2c3d4...= (Base64)
驗證步驟:
-
取出
X-Timestamp、X-Nonce、X-Signature與 raw body 原始字串 -
檢查
|現在時間 − X-Timestamp| ≤ 300秒,超過則拒絕 -
檢查
X-Nonce是否已處理過(請自行儲存 5 分鐘,防重放) -
用簽章金鑰計算:
Base64( HMAC-SHA256( 簽章金鑰, timestamp + "." + nonce + "." + raw_body ) )⚠️ 用哪一把金鑰,取決於這筆回調怎麼設定的:
情境 簽章金鑰 建單時帶 callbackUrl(per-order 回調)API Secret(與您呼叫 API 用的同一把) 在商戶後台設定 Webhook URL Webhook Secret 兩者演算法完全相同,只差在金鑰。若您沒有在後台設定 Webhook、 只在建單時帶
callbackUrl,請直接用 API Secret 驗章。 -
與
X-Signature比對(建議用常數時間比較函式) -
全部通過才處理內容
Node.js 範例
const crypto = require('crypto');
function verifyWebhook(req, secret) {
const ts = req.headers['x-timestamp'];
const nonce = req.headers['x-nonce'];
const sig = req.headers['x-signature'];
const raw = req.rawBody; // 必須是「原始字串」,不可用解析後再序列化的結果
if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false;
// 另需自行檢查 nonce 是否重複
const expected = crypto
.createHmac('sha256', secret)
.update(`${ts}.${nonce}.${raw}`)
.digest('base64');
return crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected));
}
⚠️ 務必使用 raw body。若您的框架(如 Express 的
express.json())已把 body 解析成物件, 再JSON.stringify()回去,字串很可能與原始位元組不同,導致驗章失敗。 Express 請於express.json({ verify: (req, res, buf) => { req.rawBody = buf.toString(); } })保留原文。
8.4 您應該回應什麼
2026-10-05 起,平台收到以下回應才視為「已送達」,否則視為失敗並依 8.5 重送:
| 項目 | 要求 |
|---|---|
| HTTP 狀態碼 | 2xx |
| 回應內容 | 必須為 SUCCESS 或 OK(不分大小寫,前後空白不影響) |
| 回應時間 | 建議 5 秒內(逾時上限 30 秒) |
| 您的回應內容 | 結果 |
|---|---|
SUCCESS、success、OK、ok |
✅ 已送達,不再重送 |
"SUCCESS"(JSON 字串) |
✅ 已送達 |
空白、FAILED、received、{"code":"SUCCESS"} 等其他內容 |
❌ 視為失敗,依 8.5 重送 |
非 2xx(如 500)、逾時、無法連線 |
❌ 視為失敗,依 8.5 重送 |
回應內容不符時,平台後台的訂單「失敗原因」會顯示您實際回覆的內容,方便雙方排查。
回應範例
# Python (Flask)
return "SUCCESS", 200, {"Content-Type": "text/plain"}
// Node.js (Express)
res.status(200).type("text/plain").send("SUCCESS");
// PHP
http_response_code(200);
echo "SUCCESS";
// Java (Spring)
return ResponseEntity.ok("SUCCESS");
建議做法:驗章通過後先回
SUCCESS,再非同步處理業務邏輯。 若您同步處理耗時過久導致逾時,平台會視為失敗並重送,可能造成重複處理。驗章失敗或業務處理失敗、希望平台稍後重送時,回任何非
SUCCESS/OK的內容即可(例如FAILED)。
8.5 重試機制
回調失敗(非 2xx、逾時、無法連線,或回應內容不是 SUCCESS / OK)時,平台會自動重試,採指數退避:
| 次數 | 距上次的間隔 |
|---|---|
| 第 1 次重試 | 10 秒 |
| 第 2 次重試 | 20 秒 |
| 第 3 次重試 | 40 秒 |
| 第 4 次重試 | 80 秒 |
| 第 5 次重試 | 160 秒 |
預設共重試 5 次(首次發送後合計約 5 分鐘)。若您在商戶後台的 Webhook 設定中調整過重試次數或間隔,以您的設定為準。 全數失敗後即不再自動重送,訂單的回調狀態會標為「回調失敗」;請聯繫平台人工重送,或改用主動查詢(8.6)取得結果。
熔斷保護:若您的端點連續失敗 5 次,平台會暫停發送 5 分鐘,之後再嘗試恢復。
8.6 ⭐ 請務必實作主動查詢
Webhook 是「盡力送達」,不保證 100% 到達。
請務必額外實作主動輪詢機制:對於超過預期時間仍為
PENDING/PROCESSING的訂單, 定期呼叫/v1/collection/query或/v1/payout/query確認最終狀態。建議策略:建單後 5 分鐘、15 分鐘、30 分鐘各查詢一次,直到訂單進入終態。
8.7 冪等處理(必做)
由於有重試機制,同一筆通知可能送達多次。請以 merchantOrderNo 或
systemTransactionNo 為鍵做冪等控制,確保重複收到時不會重複入帳。
9. 錯誤碼
9.1 認證類(HTTP 401 / 403)
errorCode |
HTTP | 原因 | 處理建議 |
|---|---|---|---|
MISSING_API_KEY |
401 | 缺少 X-API-Key |
檢查標頭 |
MISSING_SIGNATURE |
401 | 缺少 X-Signature |
檢查標頭 |
MISSING_TIMESTAMP |
401 | 缺少 X-Timestamp |
檢查標頭 |
MISSING_NONCE |
401 | 缺少 X-Nonce |
檢查標頭 |
INVALID_TIMESTAMP |
400 | 時間戳格式錯誤 | 應為數字字串(秒) |
INVALID_API_KEY |
401 | API Key 不存在 | 確認金鑰與環境是否對應 |
API_KEY_DISABLED |
401 | API Key 已停用 | 聯繫平台 |
API_KEY_EXPIRED |
401 | API Key 已過期 | 申請新金鑰 |
INVALID_SIGNATURE |
401 | 簽章錯誤或時間戳超出 ±5 分鐘 | 先檢查伺服器時鐘,再檢查簽章串接與 Base64 |
INVALID_NONCE |
401 | Nonce 重複使用 | 每次請求產生新的 nonce |
IP_NOT_ALLOWED |
403 | 來源 IP 不在白名單 | 確認對外出口 IP 並請平台加入 |
MERCHANT_DISABLED |
403 | 商戶帳號已停用 | 聯繫平台 |
API_DISABLED |
403 | 商戶未開通 API 功能 | 聯繫平台 |
9.2 業務類
errorCode |
HTTP | 意義 | 處理建議 |
|---|---|---|---|
0001002 |
400 | 參數錯誤(如兩個訂單號都沒帶) | 檢查請求 |
0001010 |
403 | API Key 權限不足 | 申請對應權限 |
0001011 |
400 | 業務規則錯誤(餘額不足、通道不可用、風控攔截等) | 請看 message 內容判斷實際原因 |
0001012 |
200 | 查無訂單 | 確認訂單號 |
0001013 |
400 | 訂單狀態不允許此操作 | 先查詢當前狀態 |
1001004 |
403 | 該訂單不屬於您 | 檢查訂單號 |
0001001 |
500 | 系統異常 | 退避後重試;持續發生請聯繫平台 |
RATE_LIMIT_EXCEEDED |
429 | 超過流量限制 | 依 Retry-After 退避 |
⚠️
0001011是通用業務錯誤碼。餘額不足、通道不可用、超出限額等都會回這個碼, 實際原因在message欄位。建議記錄完整message以利排查。
9.3 建議的錯誤處理策略
| 情況 | 建議 |
|---|---|
| HTTP 401 / 403 | 不要重試,屬設定問題,記錄並告警 |
| HTTP 429 | 依 Retry-After 退避後重試 |
| HTTP 500 | 指數退避重試(最多 3 次) |
| 網路逾時(建單) | 可安全重試相同 merchantOrderNo,不會重複建單 |
success: false + 0001011 |
記錄 message,依業務判斷是否重試 |
10. 介接檢查清單
上線前請逐項確認:
認證
- [ ]
X-Timestamp使用秒(非毫秒) - [ ] 簽章串接使用
.(非|或,) - [ ] 簽章輸出為 Base64(非 hex)
- [ ] 簽章用的字串與實際送出的 body 完全一致
- [ ] 每次請求產生新的 nonce
- [ ] 伺服器已設定 NTP 校時
- [ ] 對外出口 IP 已加入白名單
請求
- [ ] URL 含
/api前綴 - [ ] 每筆訂單都帶唯一的
merchantOrderNo - [ ]
currency明確帶值 - [ ] 已依
X-RateLimit-Remaining做流量控制
回應處理
- [ ] 以 body 的
success判斷成敗(非只看 HTTP 狀態碼) - [ ] 已保存
systemTransactionNo供後續查詢 - [ ] 網路逾時採「相同訂單號重試」策略
Webhook
- [ ] 已實作簽章驗證(含時間戳與 nonce 檢查)
- [ ] 使用 raw body 驗章
- [ ] 收到後回 HTTP 2xx + 內容
SUCCESS(或OK),再非同步處理(其他內容會被視為失敗而重送,見 8.4) - [ ] 已實作冪等處理(重複通知不重複入帳)
- [ ] ⭐ 已實作主動輪詢補償機制
金額
- [ ] 理解代收為內扣(入帳 = 金額 − 手續費)
- [ ] 理解代付為外扣(扣款 = 金額 + 手續費)
11. 外幣下單(USDT)
若貴商戶以外幣計價,建單時在 currency 帶入該幣別(例如 USDT),
amount 直接填外幣數量,系統會依平台設定的匯率換算成 PHP:
- 收銀台顯示換算後的
PHP,付款人轉的是PHP(畫面會加註「等值 100 USDT」)。 - 回調回傳原幣別:
currency與amount回外幣,另附PHP金額供核對。 - 收、付使用不同匯率:建代收單用「收匯率」,建代付單用「付匯率」。
- 匯率於建單當下鎖定,之後平台調整匯率不影響已建立的訂單。
- 手續費以
PHP計算(回調的fee欄位為PHP金額)。
11.1 建單範例
{
"merchantOrderNo": "ORDER_20260921_001",
"amount": 100,
"currency": "USDT"
}
當時 USDT 收匯率為 58.00 → 系統建立一張 5800.00 PHP 的收款單。
11.2 回調範例
{
"event": "payment.success",
"data": {
"merchantOrderNo": "ORDER_20260921_001",
"amount": "100.00000000",
"currency": "USDT",
"settlementAmount": "5800.00",
"settlementCurrency": "PHP",
"exchangeRate": "58.000000000",
"fee": "116.00",
"status": "SUCCESS"
}
}
| 欄位 | 說明 |
|---|---|
amount / currency |
原幣別金額(與建單時完全一致,非換算回推) |
settlementAmount / settlementCurrency |
實際收付的 PHP 金額 |
exchangeRate |
建單當下鎖定的匯率(1 外幣 = ? PHP) |
fee |
手續費,以 PHP 計算 |
既有介接無須修改:未帶
currency、或帶的就是PHP時,行為與過去完全相同, 回調也不會出現settlementAmount等欄位。
11.3 精度規則
| 項目 | 規則 |
|---|---|
| 外幣金額 | 最多 8 位小數(USDT 業界慣例) |
PHP 金額 |
最多 2 位小數,超過會回錯誤 |
| 換算結果 | 四捨五入至 2 位小數 |
11.4 錯誤情形
| 情況 | 錯誤碼 | 訊息 |
|---|---|---|
| 該幣別未開通 | 0001002 |
不支援的幣別 XXX:查無 XXX 對 PHP 的有效匯率 |
| 只設了單邊匯率 | 0001002 |
幣別 XXX 尚未設定收款匯率 / 付款匯率 |
| 法幣小數超過 2 位 | 0001002 |
PHP 金額最多 2 位小數:... |
| 換算後金額過小 | 0001002 |
換算後金額過小:... |
需要開通新幣別或調整匯率,請聯繫平台管理員。
附錄:技術支援
介接過程如遇問題,請聯繫您的客戶經理,並提供:
- 完整的請求內容(請遮蔽 API Secret)
- 收到的完整回應(含
errorCode與message) - 請求時間(精確到秒)
merchantOrderNo與systemTransactionNo
文件結束