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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 04:07:33