You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何让3DSecure弹窗在Stripe Payment Intents中正常工作?

解决Stripe handleCardPayment处理3DS验证时不进入then回调的问题

我帮你分析下这个问题:你遇到的情况是Stripe在处理需要3DS验证的支付时,没有进入你写的.then()分支里的错误或成功逻辑,反而控制台提示需要额外验证步骤,还抛出无意义的JS异常。这其实是因为你没有处理**支付需要额外操作(比如3DS验证)**的场景——这种情况既不是直接支付失败,也不是成功,而是需要引导用户完成验证流程。

问题根源

当使用需要3DS的测试卡时,stripe.handleCardPayment会返回一个包含paymentIntent.status = 'requires_action'的结果,而你的代码目前只处理了result.error和成功的情况,完全没覆盖这个中间状态,导致Stripe的JS无法正确触发验证流程,进而抛出异常,也不会进入你预期的回调分支。

修复后的代码

你需要修改前端JS逻辑,加入对requires_action状态的判断,调用stripe.handleCardAction来完成3DS验证:

<head runat="server">
    <title></title>
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <script src="https://js.stripe.com/v3/"></script>
    <style type="text/css">
        .auto-style1 {
            width: 930px;
        }
    </style>
</head>
<form id="form1" runat="server">
    <div>
        <asp:HiddenField ID="hiddenClientSecret" runat="server" />
        <input id="cardholder-name" type="text"/>
        <!-- placeholder for Elements -->
        <div id="card-element" class="auto-style1"></div>
        <button id="card-button"> Submit Payment </button>
    </div>
</form>
<script >
    var stripe = Stripe('mykey', {locale: 'en'});
    var elements = stripe.elements({locale: 'en'});
    var cardElement = elements.create('card', {hidePostalCode: true});
    cardElement.mount('#card-element');
    var cardholderName = document.getElementById('cardholder-name');
    var cardButton = document.getElementById('card-button');
    var clientSecret = document.getElementById('hiddenClientSecret').value;

    cardButton.addEventListener('click', function(ev) {
        ev.preventDefault(); // 阻止表单默认提交,避免页面刷新打断流程
        stripe.handleCardPayment(
            clientSecret,
            cardElement,
            {
                payment_method_data: {
                    billing_details: {name: cardholderName.value}
                }
            }
        ).then(function(result) {
            if (result.error) {
                // 处理直接支付失败的情况
                alert(result.error.message);
            } else if (result.paymentIntent.status === 'succeeded') {
                // 支付直接成功
                alert('Success!');
            } else if (result.paymentIntent.status === 'requires_action') {
                // 需要完成3DS验证,启动验证流程
                stripe.handleCardAction(result.paymentIntent.client_secret)
                    .then(function(actionResult) {
                        if (actionResult.error) {
                            // 验证失败
                            alert('验证失败:' + actionResult.error.message);
                        } else {
                            // 验证成功,确认支付状态
                            if (actionResult.paymentIntent.status === 'succeeded') {
                                alert('支付成功!');
                            }
                        }
                    });
            }
        }).catch(function(error) {
            // 捕获未预期的异常,方便调试和给用户反馈
            console.error('支付流程异常:', error);
            alert('支付过程中出现未知错误,请重试');
        });
    });
</script>

关键修改点

  1. 加入ev.preventDefault():阻止按钮默认的表单提交行为,避免页面刷新打断3DS验证流程
  2. 新增paymentIntent.status判断分支:
    • 当状态为requires_action时,调用stripe.handleCardAction唤起3DS验证弹窗
    • 单独处理验证后的成功/失败结果
  3. 新增.catch()分支:捕获未覆盖的异常场景,方便调试同时给用户友好提示

测试提示

你可以用Stripe的3DS测试卡4000 0000 0000 3220来测试流程,输入任意过期日期和CVC,提交后会触发3DS验证弹窗,完成验证后就能正常进入成功分支了。

内容的提问来源于stack exchange,提问作者Martin Skau

火山引擎 最新活动