申請虛擬卡
非同步開卡介面。請求成功只代表"已入隊",開卡完成結果透過 card.opened / card.open_failed webhook 推送,或透過 card/info 輪詢。
端點
| 項 | 值 |
|---|---|
| Method | POST |
| Path | /api/v1/openapi/card/apply |
| 鑑權 | HMAC |
| 冪等鍵 | 必填 Idempotency-Key 頭 |
請求欄位
不要傳 card_type
本介面不需要傳 card_type。伺服器從 header_id 自動推導,並校驗是否在虛擬卡範圍內。
| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
header_id | string | ✅ | hdr_<id> 格式(列卡頭) |
package_id | string | ✅ | pkg_<id> 格式(列卡配置) |
first_name | string | ⚠️ | 持卡人名(看 header.require_phone/email) |
last_name | string | ⚠️ | 持卡人姓 |
phone_code | string | ⚠️ | 國家區號(如 86 / 1) |
phone | string | ⚠️ | 手機號(不含區號) |
email | string | ⚠️ | 郵箱 |
use_bound_email | bool | 否 | 預設 false,使用請求中傳的 email。傳 true 時回退到賬號繫結的郵箱。 |
use_bound_phone | bool | 否 | 預設 false,使用請求中傳的 phone + phone_code。傳 true 時回退到賬號繫結的手機號。 |
first_deposit_amount | string | 否 | 自定義首充總額,非負整數(支付/充值資產單位,如 USDT)。空 = 用配置底額。必須 ≥ 套餐的 min_first_deposit(列卡配置);該最低值高於 base 時本欄位必填(留空會被 first_recharge_below_min 拒絕)。超出 base 的部分按充值口徑收手續費,並與開卡費一併凍結。可用首充預覽精確試算。 |
持卡人欄位是否必填
header.require_phone == true時需要phone_code+phone(或顯式傳use_bound_phone == true讓服務端讀賬號繫結的手機號)header.require_email == true時需要email(或顯式傳use_bound_email == true讓服務端讀賬號繫結的郵箱)first_name/last_name多數情況下需要(virtual_g/virtual_a始終必填,見下文)- OpenAPI 與 H5 使用者端的預設值相反:本介面的
use_bound_phone/use_bound_email預設為false—— 服務端使用你傳的值。商戶的終端使用者一般沒有在我們這邊繫結過手機號/郵箱,回退到"繫結值"會失敗。只有當你確實需要使用賬號繫結值時才顯式傳true。
自定義首充
預設按配置底額開卡。如需加充,傳 first_deposit_amount(非負整數,且 ≥ base)。底額按 1:1 免費進卡;超出底額的超額按充值口徑收手續費,扣費後按 1:1 進卡(USDT/USD,不走匯率),發給提供方的首充取整為整數。開卡費 + 完整計算出的凍結額在開卡時一併從錢包凍結。
套餐還可能設定高於底額的首充最低金額(列卡配置的 min_first_deposit,預覽裡的 min_first_deposit_amount)。此時 first_deposit_amount 必填且須 ≥ 該最低值,仍然只有底額部分免手續費(例:底額 10、最低 500 → 至少首充 500,10 免費、490 收費)。
開卡前先預覽
校驗邏輯(格式 / >= base / 最大充值 / 充值限額 / 餘額)與首充預覽共用。先調預覽即可向客戶展示精確的 total_freeze / first_recharge_card——數值與本介面 1:1 一致。非法金額在本介面同樣被拒,錯誤 key 與預覽的 invalid_reason 相同(見典型錯誤)。
G 卡(virtual_g)特殊約束
申請 virtual_g 時,服務端會在 header 配置之外強制以下約束:
| 約束 | 規則 | 失敗 message_key |
|---|---|---|
| 郵箱 | 始終必填(服務端強制覆蓋 header 設定) | bank_card_email_required |
| 手機號 | phone_code + phone 始終必填(服務端強制覆蓋 header 設定) | bank_card_phone_required |
| 持卡人姓名 | first_name + last_name 拼接後必須匹配 ^[A-Za-z]+(?: [A-Za-z]+)*$,總長度 ≤ 40 字元(僅 ASCII 字母 + 單個空格分隔;不含數字、符號、非 ASCII 字元) | virtual_g_name_invalid |
| 生日與賬單地址 | 不要傳 —— 服務端自動生成 | virtual_g_required_fields_missing(僅當服務端自動填充失敗時返回) |
申請前先校驗姓名
真實世界的持卡人姓名常帶變音符號、連字元或撇號——這些字元會被提供方拒絕。請在前端引導終端使用者輸入只含 ASCII 字母 + 單空格分隔的姓名。
A 卡(virtual_a)特殊約束
virtual_a(A 卡)與 V / R / G 同一提供方體系,遵循提供方自己的開卡協議。無論 header.require_* 配置如何,服務端都會強制以下規則 —— 列卡頭返回的已是生效值(A 卡卡頭恆為 require_email=true、require_phone=false):
| 約束 | 規則 | 失敗 message_key |
|---|---|---|
| 郵箱 | 始終必填 —— 傳 email(賬號已繫結郵箱時也可 use_bound_email=true) | bank_card_email_required |
| 手機號 | 不收集 —— phone_code / phone / use_bound_phone 會被忽略,提供方協議沒有手機號欄位 | — |
| 持卡人姓名 | first_name 與 last_name 均必填且不能為空白(純空格會被拒絕);在凍結任何資金之前校驗 | virtual_a_required_fields_missing |
| 生日與賬單地址 | 不要傳 —— 服務端自動生成 | virtual_a_required_fields_missing(僅當服務端自動填充失敗時返回) |
其餘能力(自定義首充、冪等、card.opened / card.open_failed 非同步結果、充值、凍結 / 解凍、銷卡、交易明細、敏感卡資訊)與其他虛擬卡型別完全一致。
請求示例
{
"header_id": "hdr_5",
"package_id": "pkg_12",
"first_name": "John",
"last_name": "Doe"
}請求頭必須含:
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000響應欄位
| 欄位 | 型別 | 說明 |
|---|---|---|
card_id | string | card_<id> 格式(用於後續查詢) |
status | int | 1=pending 2=active 3=failed 4=closing 5=closed 6=frozen |
status_desc | string | 英文描述 |
created_at | string | RFC3339 時間 |
響應示例(成功入隊)
{
"code": 200,
"message": "成功",
"data": {
"card_id": "card_12345",
"status": 1,
"status_desc": "pending",
"created_at": "2026-04-29T11:00:12Z"
}
}status=1 不代表卡已可用
返回 status=1 (pending) 只是"已入隊"。等 card.opened webhook 到達,或輪詢 card/info 看到 status=2 (active) 才能用。
後續流程
典型錯誤
| HTTP | message_key | 說明 |
|---|---|---|
| 400 | insufficient_balance | 餘額不足(開卡費 + 首充) |
| 400 | invalid_first_deposit_amount | first_deposit_amount 不是純非負整數(小數 / 符號 / 科學計數法 / 位數過多) |
| 400 | first_recharge_below_base | first_deposit_amount 低於配置底額 |
| 400 | first_recharge_below_min | first_deposit_amount(或留空的預設底額)低於套餐首充最低金額 min_first_deposit |
| 400 | first_recharge_exceeds_max | 超額超過 max_recharge_amount |
| 400 | first_recharge_limit_exceeded | 超額超過賬號充值限額 |
| 400 | first_recharge_asset_mismatch | 開卡費資產 ≠ 充值資產;該配置不支援自定義超額 |
| 400 | first_recharge_excess_too_small | 扣費 + 取整後到卡為 0,請加大金額 |
| 400 | kyc_required | 請先完成實名認證 |
| 400 | kyc_not_approved | 賬號 KYC 未透過 |
| 400 | bank_card_email_required | 卡型別需要郵箱;請傳 email 或顯式設 use_bound_email=true(僅當賬號已繫結時) |
| 400 | bank_card_phone_required | 卡型別需要手機號;請傳 phone_code+phone 或顯式設 use_bound_phone=true(僅當賬號已繫結時) |
| 400 | openapi_card_type_not_supported | header 對應的卡型別不在虛擬卡範圍 |
| 400 | account_manager_bind_required | 客戶經理管控卡頭,而賬號未繫結客戶經理 —— header_id 只能用 /card_headers/list 返回的值 |
| 400 | account_manager_unavailable | 賬號繫結的客戶經理已失效;請聯絡平臺客服 |
| 400 | account_manager_open_disabled | 客戶經理未對該賬號放開此卡頭 / 套餐;請只使用列表介面返回的卡頭與套餐(列表已不含未放開 / 被關閉的) |
| 400 | virtual_g_name_invalid | (G 卡)持卡人姓名不滿足 ASCII 字母 / 單空格 / ≤40 字元規則 |
| 400 | virtual_g_required_fields_missing | (G 卡)服務端無法組裝完整的開卡請求(罕見,通常是配置或地址池缺失) |
| 400 | virtual_a_required_fields_missing | (A 卡)first_name / last_name 缺失或為空白,或服務端無法組裝完整的開卡請求(卡頭 / 地址池問題) |
| 400 | openapi_invalid_header_id | header_id 缺字首或不存在 |
| 400 | openapi_invalid_package_id | package_id 缺字首 / 不屬於該 header |
| 400 | openapi_idempotency_key_required | 缺冪等鍵 |
| 400 | openapi_idempotency_key_too_long | 冪等鍵超 128 字元 |
| 400 | openapi_idempotency_key_invalid_chars | 冪等鍵含非法字元 |
| 401 | openapi_invalid_credentials | 鑑權失敗 |
| 409 | openapi_idempotency_key_conflict | 同 key 不同 body |
| 500 | openapi_apply_card_failed | 服務端異常 |
注意事項
- 開卡費 + 首充會在請求時一併扣款 / 凍結。傳自定義
first_deposit_amount時,超額手續費也會一併凍結(見自定義首充)。失敗的開卡會自動退還 - 非同步任務最長可能跑幾分鐘。不要因為客戶端 30 秒內沒收到 webhook 就重試 apply(重試需帶相同
Idempotency-Key) card_id一旦返回就永久指向這次請求的卡(即使最終失敗)