Postman运行正常生产环境异常:Django变量无法填充login.html
问题根源与解决方案
核心问题:回调响应的接收对象错误
Mpesa的回调请求是直接发送到你的服务器,并非来自用户的浏览器。你在confirmation函数里返回render(request, template, context),这个响应是发给Mpesa服务器的,用户的浏览器完全接收不到,自然无法填充voucher变量。
另外,你使用了全局变量voucher,这在多用户场景下会导致严重的数据混乱——如果同时有两个用户支付,后生成的voucher会覆盖前一个的,完全不可用。
修复步骤
1. 移除全局变量,将voucher关联到支付记录
首先修改MpesaPayment模型,添加voucher字段:
# models.py class MpesaPayment(models.Model): # 保留原有字段... voucher = models.CharField(max_length=20, null=True, blank=True)
执行数据库迁移后,在confirmation函数里把voucher保存到支付记录中,替换全局变量:
# views.py def confirmation(request): # 删除全局变量声明:global profile, voucher # ...其他原有代码... if result_code == 0: # ...生成voucher的代码... payment.voucher = voucher # 新增:将voucher绑定到当前支付对象 payment.save() # ...其他原有代码... # Mpesa只需要确认响应,无需返回HTML页面 return HttpResponse("Success") # 给Mpesa返回简单成功响应
2. 实现前端轮询获取voucher
用户发起支付后,浏览器需要持续检查支付是否完成、voucher是否生成。
步骤2.1:新增检查voucher的视图
# views.py from django.http import JsonResponse from .models import MpesaPayment def check_voucher(request): # 假设用户发起支付时,你已将手机号存入session phone_number = request.session.get('user_phone') if not phone_number: return JsonResponse({'voucher': None}) # 查询该用户最新的成功支付记录 try: latest_payment = MpesaPayment.objects.filter( PhoneNumber=phone_number, ResultCode=0, voucher__isnull=False ).latest('TransactionDate') return JsonResponse({'voucher': latest_payment.voucher}) except MpesaPayment.DoesNotExist: return JsonResponse({'voucher': None})
在urls.py添加对应路由:
path('check-voucher/', views.check_voucher, name='check_voucher'),
步骤2.2:前端login.html添加轮询逻辑
<!-- login.html --> <div>你的WiFi凭证:<span id="voucher-display">等待生成...</span></div> <script> // 每3秒检查一次凭证状态 const pollInterval = setInterval(() => { fetch('/check-voucher/') .then(res => res.json()) .then(data => { if (data.voucher) { document.getElementById('voucher-display').textContent = data.voucher; // 拿到凭证后停止轮询 clearInterval(pollInterval); } }) .catch(err => console.error('检查凭证失败:', err)); }, 3000); </script>
3. 支付发起时存储用户手机号到Session
用户点击支付按钮发起请求时,要把手机号存入session,这样后续check_voucher才能关联到对应的支付记录:
# 处理用户发起支付的视图 def initiate_payment(request): if request.method == 'POST': phone_number = request.POST.get('phone_number') request.session['user_phone'] = phone_number # 发起Mpesa STK请求的代码... return render(request, 'payment_waiting.html')
额外注意事项
- 绝对不要用全局变量存储用户相关数据,必须用数据库或session隔离不同用户的信息
- Mpesa回调只需要你返回200状态码的响应即可,无需返回HTML页面
- 若用户量较大,可考虑用WebSocket替代轮询,实现更实时的凭证推送
内容的提问来源于stack exchange,提问作者Michael Joseph
相关产品推荐
相关产品推荐

