Skip to content

查询卡片信息

默认返回卡的状态、余额、脱敏卡号

当请求体传 with_sensitive=true 时,同一接口会在响应里额外返回 完整 PAN / CVV / 到期日 / 持卡人姓名,授权方式二选一:

  1. 用户二次验证(默认可用,自助) —— 与 Web 端查看 CVV 完全一致:按账户配置的交易认证方式提交 email_code / pin / two_fa_code / sms_code。验证码通过 /openapi/card/sensitive/send_code 获取,无需管理员审批。
  2. 凭证免验证特权(需审批) —— 面向全自动 server-to-server 场景,凭证可被授予免二次验证特权。默认关闭 —— 详见下文 敏感卡片信息

常用于:

  • 异步开卡后轮询,直到 status=2 (active)
  • 业务侧定期同步余额
  • 排查 webhook 是否漏投递
  • 在自有收银台 / 钱包 UI 中展示 PAN / CVV(两条授权路径均可)

端点

MethodPOST
Path/api/v1/openapi/card/info
鉴权HMAC
幂等键不需要

请求字段

字段类型必填说明
card_idstringcard_<id> 格式
with_sensitivebool可选true 表示请求完整 PAN / CVV / 到期日 / 持卡人姓名。默认 false。需卡片为 active,且通过两条授权路径之一。
email_codestring条件必填邮箱验证码(账户交易认证方式含邮箱时必填)。通过 /openapi/card/sensitive/send_code 发送。免验证特权凭证无需传。
sms_codestring条件必填短信验证码(认证方式含短信时必填)。
pinstring条件必填交易 PIN 码(认证方式含 PIN 时必填;用户在 Web 端设置)。
two_fa_codestring条件必填2FA / Google Authenticator 动态码(认证方式含 2FA 时必填)。

请求示例 — 默认(仅状态与余额)

json
{ "card_id": "card_12345" }

请求示例 — 请求敏感字段(用户二次验证路径)

json
{
  "card_id": "card_12345",
  "with_sensitive": true,
  "email_code": "482913"
}

请求示例 — 请求敏感字段(免验证特权凭证)

json
{
  "card_id": "card_12345",
  "with_sensitive": true
}

响应字段

始终返回

字段类型说明
card_idstring回显
card_typestring全小写:virtual_l / virtual_p / virtual_v / virtual_r / virtual_g / virtual_a
card_brandstring卡组织
currencystring卡内币种
statusint1=pending 2=active 3=failed 4=closing 5=closed 6=frozen
status_descstring英文描述
masked_card_nostring脱敏卡号(前 6 后 4),如 424242******1234
last_fourstring卡号末 4 位
balancestring (decimal)卡内可用余额
frozen_balancestring (decimal)卡内冻结余额
activated_atstring nullable激活时间(成功后才有)
created_atstring创建时间

仅当 with_sensitive=true、授权通过、且 status=2 时返回

字段类型说明
card_numberstring完整 PAN(无空格 / 无横线)
cvvstring卡安全码(Visa / Mastercard 为 3 位)
expiry_datestring到期日,格式 MM/YY
first_namestring持卡人名(开卡时提供)
last_namestring持卡人姓

任一前提不满足时,这些字段会整体省略(不存在、不为 null 也不为空串)。

响应示例 — 默认

json
{
  "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"
  }
}

响应示例 — 含敏感字段

json
{
  "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)

含义说明
1pending开卡中
2active已激活,可用
3failed开卡失败(终态)
4closing销卡中
5closed已销卡(终态)
6frozen已冻结(用户 / 风控)

敏感卡片信息

敏感字段(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 — 用户二次验证(默认)

第一步:发送验证码

MethodPOST
Path/api/v1/openapi/card/sensitive/send_code
鉴权HMAC
幂等键不需要
请求体{}(空 JSON 对象)
冷却同一账户 60 秒内限 1 次 → 超频返回 429 + Retry-After

验证用途由服务端固定(get_card_sensitive)—— 本接口不能用于触发转账、提现等其他操作的验证码,其他用途的验证码在这里也不被接受。

响应 data

字段类型说明
require_emailbool第二步需提交 email_code
require_smsbool第二步需提交 sms_code
require_pinbool第二步需提交 pin(不发码 —— PIN 由用户本人持有)
require_2fabool第二步需提交 two_fa_code(不发码 —— 由认证器 App 生成)
email_sentbool6 位验证码已发送至账户绑定邮箱
sms_sentbool6 位验证码已发送至账户绑定手机
grace_activebool信任窗口生效中:不发码、不消耗冷却 —— 跳过第二步的验证字段直接调 /card/info

如果账户只使用 PIN 和/或 2FA,则不会发送任何验证码(email_sent=false, sms_sent=false)且不消耗冷却窗口 —— 直接携带本地因子进入第二步。

第二步:携带验证码调用 /card/info

with_sensitive=true,并附上第一步响应中标记为需要的所有字段。验证码特性:

  • 一次性 —— 验证通过即消费;每次验证都需重新发码(信任窗口生效期间无需验证)
  • 限时 —— 10 分钟内有效
  • 防爆破 —— 连续输错会临时锁定验证(返回 429

验证成功即开启信任窗口 —— 窗口内的后续获取完全无需验证字段。

路径 B — 凭证免验证特权(需审批)

默认关闭 — 需显式审批后开通

免验证特权对每一份 API 凭证默认关闭,且受平台级总开关额外保护。如需开通,请联系你的客户经理或提交工单。开通时需要:

  • 说明业务用途(如无法引入人工二次验证的无人值守自动化)
  • 确认你的环境符合 PCI-DSS 对 PAN 存储 / 展示的要求
  • 提供你的 AppIDcp_xxxx...),授权将下发到正确的凭证

凭证开关与平台开关均为 ON 时,with_sensitive=true 无需任何验证码字段即可成功。免验证特权凭证仍可携带验证码字段 —— 会被忽略。

访问前提

下列四个条件必须同时满足,敏感字段才会下发:

  1. 请求 — 调用方显式传 with_sensitive: true
  2. 授权 — 有效的用户二次验证凭据(路径 A)凭证具备免验证特权(路径 B)
  3. 卡状态 — 卡必须处于 status=2 (active)pending / failed / closing / closed / frozen 一律拒绝
  4. 无管理员封禁 — 平台管理员可按卡或按账户维度强制关闭敏感信息获取。封禁同时压制两条授权路径(验证码正确、免验证特权凭证均无效),返回 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,返回对应的 *_required key。
  • 这些 403 错误码与鉴权中间件返回的 401 不同 —— 403 表示鉴权通过但二次验证缺失/未通过,或卡状态拒绝敏感访问。
  • 此接口绝会通过 webhook payload 推送敏感字段;敏感字段只能通过本同步接口获取。

典型错误

HTTPmessage_key说明
400openapi_invalid_card_id卡 ID 错误
400openapi_card_type_not_supported对非虚拟卡类型请求敏感字段
401openapi_invalid_credentials鉴权失败
403email_code_required / sms_code_required / pin_required / 2fa_code_required传了 with_sensitive=true 但缺对应的二次验证字段(且凭证无免验证特权)。先调用 send_code 再重试。
403invalid_email_code / invalid_sms_code / invalid_pin / invalid_2fa_code提交的二次验证凭据错误或已过期
403email_not_verified / phone_not_verified / no_usable_auth_method账户对应验证渠道不可用 —— 请在 Web 端完善安全设置
403card_sensitive_access_banned该卡或该账户的敏感信息获取已被管理员强制关闭,任何授权路径均无效
403openapi_sensitive_card_only_active卡不在 status=2 (active),敏感字段拒绝下发
404card_not_found卡不存在 / 不属于当前账号
429too_many_requests验证连续失败被临时锁定(或 PIN 锁定);请遵循 Retry-After
500openapi_get_card_info_failed服务端异常
500openapi_internal_error加载凭证上下文失败

/card/sensitive/send_code 的错误:

HTTPmessage_key说明
401openapi_invalid_credentials鉴权失败
403card_sensitive_access_banned该账户的敏感信息获取已被管理员强制关闭,不发送验证码
429too_many_requests发码冷却中(同一账户 60 秒 1 次);请遵循 Retry-After
500send_code_failed发送失败 —— 冷却已释放,可立即重试

推荐轮询策略

python
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 接收器临时不可用、或对账场景。

采用 MIT 等价条款发布