Django 从 0 到 1 打造完整电商平台:集成支付宝沙箱支付
IT策士 10余年一线大厂经验,专注 IT 思维、架构、职场进阶。我会在公众号、今日头条持续发布最新文章,助你少走弯路。
上一篇我们完成了从购物车到订单的全流程——用户勾选商品、选择地址、提交订单,数据库里已经有了待支付的订单。但用户还没有付钱!今天我们要打通电商的“最后一公里”——集成支付宝沙箱支付,让用户真正扫码付款,完成交易闭环。
支付宝沙箱环境是支付宝官方提供的测试环境,不需要真实资金,不需要营业执照,任何人都可以免费申请。开发阶段用它来调试支付流程,完全够用。本篇我会手把手带你申请沙箱、配置密钥、安装 SDK、编写支付视图,并处理好同步回跳和异步通知两个回调逻辑。
提示:接入支付宝支付并非只有
python-alipay-sdk这一个库,你也可以选择支付宝官方提供的alipay-sdk-python。python-alipay-sdk是社区维护的第三方 SDK(GitHub 仓库:fzlee/alipay),因其封装简洁、对 Django 友好,本系列选择使用它。-
一、支付宝沙箱环境申请与配置
1.1 什么是沙箱环境?
支付宝沙箱是支付宝提供给开发者的测试环境,它拥有独立的 APPID、独立的密钥、独立的网关地址,以及专属的测试账号(买家、卖家各一个)。在沙箱环境中,所有支付操作都不会产生真实资金流动,余额可以随意设置,支付密码也是固定的。-1
沙箱的核心信息包括:
1.2 申请步骤
步骤 1:登录支付宝开放平台
访问 支付宝开放平台,用你的支付宝账号登录(推荐手机扫码登录)。-17
步骤 2:进入沙箱环境
登录后,点击右上角「控制台」→ 在左侧菜单或页面底部找到「沙箱环境」,进入沙箱应用页面。沙箱环境的链接是:https://open.alipay.com/develop/sandbox/app[reference:3]
进入后,你会看到系统已自动为你创建了一个沙箱应用,页面上展示了:
APPID(记录下来,后续配置用)
应用私钥 / 应用公钥 / 支付宝公钥(沙箱默认提供,可直接使用)
支付宝网关地址
沙箱买家账号和卖家账号(含登录密码、支付密码)
步骤 3:获取密钥(两种方式)
方式一(推荐新手):使用沙箱页面自动生成的默认密钥。直接复制应用私钥和支付宝公钥即可,无需自己生成。-1
方式二:自定义密钥。下载支付宝官方「开放平台开发助手」工具,选择 RSA2、PKCS1 格式生成密钥对,将应用公钥上传到沙箱的「接口加签方式」中,保存后获取支付宝公钥。-17
⚠️重要提示:沙箱的 APPID、密钥和正式环境完全不同,不能混用。沙箱买家账号也必须在沙箱页面查看,用你自己的真实支付宝扫码支付会报错。-27
步骤 4:保存关键信息
申请完成后,你需要保存以下信息备用(建议新建一个文本文件):
APPID: 你的沙箱APPID 应用私钥: MIIEpQIBAAKCAQEA...(完整复制,不要漏字符) 支付宝公钥: MIIBIjANBgkqhkiG...(完整复制) 网关地址: https://openapi-sandbox.dl.alipaydev.com/gateway.do 沙箱买家账号: sandbox_xxx@xxx.com 沙箱买家登录密码:111111沙箱买家支付密码:111111二、安装 Python 支付宝 SDK
进入项目虚拟环境,安装python-alipay-sdk:
pipinstallpython-alipay-sdk==3.3.0控制台输出:
Collecting python-alipay-sdk==3.3.0 Downloading python_alipay_sdk-3.3.0-py3-none-any.whl Installing collected packages: python-alipay-sdk Successfully installed python-alipay-sdk-3.3.0这个 SDK 封装了支付宝复杂的签名和验签逻辑,我们只需要调用几个关键方法即可。-1
同时,SDK 依赖pycryptodome进行加密运算,确保已安装:
三、配置 settings.py
将支付宝沙箱的参数写入django_ecommerce/settings.py末尾:
# ==================== 支付宝沙箱环境配置 ====================ALIPAY_APPID='你的沙箱APPID'# 替换为真实 APPIDALIPAY_DEBUG=True# 沙箱环境设为 True,正式环境改为 False# 支付宝沙箱网关地址ALIPAY_GATEWAY='https://openapi-sandbox.dl.alipaydev.com/gateway.do'# 同步回调地址:用户支付成功后,支付宝自动跳回的页面(后续会配置路由)ALIPAY_RETURN_URL='http://127.0.0.1:8000/payment/return/'# 异步通知地址:支付宝服务器主动 POST 通知支付结果(本地开发时可用内网穿透工具)ALIPAY_NOTIFY_URL='http://127.0.0.1:8000/payment/notify/'# 应用私钥(从沙箱页面复制,注意保持原始格式)ALIPAY_APP_PRIVATE_KEY='''-----BEGIN RSA PRIVATE KEY----- 你的应用私钥内容(保持原样,包括换行) -----END RSA PRIVATE KEY-----'''# 支付宝公钥(从沙箱页面复制)ALIPAY_PUBLIC_KEY='''-----BEGIN PUBLIC KEY----- 你的支付宝公钥内容(保持原样,包括换行) -----END PUBLIC KEY-----'''重要说明:
私钥和公钥字符串一定要保持原始的 PEM 格式(包含
-----BEGIN/END-----标记和换行符),复制时注意不要多或少空格、换行。-1ALIPAY_RETURN_URL和ALIPAY_NOTIFY_URL必须是绝对 URL。本地开发用127.0.0.1:8000,异步通知(notify_url)在本地无法被支付宝回调,需要使用内网穿透工具(如 ngrok、花生壳)暴露一个公网地址。我们会在后面详细讲解回调处理。-27-
四、创建支付模块(payment app)
第 2 篇我们已经创建了paymentapp,并定义了Payment模型。现在让它“工作”起来。
4.1 配置 URL 路由
编辑apps/payment/urls.py:
from django.urlsimportpath from.importviews app_name='payment'urlpatterns=[path('go/<int:order_id>/', views.payment_go,name='payment_go'),# 去支付path('return/', views.payment_return,name='payment_return'),# 同步回跳path('notify/', views.payment_notify,name='payment_notify'),# 异步通知]然后在项目django_ecommerce/urls.py中 include:
path('payment/', include('apps.payment.urls')),4.2 修改提交订单后的跳转
打开apps/orders/views.py,找到order_submit视图,将提交成功后的跳转改为支付页面:
# 原来:# return redirect('orders:order_detail', pk=order.pk)# 改为:returnredirect('payment:payment_go',order_id=order.pk)五、编写支付视图
5.1 发起支付——生成支付宝支付链接
编辑apps/payment/views.py:
importlogging from django.shortcutsimportrender, redirect, get_object_or_404 from django.contrib.auth.decoratorsimportlogin_required from django.contribimportmessages from django.views.decorators.csrfimportcsrf_exempt from django.httpimportHttpResponse from django.confimportsettings from alipayimportAliPay, AliPayConfig from orders.modelsimportOrder from .modelsimportPayment logger=logging.getLogger('payment')def get_alipay_client():"""创建并返回 AliPay 实例""" alipay=AliPay(appid=settings.ALIPAY_APPID,app_notify_url=settings.ALIPAY_NOTIFY_URL,# 异步通知地址app_private_key_string=settings.ALIPAY_APP_PRIVATE_KEY,alipay_public_key_string=settings.ALIPAY_PUBLIC_KEY,sign_type='RSA2',debug=settings.ALIPAY_DEBUG,verbose=True,# 控制台输出调试信息config=AliPayConfig(timeout=15),)returnalipay @login_required(login_url='users:login')def payment_go(request, order_id):"""发起支付——跳转到支付宝收银台""" user=request.user order=get_object_or_404(Order,pk=order_id,user=user)# 只有待支付的订单才能发起支付iforder.status!=0: messages.warning(request, f'该订单当前状态为「{order.get_status_display()}」,无法支付。')returnredirect('orders:order_detail',pk=order.pk)# 创建或更新支付记录payment, created=Payment.objects.get_or_create(order=order,defaults={'amount':order.total_amount,'status':0})# 使用 AliPay SDK 生成支付链接alipay=get_alipay_client()order_string=alipay.api_alipay_trade_page_pay(out_trade_no=order.order_no,# 商户订单号total_amount=float(order.total_amount),# 订单金额(元)subject=f'Django商城订单-{order.order_no}',# 订单标题return_url=settings.ALIPAY_RETURN_URL,# 支付成功后同步回跳地址)# 拼接完整的支付宝收银台 URLpay_url=settings.ALIPAY_GATEWAY +'?'+ order_stringreturnredirect(pay_url)核心逻辑解读:
get_alipay_client()每次调用都新建一个AliPay实例,传入 APPID、密钥和网关信息。alipay.api_alipay_trade_page_pay()生成支付宝电脑网站支付的参数字符串,SDK 内部会自动进行 RSA2 签名。参数out_trade_no用我们之前生成的订单号,total_amount是支付金额。-18拼接
网关地址 + '?' + order_string,得到完整的支付链接,用redirect让用户浏览器跳转到支付宝收银台。
5.2 同步回跳——用户支付后返回网站
用户支付成功后(或中途取消),支付宝会将浏览器重定向回return_url。注意:同步回跳不可靠(用户可能关闭浏览器、网络超时等),只用于展示支付结果给用户看,真正的订单状态更新要依赖异步通知。
@login_required(login_url='users:login')def payment_return(request):"""支付完成后的同步回跳页面""" alipay=get_alipay_client()# 从 GET 参数中取出 sign 进行验签data=request.GET.dict()sign=data.pop('sign', None)ifnot sign or not alipay.verify(data, sign): messages.error(request,'支付验证失败,请联系客服。')returnredirect('home')# 验签通过,获取订单信息out_trade_no=data.get('out_trade_no')trade_no=data.get('trade_no')# 支付宝交易号try: order=Order.objects.get(order_no=out_trade_no)payment=order.payment# 如果支付记录尚未标记成功,则更新(通常在异步通知中已更新,此处为兜底)ifpayment.status!=1: payment.trade_no=trade_no payment.status=1payment.save(update_fields=['trade_no','status','update_time'])# 更新订单状态iforder.status==0: order.set_status(1)# 待支付 → 待发货returnrender(request,'payment/pay_success.html',{'order':order,'trade_no':trade_no,})except Order.DoesNotExist: messages.error(request,'订单不存在。')returnredirect('home')关键点:
request.GET.dict()将 QueryDict 转为普通字典,方便验签。-45alipay.verify(data, sign)验证支付宝返回数据的签名,防止伪造回调。-27验签通过后,根据
out_trade_no(订单号)找到对应订单并更新状态。
5.3 异步通知——支付宝服务端主动回调(⭐核心)
这是支付宝最关键的接口。用户支付成功后,支付宝服务器会以 POST 方式主动请求notify_url,告知支付结果。这个请求不经过浏览器,直接由支付宝服务器发起。我们需要在这个视图中完成最终的业务处理。
@csrf_exempt def payment_notify(request):""" 支付宝异步通知视图 注意:1. 必须加 @csrf_exempt,支付宝的请求不带 CSRF Token2. 验签通过后,必须返回纯文本'success',否则支付宝会重复发送3. 需要做幂等处理,防止重复通知"""ifrequest.method!='POST':returnHttpResponse('Method Not Allowed',status=405)alipay=get_alipay_client()# 将 POST 数据转为字典data=request.POST.dict()# 取出签名sign=data.pop('sign', None)sign_type=data.get('sign_type','RSA2')# 验签ifnot sign or not alipay.verify(data, sign): logger.error('支付宝异步通知验签失败')returnHttpResponse('failure')# 验签通过,处理业务逻辑out_trade_no=data.get('out_trade_no')trade_no=data.get('trade_no')trade_status=data.get('trade_status')total_amount=data.get('total_amount')logger.info(f'收到支付宝通知:订单号={out_trade_no}, 交易号={trade_no}, 状态={trade_status}')try: order=Order.objects.select_for_update().get(order_no=out_trade_no)# 防止重复处理(幂等性)iforder.status!=0: logger.info(f'订单 {out_trade_no} 已处理过,跳过')returnHttpResponse('success')# 校验金额iffloat(total_amount)!=float(order.total_amount): logger.error(f'订单 {out_trade_no} 金额不一致:支付宝={total_amount}, 本地={order.total_amount}')returnHttpResponse('failure')# 根据支付宝交易状态更新订单iftrade_status=='TRADE_SUCCESS'or trade_status=='TRADE_FINISHED':payment=order.payment payment.trade_no=trade_no payment.status=1payment.save(update_fields=['trade_no','status','update_time'])order.set_status(1)# 待支付 → 待发货logger.info(f'订单 {out_trade_no} 支付成功')except Order.DoesNotExist: logger.error(f'订单 {out_trade_no} 不存在')returnHttpResponse('failure')# ⚠️ 必须返回 'success',否则支付宝会持续重发(最多25小时)returnHttpResponse('success')关键点:
关于本地开发的异步通知:
支付宝异步通知需要支付宝服务器能访问到你的notify_url,127.0.0.1显然不行。解决方案:
使用内网穿透工具(推荐):如 ngrok、花生壳等,生成一个公网 URL,替换
ALIPAY_NOTIFY_URL。例如运行ngrok http 8000后获得https://xxxx.ngrok.io,然后修改 settings 为https://xxxx.ngrok.io/payment/notify/。不依赖异步通知:在同步回跳中更新订单状态(我们的
payment_return已做兜底),异步通知留到生产环境再配置。
六、支付成功页面模板
创建apps/payment/templates/payment/pay_success.html:
{% extends'base.html'%}{% block title %}支付成功{% endblock %}{% block content %}<divclass="row justify-content-center"><divclass="col-md-6 text-center"><divclass="card shadow-sm"><divclass="card-body py-5"><h1class="text-success mb-3">✅</h1><h3>支付成功!</h3><pclass="text-muted">感谢您的购买,我们会尽快为您发货。</p><hr><p><strong>订单号:</strong>{{order.order_no}}</p><p><strong>支付宝交易号:</strong>{{trade_no}}</p><p><strong>支付金额:</strong>¥{{order.total_amount|floatformat:2}}</p><ahref="{% url 'orders:order_detail' order.pk %}"class="btn btn-primary mt-2">查看订单详情</a><ahref="{% url 'home' %}"class="btn btn-outline-secondary mt-2">返回首页</a></div></div></div></div>{% endblock %}七、完整支付流程测试
启动开发服务器:
python manage.py runserver⚠️测试前提:确保已有一个状态为「待支付」的订单。如果没有,请先走一遍购物车 → 下单流程。
7.1 进入支付页
访问订单详情,点击“去支付”(或直接访问/payment/go/1/)。
终端输出:
[26/May/2026 09:30:12]"GET /payment/go/1/ HTTP/1.1"3020页面自动跳转到支付宝沙箱收银台(URL 为https://openapi-sandbox.dl.alipaydev.com/gateway.do?...)。
7.2 登录沙箱买家账号
在支付宝收银台页面,使用沙箱买家账号登录(账号密码在沙箱页面查看),确认金额后点击支付。沙箱支付密码通常为111111。
支付成功后,浏览器跳转回:
http://127.0.0.1:8000/payment/return/?charset=utf-8&out_trade_no=20260526093012X7K9M2&...&sign=...页面显示“支付成功”,展示订单号和支付宝交易号。
终端输出:
[26/May/2026 09:31:45]"GET /payment/return/?charset=utf-8&out_trade_no=20260526093012X7K9M2&... HTTP/1.1"20065437.3 验证数据库
打开dbshell:
-- 查看订单状态 SELECT id, order_no, status, total_amount FROM tb_order WHERE order_no='20260526093012X7K9M2';状态应为1(待发货)。
-- 查看支付记录 SELECT id, order_id, trade_no, amount, status FROM tb_payment WHERE order_id=1;支付记录状态为1,trade_no有值。
八、常见问题与避坑指南
九、总结与下集预告
今天我们完成了电商交易闭环的最后一步——支付宝沙箱支付:
申请了支付宝沙箱环境,获取了 APPID 和密钥;
安装了
python-alipay-sdk,配置了 settings.py;编写了发起支付视图,生成支付宝收银台跳转链接;
实现了同步回跳页面,展示支付结果;
实现了异步通知视图,安全更新订单状态。
现在,用户从浏览商品、加购、下单到支付,整个链路全部打通!但支付后的订单状态管理还有很多细节需要完善:退款、支付超时取消、状态同步等。第 21 篇,我将带大家深入处理支付结果与订单状态更新,包括定时查询支付结果、超时未支付自动取消订单、以及支付回滚等实战技巧。
想了解更多还可以去公众号、今日头条搜索「IT策士」,一起升级 IT 思维 !
本文为《Django 从 0 到 1 打造完整电商平台》系列第 20 篇。
