Django支付返回successful时如何将课程购买数据存入数据库
实现方案
当前逻辑的核心断档点:支付网关回调时仅返回status和tx_ref两个参数,你没有提前把交易编号和对应的用户、课程做绑定,所以回调阶段拿不到需要关联的主体数据,同时现有代码存在缩进、参数不匹配的语法问题,先按以下步骤修改:
第一步:新增交易记录模型
用于绑定支付交易号、用户、课程的关联关系,避免依赖session(跨设备跳转时session会失效),在models.py中添加如下模型:
class PaymentTransaction(models.Model): PENDING = 'pending' SUCCESS = 'success' FAILED = 'failed' STATUS_CHOICES = ( (PENDING, '待支付'), (SUCCESS, '支付成功'), (FAILED, '支付失败'), ) tx_ref = models.CharField(max_length=50, unique=True, verbose_name="交易流水号") user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name="支付用户") course = models.ForeignKey(Course, on_delete=models.CASCADE, verbose_name="购买课程") amount = models.IntegerField(verbose_name="支付金额") status = models.CharField(max_length=20, choices=STATUS_CHOICES, default=PENDING, verbose_name="支付状态") created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True)
添加完成后执行python manage.py makemigrations和python manage.py migrate完成数据表创建。
第二步:修正结算页与支付发起逻辑
原有代码存在缩进错误、参数不匹配问题,将views.py中对应代码替换为如下内容:
import math import random import requests from django.conf import settings from django.shortcuts import render, redirect from django.contrib.auth.decorators import login_required from .models import Course, UserCourse, PaymentTransaction @login_required def course_checkout(request, slug): course = Course.objects.get(slug=slug) user = request.user if request.method == "POST": name = request.POST.get("name") email = request.POST.get("email") # 正式环境不要取前端传的amount,直接用course.price防止篡改 amount = course.price # 免费课直接关联 if amount == 0: UserCourse.objects.get_or_create(user=user, course=course) return redirect('course:course-content', course.slug) # 付费课发起支付 payment_link = process_payment(request, name, email, amount, course, user) return redirect(payment_link) return render(request, "course_checkout.html", {"course": course}) def process_payment(request, name, email, amount, course, user): auth_token = settings.FLUTTER_SECRET_KEY hed = {'Authorization': 'Bearer ' + auth_token} # 生成唯一交易流水号 tx_ref = str(math.floor(1000000 + random.random()*9000000)) # 提前写入待支付交易记录,绑定用户、课程、流水号的关联关系 PaymentTransaction.objects.create( tx_ref=tx_ref, user=user, course=course, amount=amount ) data = { "tx_ref": tx_ref, "amount": amount, "currency": "USD", "redirect_url": f"{request.scheme}://{request.get_host()}/callback", "payment_options": "card", "customer": { "email": email, "name": name }, "customizations": { "title": "DexxaEd", "description": "Limitless Learning For Everyone" } } # 支付网关请求地址 url = 'https://api.flutterwave.com/v3/payments' response = requests.post(url, json=data, headers=hed) response_data = response.json() return response_data['data']['link']
第三步:修改回调视图写入关联数据
回调阶段通过tx_ref查询对应的交易记录,校验支付状态后写入UserCourse关联,同时加幂等判断避免重复写入,替换payment_response视图代码:
from django.views.decorators.http import require_http_methods @require_http_methods(['GET', 'POST']) def payment_response(request): status = request.GET.get('status', None) tx_ref = request.GET.get('tx_ref', None) # 流水号不存在直接返回失败页 try: transaction = PaymentTransaction.objects.get(tx_ref=tx_ref) except PaymentTransaction.DoesNotExist: return render(request, "payment/payment-failed.html") if status == "successful": # 幂等校验:交易未处理过才更新状态、写入关联 if transaction.status != PaymentTransaction.SUCCESS: transaction.status = PaymentTransaction.SUCCESS transaction.save() # get_or_create避免重复生成关联记录 UserCourse.objects.get_or_create(user=transaction.user, course=transaction.course) return render(request, "payment/payment-success.html", {"course": transaction.course}) if status == "cancelled": transaction.status = PaymentTransaction.FAILED transaction.save() return render(request, "payment/payment-failed.html")
注意事项
- 正式环境必须加支付签名校验,不要完全信任前端回调带的
status参数,建议回调时服务端主动调用支付网关的查询接口,用tx_ref核实真实支付状态,避免伪造回调 - 免费课、支付成功写入关联时都要用
get_or_create,避免用户重复点击生成重复的购课记录 - 原有
course_details视图缺少返回逻辑,访问会报错,记得补充对应的render响应
内容的提问来源于stack exchange,提问作者Destiny Franks
相关产品推荐
相关产品推荐

