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

如何在Stripe Elements支付流程中正确集成CAPTCHA防范卡测试

解决方案

纯前端校验CAPTCHA没有任何防攻击价值,攻击者完全可以绕过前端判断直接发起支付请求,你不需要把stripe.confirmCardPayment迁移到后端就能实现安全的CAPTCHA集成,整个流程完全兼容3D Secure验证逻辑,核心是把CAPTCHA的有效性校验放到你已有的后端API路由里执行。

调整后的流程逻辑

  • 用户点击提交支付时,前端先触发reCAPTCHA组件获取用户校验令牌,不在前端做任何有效性判断
  • 前端调用已有的/api/payment-intent/update接口时,把CAPTCHA令牌作为参数一起传给后端
  • 后端收到请求后,第一时间用服务端密钥校验CAPTCHA令牌的真实性:
    • 校验不通过直接返回403错误,终止所有后续逻辑,前端收到错误直接提示,不会走到支付确认步骤
    • 校验通过再执行原本的支付意图更新逻辑,返回成功响应
  • 前端收到后端更新成功的响应后,完全沿用原本的stripe.confirmCardPayment逻辑,不影响3D验证弹窗等原生能力

代码调整示例

前端提交逻辑修改

// 页面初始化时加载reCAPTCHA脚本,以下以v3版本为例
const getRecaptchaToken = async () => {
  return await window.grecaptcha.execute(process.env.NEXT_PUBLIC_RECAPTCHA_SITE_KEY, {
    action: 'one_off_donation'
  });
};

export const makeOneOffPayment = async (
  e,
  stripe,
  elements,
  clientSecret,
  setError,
  billingInfo,
  paymentAmount,
  paymentIntentId,
  CardNumberElement,
): Promise<boolean> => {
  e.preventDefault();

  // 获取CAPTCHA令牌,不做本地校验
  let recaptchaToken;
  try {
    recaptchaToken = await getRecaptchaToken();
  } catch (err) {
    setError('安全校验失败,请刷新页面重试');
    return false;
  }

  // 更新支付意图时携带CAPTCHA令牌
  try {
    const res = await fetch('/api/payment-intent/update', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        amount: paymentAmount * 100,
        paymentIntentId: paymentIntentId,
        recaptchaToken
      }),
    });

    if (!(res.status >= 200 && res.status <= 299)) {
      const result = await res.json();
      setError(`支付失败:${result.message}`);
      return false;
    }
  } catch (err) {
    setError('客户端处理异常,请重试');
    return false;
  }

  // 后端校验通过后再执行卡支付确认,原有逻辑完全保留,支持3D验证
  try {
    const payload = await stripe.confirmCardPayment(clientSecret, {
      payment_method: {
        card: elements.getElement(CardNumberElement),
        billing_details: {
          address: {
            city: billingInfo.city || '',
            line1: billingInfo.AddressLine1 || '',
            line2: billingInfo.AddressLine2 || '',
          },
          email: billingInfo.email,
          name: billingInfo.name,
          phone: billingInfo.phone,
        },
      },
    });
    if (payload.error) {
      setError(`支付失败:${payload.error.message}`);
      return false;
    }
    return true;
  } catch (err) {
    setError('客户端处理异常,请重试');
    return false;
  }
};

后端API路由修改

在你已有的/api/payment-intent/update接口里增加CAPTCHA服务端校验逻辑:

import Stripe from 'stripe';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);

export default async function handler(req, res) {
  if (req.method !== 'POST') {
    return res.status(405).json({ message: '请求方法不允许' });
  }

  const { amount, paymentIntentId, recaptchaToken } = req.body;

  // 服务端校验CAPTCHA,密钥存在环境变量中,前端无法获取
  const captchaVerifyRes = await fetch('RECAPTCHA官方服务端校验地址', {
    method: 'POST',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    body: new URLSearchParams({
      secret: process.env.RECAPTCHA_SECRET_KEY,
      response: recaptchaToken,
      remoteip: req.headers['x-forwarded-for']?.split(',')[0] || req.socket.remoteAddress
    })
  });
  const captchaResult = await captchaVerifyRes.json();

  // 校验规则可根据需求调整:验证成功、分数达标、动作匹配
  if (
    !captchaResult.success ||
    captchaResult.score < 0.5 ||
    captchaResult.action !== 'one_off_donation'
  ) {
    return res.status(403).json({ message: '安全校验未通过,请稍后重试' });
  }

  // 校验通过后执行原有支付意图更新逻辑
  try {
    await stripe.paymentIntents.update(paymentIntentId, { amount });
    return res.status(200).json({ success: true });
  } catch (err) {
    return res.status(400).json({ message: err.message });
  }
}

额外防卡测试优化建议

  • 前端不要返回精准的卡错误信息,比如不要单独提示“CVV错误”“卡号不存在”“余额不足”,统一返回“支付失败,请检查卡信息后重试”,减少攻击者试卡的信息反馈
  • 开启支付接口的频率限制,单IP1分钟内最多允许3次支付提交,超出直接拦截
  • 配合Stripe自带的Radar防欺诈规则,对短时间内多次支付失败的IP、卡号段做自动拦截
  • 如果用reCAPTCHA v3,对分数在0.3-0.7之间的请求弹出滑块验证,进一步提高攻击成本

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 22:48:26