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;
额外说明
- 确保请求体中的
price值是分单位(和原form一致),否则Stripe会报错。 - 认证令牌需要从前端的登录状态中获取,替换代码中的
YOUR_AUTH_TOKEN。 - Django的CORS配置保持
CORS_ALLOW_ALL_ORIGINS = True即可,或者更严格地配置允许你的Next.js域名,提升安全性。
内容的提问来源于stack exchange,提问作者Federico Raponi
相关产品推荐
相关产品推荐

