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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 21:48:23