什么是卡 Tokenization,tokenId 是什么?
Tokenization 用一个可复用的 tokenId 替代用户的原始卡信息。
在你这边安全地存储 tokenId;发起用户支付或
后端扣款时,用它代替卡信息。支持的
tokenType 是 CARD。一旦持有 token,你可以把它用于一次性
支付,以及你自己管理的订阅式扣款,按计划
或不按计划。
我如何绑卡并获取 token?
标准流程让原始卡数据不经过你的服务器:- 调用
/api/v1/tokenization/generate,带唯一的tokenRequestId(幂等键)、你的merchantUserId、tokenType: "CARD"和一个notifyUrl。响应返回一个tokenSessionId。 - 在你的前端,初始化 Waffo JavaScript SDK
(
@waffo/payment-sdk)并调用tokenizationSubmit(tokenSessionId, tokenData)。 SDK 在把卡数据发送给 Waffo 之前会加密。卡的expiry接受MM/YYYY(输入时也接受MM/YY),但查询和通知 始终返回MM/YYYY。 - Waffo 向你的
notifyUrl推送一条TOKENIZATION_NOTIFICATIONWebhook 通知, 携带tokenId、tokenStatus和脱敏的卡信息 (maskedCardInfo、cardBin、cardScheme、cardType等)。 - 随时使用
/api/v1/tokenization/inquiry查询 token 详情——按tokenId(返回一条)或按merchantUserId(可能返回多条)。
/api/v1/tokenization/generate 的 tokenData 对象中直接传
pan、name、expiry 和 cvv;在这种
情况下 tokenId 会在 API 响应中同步返回。
token 的生命周期是什么样的?
tokenStatus 有四个值:
每当 token 状态变化或 token 数据更新时,你会收到一条
TOKENIZATION_NOTIFICATION。处理后回复 success;如果 Waffo
收到 failed 或 unknown,Waffo 会在接下来一天内按退避
计划重试通知;计划参见
Webhook 通知重试。
tokenSessionId 和 tokenId 各有效多久?
两个不同的时钟,其中只有一个是固定期限:
所以即将过期的卡产生的 token 会在其有效期月份结束时停止可用,
即使它是今天绑定的;而有效期很远的卡产生的 token
会跑完整个平台有效期。
两个实际后果:
- token 自身的过期时间戳不在
/api/v1/tokenization/inquiry的响应中。你拿回的是tokenStatus加上 token 数据中的卡expiry——如果你想在续订失败前提醒用户, 这足以让你自己推导出上述规则。 tokenStatus由周期性扫描而不是在 token 失效的那一秒 转为EXPIRED,所以 token 可能在略过其计算出的过期时间后 仍显示为可用。按过期规则安排;把状态和 订单创建错误码当作最终定论。
同一个用户把同一张卡绑两次,我会得到两个 tokenId 吗?
不会。卡 token 在重新绑定时会去重:当以下全部
与一个未被移除的 token 匹配时,你会拿回已有的
tokenId——你的 merchantId(加上 subMerchantId,如果你传了)、相同的
merchantUserId、相同的卡、相同的持卡人姓名,以及相同的
tokenType。
值得围绕它设计的:
- 你不需要自己对已保存的卡列表去重;但要以
tokenId作为你自己 存储的键,因为第二次绑定返回的是第一个 token 而不是 新的。 merchantUserId是键的一部分,所以两个不同的merchantUserId绑定同一张实体卡会得到两个不同的tokenId。没有 跨用户的卡标识暴露给你用于关联。- 同一张卡上不同的持卡人姓名被视为不同的 凭据,会产生新的 token。
/api/v1/tokenization/remove之后,该 token 从查询中排除且 永不复用——再次绑定该卡会发放一个新的tokenId。
CIT 和 MIT 有什么区别,各在什么时候用?
两条 MIT 特有的规则需要注意:
- CIT 可以用刚创建的 token——首次支付就是验证卡的过程, 所以你不需要等待”已验证”状态。
- MIT 需要一个已经可用的存储凭据。 如果 token 从未
完成过成功支付(或其存储凭据已不再有效),
MIT 扣款会以
A0045被拒绝。实践中:首次扣款用 CIT,之后的扣款用 MIT。 threeDsDecision字段(3DS_FORCE/NO_3DS)只对 卡 / ApplePay / GooglePay 的 CIT 支付有效;对 MIT 会被忽略。
首次支付和后续扣款如何衔接?
- 绑卡并获取
tokenId(见上文)。 - 等待 token 到达可扣款状态——关注
TOKENIZATION_NOTIFICATION或轮询/api/v1/tokenization/inquiry。 - 对于用户参与的支付,用
userPaymentAccessToken创建 CIT 订单。 - 对于之后你自己发起的扣款,用
merchantInitiatedMode创建 MIT 订单 (scheduled用于你管理的固定节奏,unscheduled用于事件驱动的扣款)。
为什么 /order/create 返回了 AUTHORIZATION_REQUIRED 而没有扣我已保存的 token?
因为针对已保存 token 的 CIT 不是服务端扣款。当你发送
userPaymentAccessToken 而不带 merchantInitiatedMode 时,Waffo 创建
订单并以 orderStatus: AUTHORIZATION_REQUIRED 和填充了的
orderAction 返回。此时还没有资金流动;持卡人仍有一步要完成。
这是一键 CIT 的正常结果,不是错误。
该怎么处理:
- 解析
orderAction并读取actionType——WEB表示把用户重定向到webUrl,DEEPLINK表示重定向到deeplinkUrl。 - 在 Waffo 的托管收银台上,已保存的卡已被选中并以脱敏形式显示; CVV 通常是用户唯一需要填写的字段。之后可能跟随 3DS 挑战。
- 把
AUTHORIZATION_REQUIRED视为非终态,等待支付 通知(或轮询订单查询)获取结果。
/order/create 是否返回成功”分支——要基于
orderStatus 分支,并且只要 orderAction 存在,就假设有一个用户步骤
尚未完成。
如果你需要在没有顾客在场的情况下扣款,那正是
merchantInitiatedMode 的用途;参见上文的 CIT/MIT 章节。
token 过期了或支付被拒绝了,我该怎么办?
- 订单创建时的
A0037(用户支付 token 无效 / 已过期)或A0023(因 token 过期无法创建订单):存储的 token 已 不可用。调用/api/v1/tokenization/remove解绑它(按tokenId,或按merchantUserId解绑用户的所有 token),然后让用户 绑定新卡。 - 绑定期间的
B1002(卡已过期)或B1001(卡信息无效): 问题在卡本身——用户必须提供有效的卡。 B1005(token 会话 ID 已过期):generate返回的tokenSessionId已不再有效——再次调用/api/v1/tokenization/generate。B1003(超出绑卡数量限制)和B1004(当前商户合同不支持 该卡组织):这些取决于你签约的 配置——请查看商户 Portal 或联系集成支持。
我在支付过程中绑卡。Tokenization 结果通知到哪里?
在商户 Portal(Settings → Integration)中针对你的 API key 配置一个专用的 Tokenization 通知 URL。Waffo 会把它快照到用该 key 创建的每个 订单上,绑卡结果会通知到那里。- 它是一个独立通道:你的支付结果
notifyUrl照常继续接收 支付通知。 - 它不是订单创建参数——你不能按订单覆盖它。
- 如果未配置,Tokenization 通知行为不变。
我能设置用户的默认卡吗?
可以,通过一个专用端点而不是在订单创建时。调用/api/v1/saved-payment-methods/config,带上 token 和你想要的操作:
默认卡用于对顾客在收银台看到的已保存卡列表排序——
过期的卡被推到列表末尾。
用户的卡被银行换新了。存储的 token 还能用吗?
不要假设能用。Waffo 目前不会自行根据发卡行侧的更新 刷新存储的卡,所以请为重新绑定做好计划:- 关注报告 token 不再可用的 Tokenization 和支付通知, 并提示用户再次绑卡。
- 如果卡在钱包或发卡行侧被解绑,存储的凭据 会变为不可用且无法重新激活。
- 过期的 token 不能再扣款,但你仍可以调用
/api/v1/tokenization/remove解绑它,然后再向用户索要新的 卡凭据。