Skip to content

申请虚拟卡

异步开卡接口。请求成功只代表"已入队",开卡完成结果通过 card.opened / card.open_failed webhook 推送,或通过 card/info 轮询。

端点

MethodPOST
Path/api/v1/openapi/card/apply
鉴权HMAC
幂等键必填 Idempotency-Key

请求字段

不要传 card_type

本接口不需要card_type。服务器从 header_id 自动推导,并校验是否在虚拟卡范围内。

字段类型必填说明
header_idstringhdr_<id> 格式(列卡头
package_idstringpkg_<id> 格式(列卡配置
first_namestring⚠️持卡人名(看 header.require_phone/email)
last_namestring⚠️持卡人姓
phone_codestring⚠️国家区号(如 86 / 1
phonestring⚠️手机号(不含区号)
emailstring⚠️邮箱
use_bound_emailbool默认 false,使用请求中传的 email。传 true 时回退到账号绑定的邮箱。
use_bound_phonebool默认 false,使用请求中传的 phone + phone_code。传 true 时回退到账号绑定的手机号。
first_deposit_amountstring自定义首充总额,非负整数(支付/充值资产单位,如 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 → 至少首充 50010 免费、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=truerequire_phone=false):

约束规则失败 message_key
邮箱始终必填 —— 传 email(账号已绑定邮箱时也可 use_bound_email=truebank_card_email_required
手机号不收集 —— phone_code / phone / use_bound_phone 会被忽略,提供方协议没有手机号字段
持卡人姓名first_namelast_name 均必填且不能为空白(纯空格会被拒绝);在冻结任何资金之前校验virtual_a_required_fields_missing
生日与账单地址不要传 —— 服务端自动生成virtual_a_required_fields_missing(仅当服务端自动填充失败时返回)

其余能力(自定义首充、幂等、card.opened / card.open_failed 异步结果、充值、冻结 / 解冻、销卡、交易明细、敏感卡信息)与其他虚拟卡类型完全一致。

请求示例

json
{
  "header_id": "hdr_5",
  "package_id": "pkg_12",
  "first_name": "John",
  "last_name": "Doe"
}

请求头必须含:

http
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000

响应字段

字段类型说明
card_idstringcard_<id> 格式(用于后续查询)
statusint1=pending 2=active 3=failed 4=closing 5=closed 6=frozen
status_descstring英文描述
created_atstringRFC3339 时间

响应示例(成功入队)

json
{
  "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) 才能用。

后续流程

diagram

典型错误

HTTPmessage_key说明
400insufficient_balance余额不足(开卡费 + 首充)
400invalid_first_deposit_amountfirst_deposit_amount 不是纯非负整数(小数 / 符号 / 科学计数法 / 位数过多)
400first_recharge_below_basefirst_deposit_amount 低于配置底额
400first_recharge_below_minfirst_deposit_amount(或留空的默认底额)低于套餐首充最低金额 min_first_deposit
400first_recharge_exceeds_max超额超过 max_recharge_amount
400first_recharge_limit_exceeded超额超过账号充值限额
400first_recharge_asset_mismatch开卡费资产 ≠ 充值资产;该配置不支持自定义超额
400first_recharge_excess_too_small扣费 + 取整后到卡为 0,请加大金额
400kyc_required请先完成实名认证
400kyc_not_approved账号 KYC 未通过
400bank_card_email_required卡类型需要邮箱;请传 email 或显式设 use_bound_email=true(仅当账号已绑定时)
400bank_card_phone_required卡类型需要手机号;请传 phone_code+phone 或显式设 use_bound_phone=true(仅当账号已绑定时)
400openapi_card_type_not_supportedheader 对应的卡类型不在虚拟卡范围
400account_manager_bind_required客户经理管控卡头,而账号未绑定客户经理 —— header_id 只能用 /card_headers/list 返回的值
400account_manager_unavailable账号绑定的客户经理已失效;请联系平台客服
400account_manager_open_disabled客户经理未对该账号放开此卡头 / 套餐;请只使用列表接口返回的卡头与套餐(列表已不含未放开 / 被关闭的)
400virtual_g_name_invalid(G 卡)持卡人姓名不满足 ASCII 字母 / 单空格 / ≤40 字符规则
400virtual_g_required_fields_missing(G 卡)服务端无法组装完整的开卡请求(罕见,通常是配置或地址池缺失)
400virtual_a_required_fields_missing(A 卡)first_name / last_name 缺失或为空白,或服务端无法组装完整的开卡请求(卡头 / 地址池问题)
400openapi_invalid_header_idheader_id 缺前缀或不存在
400openapi_invalid_package_idpackage_id 缺前缀 / 不属于该 header
400openapi_idempotency_key_required缺幂等键
400openapi_idempotency_key_too_long幂等键超 128 字符
400openapi_idempotency_key_invalid_chars幂等键含非法字符
401openapi_invalid_credentials鉴权失败
409openapi_idempotency_key_conflict同 key 不同 body
500openapi_apply_card_failed服务端异常

注意事项

  • 开卡费 + 首充会在请求时一并扣款 / 冻结。传自定义 first_deposit_amount 时,超额手续费也会一并冻结(见自定义首充)。失败的开卡会自动退还
  • 异步任务最长可能跑几分钟。不要因为客户端 30 秒内没收到 webhook 就重试 apply(重试需带相同 Idempotency-Key
  • card_id 一旦返回就永久指向这次请求的卡(即使最终失败)

采用 MIT 等价条款发布