Django集成Stripe支付成功后回调URL报404错误求助
Django Stripe支付成功后回调404问题
问题描述
开发集成Stripe支付的Django项目时,Stripe平台显示支付已成功,前端控制台也能收到支付确认日志,但向/dashboard/services/payment/success/发起POST请求时持续返回404 Not Found错误。
错误日志
[30/Dec/2023 21:33:10] "POST /dashboard/services/payment/success/ HTTP/1.1" 404 4949 Error notifying backend:"Request failed with status code 404"
相关代码
应用URL配置(services/urls.py)
urlpatterns = [ # ... 其他URL配置 ... path('payment/', views.make_payment, name='make_payment'), path('payment/success/', views.payment_success, name='payment_success'), path('payment/success_page/', views.payment_succeed, name='payment_succeed'), ]
视图函数(services/views.py)
from django.contrib.auth.decorators import login_required from django.http import JsonResponse, redirect from django.contrib import messages from django.db.models import Sum from django.shortcuts import get_object_or_404, render import stripe from .models import DiagnosticRequest, Payment from django.conf import settings from django.views.decorators.csrf import csrf_exempt @login_required def make_payment(request): if not request.user.is_client: messages.error(request, "仅客户可发起支付") return redirect('services:dashboard') diagnostic_requests = DiagnosticRequest.objects.filter(client=request.user, payments__isnull=True) if not diagnostic_requests.exists(): messages.info(request, "暂无未支付的诊断请求") return redirect('services:dashboard') total_cost = diagnostic_requests.aggregate(Sum('service__cost'))['service__cost__sum'] if request.method == 'POST': if request.headers.get('X-Requested-With') == 'XMLHttpRequest': # 初始化Stripe API stripe.api_key = settings.STRIPE_SECRET_KEY # 创建Stripe Payment Intent intent = stripe.PaymentIntent.create( amount=int(total_cost * 100), # 转换为分 currency='usd', payment_method_types=['card'] ) # 返回客户端密钥 return JsonResponse({'client_secret': intent.client_secret}) # 创建Payment记录 payment = Payment.objects.create( user=request.user, stripe_payment_intent_id=intent.id, paid_amount=total_cost, currency='USD' ) # 关联支付与所有诊断请求 payment.diagnostic_requests.set(diagnostic_requests) # 处理GET请求 return render(request, 'services/payment.html', { 'diagnostic_requests': diagnostic_requests, 'total_cost': total_cost }) @login_required @csrf_exempt def payment_success(request): if request.method == 'POST': payment_id = request.POST.get('payment_id') # 获取支付对象 payment = get_object_or_404(Payment, stripe_payment_intent_id=payment_id) # 检查支付是否属于当前用户 if payment.user != request.user: return JsonResponse({'message': '未授权'}, status=401) # 更新支付状态为已完成 payment.status = Payment.COMPLETED payment.save() # 获取并更新所有关联的诊断请求 diagnostic_requests = payment.diagnostic_requests.all() for req in diagnostic_requests: # 根据你的模型调整状态更新逻辑 # req.status = '已支付' req.save() return JsonResponse({'message': '支付处理成功'}) return JsonResponse({'message': '无效请求'}, status=400) @login_required def payment_succeed(request): return render(request, 'pay_success.html')
前端JavaScript代码
document.addEventListener('DOMContentLoaded', function () { const stripe = Stripe('stripe公钥'); const elements = stripe.elements(); const card = elements.create('card'); card.mount('#card-element'); card.on('change', function (event) { const displayError = document.getElementById('card-errors'); if (event.error) { displayError.textContent = event.error.message; } else { displayError.textContent = ''; } }); const form = document.getElementById('payment-form'); form.addEventListener('submit', function (event) { event.preventDefault(); fetchClientSecret().then(function (clientSecret) { stripe.confirmCardPayment(clientSecret, { payment_method: { card: card } }).then(function (result) { console.log('confirmCardPayment响应:', result); if (result.error) { console.log('支付错误:', result.error.message); } else { console.log('支付成功:', result.paymentIntent.id); axios.post("{% url 'services:payment_success' %}", { payment_id: result.paymentIntent.id }, { headers: { 'X-Requested-With': 'XMLHttpRequest', 'X-CSRFToken': getCsrfToken() } }).then(function (response) { console.log(response.data.message); // 跳转到成功页面 window.location.href = "{% url 'services:payment_succeed' %}"; }).catch(function (error) { console.error('通知后端失败:', error); }); } }).catch(function (error) { console.error('确认支付后出错:', error); }); }).catch(function (error) { console.error('获取客户端密钥失败:', error); }); }); }); function getCsrfToken() { const csrfTokenElement = document.querySelector('[name=csrfmiddlewaretoken]'); return csrfTokenElement ? csrfTokenElement.value : ''; } function fetchClientSecret() { const csrfToken = getCsrfToken(); return axios.post("{% url 'services:make_payment' %}", {}, { withCredentials: true, headers: { 'X-Requested-With': 'XMLHttpRequest', 'X-CSRFToken': csrfToken } }).then(function (response) { return response.data.client_secret; }).catch(function (error) { console.error('获取客户端密钥失败:', error); }); }
解决方案
1. 确认URL层级与命名空间配置
- 检查项目根URL配置中,services应用的挂载路径是否为
dashboard/services/:# 项目根urls.py from django.urls import path, include urlpatterns = [ # ... 其他根URL ... path('dashboard/services/', include('services.urls', namespace='services')), ] - 确保services应用的urls.py顶部已设置应用名称,否则反向解析会出错:
# services/urls.py app_name = 'services' urlpatterns = [ # ... 现有URL配置 ... ]
2. 修复前端请求的会话携带设置
在调用payment_success接口的axios请求中添加withCredentials: true,确保会话Cookie被携带,避免@login_required拦截导致的隐式重定向:
axios.post("{% url 'services:payment_success' %}", { payment_id: result.paymentIntent.id }, { withCredentials: true, // 新增此行 headers: { 'X-Requested-With': 'XMLHttpRequest', 'X-CSRFToken': getCsrfToken() } })
3. 验证URL反向解析结果
在模板中输出{% url 'services:payment_success' %},确认生成的路径是否为/dashboard/services/payment/success/,如果不是,说明命名空间或层级配置有误。
4. 检查视图装饰器顺序
确保@csrf_exempt在@login_required之后,避免CSRF验证先于登录验证执行:
@login_required @csrf_exempt def payment_success(request): # 视图逻辑
内容的提问来源于stack exchange,提问作者Narsil91
相关产品推荐
相关产品推荐

