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

使用Stripe+Firebase实现订阅购买时遭遇CORS错误求助

Firebase Cloud Functions + Stripe 订阅接口 CORS 错误解决方案

调用Stripe订阅购买函数时,浏览器抛出CORS错误:

Access to fetch at 'https://us-central1-smallgistics-cd63d.cloudfunctions.net/createStripeSubscription' from origin 'https://smallgistics.com' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

已反复修改前后端代码,参考过教程及VS Code Copilot,问题仍未解决,求可行方案。

服务端代码(Firebase Cloud Functions)

const functions = require('firebase-functions');
const admin = require('firebase-admin');
const cors = require('cors');
const stripe = require('stripe')('sk_test_51NdbMmEls8dLhARfYdtPdL3o7oSszlYaZJE7GMYhvPtpjgGXjGYXLPlEwhD5n4sXZFR0NtaAar1qQQzG6PKEnI9M000ccfgqwX');

var serviceAccount = require('./smallgistics-cd63d-firebase-adminsdk-dev8o-b099ac3121.json');

admin.initializeApp({
  credential: admin.credential.cert(serviceAccount),
  databaseURL: 'https://smallgistics-cd63d.firebaseio.com'
});

exports.createStripeSubscription = functions.https.onRequest((request, response) => {
  const corsHandler = cors({origin: true});
  corsHandler(request, response, async () => {
    const userId = request.body.userId; // The ID of the user in your Firebase auth
    const productId = 'prod_Pw1OIYAqg3JuG8'; // The ID of the product in Stripe

    try {
      // Retrieve the Stripe customer ID from Firestore
      const doc = await admin.firestore().collection('users').doc(userId).get();
      const customer = doc.data().stripeCustomerId;

      // Create the subscription
      const subscription = await stripe.subscriptions.create({
        customer: customer,
        items: [{ product: productId }],
      });

      response.send({ subscriptionId: subscription.id });
    } catch (error) {
      console.error(error);
      response.status(500).send(error);
    }
  });
});

客户端代码

<script>
    // Your web app's Firebase configuration
    var firebaseConfig = {
      apiKey: "AIzaSyBmLNOykJH7eAaSxQZsD5P7IlQhUQtBm5k",
      authDomain: "smallgistics-cd63d.firebaseapp.com",
      projectId: "smallgistics-cd63d",
      storageBucket: "smallgistics-cd63d.appspot.com",
      messagingSenderId: "641898304430",
      appId: "1:641898304430:web:9a7aecf59ea12dc0822e3f",
      measurementId: "G-WR83XXQHC0"
    };

    // Initialize Firebase
    firebase.initializeApp(firebaseConfig);

    window.onload = function() {
      // Set up an auth state changed listener
      firebase.auth().onAuthStateChanged(user => {
        if (user) {
          // User is signed in, set up the button click handler
          const subscribeButton = document.getElementById('subscribeButton');
          if (subscribeButton) {
            subscribeButton.addEventListener('click', () => {
              // Determine the URL to use based on the environment
              const functionURL = location.hostname === 'localhost' || location.hostname === '127.0.0.1'
                ? 'http://127.0.0.1:5002/smallgistics-cd63d/us-central1/createStripeSubscription'
                : 'https://us-central1-smallgistics-cd63d.cloudfunctions.net/createStripeSubscription';

              // Call the Firebase function
              fetch(functionURL, {
                method: 'POST',
                body: JSON.stringify({ userId: user.uid }),
                headers: { 'Content-Type': 'application/json' }
              })
              .then(response => response.json())
              .then(data => console.log(data))
              .catch(error => console.error(error));
            });
          } else {
            console.error('The element with ID subscribeButton does not exist.');
          }
        } else {
          // User is signed out
          console.log('No user is signed in.');
        }
      });
    };
  </script>

解决方案

1. 修正CORS中间件的初始化方式

当前代码在函数内部初始化cors handler,可能导致预请求(OPTIONS)无法被正确处理。改为直接将cors作为中间件包裹整个函数:

// 替换原来的函数定义
exports.createStripeSubscription = functions.https.onRequest(cors({origin: true}), async (request, response) => {
  const userId = request.body.userId;
  const productId = 'prod_Pw1OIYAqg3JuG8';

  try {
    const doc = await admin.firestore().collection('users').doc(userId).get();
    const customer = doc.data().stripeCustomerId;

    const subscription = await stripe.subscriptions.create({
      customer: customer,
      items: [{ product: productId }],
    });

    response.send({ subscriptionId: subscription.id });
  } catch (error) {
    console.error(error);
    response.status(500).send(error);
  }
});

2. 显式指定允许的Origin(可选,更安全)

如果不想允许所有来源,可将{origin: true}替换为具体域名列表:

cors({origin: ['https://smallgistics.com', 'http://localhost:5002']})

3. 检查函数部署状态

  • 确保修改后的代码已重新部署:运行firebase deploy --only functions:createStripeSubscription
  • 查看Firebase控制台的函数日志,确认预请求(OPTIONS)是否被正常处理
  • 检查函数是否处于错误状态(比如依赖缺失、初始化失败)

4. 确认CORS依赖安装

在函数目录下运行npm install cors --save,确保cors依赖已正确安装并被package.json记录

5. 客户端请求优化(可选)

显式指定请求模式为cors(默认已生效,可避免潜在环境问题):

fetch(functionURL, {
  method: 'POST',
  mode: 'cors',
  body: JSON.stringify({ userId: user.uid }),
  headers: { 'Content-Type': 'application/json' }
})

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 22:07:52