查詢卡片資訊
預設返回卡的狀態、餘額、脫敏卡號。
當請求體傳 with_sensitive=true 時,同一介面會在響應裡額外返回 完整 PAN / CVV / 到期日 / 持卡人姓名,授權方式二選一:
- 使用者二次驗證(預設可用,自助) —— 與 Web 端檢視 CVV 完全一致:按賬戶配置的交易認證方式提交
email_code/pin/two_fa_code/sms_code。驗證碼透過/openapi/card/sensitive/send_code獲取,無需管理員審批。 - 憑證免驗證特權(需審批) —— 面向全自動 server-to-server 場景,憑證可被授予免二次驗證特權。預設關閉 —— 詳見下文 敏感卡片資訊。
常用於:
- 非同步開卡後輪詢,直到
status=2 (active) - 業務側定期同步餘額
- 排查 webhook 是否漏投遞
- 在自有收銀臺 / 錢包 UI 中展示 PAN / CVV(兩條授權路徑均可)
端點
| 項 | 值 |
|---|---|
| Method | POST |
| Path | /api/v1/openapi/card/info |
| 鑑權 | HMAC |
| 冪等鍵 | 不需要 |
請求欄位
| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
card_id | string | ✅ | card_<id> 格式 |
with_sensitive | bool | 可選 | 傳 true 表示請求完整 PAN / CVV / 到期日 / 持卡人姓名。預設 false。需卡片為 active,且透過兩條授權路徑之一。 |
email_code | string | 條件必填 | 郵箱驗證碼(賬戶交易認證方式含郵箱時必填)。透過 /openapi/card/sensitive/send_code 傳送。免驗證特權憑證無需傳。 |
sms_code | string | 條件必填 | 簡訊驗證碼(認證方式含簡訊時必填)。 |
pin | string | 條件必填 | 交易 PIN 碼(認證方式含 PIN 時必填;使用者在 Web 端設定)。 |
two_fa_code | string | 條件必填 | 2FA / Google Authenticator 動態碼(認證方式含 2FA 時必填)。 |
請求示例 — 預設(僅狀態與餘額)
{ "card_id": "card_12345" }請求示例 — 請求敏感欄位(使用者二次驗證路徑)
{
"card_id": "card_12345",
"with_sensitive": true,
"email_code": "482913"
}請求示例 — 請求敏感欄位(免驗證特權憑證)
{
"card_id": "card_12345",
"with_sensitive": true
}響應欄位
始終返回
| 欄位 | 型別 | 說明 |
|---|---|---|
card_id | string | 回顯 |
card_type | string | 全小寫:virtual_l / virtual_p / virtual_v / virtual_r / virtual_g / virtual_a |
card_brand | string | 卡組織 |
currency | string | 卡內幣種 |
status | int | 1=pending 2=active 3=failed 4=closing 5=closed 6=frozen |
status_desc | string | 英文描述 |
masked_card_no | string | 脫敏卡號(前 6 後 4),如 424242******1234 |
last_four | string | 卡號末 4 位 |
balance | string (decimal) | 卡內可用餘額 |
frozen_balance | string (decimal) | 卡內凍結餘額 |
activated_at | string nullable | 啟用時間(成功後才有) |
created_at | string | 建立時間 |
僅當 with_sensitive=true、授權透過、且 status=2 時返回
| 欄位 | 型別 | 說明 |
|---|---|---|
card_number | string | 完整 PAN(無空格 / 無橫線) |
cvv | string | 卡安全碼(Visa / Mastercard 為 3 位) |
expiry_date | string | 到期日,格式 MM/YY |
first_name | string | 持卡人名(開卡時提供) |
last_name | string | 持卡人姓 |
任一前提不滿足時,這些欄位會整體省略(不存在、不為 null 也不為空串)。
響應示例 — 預設
{
"code": 200,
"message": "成功",
"data": {
"card_id": "card_12345",
"card_type": "virtual_v",
"card_brand": "VISA",
"currency": "USD",
"status": 2,
"status_desc": "active",
"masked_card_no": "424242******1234",
"last_four": "1234",
"balance": "85.50",
"frozen_balance": "0",
"activated_at": "2026-04-29T11:00:30Z",
"created_at": "2026-04-29T11:00:12Z"
}
}響應示例 — 含敏感欄位
{
"code": 200,
"message": "成功",
"data": {
"card_id": "card_12345",
"card_type": "virtual_v",
"card_brand": "VISA",
"currency": "USD",
"status": 2,
"status_desc": "active",
"masked_card_no": "424242******1234",
"last_four": "1234",
"balance": "85.50",
"frozen_balance": "0",
"activated_at": "2026-04-29T11:00:30Z",
"created_at": "2026-04-29T11:00:12Z",
"card_number": "4242420000001234",
"cvv": "123",
"expiry_date": "12/28",
"first_name": "JOHN",
"last_name": "DOE"
}
}狀態碼(status)
| 值 | 含義 | 說明 |
|---|---|---|
| 1 | pending | 開卡中 |
| 2 | active | 已啟用,可用 |
| 3 | failed | 開卡失敗(終態) |
| 4 | closing | 銷卡中 |
| 5 | closed | 已銷卡(終態) |
| 6 | frozen | 已凍結(使用者 / 風控) |
敏感卡片資訊
敏感欄位(card_number / cvv / expiry_date / first_name / last_name)受兩條授權路徑之一保護:
| 路徑 | 適用物件 | 每次請求需要 | 開通方式 |
|---|---|---|---|
| A. 使用者二次驗證 | 所有憑證(預設) | 按賬戶交易認證配置提交 email_code / pin / two_fa_code / sms_code —— 僅在信任視窗外需要(見下) | 無需開通 —— 自助可用 |
| B. 憑證免驗證特權 | 審批透過的對接方 | 無(不需要驗證碼) | 需管理員審批,預設關閉 |
路徑 A 與 Web 端使用同一套交易認證配置(安全設定 → 交易認證)、同一驗證用途、同樣的一次性驗證碼。路徑 B 面向無人值守的 server-to-server 自動化 —— 該場景下無法每次請求都由人完成二次驗證。
信任視窗 —— 驗證一次,視窗內免驗證
透過本介面成功完成一次二次驗證後,賬戶進入信任視窗(時長由平臺管理員配置,預設 30 分鐘,0 = 關閉)。視窗生效期間,再次對自己名下的卡發起 with_sensitive=true 請求無需攜帶任何驗證欄位即可成功 —— 一次人工驗證後即可程式化批次獲取多張卡的 PAN/CVV(例如把卡分發給下游使用者的場景)。
- 視窗為固定視窗,不滑動:從驗證成功時刻起算,視窗內的免驗請求不會續期;到期需重新驗證。
- 視窗按賬戶維度生效,覆蓋該憑證名下所有卡。
- 視窗生效期間
send_code返回grace_active=true且不發碼 —— 對接方可低成本探測當前是否需要驗證碼。注意:視窗外呼叫會實際發碼並佔用 60 秒冷卻,請勿把它當作無副作用的狀態探針輪詢。 - 管理員封禁始終凌駕於信任視窗之上。
路徑 A — 使用者二次驗證(預設)
第一步:傳送驗證碼
| 項 | 值 |
|---|---|
| Method | POST |
| Path | /api/v1/openapi/card/sensitive/send_code |
| 鑑權 | HMAC |
| 冪等鍵 | 不需要 |
| 請求體 | {}(空 JSON 物件) |
| 冷卻 | 同一賬戶 60 秒內限 1 次 → 超頻返回 429 + Retry-After 頭 |
驗證用途由服務端固定(get_card_sensitive)—— 本介面不能用於觸發轉賬、提現等其他操作的驗證碼,其他用途的驗證碼在這裡也不被接受。
響應 data:
| 欄位 | 型別 | 說明 |
|---|---|---|
require_email | bool | 第二步需提交 email_code |
require_sms | bool | 第二步需提交 sms_code |
require_pin | bool | 第二步需提交 pin(不發碼 —— PIN 由使用者本人持有) |
require_2fa | bool | 第二步需提交 two_fa_code(不發碼 —— 由認證器 App 生成) |
email_sent | bool | 6 位驗證碼已傳送至賬戶繫結郵箱 |
sms_sent | bool | 6 位驗證碼已傳送至賬戶繫結手機 |
grace_active | bool | 信任視窗生效中:不發碼、不消耗冷卻 —— 跳過第二步的驗證欄位直接調 /card/info |
如果賬戶只使用 PIN 和/或 2FA,則不會傳送任何驗證碼(email_sent=false, sms_sent=false)且不消耗冷卻視窗 —— 直接攜帶本地因子進入第二步。
第二步:攜帶驗證碼呼叫 /card/info
傳 with_sensitive=true,並附上第一步響應中標記為需要的所有欄位。驗證碼特性:
- 一次性 —— 驗證透過即消費;每次驗證都需重新發碼(信任視窗生效期間無需驗證)
- 限時 —— 10 分鐘內有效
- 防爆破 —— 連續輸錯會臨時鎖定驗證(返回
429)
驗證成功即開啟信任視窗 —— 視窗內的後續獲取完全無需驗證欄位。
路徑 B — 憑證免驗證特權(需審批)
預設關閉 — 需顯式審批後開通
免驗證特權對每一份 API 憑證預設關閉,且受平臺級總開關額外保護。如需開通,請聯絡你的客戶經理或提交工單。開通時需要:
- 說明業務用途(如無法引入人工二次驗證的無人值守自動化)
- 確認你的環境符合 PCI-DSS 對 PAN 儲存 / 展示的要求
- 提供你的
AppID(cp_xxxx...),授權將下發到正確的憑證
憑證開關與平臺開關均為 ON 時,with_sensitive=true 無需任何驗證碼欄位即可成功。免驗證特權憑證仍可攜帶驗證碼欄位 —— 會被忽略。
訪問前提
下列四個條件必須同時滿足,敏感欄位才會下發:
- 請求 — 呼叫方顯式傳
with_sensitive: true - 授權 — 有效的使用者二次驗證憑據(路徑 A)或憑證具備免驗證特權(路徑 B)
- 卡狀態 — 卡必須處於
status=2 (active);pending/failed/closing/closed/frozen一律拒絕 - 無管理員封禁 — 平臺管理員可按卡或按賬戶維度強制關閉敏感資訊獲取。封禁同時壓制兩條授權路徑(驗證碼正確、免驗證特權憑證均無效),返回
403 card_sensitive_access_banned;賬戶維度封禁同時攔截send_code。如認為封禁有誤請聯絡客服。
任一不滿足,響應為 403(驗證被鎖定時為 429)加對應的 message_key。欄位不會部分返回 —— 單次請求是"全有或全無"。
合規與處理建議
把返回的 PAN 視為高敏感 PCI 資料
- 不要日誌:完整
card_number/cvv不要寫到應用日誌或 APM - 不要落庫:PAN / CVV 不要持久化到自有資料庫或分析數倉
- 僅透過 TLS 透傳到你的前端,渲染後立即丟棄
- 非展示狀態下務必脫敏;優先使用
masked_card_no - 一旦懷疑憑證洩漏,立即在使用者後臺 Reset OpenAPI Secret
運維說明
- 免驗證特權(路徑 B)可被回收。一旦被回收,後續不帶驗證碼的
with_sensitive=true請求將在 ~5 分鐘內(快取傳播視窗)失效 —— 之後自動落入路徑 A,返回對應的*_requiredkey。 - 這些
403錯誤碼與鑑權中介軟體返回的401不同 ——403表示鑑權透過但二次驗證缺失/未透過,或卡狀態拒絕敏感訪問。 - 此介面絕不會透過 webhook payload 推送敏感欄位;敏感欄位只能透過本同步介面獲取。
典型錯誤
| HTTP | message_key | 說明 |
|---|---|---|
| 400 | openapi_invalid_card_id | 卡 ID 錯誤 |
| 400 | openapi_card_type_not_supported | 對非虛擬卡型別請求敏感欄位 |
| 401 | openapi_invalid_credentials | 鑑權失敗 |
| 403 | email_code_required / sms_code_required / pin_required / 2fa_code_required | 傳了 with_sensitive=true 但缺對應的二次驗證欄位(且憑證無免驗證特權)。先呼叫 send_code 再重試。 |
| 403 | invalid_email_code / invalid_sms_code / invalid_pin / invalid_2fa_code | 提交的二次驗證憑據錯誤或已過期 |
| 403 | email_not_verified / phone_not_verified / no_usable_auth_method | 賬戶對應驗證渠道不可用 —— 請在 Web 端完善安全設定 |
| 403 | card_sensitive_access_banned | 該卡或該賬戶的敏感資訊獲取已被管理員強制關閉,任何授權路徑均無效 |
| 403 | openapi_sensitive_card_only_active | 卡不在 status=2 (active),敏感欄位拒絕下發 |
| 404 | card_not_found | 卡不存在 / 不屬於當前賬號 |
| 429 | too_many_requests | 驗證連續失敗被臨時鎖定(或 PIN 鎖定);請遵循 Retry-After 頭 |
| 500 | openapi_get_card_info_failed | 服務端異常 |
| 500 | openapi_internal_error | 載入憑證上下文失敗 |
/card/sensitive/send_code 的錯誤:
| HTTP | message_key | 說明 |
|---|---|---|
| 401 | openapi_invalid_credentials | 鑑權失敗 |
| 403 | card_sensitive_access_banned | 該賬戶的敏感資訊獲取已被管理員強制關閉,不傳送驗證碼 |
| 429 | too_many_requests | 發碼冷卻中(同一賬戶 60 秒 1 次);請遵循 Retry-After 頭 |
| 500 | send_code_failed | 傳送失敗 —— 冷卻已釋放,可立即重試 |
推薦輪詢策略
import time
def wait_card_active(card_id, max_minutes=10):
"""非同步開卡後輪詢,直到 active 或失敗"""
deadline = time.time() + max_minutes * 60
backoff = 2
while time.time() < deadline:
info = call("/api/v1/openapi/card/info", {"card_id": card_id})
status = info['data']['status']
if status == 2:
return info['data'] # active
if status in (3, 5):
raise CardOpenFailed(info['data'])
time.sleep(backoff)
backoff = min(backoff * 2, 30) # 指數退避,最多 30 秒
raise TimeoutError(f"Card {card_id} not active in {max_minutes} min")
def fetch_pan_for_display(card_id, email_code=None, sms_code=None, pin=None, two_fa_code=None):
"""一次性拿敏感欄位渲染前端,絕不落庫。
路徑 A(預設):先 POST /openapi/card/sensitive/send_code,向賬戶所有人
收集所需二次驗證憑據後傳入本函式。
路徑 B(免驗證特權憑證):不傳任何驗證碼直接呼叫。
"""
body = {"card_id": card_id, "with_sensitive": True}
for k, v in {"email_code": email_code, "sms_code": sms_code,
"pin": pin, "two_fa_code": two_fa_code}.items():
if v:
body[k] = v
resp = call("/api/v1/openapi/card/info", body)
if resp.get('code') == 403:
key = resp.get('message_key')
if key and key.endswith('_required'):
raise PermissionError(f"缺少二次驗證憑據:{key} —— "
"請先呼叫 /openapi/card/sensitive/send_code")
if key and key.startswith('invalid_'):
raise PermissionError(f"二次驗證未透過:{key}")
if key == 'openapi_sensitive_card_only_active':
raise RuntimeError("卡未啟用,敏感欄位暫不可獲取")
if resp.get('code') == 429:
raise RuntimeError("驗證已鎖定,請按 Retry-After 時間後重試")
if resp.get('code') != 200:
raise RuntimeError(f"獲取卡資訊失敗:{resp.get('message_key')}")
return resp['data'] # 含 card_number / cvv / expiry_date / first_name / last_name
def request_sensitive_code():
"""路徑 A 第一步:觸發郵箱/簡訊驗證碼,並獲知需要哪些驗證因子"""
resp = call("/api/v1/openapi/card/sensitive/send_code", {})
if resp.get('code') != 200:
raise RuntimeError(f"發碼失敗:{resp.get('message_key')}")
return resp['data'] # require_email / require_sms / require_pin / require_2fa / email_sent / sms_sent優先用 webhook
能用 webhook 就用 webhook。card/info 輪詢是 fallback —— 僅用於 webhook 接收器臨時不可用、或對賬場景。