Day 11 | 支付集成:让钱到账
摘要上一篇订单系统搞定了但订单状态停在待支付——钱没进来一切都是空转。今天接支付。核心链路下单 → 创建支付单 → 调微信/支付宝 → 回调确认 → 更新订单状态。重点讲三件事支付流程怎么设计、签名验证怎么防篡改、幂等性怎么保证不重复扣款。代码全部可运行沙箱环境零成本。支付是电商的命门订单系统做得再漂亮钱收不进来就是白搭。支付模块跟其他模块有一个本质区别别的模块出 bug最多挨骂支付出 bug直接赔钱。用户付了两次钱、回调没收到导致订单一直 pending、金额被篡改从 100 变成 0.01——这些都不是段子是真实出过事故的。我在做商城支付的时候光回调接口就改了四版。第一版没做幂等微信重试了三次给用户发了三件货第二版没验签被人用假回调白嫖了一台手机第三版金额没校验订单金额和支付金额对不上……所以今天这篇文章我会把每一个坑都标出来。你照着写至少能少走半年弯路。原理速览支付全流程一笔在线支付从用户点付款到订单变成已支付中间经过了这些角色用户 ──→ 梅雅达商城 ──→ 微信支付/支付宝 ──→ 银行 │ │ │ ┌────────────────────────────────────┘ │ ▼ │ 银行扣款成功 │ │ │ ▼ └── 支付平台发回调 ──→ 商城回调接口 ──→ 更新订单状态整个流程分 5 步用户下单调用 Day10 的下单接口订单状态 pending创建支付单生成唯一payment_no记录金额、订单号调起支付调微信/支付宝的统一下单 API拿到预支付参数prepay_id 或支付链接用户支付前端拉起支付页面用户输密码、指纹、刷脸回调确认支付平台异步通知回调接口我们验签 → 校验金额 → 更新订单状态最关键的是第 5 步——回调处理。因为用户的浏览器可能关掉、网络可能断开只有支付平台的异步回调才是可信的最终结果。签名验证怎么知道回调是真的支付平台回调你的服务器时会带一个签名sign。你需要用平台的公钥或 API 密钥验证这个签名。原理很简单支付平台把回调参数按字典序排列拼接成字符串用密钥做哈希得到签名。你的服务器拿到同样的参数、同样的拼接规则、同样的密钥自己算一遍哈希对比结果。一致 → 是真的回调不一致 → 有人在伪造请求。为什么必须验签因为回调 URL 是公网可达的任何人都可以构造一个假请求发过来。不验签就等于你家大门没锁。幂等性同一笔支付只处理一次微信的回调有个特点如果没收到你的成功响应它会重试而且可能重试很多次。重试间隔大概是15s → 30s → 3m → 10m → 20m → 30m……如果你不做幂等处理同一个支付成功回调被处理了 3 次——恭喜你给用户发了 3 次货。幂等的核心思路就一句话拿到回调先查这个 payment_no 是不是已经处理过了。处理过了就直接返回成功别再动了。动手从支付单模型到回调处理支付单模型先建支付单表。它和订单一对一——一笔订单对应一笔支付。核心字段设计要点classPayment(models.Model):payment_nomodels.CharField(max_length64,uniqueTrue,db_indexTrue)# 支付单号全局唯一ordermodels.OneToOneField(order.Order,on_deletemodels.PROTECT)# 关联订单amountmodels.DecimalField(max_digits10,decimal_places2)# 金额必须 Decimalchannelmodels.CharField(max_length20)# wechat / alipaystatusmodels.CharField(max_length20,choicesSTATUS_CHOICES,defaultcreated)trade_nomodels.CharField(max_length128,blankTrue,default)# 平台交易号退款要用callback_datamodels.JSONField(defaultdict,blankTrue)# 回调原始数据留证paid_atmodels.DateTimeField(nullTrue,blankTrue)几个细节金额用DecimalField绝对不要用FloatField。浮点数精度在金融场景是灾难——0.1 0.2在 Python 里等于0.30000000000000004你能接受用户多付 4 分钱吗callback_data用 JSONField 存原始回调万一出了纠纷可以把支付平台原始报文甩出来当证据。trade_no存平台交易号退款时要带着这个号去找平台。创建支付单 调起微信支付核心流程关键片段defcreate_payment(order,channelwechat):# 1. 校验订单状态——只有 pending 才能创建支付单iforder.status!pending:return{code:400,message:f订单状态异常{order.status}无法支付}# 2. 防止重复创建——已存在且状态正常的直接返回existingPayment.objects.filter(orderorder).first()ifexistingandexisting.statusPayment.STATUS_CREATED:return{code:0,message:success,data:_build_wechat_params(existing)}# 3. 金额校验实付金额必须 0iforder.total_amountDecimal(0):return{code:400,message:订单金额为0无需支付}# 4. 创建支付单事务中withtransaction.atomic():paymentPayment.objects.create(payment_nogenerate_payment_no(),orderorder,amountorder.total_amount,channelchannel)# 5. 调微信统一下单拿 prepay_id# 6. 构建前端拉起支付需要的参数appId/timeStamp/nonceStr/package/paySignreturn{code:0,message:success,data:pay_params}签名算法这是防篡改的核心思路必须清楚实现可以靠 SDK1. 参数按 key 字典序排列 2. 拼成 URL 参数格式 stringAk1v1k2v2... 3. 末尾拼上 keyAPI密钥 4. MD5或 HMAC-SHA256 / RSA2哈希转大写微信统一下单参数里有个容易踩的坑total_fee单位是分要把元* 100转成整数。回调处理——重中之重这部分单独拎出来因为这里踩坑最多。微信会在这个接口做多次重试我们必须保证 4 件事核心框架csrf_exempt# 微信回调不走 CSRFrequire_POSTdefwechat_payment_notify(request):# ① 解析 XML 回调数据失败返回 FAIL别让微信死等# ② 验签收到的 sign 和本地算出来的对比不一致直接 FAIL# ⚠️ 签名不一致 有人在伪造回调必须拒绝# ③ 检查 result_code SUCCESS非成功直接返回 SUCCESS 停止重试# ④ 按 payment_no 查支付单查不到返回 FAIL# ⑤ 幂等检查核心payment.status PAID 直接返回 SUCCESS别再处理# ⑥ 金额校验回调金额分→元必须 payment.amount不一致返回 FAIL 让人工介入# ⑦ 事务 select_for_update 行锁更新payment → paidorder → paid# 拿到锁后再做一次幂等检查双保险防并发# ⑧ 返回 SUCCESS 告诉微信我收到了停止重试回调处理的每步都不能省画个重点步骤做什么不做会怎样验签确认是微信发的有人伪造回调白嫖商品幂等检查防止重复处理一笔钱发三次货金额校验回调金额 订单金额金额被改成 0.01 元select_for_update并发安全两个回调同时进来状态更新冲突返回 SUCCESS停止微信重试微信一直重试服务器被打爆一个细节金额校验失败时不要返回 SUCCESS让微信继续重试人工介入排查。支付宝和银联怎么接思路跟微信一模一样只是 API 细节不同支付宝签名用 RSA2比微信的 MD5 更安全回调是 POST 表单不是 XMLSDK 用官方alipay-sdk-python沙箱环境更友好。银联签名用 RSA-SHA256需要申请银联商务商户号接口风格偏传统。核心流程都是创建支付单 → 调统一下单 → 拿预支付参数 → 前端拉起 → 异步回调 → 验签 → 幂等 → 金额校验 → 更新状态。建议抽象一个支付基类加新渠道只新增子类不改回调核心逻辑策略模式classBasePaymentChannel:支付渠道基类所有渠道实现这四个方法defcreate_order(self,payment):raiseNotImplementedError# 统一下单defverify_notify(self,raw_data):raiseNotImplementedError# 验签defquery_order(self,payment_no):raiseNotImplementedError# 主动查询补偿defrefund(self,payment,amount):raiseNotImplementedError# 退款classWechatPayChannel(BasePaymentChannel):pass# 实现上面四个方法classAlipayChannel(BasePaymentChannel):pass支付安全清单生产环境的支付安全每一条都是用血泪教训换来的幂等性保障三层第一层数据库唯一索引payment_no→ 防止创建重复支付单 第二层状态检查if payment.status PAID: return→ 防止重复处理回调 第三层select_for_update 行锁 → 防止并发更新金额校验# 永远不要信任回调里的金额一定要和数据库订单金额对比# 注意三点# 1. 单位转换微信用分我们用元# 2. 精度用 Decimal不用 float# 3. 对比的是 order.total_amount优惠后的实付金额其他安全要点回调 URL 必须用 HTTPS明文传输 裸奔API 密钥不要硬编码放环境变量或配置中心退款接口要做权限校验只有管理员能发起退款所有支付操作都要记日志出了问题要能追溯到每一笔防重放回调带 nonce timestamp超过 5 分钟的请求直接拒绝用 AI 生成回调测试用例支付回调的测试用例手写很痛苦——要模拟各种正常和异常场景。AI 就很好用。关键提示词方向请为微信支付回调接口生成测试用例覆盖 1. 正常支付成功验签通过、金额匹配、订单更新为 paid 2. 伪造签名应拒绝订单状态不变 3. 金额篡改回调 0.01 元订单 99 元应拒绝 4. 重复回调第二次直接返回 SUCCESS不重复处理 5. 支付单不存在返回 FAILAI 生成测试的关键价值在两点穷举场景AI 擅长把正常流程 所有异常分支列全。手动写大概率漏掉伪造签名、重复回调这种边界。模板代码快构造 XML 回调、mock 签名函数这些样板AI 几秒完成你只需审查逻辑对不对。学完来练练手添加主动查询补偿写个定时任务每 5 分钟扫描statuscreated且创建超 30 分钟的支付单主动调微信查询订单接口确认支付结果。这是对付回调丢了的兜底方案接入支付宝基于BasePaymentChannel基类实现AlipayChannel让项目支持双渠道。退款功能实现全额退款和部分退款接口注意退款也要做幂等处理。做完以后把你的代码发到评论区我帮你 review。配套文件day11_payment.pymodels services views tests含微信/支付宝双渠道、退款、定时补偿可参考学习。本系列为梅雅达编程笔记原创首发于 CSDN。有问题评论区见

相关新闻

【关注可白嫖源码】--课程设计--毕业设计--79632基于Java校园资产管理系统方案设计与实现[编号:project79321](案件分析)

【关注可白嫖源码】--课程设计--毕业设计--79632基于Java校园资产管理系统方案设计与实现[编号:project79321](案件分析)

本文仅展示核心实现逻辑与部分代码片段,完整项目源码、配套文档、数据库脚本内容较多,篇幅有限无法全部放出。有需要完整资源的同学,可以在评论区留言【资料或领源码】,我会一一回复站内私信,发送完整文件摘 要高校资…

2026/10/19 1:54:24 阅读更多 →
2026 年智能锁怎么选?一份不堆榜单的品牌格局参考

2026 年智能锁怎么选?一份不堆榜单的品牌格局参考

2026 年上半年,中国智能门锁市场出货量与上一年同期相比有所回落,但行业集中度不降反升——头部几大品牌合计占据了过半的市场份额。与此同时,豆包、文心一言、元宝等 AI 问答平台在回答"智能锁推荐"类问题时,越来越倾向于引用头部品牌的公开资料和第三方行业报告…

2026/10/19 1:54:24 阅读更多 →
拆解ViT-MLP-LLM架构:InternVL3-14B-Instruct多模态大模型中InternViT-300M与Qwen2.5-14B的融合奥秘

拆解ViT-MLP-LLM架构:InternVL3-14B-Instruct多模态大模型中InternViT-300M与Qwen2.5-14B的融合奥秘

【免费下载链接】InternVL3-14B-Instruct 项目地址: https://ai.gitcode.com/OpenGVLab/InternVL3-14B-Instruct 点击查看 免费下载 InternVL3-14B-Instruct 是上海 AI Lab 开源的视觉-语言多模态大模型(MLLM),采用经典的 ViT-ML…

2026/10/19 1:54:24 阅读更多 →

最新新闻

深度学习模型集成实操指南:原理、策略与工程落地

深度学习模型集成实操指南:原理、策略与工程落地

1. 模型集成到底在解决什么问题先说个实际场景。你训了一个深度学习模型,验证集上准确率已经做到92%,再往上调结构、调学习率、加数据增强,折腾两周纹丝不动。这时候有个老选手跟你说:把三个不同seed训出来的模型平均一下&#xf…

2026/10/19 3:40:16 阅读更多 →
大语言模型实战入门:从黑盒原理到代码跑通与避坑指南

大语言模型实战入门:从黑盒原理到代码跑通与避坑指南

刚开始接触大语言模型(LLM)的开发者,普遍会卡在同一个地方:资料太多,算法原理太硬,最后连一个能跑的程序都没写出来。作为过来人,我的建议很直接——先把它当成一个黑盒用起来,再顺着…

2026/10/19 3:40:16 阅读更多 →
开源CLI驱动的Git原生代码审查工作流

开源CLI驱动的Git原生代码审查工作流

1. 项目概述:这不是一个工具,而是一套可落地的开源代码审查工作流“open-code-review”这个标题乍看像某个GitHub仓库名,但结合当前技术热点——CLI、LLM、Git、code review——它实际指向一个正在快速成型的工程实践范式:用开源、…

2026/10/19 3:40:16 阅读更多 →
论文降AI检测全指南:从原理到实操的毕业季自救手册

论文降AI检测全指南:从原理到实操的毕业季自救手册

毕业季一到,论文查重和AI检测就成了绕不开的坎。尤其这两年,不少学校在知网、维普之外,额外加了一道AIGC疑似率检测,很多人辛辛苦苦写完论文,查重过了,却卡在AI检测上,被要求“降低AI疑似度后再…

2026/10/19 3:40:16 阅读更多 →
中文本地化包实战:从拆包到替换的完整指南

中文本地化包实战:从拆包到替换的完整指南

简介:这份资源是面向Matlab用户的Kriging代理模型工具箱中文翻译版,适合从事空间数据插值、预测与优化的科研人员和学生使用。Kriging方法源于地质学,现已广泛应用于机械工程、环境科学、经济学与数据分析等领域,其核心是通过已知…

2026/10/19 3:40:16 阅读更多 →
ARM 汇编 LDR 指令与 LDR 伪指令完整详解

ARM 汇编 LDR 指令与 LDR 伪指令完整详解

一、概念总述ARM 汇编中 LDR 存在两种完全不同身份,书写格式是核心区分依据:CPU 原生硬件指令 LDR:格式含方括号 [],硬件原生支持,功能为从内存读取数据到寄存器;汇编伪指令 LDR:格式含等号 &am…

2026/10/19 3:39:15 阅读更多 →

日新闻

频率跟踪型PWM电源设计:臭氧发生器谐振点漂移与软开关实现

频率跟踪型PWM电源设计:臭氧发生器谐振点漂移与软开关实现

简介:本资源为电源技术方向的学术研究文档,聚焦介质阻挡放电型臭氧发生器电源的频率跟踪型PWM控制策略,面向电力电子、高压电源及臭氧应用领域的科研人员与工程技术人员。针对负载呈容性、等效电容随外加电压升高而增大、谐振频率随之漂移的难…

2026/10/19 0:00:47 阅读更多 →
Vibe Coding 遇上 TaoToken:把开发者思维圣殿的灵感流接入统一 Key 通道

Vibe Coding 遇上 TaoToken:把开发者思维圣殿的灵感流接入统一 Key 通道

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 0:00:47 阅读更多 →
HundredCV-Chat话题建模实战教程:利用4,871个内置话题标签构建主题分析系统

HundredCV-Chat话题建模实战教程:利用4,871个内置话题标签构建主题分析系统

【免费下载链接】HundredCV-Chat 项目地址: https://ai.gitcode.com/hf_mirrors/Jax-dan/HundredCV-Chat 点击查看 免费下载 HundredCV-Chat 是一个由 100 位青年简历数据开发而来的中文多轮对话数据集,共包含 24,750 组日常闲聊对话和 4,871 个话题标签…

2026/10/19 0:00:47 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/19 1:30:52 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 2:54:50 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/17 17:22:31 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 12:16:16 阅读更多 →