如何在Django中实现NETS支付webhook 解决支付确认及页面重载问题
首先明确webhook的运行逻辑:它不是你需要主动发起的轮询请求,是NETS平台在支付状态发生变更时,主动向你预先配置的服务器地址发送的POST回调请求,你只需要提供一个公网可访问的接口接收这个请求,更新对应订单的支付状态即可。
具体实现步骤
1. 新增专属Webhook视图和路由
不要把webhook处理逻辑塞到现有Payment视图里,单独创建一个视图专门处理NETS回调,需要豁免CSRF校验(第三方平台请求不会携带你的站点CSRF Token),同时校验请求头的Authorization字段防止伪造请求:
import json from django.views import View from django.http import HttpResponse from django.views.decorators.csrf import csrf_exempt from django.utils.decorators import method_decorator # 导入你自己的订单工具类/模型 @method_decorator(csrf_exempt, name='dispatch') class NETSWebhookView(View): # 自行生成8-32位字母数字串作为校验密钥,建议存在Django settings中不要硬编码 WEBHOOK_AUTH_KEY = "自行生成的随机字母数字串" def post(self, request, session_id, *args, **kwargs): # 校验请求合法性,防止恶意伪造回调 if request.headers.get("Authorization") != self.WEBHOOK_AUTH_KEY: return HttpResponse(status=403) # 解析回调事件数据 event = json.loads(request.body) # 判断是否为支付完成事件,事件名以NETS官方文档为准 if event.get("eventName") == "payment.completed": # 更新对应session订单的支付状态为已支付 webshopUtils.update_order_payment_status( session_id_key=session_id, status="paid" ) # 必须返回200状态码,否则NETS会认为回调失败重复发送请求 return HttpResponse(status=200)
在urls.py中添加对应路由,路由路径中携带session_id参数,这样NETS回调时你可以直接从路径中拿到标识关联对应订单:
urlpatterns = [ # 原有路由保持不变 path("nets-webhook/<str:session_id>/", views.NETSWebhookView.as_view(), name="nets_webhook"), ]
注意:生产环境该地址必须为HTTPS,本地开发可使用ngrok等内网穿透工具获取临时公网HTTPS地址测试。
2. 创建支付ID时注册Webhook
修改你调用get_paymentId的代码,补上notifications参数,把刚才配置的webhook地址注册到NETS:
# 拼接webhook公网地址,生产环境替换为你自己的站点域名 webhook_url = f"https://你的站点域名/nets-webhook/{session_id_key}/" notifications_params = { "webhooks": [ { "eventName": "payment.completed", "url": webhook_url, "authorization": NETSWebhookView.WEBHOOK_AUTH_KEY } ] } # 调用创建支付接口时传入notifications参数 paymentId = payment.get_paymentId( platform = platform, reference = reference, name = 'Hidden Dimsum 2900 Takeaway', paymentReference = session_id_key, unitPrice = int(totalPrice) *100, notifications = notifications_params # 新增该参数 )
3. 修改现有Payment视图逻辑
你原来留空的elif分支不需要放webhook处理逻辑,这里是用户跳转回站点的访问场景,直接查询当前订单的支付状态做跳转即可,解决用户刷新页面重复生成支付单的问题:
elif ('deliveryForm' not in request.GET) and ('pickupForm' not in request.GET): # 查询当前session对应的订单支付状态 current_order = webshopUtils.get_order(session_id_key=request.session[session_id_key]) # 已支付直接跳转到支付完成页,不再生成新的支付ID if current_order.payment_status == "paid": return redirect("paymentComplete")
4. 兼容原有前端逻辑
原有JS的支付完成跳转逻辑可以保留,webhook作为兜底方案:即使用户刷新页面、中途关闭支付页再回来,只要NETS已经推送了支付完成的回调,后台订单状态已经更新,用户访问支付页就会直接跳转到完成页,不会出现JS失效无法跳转的问题。
内容的提问来源于stack exchange,提问作者jxw
相关产品推荐
相关产品推荐

