Flask双支付通道实战:支付宝+微信订单系统搭建
2026/9/16 4:17:50 网站建设 项目流程

简介:这是一套基于Flask框架开发的轻量级订单支付系统实战项目,面向Python初学者与Web开发入门者,解决电商类应用中支付宝与微信双通道支付集成的核心需求。资源包含完整可运行源码、详细部署文档及配套数据资料,覆盖支付流程设计、密钥管理、回调处理与前端交互等关键环节,小白替换配置后即可本地调试。压缩包共29个文件,含14个核心Python模块(如__init__.py、config.py、支付路由与工具类)、3份Markdown部署说明、3个HTML页面模板、3个PEM证书文件用于签名验证,以及Git相关元数据和LICENSE协议,整体仅55KB,结构精简、依赖明确。目前已有131人学习下载,提供清晰的模块划分(pay_flask主包、statics静态资源、utils工具函数、keys密钥目录)和日志/配置分离设计,便于理解Flask项目工程化组织方式与支付安全实践要点。

1. 这不是一个“支付 demo”,而是一套可上线的订单支付骨架:Flask + 支付宝 + 微信,三端闭环跑通才是硬指标

很多开发者下载到“Flask 支付系统源码”后,第一反应是:能跑通扫码付款吗?回调地址配对了吗?订单状态在数据库里真能自动更新吗?——这恰恰暴露了标题里“优秀项目”的真实分量:它不是教你怎么调用alipay.api_alipay_trade_page_pay的教学示例,而是以生产环境为标尺,把「用户下单 → 支付网关跳转 → 异步通知验签 → 订单状态机驱动 → 前端轮询/重定向」整条链路压进一个 Flask 应用,并附带完整部署文档和初始化数据。它面向的是需要快速搭建合规支付能力的中小业务系统(如 SaaS 后台、内部商城、活动报名平台),而非 Python 初学者练手。核心价值不在“用了 Flask”,而在“支付宝支付接口 + 微信支付接口”在同一个订单模型下共存且互不干扰——比如微信回调走/wx/notify,支付宝回调走/alipay/notify,但都统一触发OrderService.update_status()方法;数据库字段设计预留了pay_channel('alipay'/'wechat')、trade_no(第三方流水号)、out_trade_no(本系统订单号)三元组,避免后续扩展时推倒重来。如果你正卡在“微信支付投诉回调验签失败”或“Linux 系统部署后支付宝同步返回 404”,这篇就是为你写的实操路径。

2. 从零复现:用 Flask 搭建双支付通道的最小可行骨架

2.1 为什么选 Flask 而非 Django 或 FastAPI?关键在轻量与可控性

支付系统对框架的核心诉求不是 ORM 多强大,而是请求生命周期清晰、中间件可插拔、异步回调处理无歧义。Django 的 CSRF 中间件默认拦截 POST 回调,需额外白名单配置;FastAPI 的异步模型在支付宝同步跳转(GET)与微信异步通知(POST)混合场景下,容易因依赖注入顺序导致request.body读取冲突。Flask 的 WSGI 原生同步模型反而更稳:所有回调入口函数显式声明@app.route('/alipay/notify', methods=['POST'])request.get_data()直接获取原始字节流,避免 JSON 解析失败导致验签中断。项目中app.py的路由注册逻辑极简:

# app.py 核心路由片段 from flask import Flask, request, jsonify from services.alipay_service import handle_alipay_notify from services.wechat_service import handle_wechat_notify app = Flask(__name__) @app.route('/alipay/notify', methods=['POST']) def alipay_notify(): # 原始 POST 数据,不解析 form/json,直接传给验签函数 raw_data = request.get_data() return handle_alipay_notify(raw_data) @app.route('/wx/notify', methods=['POST']) def wechat_notify(): raw_data = request.get_data() return handle_wechat_notify(raw_data)

提示:微信支付回调必须返回<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>且 HTTP 状态码为 200,否则会持续重发。Flask 默认返回 JSON,此处必须用make_response构造 XML 响应体,代码见 2.3 节。

2.2 支付宝支付接口接入:沙箱环境调试与正式环境切换的三处硬编码点

支付宝开放平台提供沙箱环境(https://openhome.alipay.com/platform/appDaily.htm),但项目源码中的三处配置必须手动替换,否则无法联调:

配置项沙箱值(示例)正式环境替换位置说明
ALIPAY_APP_ID2021000123456789config.py第 12 行沙箱 APPID 在「沙箱应用」页获取,正式环境需在「我的应用」中创建新应用并签约
ALIPAY_PRIVATE_KEY_PATHkeys/alipay_sandbox_private_key.pemservices/alipay_service.py第 35 行私钥文件路径,沙箱密钥需在「沙箱密钥」页下载,正式环境需用 OpenSSL 生成 RSA2 密钥对
ALIPAY_PUBLIC_KEY_PATHkeys/alipay_sandbox_public_key.pemservices/alipay_service.py第 36 行支付宝公钥,沙箱环境固定为MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...,正式环境需在「支付宝公钥」页下载

验证是否生效:运行python app.py后访问http://localhost:5000/pay?order_id=ORD20240001&amount=99.99&channel=alipay,页面应跳转至支付宝沙箱登录页。若提示“APPID不存在”,检查ALIPAY_APP_ID是否复制完整(含空格);若跳转后报“签名错误”,用openssl rsa -in keys/alipay_sandbox_private_key.pem -pubout -outform PEM检查私钥格式是否为 PEM。

2.3 微信支付接口接入:V3 版本 SDK 的强制要求与证书加载陷阱

微信支付 V3 接口强制要求 HTTPS + 平台证书双向认证,项目使用wechatpy库(v3.6.0+),但源码中wechat_service.py的证书加载存在两个易错点:

  1. 证书路径必须绝对路径:相对路径./cert/apiclient_cert.pem在 Gunicorn 部署时会因工作目录变更失效,需改为os.path.join(os.path.dirname(__file__), 'cert', 'apiclient_cert.pem')
  2. 私钥密码不能为空字符串:微信商户平台下载的apiclient_key.pem文件开头含-----BEGIN RSA PRIVATE KEY-----,若用 OpenSSL 转换过格式,需确保密码参数传入空字符串''而非None
# services/wechat_service.py 关键片段 from wechatpy.pay import WeChatPay import os CERT_DIR = os.path.join(os.path.dirname(__file__), 'cert') wechat_pay = WeChatPay( appid="wx1234567890abcdef", # 替换为你的公众号 APPID api_key="your_api_key_here", # 微信商户平台 API 密钥(32位) mch_id="1234567890", # 商户号 mch_cert_dir=CERT_DIR, # 自动加载 cert/ 目录下全部证书 )

注意:微信支付回调验签必须使用wechatpyparse_payment_result方法,不能自行解析 XML。正确写法:

from wechatpy.pay import WeChatPay def handle_wechat_notify(raw_data): try: result = wechat_pay.parse_payment_result(raw_data) # 自动验签+解密 if result['result_code'] == 'SUCCESS': OrderService.update_status(result['out_trade_no'], 'paid') return make_response('<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>', 200) except Exception as e: return make_response('<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[验签失败]]></return_msg></xml>', 200)

2.4 双通道共用的订单模型设计:避免支付渠道耦合的关键字段

项目models.pyOrder表结构刻意规避了“为每个支付渠道单独建表”的反模式,核心字段设计如下:

字段名类型说明示例
idBIGINT PK主键10001
out_trade_noVARCHAR(64) UNIQUE本系统订单号(业务生成)ORD20240001
pay_channelENUM('alipay','wechat')支付渠道标识'alipay'
trade_noVARCHAR(128)第三方流水号(支付宝 transaction_id / 微信 transaction_id)'2024050122001406121400000000'
amountDECIMAL(10,2)实际支付金额(单位:元)99.99
statusENUM('unpaid','paid','refunded','closed')订单状态机'paid'
notify_timeDATETIME最后一次成功回调时间2024-05-01 14:23:11

此设计使OrderService.update_status()方法可统一处理双通道回调:

# services/order_service.py def update_status(out_trade_no, new_status): order = Order.query.filter_by(out_trade_no=out_trade_no).first() if not order: raise ValueError(f"Order {out_trade_no} not found") # 状态机校验:unpaid → paid 合法,paid → unpaid 非法 if order.status == 'unpaid' and new_status == 'paid': order.status = 'paid' order.notify_time = datetime.now() db.session.commit() # 触发后续动作:发短信、更新库存... else: raise RuntimeError(f"Invalid status transition: {order.status} → {new_status}")

3. 部署文档落地:Linux 服务器上从源码到可访问服务的七步命令

3.1 环境准备:Python 3.9+ 与系统依赖的精准安装

项目要求 Python ≥ 3.9(因dataclasseszoneinfo为支付时间戳处理必需),在 Ubuntu 22.04 上执行以下命令:

# 升级系统包管理器 sudo apt update && sudo apt upgrade -y # 安装 Python 3.9 及开发头文件(关键!否则 pip install pycryptodome 失败) sudo apt install -y python3.9 python3.9-venv python3.9-dev # 安装 OpenSSL 开发库(wechatpy 依赖) sudo apt install -y libssl-dev libffi-dev # 验证 Python 版本 python3.9 --version # 应输出 Python 3.9.x

提示:不要用apt install python3,Ubuntu 22.04 默认 Python 3.10,但项目requirements.txtpymysql==1.0.2与 Python 3.10 存在兼容性问题,强制指定python3.9可规避。

3.2 虚拟环境与依赖安装:requirements.txt 的三个隐藏依赖项

项目requirements.txt明确列出flask==2.2.5,但实际运行还需三类隐式依赖:

依赖类型包名安装命令说明
加密库pycryptodomepip install pycryptodome支付宝 RSA2 签名必需,requirements.txt未显式声明
数据库驱动pymysqlpip install pymysqlMySQL 连接驱动,requirements.txt中版本需锁定为1.0.2
微信 SDKwechatpy[finance]pip install "wechatpy[finance]"finance额外包包含 V3 支付模块,仅wechatpy不足

完整安装流程:

# 创建虚拟环境(使用 Python 3.9) python3.9 -m venv venv source venv/bin/activate # 升级 pip(避免旧版 pip 安装失败) pip install --upgrade pip # 安装显式依赖 pip install -r requirements.txt # 安装隐式依赖 pip install pycryptodome pymysql "wechatpy[finance]"

3.3 数据库初始化:MySQL 8.0 的字符集与用户权限配置

项目使用 MySQL 8.0,必须确保数据库字符集为utf8mb4(支持 emoji 和四字节 UTF-8),且用户拥有SELECT, INSERT, UPDATE, DELETE权限:

-- 创建数据库(utf8mb4 兼容) CREATE DATABASE IF NOT EXISTS payment_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- 创建专用用户(避免 root 权限) CREATE USER 'payment_user'@'localhost' IDENTIFIED BY 'StrongPass123!'; GRANT SELECT, INSERT, UPDATE, DELETE ON payment_db.* TO 'payment_user'@'localhost'; FLUSH PRIVILEGES; -- 初始化表结构(执行项目内 init_db.sql) mysql -u payment_user -p payment_db < init_db.sql

init_db.sql中关键语句验证:

-- 检查 orders 表字符集 SHOW CREATE TABLE orders; -- 应含 ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 -- 检查 pay_channel 字段枚举值 DESCRIBE orders; -- pay_channel 字段类型应为 enum('alipay','wechat')

3.4 Gunicorn 生产部署:进程管理与端口映射的最小配置

Flask 自带服务器仅用于开发,生产环境必须用 Gunicorn。项目gunicorn.conf.py配置精简有效:

# gunicorn.conf.py bind = "127.0.0.1:8000" # 内部监听端口,不对外暴露 workers = 4 # CPU 核心数 × 2 worker_class = "sync" # 同步模型,适配支付回调的阻塞特性 timeout = 30 # 支付回调超时需 ≥ 30s(微信要求) keepalive = 5 accesslog = "/var/log/payment_access.log" errorlog = "/var/log/payment_error.log" loglevel = "info"

启动命令:

# 启动 Gunicorn(后台运行) gunicorn -c gunicorn.conf.py app:app & # 查看进程 ps aux | grep gunicorn # 测试本地访问(应返回 200) curl http://127.0.0.1:8000/health

3.5 Nginx 反向代理:HTTPS 强制跳转与静态资源托管

Nginx 配置payment.conf必须包含两处支付安全关键设置:

# /etc/nginx/sites-available/payment.conf server { listen 80; server_name your-domain.com; return 301 https://$server_name$request_uri; # 强制 HTTPS } server { listen 443 ssl; server_name your-domain.com; ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem; location / { proxy_pass http://127.0.0.1:8000; # 转发到 Gunicorn proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } # 支付回调路径必须透传原始 body(关键!) location /alipay/notify { proxy_pass http://127.0.0.1:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header Content-Type "application/x-www-form-urlencoded"; proxy_pass_request_body on; } location /wx/notify { proxy_pass http://127.0.0.1:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_pass_request_body on; # 必须开启,否则微信回调 body 为空 } }

启用配置:

sudo ln -sf /etc/nginx/sites-available/payment.conf /etc/nginx/sites-enabled/ sudo nginx -t && sudo systemctl reload nginx

4. 支付回调排错:三类高频失败场景的定位与修复指令

4.1 支付宝同步返回 404:路由注册与 DEBUG 模式的双重校验

当用户扫码支付后跳转回https://your-domain.com/alipay/return?...显示 404,本质是 Flask 路由未注册或 DEBUG 模式干扰:

  1. 检查路由是否存在:在app.py中搜索@app.route('/alipay/return'),确认函数名非alipay_return而是alipay_return_view(项目实际命名)
  2. 关闭 DEBUG 模式config.pyDEBUG = False,否则 Flask 会禁用@app.route的 GET 方法注册
  3. 验证路由列表:启动应用后执行flask routes(需安装flask-cli):
    source venv/bin/activate export FLASK_APP=app.py flask routes | grep alipay # 正常输出:GET /alipay/return => alipay_return_view

若无输出,检查app.py是否漏掉if __name__ == '__main__': app.run()的启动逻辑,或app.register_blueprint()未调用。

4.2 微信支付回调验签失败:OpenSSL 版本与证书时效的交叉验证

微信回调验签失败日志通常为wechatpy.exceptions.InvalidSignatureException,根源多为:

原因验证命令修复方案
OpenSSL 版本过低(<1.1.1)openssl versionUbuntu 20.04+ 默认满足,旧系统需sudo apt install openssl升级
平台证书过期openssl x509 -in cert/apiclient_cert.pem -noout -enddate登录微信商户平台 → 「API 安全」→ 下载最新证书替换cert/目录
商户 API 密钥错误手动构造签名测试使用微信官方 签名工具 输入mch_id+api_key生成签名,对比代码中wechat_pay.sign()输出

关键调试代码:

# 在 handle_wechat_notify 开头添加 import logging logging.basicConfig(level=logging.DEBUG) logger = logging.getLogger(__name__) def handle_wechat_notify(raw_data): logger.debug(f"Raw data length: {len(raw_data)}") # 应 > 100 字节 logger.debug(f"First 100 chars: {raw_data[:100]}") # ...后续验签逻辑

4.3 订单状态不更新:数据库事务与回调幂等性的原子操作

支付成功后数据库orders.status仍为unpaid,常见于事务未提交或并发重复回调:

  1. 强制提交事务OrderService.update_status()db.session.commit()后添加日志:
    db.session.commit() logger.info(f"Order {out_trade_no} status updated to {new_status}")
  2. 添加幂等锁:在更新前查询当前状态,避免重复处理:
    order = Order.query.filter_by(out_trade_no=out_trade_no).with_for_update().first() if order.status != 'unpaid': # 已处理过,直接返回 SUCCESS return make_response(..., 200) # 继续更新逻辑
  3. 检查数据库连接池SQLALCHEMY_ENGINE_OPTIONSpool_pre_ping=True可自动检测断连:
    # config.py SQLALCHEMY_ENGINE_OPTIONS = { "pool_pre_ping": True, "pool_recycle": 3600, }

5. 进阶技巧:用 Flask-Login 集成支付宝授权登录,复用同一套用户体系

5.1 支付宝授权登录(OAuth2.0)与支付账户的绑定逻辑

项目未内置支付宝授权登录,但可基于现有架构快速扩展。核心是复用User模型与Order关联关系:

# models.py 新增字段 class User(db.Model): id = db.Column(db.Integer, primary_key=True) alipay_user_id = db.Column(db.String(64), unique=True) # 支付宝 user_id alipay_avatar = db.Column(db.String(255)) # 头像 URL # ...其他字段 # 授权跳转路由 @app.route('/alipay/auth') def alipay_auth(): auth_url = "https://authz.alipay.com/oauth2/public/authorize.htm" params = { "app_id": current_app.config['ALIPAY_APP_ID'], "scope": "auth_user", "redirect_uri": url_for('alipay_callback', _external=True), "state": "random_string" # 防 CSRF } return redirect(f"{auth_url}?{urlencode(params)}") # 授权回调处理 @app.route('/alipay/callback') def alipay_callback(): auth_code = request.args.get('auth_code') # 调用支付宝 openapi 获取用户信息 token_resp = requests.post( "https://openapi.alipay.com/gateway.do", data={ "app_id": current_app.config['ALIPAY_APP_ID'], "method": "alipay.system.oauth.token", "format": "JSON", "charset": "utf-8", "sign_type": "RSA2", "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"), "version": "1.0", "grant_type": "authorization_code", "code": auth_code, "refresh_token": "", }, # 签名逻辑同支付接口 ) user_info = token_resp.json()['alipay_system_oauth_token_response'] # 绑定到本地用户 user = User.query.filter_by(alipay_user_id=user_info['user_id']).first() if not user: user = User(alipay_user_id=user_info['user_id'], alipay_avatar=user_info['avatar']) db.session.add(user) login_user(user) # Flask-Login 登录态 return redirect(url_for('dashboard'))

5.2 微信支付投诉回调的独立路由:与普通支付回调分离的必要性

微信支付投诉回调(/pay/complain/notify)必须与/wx/notify分离,因:

  • 投诉回调使用独立密钥(非 API 密钥)
  • 投诉数据结构为加密 JSON,需wechatpyparse_complaint_result方法
  • 投诉处理需人工介入,不应触发自动发货等动作
# 新增路由 @app.route('/wx/complain/notify', methods=['POST']) def wechat_complain_notify(): raw_data = request.get_data() try: result = wechat_pay.parse_complaint_result(raw_data) # 投诉专用验签 # 记录投诉日志到独立表 ComplaintLog.create( out_trade_no=result['out_trade_no'], complain_id=result['complain_id'], content=result['content'], ) return make_response("success", 200) # 微信投诉回调要求纯文本 success except Exception as e: logger.error(f"Wechat complain notify failed: {e}") return make_response("fail", 200)

提示:微信投诉回调 URL 需在商户平台「投诉管理」中单独配置,且必须为 HTTPS,与普通支付回调 URL 不同。

5.3 Linux 系统安装 Python 的终极方案:pyenv + pyenv-virtualenv 组合

若服务器预装 Python 版本不符合要求(如 CentOS 7 默认 Python 2.7),推荐pyenv方案:

# 安装 pyenv curl https://pyenv.run | bash export PYENV_ROOT="$HOME/.pyenv" export PATH="$PYENV_ROOT/bin:$PATH" eval "$(pyenv init -)" # 安装 Python 3.9.18(解决部分系统 OpenSSL 兼容问题) pyenv install 3.9.18 pyenv global 3.9.18 # 创建项目专属虚拟环境 pyenv virtualenv 3.9.18 payment-env pyenv local payment-env # 后续 pip install 步骤同 3.2 节

此方案彻底隔离系统 Python,避免yum依赖冲突,且pyenv local使项目目录下自动激活对应环境。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询