Stripe 测试卡无法触发预期失败:原因与正确实现方案

Stripe 测试卡无法触发预期失败:原因与正确实现方案

本文解释为何 stripe 旧版 checkout(弹窗模式)中使用官方测试卡(如 4000000000000002)仍会成功扣款,并指出根本原因在于未正确使用前端生成的 `stripetoken`,而是错误地复用了已有客户默认卡片;同时提供迁移至现代支付流程的必要性说明与代码修正方案。

您遇到的问题非常典型:Stripe 测试卡在旧版 Checkout 中“全部通过”,并非测试卡失效,而是后端根本没有使用用户刚输入的卡片信息

关键问题出在您的服务端代码中:

'customer' => $_POST['customer_id'],

这段逻辑意味着:Stripe 后端直接对指定 customer_id 的默认支付方式(即该 Customer 对象下已绑定并设为 default 的 Card 或 PaymentMethod)发起扣款。如果该 Customer 之前用 4242424242424242(成功卡)创建过订阅或保存过卡,那么无论前端输入什么测试卡(哪怕是 4000000000000002),实际扣款的仍是那张成功的卡——自然永远“succeeded”。

✅ 正确做法是:从前端获取 Checkout 生成的单次有效 token(stripeToken),并在 Charge 创建时显式使用它,而非依赖 Customer 默认卡

虎课网

虎课网

虎课网是超过1800万用户信赖的自学平台,拥有海量设计、绘画、摄影、办公软件、职业技能等优质的高清教程视频,用户可以根据行业和兴趣爱好,自主选择学习内容,每天免费学习一个…

下载

✅ 修复旧版 Checkout(仅作临时兼容,不推荐长期使用)

  1. 确保前端提交 stripeToken
    Stripe Checkout 会自动将生成的 token 作为 stripeToken 字段 POST 到您的服务端 URL(无需手动收集)。请确认表单提交目标(如 /charge.php)能接收该参数:

  2. 修改后端 Charge 创建逻辑
    替换 customer 参数为 source,并传入 $_POST[‘stripeToken’]:

try {
    $charge = /Stripe/Charge::create([
        'amount' => 1000,
        'currency' => 'usd',
        'source' => $_POST['stripeToken'], // ✅ 关键:使用本次输入的 token
        'description' => "Single Credit Purchase"
        // 注意:移除 'customer' 参数!避免复用旧卡
    ]);
} catch (/Stripe/Exception/CardException $e) {
    $errors[] = $e->getError()->message;
} catch (/Stripe/Exception/RateLimitException $e) {
    $errors[] = 'Too many requests. Please try again later.';
} catch (/Stripe/Exception/InvalidRequestException $e) {
    $errors[] = 'Invalid parameters: ' . $e->getMessage();
} catch (/Stripe/Exception/AuthenticationException $e) {
    $errors[] = 'Authentication failed. Check your secret key.';
} catch (/Stripe/Exception/ApiConnectionException $e) {
    $errors[] = 'Network error. Please try again.';
} catch (/Stripe/Exception/ApiErrorException $e) {
    $errors[] = 'Stripe API error: ' . $e->getMessage();
} catch (Exception $e) {
    $errors[] = 'Unexpected error: ' . $e->getMessage();
}

⚠️ 注意事项:source 参数必须是前端实时生成的 token(如 tok_1P…),不可复用 cus_… 或 card_… ID;若需保存客户信息,请先用 source 创建 Customer + Card,再扣款(但测试场景无需保存);所有 Stripe_* 类名在 Stripe PHP SDK v7+ 已弃用,应使用 /Stripe/Exception/* 命名空间(如上例)。

? 强烈建议:立即迁移到 Stripe Elements + ConfirmPayment(现代标准)

Stripe 官方已于 2019 年正式废弃 Checkout.js(v2),且该方案存在严重缺陷:

  • ❌ 不支持 SCA/3D Secure 强制认证(欧盟、英国等地区交易将被拒);
  • ❌ 无 PCI 合规保障(敏感卡信息经您服务器中转);
  • ❌ 无法自定义 UI,移动端体验差;
  • ❌ 测试卡行为不一致(因绕过实时卡验证流程)。

✅ 推荐替代方案:
使用 Stripe Elements + Confirm Payment(服务端配合 PaymentIntent):

// 前端(简略)
const stripe = Stripe('pk_test_...');
const elements = stripe.elements();
const cardElement = elements.create('card');
cardElement.mount('#card-element');

document.getElementById('payment-form').addEventListener('submit', async (e) => {
  e.preventDefault();
  const { error, paymentMethod } = await stripe.createPaymentMethod({
    type: 'card',
    card: cardElement,
  });
  if (error) {
    alert(error.message);
  } else {
    // 发送 payment_method.id 至后端
    fetch('/create-payment-intent.php', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ payment_method: paymentMethod.id })
    });
  }
});
// 后端 create-payment-intent.php
/Stripe/Stripe::setApiKey('sk_test_...');
$intent = /Stripe/PaymentIntent::create([
  'amount' => 1000,
  'currency' => 'usd',
  'payment_method_types' => ['card'],
  'confirm' => true,
  'payment_method' => $_POST['payment_method'],
  'return_url' => 'https://yoursite.com/success',
]);

✅ 优势:

  • 测试卡(4000000000000002 等)100% 触发对应错误(card_declined、insufficient_funds);
  • 自动处理 SCA 认证(3D Secure);
  • PCI-DSS Level 1 合规(卡号不触达您的服务器);
  • 支持多币种、多支付方式(Apple Pay、Link、SOFA 等);
  • 官方长期维护,文档与错误提示更精准。

总结

  • ? 根本原因:您未使用 stripeToken,而是复用了 Customer 的默认卡,导致测试卡被完全绕过;
  • ✅ 短期修复:改用 ‘source’ => $_POST[‘stripeToken’],并移除 customer 参数;
  • ? 长期方案:立即弃用 Checkout.js,迁移到 Payment Intents + Elements —— 这不仅是技术升级,更是合规性与用户体验的必需选择。Stripe 的测试卡机制只在真实、合规的集成路径中才按设计工作。

https://www.php.cn/faq/2011510.html

发表回复

Your email address will not be published. Required fields are marked *