news 2026/11/3 2:48:04

Flask电商毕设接入支付宝沙箱支付|从开通到回调验签全流程(附源码)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Flask电商毕设接入支付宝沙箱支付|从开通到回调验签全流程(附源码)

⚠️免责声明:本文项目仅供技术学习参考,严禁直接作为毕业设计或课程作业提交。请结合自身需求进行二次开发与创新,学术诚信从我做起 🙂


先说我踩通的路

05篇讲订单的时候,支付那一段我只写了个"模拟支付成功"——点一下按钮,订单状态从待支付变成待发货。

能跑,但答辩的时候老师大概率会追一句:

“你这个支付,是真的吗?”

这一问,档次就分出来了。

接支付宝沙箱,不需要营业执照、不需要企业认证、不用花一分钱。沙箱是支付宝官方给开发者准备的联调环境,跟生产环境完全隔离,随便测。

跑通之后,你能拿到一整套真实链路:生成支付链接 → 跳转到支付宝 → 付款 → 支付宝服务器回调你的接口 → 验签 → 改订单状态。

答辩现场拿手机扫一下、付个 0.01 元,订单状态实时变掉,这个演示效果,比讲十分钟原理管用。

这篇就按我自己当时踩通的顺序来写,一步不跳。


第0步:先搞明白支付的完整链路

动手之前,先把链路在脑子里过一遍,不然代码会写得很懵。

用户点"去支付" ↓ 后端生成支付链接(带签名) ↓ 浏览器跳到支付宝收银台 ↓ 用户付款 ↓ 支付宝同时做两件事: ├─ 同步跳转 → return_url(用户浏览器跳回你的页面) └─ 异步通知 → notify_url(支付宝服务器 POST 给你的服务器) ↓ 你验签通过 → 把订单改成"待发货"

这里有个关键认知,答辩必问:

真正改订单状态的,是异步通知(notify_url),不是同步跳转。

  • 同步跳转(return_url)是给用户看的,浏览器跳回来显示"支付成功"。但它不可靠:用户可能中途关掉浏览器,或者网络断了,你根本收不到。
  • 异步通知(notify_url)是支付宝服务器直接 POST 给你的服务器,可靠得多。你没返回成功,它还会按 0秒、2分钟、10分钟、1小时、2小时、6小时、15小时 的间隔重试,最多 7 次。

所以记住一句话:改状态只信 notify,return 只负责给用户看。


第1步:开通沙箱,拿到 4 样东西

打开支付宝开放平台,扫码登录,进"研发服务 / 沙箱环境"。

你需要拿到 4 样东西:

名称用途
APPID沙箱应用的唯一标识
网关地址沙箱是openapi.alipaydev.com系
应用私钥你用它给请求签名
支付宝公钥你用它验证支付宝的签名

密钥怎么生成:

下载支付宝官方的"密钥生成器"(或自己用 openssl 生成 RSA2 密钥对),生成之后:

  1. 工具里会给你一个应用公钥,复制它,填到沙箱的"开发信息 → 接口加签方式"里,保存
  2. 保存设置后,页面会显示一个支付宝公钥,把这个复制下来
  3. 你自己的应用私钥也保存好

⚠️第一个大坑,也是新手 80% 报错的原因:

应用公钥 ≠ 支付宝公钥。

你自己生成的那对密钥里,公钥是交给支付宝的;支付宝会另外给你一个它自己的公钥。

代码里初始化时,填的是支付宝公钥,不是你自己生成的公钥。

我第一次接的时候就是填错了这个,报了一晚上验签失败,最后对着官方文档一行行看才发现。

还有一点:复制密钥的时候,前后绝对不能有空格和换行。多一个空格,验签就废。

沙箱买家账号:沙箱环境 → 测试账号,里面有买家账号和登录密码,付款时用。记得下载"沙箱版支付宝 App",用它登录买家账号,不然付不了款。


第2步:装 SDK,写配置

pipinstallpython-alipay-sdk

新建一个alipay_config.py:

# alipay_config.pyfromalipayimportAliPay# 应用私钥(你自己生成的,注意首尾行固定,中间是密钥串,不能有空格)APP_PRIVATE_KEY_STRING="""-----BEGIN RSA PRIVATE KEY----- 这里粘贴你的应用私钥 -----END RSA PRIVATE KEY-----"""# 支付宝公钥(注意!是支付宝给你的那个,不是你自己生成的)ALIPAY_PUBLIC_KEY_STRING="""-----BEGIN PUBLIC KEY----- 这里粘贴支付宝公钥 -----END PUBLIC KEY-----"""# 沙箱 APPIDAPP_ID="你的沙箱APPID"# 沙箱网关GATEWAY="https://openapi.alipaydev.com/gateway.do"defget_alipay():"""返回一个配置好的 AliPay 实例"""returnAliPay(appid=APP_ID,app_notify_url=None,# 默认通知地址,一般设 Noneapp_private_key_string=APP_PRIVATE_KEY_STRING,alipay_public_key_string=ALIPAY_PUBLIC_KEY_STRING,sign_type="RSA2",# 签名算法,推荐 RSA2debug=True,# True=沙箱环境,False=生产环境)

关于debug=True:它会让 SDK 用沙箱网关。但支付宝近两年调整过沙箱域名,如果你的报错提示"网关不正确",把 SDK 内部网关手动改成新版:

alipay=get_alipay()alipay._gateway="https://openapi-sandbox.dl.alipaydev.com/gateway.do"

第3步:下单之后,跳去支付

先看整条支付链路在我们 demo 里的样子(demo 用模拟支付,流程和真实沙箱一致,就是不会真的跳支付宝收银台):

图:下单后,订单列表里显示「待支付」

图:进订单详情,点「去支付」

图:进入支付页(demo 里是模拟支付,选了支付宝)

图:支付完成,订单变成「待发货」

05篇的下单接口跑完之后,返回的是order_id。用户点"去支付",就走下面这个接口。

# views/pay.pyfromflaskimportrequest,redirect,current_appfromflask_loginimportlogin_required,current_userfrommodelsimportOrderInfofromalipay_configimportget_alipay,GATEWAY@app.route('/order/pay/<int:order_id>')@login_requireddeforder_pay(order_id):"""生成支付链接并跳转"""order=OrderInfo.query.filter_by(id=order_id,user_id=current_user.id).first_or_404()# 只有待支付状态才能去付款iforder.status!=0:return"订单状态异常,无法支付"alipay=get_alipay()# 生成支付参数串(已带签名)order_string=alipay.api_alipay_trade_page_pay(out_trade_no=order.order_no,# 你的订单号(必须唯一)total_amount=str(order.pay_amount),# 金额,字符串,两位小数subject=f"MYD商城订单{order.order_no}",# 订单标题return_url="http://你的域名/order/pay/return",notify_url="http://你的域名/order/pay/notify",)# 拼成完整支付地址,跳过去pay_url=GATEWAY+"?"+order_stringreturnredirect(pay_url)

几个参数要注意:

  • out_trade_no用你自己的订单号,必须唯一,支付宝靠它跟你对账
  • total_amount是字符串,不是数字。str(order.pay_amount)就对了
  • subject随便写,用户会在收银台看到

第4步:同步回调 return_url

用户付完款,浏览器会跳回这个地址。它的作用就一个:告诉用户"付好了"。

@app.route('/order/pay/return')@login_requireddeforder_pay_return():"""同步回调:只负责给用户看结果"""data=request.args.to_dict()alipay=get_alipay()signature=data.pop('sign',None)data.pop('sign_type',None)ifalipay.verify(data,signature):# 验签通过,展示成功页returnrender_template('order/pay_success.html')else:returnrender_template('order/pay_fail.html')

注意:这里不要改订单状态。

原因前面说过了:同步回调可能丢。改状态放到异步通知里做。

同步回调里只做一件事:验签,然后给用户看个结果页。真正的状态更新,在下一步。


第5步:异步通知 notify_url(核心中的核心)

这个接口,是支付宝服务器来调你的,不是你用户点的。

@app.route('/order/pay/notify',methods=['POST'])deforder_pay_notify():"""异步通知:真正改订单状态的地方"""data=request.form.to_dict()alipay=get_alipay()signature=data.pop('sign',None)data.pop('sign_type',None)# ========== 1. 验签 ==========ifnotalipay.verify(data,signature):return"fail"# 验签失败,告诉支付宝别重试了(或者让它重试)# ========== 2. 二次校验(重要!) ==========out_trade_no=data.get('out_trade_no')trade_status=data.get('trade_status')total_amount=data.get('total_amount')order=OrderInfo.query.filter_by(order_no=out_trade_no).first()ifnotorder:return"fail"# 校验金额对不对(防止有人改金额)ifstr(order.pay_amount)!=str(total_amount):return"fail"# ========== 3. 幂等处理 ==========# 支付宝会重复通知,如果订单已经是待发货了,直接返回 success,别重复处理iforder.status!=0:return"success"# ========== 4. 只有交易成功才改状态 ==========iftrade_statusin('TRADE_SUCCESS','TRADE_FINISHED'):order.status=1# 待发货order.pay_type='alipay'order.pay_time=datetime.now()db.session.commit()# ========== 5. 必须返回 success 字符串 ==========return"success"

这段代码里有 4 个点,每一个都能变成答辩题:

① 为什么必须返回success?

支付宝收到success才算通知成功。你返回别的、或者报错了,支付宝会一直重试,重试 7 次还不行,你的订单状态可能永远停在"待支付"。

② 为什么要二次校验?

验签只证明"这条通知是支付宝发的",但不证明"这条通知的内容是对的"。还得自己核对:

  • out_trade_no是不是我系统里真实存在的订单号
  • total_amount跟我订单金额对不对得上(防止有人篡改金额)
  • app_id是不是我自己的应用

③ 为什么要做幂等?

支付宝重试机制会导致同一条通知来好几次。如果每次都执行"改状态 + 加销量",就会重复处理。所以进业务前先判断:订单已经是待发货了,直接返回 success。

④ 为什么返回 “success” 而不是 json?

支付宝只认这个纯字符串,别的都不认。这是接口约定。


第6步:本地调试怎么收到异步通知

这一步卡住了无数人。

异步通知是支付宝服务器来调你的接口,它得能通过公网访问到你。你在本地127.0.0.1:5000跑,支付宝是找不到的。

解决办法:内网穿透。

用 ngrok、花生壳、cpolar 这类工具,把你的本地 5000 端口映射到一个公网地址,然后 notify_url 填这个公网地址。

以 ngrok 为例:

ngrok http5000

它会给你一个类似https://xxxx.ngrok-free.app的公网地址,那么:

notify_url="https://xxxx.ngrok-free.app/order/pay/notify"

这样支付宝才能 POST 到你本地。

排查小技巧:ngrok 的免费版可以在它本地面板http://127.0.0.1:4040看到所有进来的请求。如果支付宝回调了但你代码没反应,先去这个面板看请求有没有到、返回了什么。


三个必踩的坑

坑1:应用公钥填成了支付宝公钥

前面说过,再强调一遍。你生成密钥对,得到的是"应用公钥 + 应用私钥"。应用公钥交给支付宝,支付宝给你"支付宝公钥"。

代码里初始化填的是支付宝公钥。

症状:所有接口都报验签失败/invalid signature。

坑2:本地调试收不到异步通知

没做内网穿透,或者 notify_url 写的是127.0.0.1/localhost。

支付宝服务器访问不到你的本地地址,通知永远不来。

判断方法:同步跳转能看到"支付成功"页面,但订单状态没变,那就是 notify 没收到。

坑3:金额传成了数字

# 错误total_amount=order.pay_amount# 正确total_amount=str(order.pay_amount)

支付宝要求金额是字符串。传数字可能报参数错误,或者金额精度出问题。


支付这块,答辩爱问这几问

1. 同步回调和异步回调有什么区别?

答:同步回调(return_url)是用户付款后浏览器跳回来的地址,作用只是展示结果给用户看,可能因为用户关页面或网络问题丢失,不可靠。异步回调(notify_url)是支付宝服务器直接 POST 到我的服务器,可靠且有重试机制。所以我只在异步回调里改订单状态,同步回调只做展示。

2. 为什么支付要验签?

答:防止伪造通知。别人可以构造一个 POST 请求假装是支付宝,告诉我"订单付好了"。验签是用支付宝的公钥验证签名,只有支付宝私钥签出来的数据才能通过,这样能确认通知确实来自支付宝。

3. 为什么验签之后还要做二次校验?

答:验签只能证明"消息是支付宝发的",不能证明"消息内容没被篡改或不是针对我别的订单的"。所以还要核对订单号是否存在于我的系统、金额是否一致、app_id 是不是我的。这是官方文档要求的步骤,双重保险。

4. 支付宝重复通知怎么办?

答:做幂等处理。进业务逻辑之前先查订单当前状态,如果订单已经是"待发货"了,说明这条通知已经处理过,直接返回 success 不再重复执行。否则会出现重复加销量、重复改状态的问题。

5. 支付超时怎么处理?

答:一是给订单设过期时间,用定时任务定期扫描,超过 30 分钟未支付的订单自动取消并回滚库存;二是在生成支付链接时用timeout_express参数限制支付时长。两者结合,避免订单一直卡在"待支付"占着库存。

6. 沙箱环境和生产环境有什么区别?

答:沙箱是支付宝提供的隔离测试环境,网关地址、APPID、密钥都不同,用沙箱买家账号付款,不走真实资金。上线时要把网关、APPID、密钥换成生产环境的,并完成应用上线审核。沙箱可以随便测,生产要慎重。


支付这一块,代码其实不多,难点全在概念上。

同步 / 异步、验签 / 二次校验、幂等——这几个词搞懂了,接支付就没什么神秘的。

答辩的时候,你能把"异步通知才是改状态的唯一依据"这句话讲清楚,老师基本就信你是真碰过这套东西。🍃


梅雅达编程笔记原创 · 首发 CSDN · 转载请注明出处

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/11/3 2:44:15

人形机器人的主要构成和测试解决方案详解

一、人形机器人的主要构成&#xff1f;人形机器人的主要构成包括执行器、感知系统、控制与交互、动力、结构件、热管理系统等运动执行系统。二、人形机器人的测试解决方案有哪些&#xff1f;1&#xff09;执行器相关测试&#xff1a;机器人执行器是将能量转化为机器人的机械运动…

作者头像 李华
网站建设 2026/11/3 2:42:39

GEO 峰会四类人:方法论开始过剩,执行还没跟上

那天会场里坐的都是些什么人作者身份说明&#xff1a;我是龙孚信息技术&#xff08;上海&#xff09;股份有限公司的售前助理&#xff0c;这届峰会我也在现场。下面是我站在签到台边的观察&#xff1b;涉及我方产品与获奖的表述标注"厂商口径"&#xff0c;嘉宾发言与…

作者头像 李华
网站建设 2026/11/3 2:41:51

Linux 内核学习(19) --- linux waitqueue

目录Linux 等待队列背景和动机核心结构和概念什么是等待队列其余的任务阻塞方法等待队列和 select/poll/epoll 的关系进程状态和等待队列的交互使用等待队列时任务所处的状态Linux 任务状态总览与进入原因主要 API 和数据结构数据结构等待队列和唤醒 API手工控制流程示例说明使…

作者头像 李华
网站建设 2026/11/3 2:41:02

2026年仍然支持 Windows7的浏览器

Windows 7在2020年就已结束官方支持&#xff0c;但国内仍有相当规模的设备在运行这个系统。浏览器作为连接互联网的核心入口&#xff0c;它的选择直接决定了这些老机器还能不能相对完整地打开今天的网页。主流通道早已关闭&#xff0c;但仍有几条路可以走&#xff0c;每一条路的…

作者头像 李华