開發文件
接入第三方充值 / 發卡 API
最近更新:2026-10-08
一句話總結
預設走內建 Mock 供應商。接真實上游:填環境變數,重啟,不改程式碼。
1. 上游只需要提供五種能力
| 能力 | 對應方法 | 用途 |
|---|---|---|
| 拉目錄 | pullCatalog() | 同步價格、庫存、缺貨狀態 |
| 查庫存 | fetchStock(ids) | 下單前校驗 |
| 下單 | createOrder(req) | 帶冪等鍵,同步回傳或非同步發貨 |
| 查單 | queryOrder(id) | Webhook 之外的兜底輪詢 |
| 回調 | verifyWebhook() | 非同步交付結果的主路徑 |
2. 環境變數
UPSTREAM_ENABLED=true
UPSTREAM_ADAPTER=rest
UPSTREAM_BASE_URL=https://api.supplier.example
UPSTREAM_API_KEY=xxxxxxxx
UPSTREAM_API_SECRET=yyyyyyyy
UPSTREAM_SIGNATURE=hmac_sha256 # none|bearer|header_key|query_key|hmac_sha256|md5
UPSTREAM_PATH_CATALOG=/api/products
UPSTREAM_PATH_CREATE_ORDER=/api/order/create
UPSTREAM_PATH_QUERY_ORDER=/api/order/query
UPSTREAM_MAPPING_JSON='{"itemsPath":"data.list","fields":{"externalId":"id","title":"name","price":"price","stock":"stock","soldOutFlag":"sold_out"},"order":{"orderIdPath":"data.order_no","contentPath":"data.content"}}'
3. 簽章做了什麼
hmac_sha256 策略:把請求體按參數名排序拼成 k1=v1&k2=v2,尾部拼上 API Secret 再取摘要。這是國內絕大多數平台的通行做法,md5 是老平台的變體。
Webhook 驗簽採用 constant-time 比較,並校驗時間戳容忍視窗,防止重送。
4. 欄位對應怎麼填
itemsPath:回應裡陣列所在的路徑,例如data.list。fields.externalId:商品唯一標識,會與站內商品的upstream_ref對齊。soldOutFlag/soldOutWhen:不同平台表達缺貨的方式五花八門(布林、字串、庫存為 0),用這兩個欄位統一。order.contentPath:交付內容所在路徑,字串按行拆成多條憑證,陣列按元素拆。
5. 換一家有多快
如果新平台的介面形態仍在這五個能力內:只改環境變數,程式碼零改動。
如果它比較另類(例如 SOAP、私有二進位協定、需要登入態會話):新增一個實作 UpstreamAdapter 的檔案,在工廠裡註冊,其餘層完全無感。
6. 自我檢查
GET /api/upstream/health
回傳目前生效的設定摘要、健康狀態、目錄條數。接新供應商後先看這個介面。
7. 穩定性策略
- 逾時 + 退避重試:預設 10s 逾時,最多重試 2 次。
- 下單不激進重試:避免重複發貨,只重試 1 次。
- 目錄快取:預設 60s。上游抖動時回傳上一次結果並標記
stale,頁面不白屏。 - 冪等鍵:
訂單號-行號,同一筆重複提交只產生一個上游單。
8. 程式碼位置
lib/server/upstream/types.ts # 介面契約(五種能力)
lib/server/upstream/mock-adapter.ts # 本地供應商
lib/server/upstream/rest-adapter.ts # 通用 REST 適配器
lib/server/upstream/index.ts # 門面:快取、降級、錯誤處理
lib/server/env.ts # 全部設定項
lib/server/http.ts # 逾時/重試/冪等
lib/server/signature.ts # 簽章與 Webhook 驗簽
app/api/upstream/health/route.ts # 自我檢查介面
app/api/upstream/webhook/route.ts # 非同步回調入口