绑卡失败率优化与异常结果页 — 迭代需求

目标:在绑卡验证金额已调整到 Monnify 可受理金额的基础上,补齐失败原因映射、异常结果页英文文案、reference 复制能力,以及“客户余额不足时自动打款后引导重新绑卡”的业务方案。

1. 背景

生产绑卡失败目前重点关注两类问题:

  1. 绑卡失败提示不清晰。当前结果页只展示 Failed 和通用重试文案,客户无法判断应该充值、换卡、重新输入 OTP、稍后查询,还是联系客服。
  2. 绑卡验证金额已调整为 Monnify 可受理金额后,客户真实卡余额不足会成为新的失败来源。为减少小额余额不足导致的绑卡流失,需要支持按业务线配置自动打款,再提醒客户重新发起绑卡。

说明:绑卡验证金额调整已完成,本需求不再定义相关配置;本需求也不实现指标、监控告警和埋点。

2. 本期范围

范围是否纳入说明
失败原因映射将 provider / 系统错误转换为稳定 failReason
异常结果页英文文案使用统一结果页样式和一张文案表覆盖各异常场景
reference 页面复制页面展示 Reference: {reference},支持复制
余额不足自动打款仅按业务线配置开启,支持 Paystack 或 Monnify 出款
自动打款后短信提醒打款成功后提醒客户重新发起绑卡
指标、监控告警、埋点本需求不实现
验证金额退款/补偿不承诺绑卡验证金退款或补偿

3. 产品原则

  • AMOUNT_BELOW_MINIMUM 不等于客户余额不足。该错误只作为历史和兼容映射保留,不作为本期金额配置需求。
  • 只有 provider 明确返回余额不足、insufficient funds 等信息时,才展示余额不足文案或触发自动打款判断。
  • 客户看到的提示必须包含下一步动作:重试、换卡、重新发起、稍后查询或联系客服。
  • 绑卡成功只代表还款授权或 card token 建立成功,不代表还款成功。
  • 自动打款只用于帮助客户完成绑卡验证,不等同于验证金额退款/补偿。
  • 前端不得展示完整银行卡号、CVV、PIN、OTP、token、authData 等敏感信息。

4. 失败原因映射建议

后端应保存 provider 原始错误码和 message,但 App 只使用标准 failReason 决定页面标题、文案和按钮。以下表格基于 Monnify 官方 Error Codes 页面可检索到的错误条目整理,主要覆盖绑卡、卡 token、自动打款、账户校验和常见配置错误。错误码全集仍需从当前生产库继续拉取更完整 provider code / message 后补齐。

官方来源:Monnify API Error Messages。该页面按 Authentication、Transaction & Payment、Recurring Payment、Direct Debit、Disbursements / Transfer、Account Validation 等分类维护错误信息。

4.1 Monnify 官方错误映射表

Monnify 官方错误 / message官方含义摘要我方 failReasonTitleMessage按钮建议处理说明
Invalid Card Number卡号字段错误INVALID_CARD_INFOInvalid Card DetailsSome card details are incorrect. Please check the card number, expiry date, CVV, and PIN, then try again.Retry / Use Another Card返回卡信息输入页;不记录完整卡号
Auth Data error常见于有效期年份或其他卡字段错误INVALID_CARD_INFOInvalid Card DetailsSome card details are incorrect. Please check the card number, expiry date, CVV, and PIN, then try again.Retry / Use Another Card优先提示检查有效期和卡信息
Merchant has not been configured for bin卡 PAN 前六位不在商户支持 BIN 内CARD_NOT_SUPPORTEDCard Not SupportedThis card cannot be used for automatic repayment. Please bind another card.Use Another Card客户侧换卡;支付侧确认商户 BIN 配置
OTP authorize 接口返回 responseCode=99 / Transaction Failed发卡行限制该卡完成 OTP 授权或线上验证CARD_NOT_SUPPORTEDCard Not SupportedYour bank does not allow this card for verification. Please use another debit card.Use Another Card不引导反复输 OTP;不触发自动打款;优先换卡
No Card Payment found for this transaction.当前交易没有先发起 card payment,却调用 OTP / 3DS 授权PROVIDER_FLOW_ERRORFailedCard verification could not be completed. Please restart card binding.Restart Binding后端流程错误或交易状态不一致;重新创建绑卡交易
Card token has expired.card token 已过期CARD_TOKEN_EXPIREDCard ExpiredThis saved card can no longer be used. Please bind your card again.Retry Binding / Use Another Card多用于已绑卡后续扣款;需重新绑卡
Invalid card tokentoken 不存在或无效CARD_TOKEN_INVALIDCard Not AvailableThis saved card is no longer available. Please bind your card again.Retry Binding / Use Another Card不继续使用旧 token 发起扣款
Invalid amount交易金额无效PROVIDER_AMOUNT_INVALIDFailedCard verification could not be started. Please try again later.Try Again Later / Contact Support系统参数或通道规则问题,不提示客户充值
Amount must be greater than 20.invoice 金额必须至少 20 NairaPROVIDER_AMOUNT_INVALIDFailedCard verification could not be started. Please try again later.Try Again Later / Contact Support官方 invoice 类错误;绑卡场景仅作金额规则参考
Could not find specified contract / Unknown Contract Code provided. / Invalid contract code supplied.contractCode 无效或不属于商户PROVIDER_CONFIG_ERRORFailedCard binding is temporarily unavailable. Please try again later.Try Again Later / Contact Support商户配置问题;客户不可自助解决
Duplicate payment reference / Supplied reference already exists!payment reference 已被使用DUPLICATE_REFERENCEProcessingCard verification is already in progress. Please check the result before trying again.Check Status / Back优先查询原交易,不立即重复创建
Invalid transaction reference suppliedtransaction reference 无效或无法识别PROVIDER_REFERENCE_INVALIDFailedCard verification could not be found. Please restart card binding.Restart Binding / Contact Support查询不到原交易时,按业务状态决定是否重建
could not find specified bank银行 code 无法识别或不支持INVALID_BANK_CODEBank Not SupportedThe selected bank is not available. Please choose another bank or use another card.Use Another Card / Contact Support多用于账户校验 / 出款,不展示内部 bankCode
Unable to validate account information账户号和银行 code 校验失败INVALID_ACCOUNT_INFOInvalid Account DetailsWe could not verify your account details. Please check the account number and bank, then try again.Retry / Contact Support自动打款前账户校验失败时使用
Account Validation 99账户信息无效INVALID_ACCOUNT_INFOInvalid Account DetailsWe could not verify your account details. Please check the account number and bank, then try again.Retry / Contact Support官方账户校验失败码
Could not find disbursement transaction for given reference出款 reference 错误或查不到交易PAYOUT_REFERENCE_NOT_FOUNDProcessingFunding status could not be confirmed yet. Please check again later.Check Status / Contact Support先补偿查询和人工排查,不重复打款
Access token expiredMonnify access token 超过 1 小时时效PROVIDER_AUTH_ERRORFailedCard binding is temporarily unavailable. Please try again later.Try Again Later / Contact Support后端应自动刷新 token 后重试一次
Cannot convert access token to jsonaccess token 格式异常PROVIDER_AUTH_ERRORFailedCard binding is temporarily unavailable. Please try again later.Try Again Later / Contact Support认证链路异常,客户不可自助解决

4.2 生产补充映射

以下错误不一定出现在官方 Error Codes 页面当前可检索结果中,但已在生产排查或 provider message 中出现,应继续保留映射,并用生产库错误码全集校准。

provider / 系统信号我方 failReason客户文案场景处理说明
AMOUNT_BELOW_MINIMUMVERIFY_AMOUNT_TOO_LOW系统暂不可用兼容历史错误;不提示客户充值
INSUFFICIENT_FUNDS / INSUFFICIENT_BALANCE / message 包含 insufficient fundsINSUFFICIENT_BALANCE卡余额不足可进入自动打款判断
OTP invalid / OTP expiredOTP_FAILEDOTP 错误或过期重新发起绑卡并输入最新 OTP
Too Many AttemptsOTP_TOO_MANY_ATTEMPTSOTP 尝试次数过多引导稍后重试或换卡
Monnify OTP authorize 返回 responseCode=99 / Transaction Failed,且 Monnify 确认为 issuer restriction / issuing bank restrictionCARD_NOT_SUPPORTED发卡行限制该卡引导客户使用其他 debit card;不再提示继续输 OTP
3DS cancelled / bank authorization failedBANK_AUTH_FAILED银行验证失败引导重试并完成银行验证
bank declined / issuer declinedBANK_DECLINED银行拒绝引导联系银行或换卡
bank registered mobile requiredBANK_PHONE_REQUIRED需要银行预留手机号验证仅在 provider 明确返回时使用
timeout / query unknown / pending timeoutPROVIDER_TIMEOUT第三方处理中或超时优先查询状态,不立即重复绑卡
App 网络失败NETWORK_ERROR网络异常重试当前请求
未分类 5xx / 异常SYSTEM_ERROR系统异常稍后重试或联系客服

5. 后端返回模型

后端返回给 App 的失败结果应包含:

字段类型是否返回 App说明
bindStatusstringFAILED / TIMEOUT / PROCESSING
failReasonstring我方标准失败原因枚举
messagestring可选;如为空,App 使用本地文案表
primaryActionstring主按钮动作
secondaryActionstring次按钮动作,可为空
retryableboolean是否允许立即重试
referencestring可展示和复制的绑卡 reference 或订单号
autoFundingSuccessboolean条件返回自动打款是否成功;成功时为 true

—— 绑卡信息怎么回显,@技术确定方案
providerCodestring仅后端、客服后台、日志使用
providerMessagestring仅后端、客服后台、日志使用

规则:

  1. title 不由后端返回,App 根据 failReason 使用本地标题文案。
  2. providerCodeproviderMessage 不直接透传到客户前端页面。
  3. reference 在页面内展示为 Reference: {reference},右侧提供 Copy 操作。
  4. 复制成功后提示 Copied;复制失败时提示 Copy failed. Please try again.
  5. Monnify OTP authorize 场景中,responseCode=99 / Transaction Failed 本身是通用失败信号,不得无条件映射为发卡行限制;只有同时满足接口为 /api/v1/merchant/cards/otp/authorize、对应 transaction reference、Monnify 人工或规则反馈确认为 issuer restriction / issuing bank restriction 时,才映射为 CARD_NOT_SUPPORTED
  6. 发卡行限制类 CARD_NOT_SUPPORTED 不触发自动打款,不引导继续输入 OTP,不优先展示 Retry;前端主操作为 Use Another Card

6. UI 基准样式

参考现有失败结果页,所有异常结果页保持同一结构:

绑卡异常结果页 UI 设计图

页面展示顺序按用户交互路径排列:用户发起绑卡时先判断是否存在同支付通道 active 订单;如果仍在冷却期,进入倒计时页;倒计时结束后进入可查询的处理中页;再根据查询结果进入自动打款处理中、打款成功后的回显绑卡页、可换卡的发卡行限制 / 银行拒绝页,或系统异常页。设计图中的倒计时态以 15s 作为起始示例,即按钮显示 Check Status (00:15)

6.1 页面主交互流程图

主流程只描述用户从发起绑卡到结果页的路径;active 订单内部判断见 6.2。

flowchart TD
  A[用户点击绑卡 / Retry / Use Another Card] --> B[前端提交绑卡发起请求]
  B --> C[后端执行 active 订单检查]
  C -- 返回可继续创建 --> D[创建新绑卡订单并生成新 reference]
  C -- 返回原订单处理中 --> E[展示 Processing 或倒计时页]
  D --> F[进入 Monnify / Paystack 绑卡验证]
  F --> G{绑卡验证结果}

  G -- 成功 --> H[绑卡成功,回到业务成功流程]
  G -- 处理中或超时未确认 --> E
  E --> I[用户点击 Check Status]
  I --> J[查询原 reference 状态]
  J --> G

  G -- 余额不足且自动打款已受理 --> K[展示 Funding Card 页面]
  K --> L[用户点击 Check Status]
  L --> M{自动打款状态}
  M -- 处理中 --> K
  M -- 成功 --> N[发送短信并自动跳转至绑卡页]
  N --> T[回显刚才的卡信息;CVV 显示为 ***]
  T --> U[用户确认或修改卡信息]
  U --> V[提交新的绑卡申请并生成新 reference]
  M -- 失败 --> O[展示 Insufficient Balance 页面]

  G -- 发卡行限制或卡不支持 --> P[展示 Card Not Supported 页面]
  G -- 银行拒绝 --> Q[展示 Bank Declined 页面]
  G -- 系统临时异常 --> R[展示 Failed / Try Again Later 页面]

  P --> S[用户点击 Use Another Card]
  Q --> S
  O --> S
  S --> B

6.2 Active 订单处理流程图

active 订单检查是创建前主拦截,结果页初始化和 Check Status 只做状态刷新,不创建新绑卡订单。

flowchart TD
  A[触发点] --> B{触发来源}
  B -- 发起绑卡 / Retry / Use Another Card --> C[创建前 active 订单检查]
  B -- 进入结果页 --> D[按当前 reference 刷新状态]
  B -- 点击 Check Status --> E[查询原 reference 状态]

  C --> F{是否存在同支付通道 active 订单?}
  F -- 否 --> G[允许创建新绑卡订单]
  F -- 是 --> H[查询原 active 订单状态]

  D --> H
  E --> H

  H --> I{原订单状态}
  I -- 已成功 --> J[更新原订单成功并返回成功状态]
  I -- 已失败 / 已取消 --> K[按失败原因返回标准 failReason]
  I -- 仍处理中且在冷却期 --> L[返回原 reference 和 countdownSeconds]
  I -- 仍处理中但无冷却期 --> M[返回 Processing 和 Check Status]
  I -- 超过 TTL 且无明确终态 --> N[标记超时或待补偿查询]

  L --> O[前端展示倒计时页]
  O --> P{倒计时结束?}
  P -- 否 --> O
  P -- 是 --> M
  M --> Q[前端展示 Processing 页面]

  N --> R{是否允许超时后重新路由?}
  R -- 否 --> Q
  R -- 是 --> S[创建新订单、新 reference、新 provider request]
层级区域样式要求
1页面标题顶部居中:Binding Bank Card;左上角保留返回箭头
2状态图标失败用红色叉号;处理中 / 自动打款中用黄色或橙色时钟 / loading 图标
3主标题居中,深色加粗;由 App 根据 failReason 本地映射,只表达当前状态
4关键信息条位于主标题下方,只允许一行加粗短句,例如 Need at least ₦20Do not retry yetTry another card;不再做双行 chip,避免信息抢层级
5说明文案居中灰色,最多 2-3 行;解释原因和下一步,不重复关键信息条
6操作区主按钮使用绿色圆角按钮;次按钮使用文本按钮,仅在需要联系客服、换卡、返回或稍后查询时展示
7倒计时仅在处理中 / active 订单冷却期内展示;主按钮置灰并显示 Check Status (mm:ss),倒计时结束后恢复绿色可点击状态
8Reference置于底部辅助区,小号灰字,格式 Reference: {reference},右侧支持 Copy

7. 异常场景 UI 文案

failReason / 状态IconTitleKey infoMessagePrimary button / actionSecondary button / action
BIND_FAILED红色叉号FailedCheck your card detailsWe could not bind your card. Please check your card information and try again.Retry / 重新进入绑卡流程
VERIFY_AMOUNT_TOO_LOW红色叉号FailedPlease try again laterCard verification could not be started.Try Again Later / 返回上一页Contact Support / 打开客服入口
INSUFFICIENT_BALANCE红色叉号Insufficient BalanceNeed at least ₦20Your card balance is not enough for card verification. Please make sure your card has at least ₦20 and try binding again.Retry Binding / 重新发起绑卡Use Another Card / 进入新卡绑定
AUTO_FUNDING_PROCESSING黄色/橙色处理中图标Funding CardNeed at least ₦20Your balance is low for card verification. We are sending money. You will receive an SMS when ready.Check Status / 查询打款和绑卡状态Use Another Card / 进入新卡绑定
AUTO_FUNDING_SUCCESS绿色成功图标Account FundedContinue binding your cardYour account has been funded for card verification. We are taking you back to card binding.自动跳转绑卡页
AUTO_FUNDING_FAILED红色叉号Insufficient BalanceNeed at least ₦20We could not add funds to your account. Please make sure your card has at least ₦20 and try binding again.Retry Binding / 重新发起绑卡Use Another Card / 进入新卡绑定
OTP_FAILED红色叉号OTP FailedEnter the latest OTPThe OTP is incorrect or expired. Please restart card binding and enter the latest OTP from your bank.Restart Binding / 重新创建绑卡交易Use Another Card / 进入新卡绑定
OTP_TOO_MANY_ATTEMPTS红色叉号Too Many AttemptsPlease wait a few minutesYou have entered the OTP too many times. Please restart card binding after a few minutes.Try Again Later / 返回上一页Use Another Card / 进入新卡绑定
BANK_AUTH_FAILED红色叉号Bank Verification FailedFollow your bank instructionsYour bank verification was not completed. Please try again and follow your bank's instructions.Retry / 重新进入绑卡流程Use Another Card / 进入新卡绑定
BANK_DECLINED红色叉号Bank DeclinedTry another cardYour bank declined this card verification. Contact your bank or use another debit card.Use Another Card / 进入新卡绑定Contact Support / 打开客服入口
CARD_NOT_SUPPORTED红色叉号Card Not SupportedTry another cardYour bank does not allow this card for verification. Please use another debit card.Use Another Card / 进入新卡绑定
INVALID_CARD_INFO红色叉号Invalid Card DetailsCheck card number and CVVSome card details are incorrect. Please check the card number, expiry date, CVV, and PIN, then try again.Retry / 返回卡信息输入页Use Another Card / 进入新卡绑定
BANK_PHONE_REQUIRED红色叉号Phone Verification NeededUse your bank phone numberYour bank needs phone verification for this card. Please use the phone number registered with your bank or try another card.Retry / 重新进入绑卡流程Use Another Card / 进入新卡绑定
BIND_CARD_PROCESSING黄色/橙色处理中图标ProcessingDo not retry yetCard verification is still processing. Please check the result before trying again.Check Status / 查询原绑卡结果Back / 返回上一页
PROVIDER_TIMEOUT黄色/橙色处理中图标ProcessingDo not retry yetCard verification is taking longer than expected. Please check the result before trying again.Check Status / 查询绑卡结果Back / 返回上一页
NETWORK_ERROR红色叉号Connection FailedCheck your internetPlease check your internet connection and try again.Retry / 重试当前请求
SYSTEM_ERROR红色叉号FailedPlease try again laterCard binding is temporarily unavailable.Try Again Later / 返回上一页Contact Support / 打开客服入口

INSUFFICIENT_BALANCE 前端展示规则:

  1. 如果后端仅返回 failReason=INSUFFICIENT_BALANCE,且未返回自动打款订单或自动打款状态,前端展示普通余额不足结果页:Title 为 Insufficient Balance,Key info 为 Need at least ₦20,主按钮为 Retry Binding,次按钮为 Use Another Card
  2. 如果后端已创建自动打款订单并返回处理中状态,前端展示 AUTO_FUNDING_PROCESSING 对应页面:Title 为 Funding Card,主按钮为 Check Status,页面按轮询间隔自动查询,不引导客户立即重复绑卡。
  3. 如果自动打款成功,前端可短暂展示成功状态后自动跳转绑卡页;如果自动打款失败、开关关闭、频控不通过、每日总量不通过、通道不可用或 provider 拒绝出款,均回落展示普通 INSUFFICIENT_BALANCE 页面。
  4. 自动跳转的绑卡页只回显本次申请已提交的非敏感卡信息;CVV 固定显示为 ***,不得回显原始 CVV。用户提交时必须创建新的绑卡订单和新的 reference,不得复用上一笔绑卡申请。

8. 余额不足自动打款需求

8.1 业务目标

当客户绑卡时因真实余额不足导致验证失败,系统可按业务线配置自动向客户账户打款,打款成功后通过短信提醒客户重新发起绑卡。该能力用于降低小额余额不足造成的绑卡流失。

8.2 触发条件

同时满足以下条件才允许自动打款:

  1. 绑卡失败原因为明确的 INSUFFICIENT_BALANCE
  2. 当前业务线自动打款开关为开启。
  3. 同一 bizCode + customerId 没有成功或处理中的自动打款记录。
  4. 未超过客户、bizCode + customerId、每日总打款单量等配置上限。
  5. 自动打款通道为可用状态,支持 Paystack 或 Monnify。

不满足任一条件时,按 INSUFFICIENT_BALANCE 展示普通余额不足结果页。

8.3 配置项

配置项示例值功能和作用
bindCard.autoFunding.bizLine.{bizLine}.enabledY/N业务线级开关。只有对应业务线为 Y 时才允许自动打款;不设置或为 N 时关闭。
bindCard.autoFunding.bizLine.{bizLine}.amount30自动打款金额,单位 NGN。由运营根据目标净到账金额和通道手续费提前测算并配置;支付中台按该配置值直接发起打款。
bindCard.autoFunding.bizLine.{bizLine}.channelPaystack / Monnify自动打款出款通道。可按业务线配置 Paystack 或 Monnify,后端按所选通道创建出款订单和查询终态。
bindCard.autoFunding.bizLine.{bizLine}.maxTimesPerCustPerDay1单客户每日自动打款上限,防止同一客户重复获取小额打款。
bindCard.autoFunding.bizLine.{bizLine}.maxTimesPerBizCodeCustomer1同一 bizCode + customerId 的自动打款上限,防止同一业务线客户重复触发。
bindCard.autoFunding.bizLine.{bizLine}.dailyTotalLimit1000 / -1业务线每日自动打款总单量上限,用于按量灰度;不配置或配置为 -1 表示不限制。
bindCard.autoFunding.bizLine.{bizLine}.cooldownMinutes30自动打款失败、处理中或客户重新绑卡失败后的冷却时间,避免短时间重复触发。
bindCard.autoFunding.bizLine.{bizLine}.smsTemplateCodeBIND_CARD_AUTO_FUND_SUCCESS自动打款成功后的短信模板编码,用于提醒客户重新发起绑卡。

手续费规则:

  • 手续费默认由我司承担,不需要单独配置手续费承担方式。
  • amount 表示用户目标净到账金额;运营需提前根据所选通道手续费测算并配置金额。
  • 支付中台不再额外计算或叠加手续费,直接按运营配置的金额发起打款。
  • 如果 provider 因手续费、账户余额或其他出款原因拒绝打款,按自动打款失败处理,展示余额不足结果页并引导重新绑卡。

8.4 状态流转

服务侧状态流转中文说明:

阶段服务侧判断 / 动作前端结果
1. 绑卡失败识别绑卡服务将 provider 返回的余额不足类错误映射为 INSUFFICIENT_BALANCE暂不直接决定页面,先进入自动打款判断
2. 业务线开关判断检查当前业务线是否开启自动打款;未开启则不创建打款订单展示普通余额不足结果页
3. 资格和频控判断bizCode + customerId 校验处理中唯一、次数限制、冷却时间和每日总打款单量不通过时展示普通余额不足结果页
4. 通道可用性判断按业务线配置选择 Paystack 或 Monnify,并确认通道可发起出款不通过时展示普通余额不足结果页
5. 创建自动打款订单创建独立自动打款订单,绑定 bizCode、客户、绑卡 reference,保证幂等创建成功后按配置金额发起出款
6. 调用出款通道调用 Paystack 或 Monnify 出款接口,记录通道 request / response 和出款 referenceaccepted / pending 时展示 Funding Card
7. 出款结果确认通过回调或主动查询确认出款终态;Funding Card 页面同时按轮询间隔自动查询,Check Status 支持用户立即查询成功发送短信并自动跳转绑卡页;失败回落普通余额不足结果页
flowchart TD
  A[绑卡失败:余额不足] --> B{业务线自动打款开关是否开启?}
  B -- 否 --> C[展示余额不足结果页]
  B -- 是 --> D{资格、频控和每日总量是否通过?}
  D -- 否 --> C
  D -- 是 --> E{出款通道是否可用?}
  E -- 否 --> C
  E -- 是 --> F[创建自动打款订单]
  F --> G[调用 Paystack 或 Monnify 出款]
  G --> H{出款状态}
  H -- 处理中 --> I[展示 Funding Card;进入页面立即查询并轮询]
  H -- 成功 --> J[发送短信并自动跳转绑卡页]
  H -- 失败 --> K[展示余额不足结果页]
  I --> L[用户 Check Status 或页面自动查询]
  L -- 处理中 --> I
  L -- 成功 --> J
  L -- 失败 --> K
  J --> M[回显脱敏卡信息;CVV 显示 ***]
  M --> N[用户确认或修改并提交]
  N --> O[创建新的绑卡订单和 reference]

8.5 后端实现要求

  1. 自动打款订单必须独立建模,不能复用绑卡订单号作为出款订单主键。
  2. 自动打款需与 bizCode、客户、绑卡 reference 绑定,支持幂等查询和客服追踪。
  3. 同一 bizCode + customerId 只允许一笔处理中自动打款。
  4. 出款 reference 使用自动打款订单号,避免和绑卡 reference 混用。
  5. 出款请求 accepted / pending 不代表到账成功,必须通过回调或主动查询确认。
  6. 自动打款通道支持 Paystack 或 Monnify,具体通道由业务线配置决定;支付中台按运营配置金额直接发起出款,不额外计算手续费。
  7. 自动打款成功后发送短信,提醒用户可以重新发起绑卡;前端收到成功终态后自动跳转绑卡页。
  8. 自动打款失败不得无限重试;超过配置次数或冷却期未结束时,展示普通余额不足文案。
  9. 所有日志和客服后台展示必须脱敏,不展示完整银行卡号。
  10. 自动打款成功后,将原绑卡申请标记为 ENDED_BY_AUTO_FUNDING,不再等待或接受该申请的后续绑卡动作作为当前申请结果;原绑卡 reference 仅用于追踪。
  11. 回显绑卡页提交时必须创建新的绑卡订单、新的 reference 和新的 provider request;不得复用原绑卡 reference、原 provider transaction 或原 OTP 会话。

8.5.1 打款结果页交互

场景页面行为用户操作服务调用与状态处理
自动打款处理中展示 Funding Card,说明正在补充绑卡验证所需余额用户可点击 Check Status进入页面立即查询一次;之后按客户端统一轮询间隔查询,查询只针对当前自动打款上下文,不创建绑卡订单
用户点击 Check Status按钮进入 loading,避免重复点击等待查询结果与自动轮询使用同一个查询接口;请求进行中暂停下一次轮询,完成后恢复
自动打款仍处理中保持 Funding Card 页面可继续等待或返回返回前端所需的自动打款结果;不得显示绑卡 Retry
自动打款成功可短暂展示 Account Funded 成功反馈无需再次点击发送一次成功短信;结束旧绑卡申请,随后自动跳转回显绑卡页
自动打款失败 / 查询明确失败展示普通 Insufficient BalanceRetry BindingUse Another Card明确提示余额不足;不自动再次打款;重新绑卡时重新执行 active 订单检查和自动打款资格判断
查询超时 / 网络失败保持当前处理中页,提示暂时无法确认Check Status 可再次查询不将未知状态当作失败,不重复创建打款订单;按原自动打款 reference 查询

轮询要求:

  1. 轮询从进入 Funding Card 页面开始,首次进入立即查询一次,后续按客户端统一轮询间隔执行。
  2. 轮询与 Check Status 必须幂等;同一时刻只能存在一个查询请求,用户点击查询时取消或跳过同一时刻的自动查询。
  3. 页面退后台、离开页面或自动打款进入终态后停止轮询;重新进入页面时按服务端状态重新开始,不以前端本地状态判断已成功。
  4. 轮询只查询自动打款订单,不能触发新的绑卡申请、不能触发新的自动打款订单。
  5. 轮询间隔和最大轮询时长由客户端统一配置执行;本需求不新增监控、告警和埋点。

8.5.2 打款成功后的绑卡页

自动打款成功后,页面直接跳转至绑卡信息页,避免客户再次手动输入。该页面是新的绑卡申请入口:

字段 / 操作展示和交互规则
卡号回显上一次申请的脱敏卡号,例如 **** **** **** 1234;允许修改;不得回显完整卡号
有效期回显上一次申请的有效期;允许修改;提交前按当前输入校验
CVV固定显示为 ***,不得回显、不得通过接口返回原始 CVV;用户如需修改必须重新输入新的 CVV
PIN / OTP不回显、不缓存、不带入新申请;在新的 provider 验证流程中重新输入
主按钮Continue / Bind Card,提交当前表单后创建新的绑卡申请
修改卡信息任一卡字段被修改后,视作新的绑卡申请;旧申请立即结束,不能继续作为当前申请更新
未修改卡信息仍创建新的绑卡订单和新的 reference;回显只是减少重复输入,不复用旧申请
返回返回业务页;不创建新绑卡订单,不改变已成功的自动打款状态

旧申请结束规则:自动打款成功时旧绑卡申请进入 ENDED_BY_AUTO_FUNDING;用户从回显页提交后,旧申请保持结束状态,新申请使用新的订单号、reference、provider transaction 和验证会话。旧申请晚到的回调只能幂等更新旧申请,不得覆盖新申请状态。

8.5.3 模块边界

支付中台尽量完整承接绑卡和自动打款处理,避免 BNS 与支付中台形成两套状态和错误映射逻辑:

模块负责内容
BNS提供 bizCode、客户标识、手机号等业务上下文;调用或转发支付中台结果
支付中台负责绑卡订单、active 订单、provider 调用、失败原因映射、自动打款资格和频控、出款、查询、回调、幂等及标准结果返回
App / H5根据标准结果展示页面;打款成功后进入回显绑卡页,打款失败后展示余额不足并引导重新绑卡
消息网关根据支付中台的成功事件发送短信

BNS 不负责 provider 错误映射、active 订单判断、自动打款订单处理、手续费计算或绑卡状态机。现有 BNS 接口可继续作为业务入口,但实际绑卡编排统一由支付中台完成。

8.6 已有同支付通道进行中绑卡订单处理

当前代码中,H5 绑卡入口主要通过客户 + 业务线维度的分布式锁防止瞬时重复请求;API 绑卡路径存在同卡号 pending 检查,但缺少统一的“同客户 / 同卡 / 同业务线 / 同支付通道进行中订单”复用和状态刷新规则。本期需补齐该流程,避免客户重复发起绑卡、重复进入三方验证,或看到不清晰的 pending 错误。

8.6.1 Active 订单识别

active 订单不是只在进入结果页时判断。完整绑卡流程中应分三处处理:

时机是否必须处理目标处理方式
发起绑卡前防止重复创建同支付通道绑卡订单、重复进入三方验证、重复扣验证金额后端在创建新的绑卡订单或生成 H5 绑卡链接前,先查询是否存在 active 绑卡订单
进入结果页时刷新当前 reference 的最新状态,避免展示过期 pending 或通用失败页前端携带 reference 进入结果页,后端按该 reference 查询订单;如发现同支付通道 active 订单,返回标准 bindStatus / failReason / countdownSeconds
点击 Check Status主动查询原订单或自动打款订单终态只查询原 reference 或自动打款 reference,不创建新绑卡订单;查询后按终态展示成功、失败、Processing 或自动打款结果页

其中“发起绑卡前判断”是主拦截点,“进入结果页判断”是状态刷新和兜底,“Check Status”是用户主动查询。前端不得依赖结果页判断来阻止重复下单。

维度规则
客户identityNumber 必须一致;如入口只有 uid,需先转换为当前业务线可识别的客户标识
业务线channel 必须一致
支付通道payChannel 必须一致;如果路由前还未确定支付通道,则先完成路由计算,再按计算后的 payChannel 查询
卡号已输入卡号的 API 绑卡场景必须校验 bankCardNo;H5 入口未输入卡号时可不校验卡号
状态status 属于 Ipendingsend_cardsend_pinsend_otpsend_birthdaysend_phonesend_address
时间仅查询配置有效期内订单,建议按支付通道配置 active TTL;超过 TTL 的订单先触发状态查询或超时关闭

建议新增配置:

配置项类型示例说明
bindCard.activeOrder.ttlMinutes.{payChannel}integer30各支付通道 active 订单有效期
bindCard.activeOrder.queryBeforeCreate.enabled.{bizLine}booleantrue当前业务线是否在创建前查询 active 订单
bindCard.activeOrder.allowReRouteAfterTimeout.{bizLine}booleantrueactive 订单超时后,我方是否允许新建绑卡订单并重新路由到其他支付通道

说明:

  1. Monnify 或 Paystack 不支持把已创建的绑卡 / 交易订单直接“切换”到另一个支付通道。
  2. 这里的“切换支付通道”是我方系统行为:原 active 订单超时且完成状态查询 / 超时处理后,新建一笔新的绑卡订单,生成新的 reference,再按路由策略选择 Monnify、Paystack 或其他可用通道。
  3. 原订单必须保留原 referencepayChannel、provider 原始状态和最终处理结果,不能覆盖成新通道订单。
  4. 新订单必须使用新的 reference。Monnify 和 Paystack 都要求交易 reference 唯一,复用旧 reference 会触发 duplicate reference 类错误。

8.6.2 处理规则

查询结果后端处理前端展示
无 active 订单正常创建新绑卡订单,按路由结果进入三方验证正常进入绑卡流程
active 订单已查到三方成功更新原订单为 success,返回原 reference 和成功状态展示成功页或回到业务成功流程
active 订单已查到三方失败 / 取消更新原订单为终态失败 / 取消;如仍允许绑卡,可创建新订单展示失败原因页,允许 RetryUse Another Card
active 订单仍处理中不创建新订单,返回原 referencepayChannelbindStatus=PROCESSING展示 Processing,Key info 为 Do not retry yet,主按钮为 Check Status
active 订单超过 TTL 且查不到明确终态先做原订单状态查询;仍无明确终态时,将原订单标记为超时或待人工补偿查询;如配置允许,则新建绑卡订单并重新路由到其他支付通道默认展示 Processing / Check Status;确认可重新路由时,进入新绑卡并使用新 reference

服务端返回建议:

{
  "bindStatus": "PROCESSING",
  "failReason": "BIND_CARD_PROCESSING",
  "reference": "BC202607210003",
  "payChannel": "Monnify",
  "message": "Card verification is still processing. Please check the result before trying again.",
  "primaryAction": "CHECK_STATUS",
  "secondaryAction": "BACK",
  "retryable": false,
  "countdownSeconds": 15
}

实现要求:

  1. active 订单检查必须在三方请求前执行,避免同支付通道重复扣验证金额或重复创建三方会话。
  2. 对同一客户、同一业务线、同一支付通道,同一时间只允许一笔 active 绑卡订单。
  3. 发现 active 订单时,优先查询原订单状态,不直接创建新订单。
  4. 如果原订单仍在处理中,返回原 reference,前端必须支持复制该 reference。
  5. 如果原订单已失败且失败原因明确,按失败原因映射返回标准 failReason,而不是继续返回 pending。
  6. 不再向客户展示 this card have a pending order...please try again tomorrow 这类不可操作文案。
  7. active 订单仍在冷却期内时,后端返回 countdownSeconds,取值为距离下一次允许查询或重试的剩余秒数。
  8. active 订单超时后的重新路由必须创建新订单、新 reference、新 provider request;不得复用原 provider 交易或原 reference。
  9. 重新路由前必须确认原订单不会继续被当作成功订单通知业务方;如后续收到原订单成功回调,需按原 reference 做幂等处理并进入人工或补偿规则,不得覆盖新订单。
  10. 建议增加 DB 索引:identity_number, channel, pay_channel, status, create_time
  11. 建议增加 Redis 锁:bindcard:active:{identityNumber}:{channel}:{payChannel},用于防止并发下重复创建 active 订单。

8.7 短信提醒

自动打款成功后,通过消息网关发送短信。短信只提醒客户重新发起绑卡,不承诺退款或补偿。

推荐英文短信文案:

Your account has been funded for card verification. Please open the app and retry binding your bank card.

短信发送规则:

  1. 仅在自动打款终态成功后发送。
  2. 同一自动打款订单只发送一次成功短信。
  3. 短信发送失败不影响自动打款终态,但需要保留发送记录供客服查询。
  4. 短信不展示完整银行卡号、金额以外的敏感账户信息或 provider reference。

9. 前端行为规则

  1. App 根据 failReason 使用本地 title 文案,不依赖后端返回 title。
  2. App 优先使用后端返回的 message;如为空,使用本地文案表。
  3. reference 必须展示并支持复制,始终展示的是绑卡的reference,不用展示出款的reference。
  4. BIND_CARD_PROCESSINGPROVIDER_TIMEOUTAUTO_FUNDING_PROCESSING 优先展示 Check Status,不直接引导重复绑卡;Funding Card 页面进入后立即查询一次,并按客户端统一轮询间隔自动轮询。
  5. 如后端返回 countdownSeconds,前端展示倒计时状态:主按钮置灰,文案为 Check Status (mm:ss);倒计时结束后按钮恢复绿色并允许点击查询。
  6. 倒计时期间不自动重复创建绑卡订单;用户点击返回后再次进入页面时,应按服务器剩余时间继续展示倒计时。
  7. 点击 Use Another Card 时,清空本次卡输入状态,不复用上次敏感输入。
  8. 点击 Contact Support 时,当前跳转至 contact us 页面。
  9. 返回上一页后,借款申请或还款业务状态不得丢失。
  10. 自动打款成功后,页面自动跳转至绑卡信息页并回显脱敏卡信息;CVV 始终显示为 ***,不回显原始值。
  11. 回显页允许用户修改卡号、有效期和 CVV;用户提交时无论是否修改,都必须作为新的绑卡申请创建新订单和新 reference

9.1 绑卡交互动作边界

用户动作是否创建新绑卡订单交互要求
首次点击绑卡可能创建后端先做 active 订单判断;无 active 订单时才创建新订单
点击 Retry / Retry Binding可能创建仅适用于明确可重试的终态失败或自动打款成功;仍需先做 active 订单判断
自动打款成功自动跳转回显页仅展示脱敏卡信息;不在跳转时创建绑卡订单,等待用户确认并提交新申请
在回显页修改并提交卡信息视作新的绑卡申请;旧绑卡申请保持结束,后端生成新的订单、reference 和 provider request
点击 Use Another Card可能创建前端清空本次卡输入状态,用户重新输入新卡;后端仍需按新卡和支付通道判断 active 订单
点击 Check Status只查询原 reference、绑卡订单或自动打款订单状态,不创建新绑卡订单
倒计时结束仅恢复 Check Status 可点击,不自动查询、不自动创建新订单
点击返回 Back返回上一页或业务页;再次进入结果页时按服务器剩余 countdownSeconds 展示
结果页初始化只做当前 reference 的状态刷新和展示;不得把“进入结果页”作为创建新订单的触发点

10. 验收标准

10.1 后端验收

  • 后端不再返回 title 字段作为页面标题来源。
  • 后端能返回稳定 failReason、可展示 message、按钮动作和可复制 reference
  • providerCodeproviderMessage 只在日志、订单明细或客服后台使用,不直接展示给客户。
  • 明确余额不足返回映射为 INSUFFICIENT_BALANCE
  • Monnify OTP authorize responseCode=99 / Transaction Failed 在确认属于发卡行限制时,映射为 CARD_NOT_SUPPORTED;未确认发卡行限制时,不得默认使用该原因。
  • 自动打款只按业务线开关控制。
  • 自动打款开启时,只有明确 INSUFFICIENT_BALANCE 且通过资格、频控、每日总量和通道可用性校验后才发起。
  • 自动打款支持按业务线选择 Paystack 或 Monnify。
  • amount 由运营提前根据目标净到账金额和通道手续费测算并配置,支付中台按配置金额直接发起出款,不额外计算手续费。
  • 本需求不新增商户余额前置校验、余额预警、监控告警或埋点;provider 出款失败时按自动打款失败处理。
  • 自动打款成功后只发送一次短信提醒。
  • 自动打款处理中支持进入页面立即查询、按间隔轮询和用户点击 Check Status 查询;查询只针对原自动打款 reference。
  • 自动打款成功后将旧绑卡申请置为 ENDED_BY_AUTO_FUNDING,并自动跳转至回显绑卡页;旧申请不得被新申请或晚到回调覆盖。
  • 回显绑卡页只能返回脱敏卡号、有效期和 cvvMasked=***;不得返回原始 CVV、PIN、OTP 或 provider token。
  • 回显绑卡页提交时必须创建新的绑卡订单、reference、provider transaction 和验证会话;修改任一字段均按新绑卡申请处理。
  • 同一 bizCode + customerId 不能存在多笔处理中的自动打款。
  • 创建绑卡订单前能识别同客户、同业务线、同支付通道的 active 绑卡订单;active 订单仍处理中时返回原 reference,不重复创建新订单。
  • active 订单判断主拦截点在发起绑卡前;结果页初始化不得创建新绑卡订单,只允许刷新当前 reference 状态。
  • active 订单已终态时,先更新原订单状态,再按成功、失败、取消或超时结果返回标准 failReason
  • active 订单仍在冷却期内时,后端返回 countdownSeconds,前端据此展示倒计时。

10.2 前端验收

  • 所有异常场景均展示 Binding Bank Card 结果页,页面结构与现有失败页一致。
  • 每个 failReason 能展示对应英文 title、message、主按钮和次按钮。
  • 页面展示 Reference: {reference},并支持复制。
  • 文案不展示内部错误码、完整银行卡号、CVV、PIN、OTP、token 等敏感信息。
  • Use Another Card 会进入新卡绑定流程,并清空本次卡输入状态。
  • 发卡行限制类 CARD_NOT_SUPPORTED 展示 Card Not Supported,Key info 为 Try another card,主按钮为 Use Another Card,不展示 Retry 作为主操作。
  • Check Status 会触发结果查询,不直接重复绑卡。
  • Check Status 和倒计时结束均不得创建新绑卡订单;只有首次绑卡、Retry / Retry BindingUse Another Card 这类重新发起动作才允许进入创建前 active 订单判断。
  • 已有同支付通道 active 绑卡订单时,前端展示 Processing,Key info 为 Do not retry yet,主按钮为 Check Status,并展示原 Reference
  • 后端返回 countdownSeconds 时,前端主按钮显示 Check Status (mm:ss) 且不可点击;倒计时结束后恢复为可点击的 Check Status
  • 自动打款处理中展示 Funding Card,不展示普通失败终态。
  • 自动打款处理中页面支持自动轮询和 Check Status 主动查询;请求进行中不得重复发起查询。
  • 自动打款成功后自动跳转绑卡信息页,回显脱敏卡号和有效期,CVV 固定显示为 ***;用户可修改后提交。
  • 自动打款成功后的回显页提交会创建新的绑卡申请,不能复用旧绑卡 reference;旧申请状态为已结束。
  • 自动打款失败、开关关闭或不满足频控/通道条件时,展示普通余额不足文案。

11. 上线TODO

事项影响
各业务线差异配置决定哪些业务线开启自动打款、各自金额、通道、频控和短信模板
每日总打款单量配置配置后按量进行灰度;不配置或配置为 -1 时不限制
当前生产库错误码全集需要从生产库拉取更完整 provider code / message,用于补齐 failReason 映射