You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Stripe支付集成:触发SCA/3D Secure重定向认证方案及跳转URL来源咨询

问题:如何处理Stripe支付中的3D/SCA认证重定向?

我当前的代码可以完成普通银行卡支付,但无法处理需要3D/SCA认证的欧洲地区银行卡。想知道是否有办法让Stripe将用户重定向至发卡银行进行身份认证以完成交易,另外根据Stripe官方文档的说明,相关返回结构包含next_action字段,想确认Stripe SDK是否会自动获取发卡银行的跳转URL,还是需要开发者手动提供该信息。


现有代码文件

1. StripePayment.php

<?php 
require_once 'vendor/autoload.php'; 
use \Stripe\Stripe; 
use \Stripe\Customer; 
use \Stripe\ApiOperations\Create; 
use \Stripe\Charge; 

class StripePayment { 
    private $apiKey; 
    private $stripeService; 

    public function __construct() { 
        require_once "config.php"; 
        $this->apiKey = STRIPE_SECRET_KEY; 
        $this->stripeService = new \Stripe\Stripe(); 
        $this->stripeService->setVerifySslCerts(false); 
        $this->stripeService->setApiKey($this->apiKey); 
    } 

    public function addCustomer($customerDetailsAry) { 
        $customer = new Customer(); 
        $customerDetails = $customer->create($customerDetailsAry); 
        return $customerDetails; 
    } 

    public function chargeAmountFromCard($cardDetails) { 
        $customerDetailsAry = array( 
            'email' => $cardDetails['email'], 
            'source' => $cardDetails['token'] 
        ); 
        $customerResult = $this->addCustomer($customerDetailsAry); 
        $charge = new Charge(); 
        $cardDetailsAry = array( 
            'customer' => $customerResult->id, 
            'amount' => $cardDetails['amount']*100 , 
            'currency' => $cardDetails['currency_code'], 
            'description' => $cardDetails['item_name'], 
            'metadata' => array( 
                'order_id' => $cardDetails['item_number'] 
            ) 
        ); 
        $result = $charge->create($cardDetailsAry); 
        return $result->jsonSerialize(); 
    } 
}

2. 支付功能调用代码

<?php 
ini_set('display_errors', '1'); 
ini_set('display_startup_errors', '1'); 
error_reporting(E_ALL); 
require_once "config.php"; 

if (!empty($_POST["token"])) { 
    require_once 'StripePayment.php'; 
    $stripePayment = new StripePayment(); 
    $stripeResponse = $stripePayment->chargeAmountFromCard($_POST); 

    require_once "DBController.php"; 
    $dbController = new DBController(); 
    $amount = $stripeResponse["amount"] /100; 
    $param_type = 'ssdssss'; 
    $param_value_array = array( 
        $_POST['email'], 
        $_POST['item_number'], 
        $amount, 
        $stripeResponse["currency"], 
        $stripeResponse["balance_transaction"], 
        $stripeResponse["status"], 
        json_encode($stripeResponse) 
    );

3. Stripe支付表单及前端代码

<form id="frmStripePayment" action="" method="post"> 
    <div class="field-row"> 
        <label>Card Holder Name</label> 
        <span id="card-holder-name-info" class="info"></span><br> 
        <input type="text" id="name" name="name" class="demoInputBox"> 
    </div> 
    <div class="field-row"> 
        <label>Email</label> 
        <span id="email-info" class="info"></span><br> 
        <input type="text" id="email" name="email" class="demoInputBox"> 
    </div> 
    <div class="field-row"> 
        <label>Card Number</label> 
        <span id="card-number-info" class="info"></span><br> 
        <input type="text" id="card-number" name="card-number" class="demoInputBox"> 
    </div> 
    <div class="field-row"> 
        <div class="contact-row column-right"> 
            <label>Expiry Month / Year</label> 
            <span id="userEmail-info" class="info"></span><br> 
            <select name="month" id="month" class="demoSelectBox"> 
                <option value="02">02</option> 
                <option value="03">03</option> 
                <option value="04">04</option> 
                <option value="05">05</option> 
                <option value="06">06</option> 
                <option value="07">07</option> 
                <option value="08">08</option> 
                <option value="08">08</option> 
                <option value="08">08</option> 
                <option value="09">09</option> 
                <option value="10">10</option> 
                <option value="11">11</option> 
                <option value="12">12</option> 
            </select> 
            <select name="year" id="year" class="demoSelectBox"> 
                <option value="21">2021</option> 
                <option value="22">2022</option> 
                <option value="23">2023</option> 
                <option value="24">2024</option> 
                <option value="25">2025</option> 
                <option value="26">2026</option> 
                <option value="27">2027</option> 
                <option value="28">2028</option> 
                <option value="29">2029</option> 
                <option value="30">2030</option> 
            </select> 
        </div> 
        <div class="contact-row cvv-box"> 
            <label>CVC</label> 
            <span id="cvv-info" class="info"></span><br> 
            <input type="text" name="cvc" id="cvc" class="demoInputBox cvv-input"> 
        </div> 
    </div> 
    <div> 
        <input type="submit" name="pay_now" value="Submit" id="submit-btn" class="btnAction" onClick="stripePay(event);"> 
        <div id="loader"> 
            <img alt="loader" src="LoaderIcon.gif"> 
        </div> 
    </div> 
    <input type='hidden' name='amount' value='1100'> 
    <input type='hidden' name='currency_code' value='USD'> 
    <input type='hidden' name='item_name' value='Solidroof Product'> 
    <input type='hidden' name='item_number' value='SCA#Test'> 
</form> 

<script> 
function cardValidation () { 
    var valid = true; 
    var name = $('#name').val(); 
    var email = $('#email').val(); 
    var cardNumber = $('#card-number').val(); 
    var month = $('#month').val(); 
    var year = $('#year').val(); 
    var cvc = $('#cvc').val(); 

    $("#error-message").html("").hide(); 

    if (name.trim() == "") { valid = false; } 
    if (email.trim() == "") { valid = false; } 
    if (cardNumber.trim() == "") { valid = false; } 
    if (month.trim() == "") { valid = false; } 
    if (year.trim() == "") { valid = false; } 
    if (cvc.trim() == "") { valid = false; } 

    if(valid == false) { 
        $("#error-message").html("All Fields are required").show(); 
    } 
    return valid; 
} 

//set your publishable key 
Stripe.setPublishableKey("<?php echo STRIPE_PUBLISHABLE_KEY; ?>"); 

//callback to handle the response from stripe 
function stripeResponseHandler(status, response) { 
    if (response.error) { 
        //enable the submit button 
        $("#submit-btn").show(); 
        $( "#loader" ).css("display", "none"); 
        //display the errors on the form 
        $("#error-message").html(response.error.message).show(); 
    } else { 
        //get token id 
        var token = response['id']; 
        //insert the token into the form 
        $("#frmStripePayment").append("<input type='hidden' name='token' value='" + token + "' />"); 
        //submit form to the server 
        $("#frmStripePayment").submit(); 
    } 
} 

function stripePay(e) { 
    e.preventDefault(); 
    var valid = cardValidation(); 
    if(valid == true) { 
        $("#submit-btn").hide(); 
        $( "#loader" ).css("display", "inline-block"); 
        Stripe.createToken({ 
            number: $('#card-number').val(), 
            cvc: $('#cvc').val(), 
            exp_month: $('#month').val(), 
            exp_year: $('#year').val() 
        }, stripeResponseHandler); 
        //submit from callback 
        return false; 
    } 
} 
</script>

解决方案:实现3D/SCA认证重定向流程

先直接回答你的核心疑问:Stripe SDK会自动生成发卡银行的跳转URL,完全不需要你手动提供。你的现有代码只是缺少了处理next_action响应的逻辑,所以才无法完成3D认证流程。下面是具体的修改步骤:

1. 修改后端StripePayment.php的支付方法

当创建Charge时,如果需要3D认证,Stripe会返回带有next_action的响应,我们需要捕获这个信息并返回给前端,同时保存Charge ID用于后续验证:

public function chargeAmountFromCard($cardDetails) {
    $customerDetailsAry = array(
        'email' => $cardDetails['email'],
        'source' => $cardDetails['token']
    );
    $customerResult = $this->addCustomer($customerDetailsAry);
    $charge = new Charge();
    $cardDetailsAry = array(
        'customer' => $customerResult->id,
        'amount' => $cardDetails['amount']*100 ,
        'currency' => $cardDetails['currency_code'],
        'description' => $cardDetails['item_name'],
        'metadata' => array(
            'order_id' => $cardDetails['item_number']
        ),
        // 关键配置:启用手动3D认证流程
        'confirmation_method' => 'manual',
        'confirm' => true,
        // 指定认证完成后Stripe跳转回你的页面
        'return_url' => 'https://你的域名/payment_return.php'
    );
    
    try {
        $result = $charge->create($cardDetailsAry);
        $response = $result->jsonSerialize();
        
        // 检查是否需要跳转至银行认证
        if (isset($response['next_action']) && $response['next_action']['type'] === 'redirect_to_url') {
            $response['requires_redirect'] = true;
            // 保存Charge ID到会话,后续验证用
            session_start();
            $_SESSION['charge_id'] = $response['id'];
        } else {
            $response['requires_redirect'] = false;
        }
        
        return $response;
    } catch (\Stripe\Exception\CardException $e) {
        // 捕获卡相关错误(包括认证失败)
        return array('error' => $e->getMessage());
    }
}

2. 修改后端支付调用逻辑

处理返回的响应,根据是否需要重定向,返回不同的结果给前端:

if (!empty($_POST["token"])) {
    session_start();
    require_once 'StripePayment.php';
    $stripePayment = new StripePayment();
    $stripeResponse = $stripePayment->chargeAmountFromCard($_POST);
    
    if (isset($stripeResponse['requires_redirect']) && $stripeResponse['requires_redirect']) {
        // 返回跳转信息给前端
        echo json_encode(array(
            'status' => 'redirect',
            'redirect_url' => $stripeResponse['next_action']['redirect_to_url']['url']
        ));
        exit;
    } elseif (!isset($stripeResponse['error'])) {
        // 支付成功,执行数据库保存逻辑
        require_once "DBController.php";
        $dbController = new DBController();
        $amount = $stripeResponse["amount"] /100;
        $param_type = 'ssdssss';
        $param_value_array = array(
            $_POST['email'],
            $_POST['item_number'],
            $amount,
            $stripeResponse["currency"],
            $stripeResponse["balance_transaction"],
            $stripeResponse["status"],
            json_encode($stripeResponse)
        );
        // 执行数据库插入操作...
        
        echo json_encode(array('status' => 'success'));
        exit;
    } else {
        // 返回错误信息
        echo json_encode(array('status' => 'error', 'message' => $stripeResponse['error']));
        exit;
    }
}

3. 修改前端AJAX提交逻辑

不再直接提交表单,改用AJAX异步处理,方便接收跳转指令:

function stripeResponseHandler(status, response) {
    if (response.error) {
        $("#submit-btn").show();
        $( "#loader" ).css("display", "none");
        $("#error-message").html(response.error.message).show();
    } else {
        var token = response['id'];
        // 用AJAX提交支付数据
        $.ajax({
            url: window.location.href, // 指向当前处理页面
            type: 'POST',
            data: {
                token: token,
                email: $('#email').val(),
                amount: $('input[name="amount"]').val(),
                currency_code: $('input[name="currency_code"]').val(),
                item_name: $('input[name="item_name"]').val(),
                item_number: $('input[name="item_number"]').val(),
                name: $('#name').val()
            },
            dataType: 'json',
            success: function(data) {
                $("#submit-btn").show();
                $( "#loader" ).css("display", "none");
                if (data.status === 'redirect') {
                    // 跳转到银行认证页面
                    window.location.href = data.redirect_url;
                } else if (data.status === 'success') {
                    // 支付成功,跳转至结果页
                    window.location.href = 'payment_success.php';
                } else {
                    $("#error-message").html(data.message).show();
                }
            },
            error: function() {
                $("#submit-btn").show();
                $( "#loader" ).css("display", "none");
                $("#error-message").html('支付处理失败,请稍后重试').show();
            }
        });
    }
}

4. 添加认证回调处理页面(payment_return.php)

用户完成银行认证后,Stripe会跳转回你指定的return_url,这里需要验证Charge的最终状态:

<?php
session_start();
require_once 'vendor/autoload.php';
require_once 'config.php';

\Stripe\Stripe::setApiKey(STRIPE_SECRET_KEY);

if (!isset($_SESSION['charge_id'])) {
    header('Location: index.php');
    exit;
}

// 获取Charge详情
$charge = \Stripe\Charge::retrieve($_SESSION['charge_id']);
unset($_SESSION['charge_id']);

if ($charge->status === 'succeeded') {
    // 支付成功,更新数据库状态并跳转至成功页
    // ...
    header('Location: payment_success.php');
} else {
    // 支付失败,跳转至失败页
    $error = $charge->failure_message ?? '认证失败,请重试';
    header('Location: payment_failed.php?error=' . urlencode($error));
}

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 09:22:43