Stripe 出海收款卡在 3DS/SCA:authentication_required 与 authentication_not_handled 排查笔记
SCA 是欧洲 2019-09-14 生效的监管要求,要求对不少欧洲线上卡支付做双因素验证。3D Secure 2(3DS2)是主流验证手段,也可以请求 SCA 豁免。3DS2 分 frictionless(无感)与 challenge(要用户跳银行验证)两条流程。一个边界先记住:请求了豁免并走 frictionless 的付款不享受 liability shift(责任转移)。
Stripe 在监管要求(欧洲 SCA、日本信用卡安全指南)、发卡行软拒绝(soft decline)、或某些优化命中时自动触发 3DS;也可以用 Radar 或 API 主动控制。不是所有交易都支持 3DS,例如钱包支付与 off-session 付款。
现象:三类报错/文案
authentication_required
卡被拒,因为交易需要验证(如 3DS)。使用 Stripe 前端时多数软拒绝会自动触发验证流程;off-session 场景可能要请客户重试。若已成功验证仍返回该码,需客户联系发卡行。
authentication_not_handled
与上一条相关。没执行要求的验证就继续,发卡行再次拒绝。处理:跑 3DS/SCA 流程;off-session 场景先在 on-session 收集并准备好验证,再回退到 on-session。
前端文案
The payment attempt failed because additional action is required before it can be completed.
典型原因:先用 createPaymentMethod 拿到 PM,再由服务端 confirm,但 createPaymentMethod 不做验证,于是 confirm 时验证没完成。看 PaymentIntent 状态通常能确认这一点。
按根因分层排查
第一层:PaymentIntent 状态与 API 版本
PaymentIntent 状态机:
requires_payment_method:失败 402,检查last_payment_error;requires_capture:未验证即完成,可继续捕获;requires_action:需额外步骤如 3DS,next_action里redirect_to_url表示需要 3DS;succeeded。
requires_action 经客户端处理后变 requires_confirmation,必须在服务端再次 confirm 才完结;若 1 小时内没有再 confirm,支付尝试失败并退回 requires_payment_method。旧 API 版本(2019-02-11 前)叫 requires_source_action。
第二层:服务端是否把“需要验证”当成失败
升级基本集成以处理验证,服务端改两处:
- 删掉
error_on_requires_action参数,不再把需要验证的付款当失败; - 加
confirmation_method参数,表示为手动确认,处理完验证后在服务端再次确认。
客户端:PaymentIntent 状态为 requires_action 时用 stripe.handleCardAction(clientSecret);成功后状态变 requires_confirmation,再让服务端 confirm。
服务端再次确认示例:
curl https://api.stripe.com/v1/payment_intents/{{PAYMENT_INTENT_ID}}/confirm -u >: -X "POST"
第三层:是否主动/动态触发 3DS 或走外部豁免
何时触发 3DS 的控制:payment_method_options[card][request_three_d_secure],在创建/确认 PaymentIntent、SetupIntent 或创建 Checkout Session 时设置,会覆盖该 Intent 上任何动态 3DS Radar 规则。
3DS 导入/外部验证场景:confirm=true、error_on_requires_action=true(防止软拒绝时 Stripe 自动发起 3DS);外部拿到低风险 SCA 豁免时用 exemption_indicator=low_risk 或 cb_exemption 参数告知 Stripe;两者都传时值要一致,否则(如 exemption_indicator=none 但 cb_exemption 表明低风险)Stripe 拒绝请求。要看 Stripe 是否申请了低风险豁免:expand latest_charge 看 payment_method_details.card.three_d_secure,授权响应里有 exemption_indicator_applied。
off-session 免验证的正确做法
off-session(订阅续费、预授权扣款等无客户参与)默认不支持 3DS。要让后续 off-session 免验证:用 SetupIntent 或首次付款时把支付方式设为 off_session 用途,Stripe 会把后续付款标为 MIT(商家发起交易),客户无需再回来验证。
SetupIntent 的 usage 参数:
on_session:仅会话内;off_session:默认,会话外;- 两者都用也填
off_session。
usage 是优化:即使卡设成 on_session 也可做 off-session 付款,但银行更可能拒绝并要求验证。无论哪种情况都可能被要求后续验证,所以要在应用里做恢复流程(revenue recovery),把客户拉回线上完成支付。
可复现的测试卡与 curl
沙盒测试卡:
4000000000003220:必须完成 3DS2 才能成功;默认 Radar 规则会要求验证。4000002760003184:所有交易都要验证。4000002500003155:off-session 付款要求 3DS2,除非你为未来支付设置过该卡;设置后 off-session 不再需要验证。(也叫 setup 或首次交易时需要)4000008400001629/pm_card_threeDSecureRequiredChargeDeclined:验证后仍会被card_declined拒绝。4000008260003178:需要验证,验证成功后被insufficient_funds拒绝。4000000000003055:支持 3DS2 但不强制(除非沙盒 Radar 规则要求)。pm_card_authenticationRequired/pm_card_threeDSecure2Required:PaymentMethod 方式的等价卡。
自动化测试构造 off-session 需 3DS 的响应:
curl https://api.stripe.com/v1/payment_intents -u ">:" -d amount=2099 -d currency=usd -d payment_method=pm_card_authenticationRequired -d confirm=true -d off_session=true
→ 得到 status=requires_confirmation 且带 next_action。
沙盒里 Stripe 显示伪造的验证页模拟成功/失败;真实模式由银行控制 UI。
豁免测试:沙盒里带 exemption_indicator 的卡都返回 exemption_indicator_applied=true;要测不通过内部 TRA 检查返回 false,用卡 40000000016123 且 exemption_indicator=low_risk(注意:应为 4000 0000 0001 6123)。
不适用场景与未实测说明
不适用或要额外注意的场景:
- 钱包支付不支持 3DS。
- off-session 付款默认不支持 3DS。
- 请求 SCA 豁免并走 frictionless 的付款不享受 liability shift。
- 若已成功验证仍返回
authentication_required,需客户联系发卡行。
未实测:钱包支付走 3DS 的完整链路、真实银行 challenge UI 的交互细节、生产环境 revenue recovery 的完整实现;这些需要按各自接入方式单独验证。
参考: