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

Next.js与Django集成Stripe Checkout时onClick请求跨域问题

Stripe支付页面改用Axios请求时的跨域问题解决

在Next.js中开发Stripe支付页面,使用form表单提交到Django后端的/pagamenti/create-checkout-session接口时功能正常,但改用div的onClick事件通过Axios发起POST请求(目的是在请求头携带认证令牌、发送自定义请求体)时,出现跨域错误,即使Django已配置CORS_ALLOW_ALL_ORIGINS = True。

原可正常工作的前端form代码

'use client';
import React from 'react';
import Container from "@/components/Container";
import axios from "axios";

const PagamentiPage = () => {

    const data = {
        product_id: 1,
        price: 599,
        name: "Test",
        description: "lorem ipsum dolor sit amet consectetur adipisicing elit.",
    }

    return (
        <Container>
            <div className="product">
                <img
                    src="https://i.imgur.com/EHyR2nP.png"
                    alt="The cover of Stubborn Attachments"
                />
                <div className="description">
                    <h3>{data.name}</h3>
                    <h5>Price: {data.price/100} €</h5>
                </div>
            </div>
            <form
                action="http://127.0.0.1:8000/pagamenti/create-checkout-session"
                method="POST"
            >
                <input type="hidden" name="product_id" value={1}/>
                <input type="hidden" name="price" value={70000}/>
                <input type="hidden" name="name" value={"Test"}/>
                <input type="hidden" name="description" value={"Frf"}/>
                <input
                    type="hidden"
                    name="username"
                    value={"Federiko98"}
                />
                <button type={"submit"} className={"bg-red-300 p-2 rounded"}>
                    Checkout
                </button>
            </form>
        </Container>
    );
};

export default PagamentiPage;

Django后端代码

class StripeCheckoutView(APIView):
    def post(self, request, *args, **kwargs):
        data = request.data
        try:
            checkout_session = stripe.checkout.Session.create(
                line_items=[{
                    'price_data': {
                        'currency': 'eur',
                        'unit_amount': data.get("price"),  # centesimi
                        'product_data': {
                            'name': data.get("name"),
                            'description': data.get("description"),
                        },
                    },
                    'quantity': 1,
                }],
                metadata={
                    'username': data.get("username"),
                    'product_id': data.get("product_id"),
                },
                mode='payment',
                success_url=f'{YOUR_DOMAIN}?success=true',
                cancel_url=f'{YOUR_DOMAIN}?canceled=true',
            )
            return redirect(checkout_session.url)
        except Exception as e:
            return Response(
                {'error': f"Errore {e}"},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )

改用Axios后的前端代码(出现跨域问题)

const PagamentiPage = () => {

    const data = {
        product_id: 1,
        price: 599,
        name: "Test",
        description: "lorem ipsum dolor sit amet consectetur adipisicing elit.",
    }

    const handleClick = async () => {
        axios.post(`http://127.0.0.1:8000/pagamenti/create-checkout-session`, data).then((res) => {
            console.log(res);
        }).catch((err) => {
            console.log(err);
        })
    }

    return (
        <Container>
            <div className="product">
                <img
                    src="https://i.imgur.com/EHyR2nP.png"
                    alt="The cover of Stubborn Attachments"
                />
                <div className="description">
                    <h3>{data.name}</h3>
                    <h5>Price: {data.price / 100} €</h5>
                </div>
            </div>
            <div onClick={handleClick}>
                Checkout
            </div>
        </Container>
    );
};

export default PagamentiPage;

出现的跨域错误

Blocked multi-origin request (cross-origin): the origin matching policy does not allow reading the remote resource from https://checkout.stripe.com/c/pay/cs_test_a1eRHeGL4xxJiduP9byAbmJkNpWxVSDnpJhpugqmVUsKvT0vDw74my6Mib6#fidkdWxOYHwnPyd1blpxYHZxWjA0SGc3MWBNPXNtdUZAclNDTXRJd2NcbEM2bmZVSVxJNE1idWZXcl9GbTFvRG0xQjBLbE9xQmN9f0BsT2dBTFF%2FTFE1SDZnT1x9Z1IyaGx2f2xzQ29%2FQTdfNTU0a2JBXGJsRCcpJ2N3amhWYHdzYHcnP3F3cGApJ2lkfGpwcVF8dWAnPyd2bGtiaWBabHFgaCcpJ2BrZGdpYFVpZGZgbWppYWB3dic%2FcXdwYHgl.

问题原因

原form表单提交是浏览器原生行为:表单提交后,后端返回的redirect会让浏览器直接跳转到Stripe的支付页面,整个过程不存在AJAX跨域请求。

改用Axios后,请求是AJAX异步请求:后端返回的redirect会被Axios接收,Axios会尝试去请求Stripe的URL,而Stripe的服务器没有配置允许你的域名跨域访问,因此触发跨域错误。

解决方案

修改后端逻辑,不再返回redirect,而是将Stripe的session URL以JSON格式返回;前端拿到URL后,通过window.location.href手动跳转到该地址。

修改后的Django后端代码

class StripeCheckoutView(APIView):
    def post(self, request, *args, **kwargs):
        data = request.data
        try:
            checkout_session = stripe.checkout.Session.create(
                line_items=[{
                    'price_data': {
                        'currency': 'eur',
                        'unit_amount': data.get("price"),  # centesimi
                        'product_data': {
                            'name': data.get("name"),
                            'description': data.get("description"),
                        },
                    },
                    'quantity': 1,
                }],
                metadata={
                    'username': data.get("username"),
                    'product_id': data.get("product_id"),
                },
                mode='payment',
                success_url=f'{YOUR_DOMAIN}?success=true',
                cancel_url=f'{YOUR_DOMAIN}?canceled=true',
            )
            # 返回session URL,而非redirect
            return Response({'url': checkout_session.url}, status=status.HTTP_200_OK)
        except Exception as e:
            return Response(
                {'error': f"Errore {e}"},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )

修改后的前端Axios请求代码

const PagamentiPage = () => {

    const data = {
        product_id: 1,
        price: 70000, // 注意保持和原form一致的分单位值
        name: "Test",
        description: "Frf",
        username: "Federiko98"
    }

    const handleClick = async () => {
        try {
            const res = await axios.post(
                `http://127.0.0.1:8000/pagamenti/create-checkout-session`,
                data,
                {
                    // 在这里添加认证令牌请求头
                    headers: {
                        'Authorization': 'Bearer ' + YOUR_AUTH_TOKEN,
                        // 其他自定义请求头
                    }
                }
            );
            // 跳转到Stripe支付页面
            window.location.href = res.data.url;
        } catch (err) {
            console.error('请求失败:', err);
        }
    }

    return (
        <Container>
            <div className="product">
                <img
                    src="https://i.imgur.com/EHyR2nP.png"
                    alt="The cover of Stubborn Attachments"
                />
                <div className="description">
                    <h3>{data.name}</h3>
                    <h5>Price: {data.price / 100} €</h5>
                </div>
            </div>
            <div onClick={handleClick} className={"bg-red-300 p-2 rounded cursor-pointer"}>
                Checkout
            </div>
        </Container>
    );
};

export default PagamentiPage;

额外说明

  1. 确保请求体中的price值是分单位(和原form一致),否则Stripe会报错。
  2. 认证令牌需要从前端的登录状态中获取,替换代码中的YOUR_AUTH_TOKEN。
  3. Django的CORS配置保持CORS_ALLOW_ALL_ORIGINS = True即可,或者更严格地配置允许你的Next.js域名,提升安全性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 00:24:53