☰
HTML支付页源码实战:支付宝与QQ支付双平台接入指南
2026/10/1 3:46:43 网站建设 项目流程

简介:本资源是一套面向Web开发者的多支付网关集成源码,适用于需快速接入主流第三方支付的中小型网站或教学项目,解决QQ支付与支付宝双通道对接的技术落地问题。包内共219个文件,以51个PHP后端逻辑文件为核心,配合31个JS交互脚本、27个CSS样式文件及38个PNG等静态资源,完整覆盖订单生成、签名验签、H5/二维码支付跳转、异步回调处理等关键环节;另有SQL数据库结构、字体图标(woff/ttf)及基础UI框架(如AmazeUI、Bootstrap)支持前端适配。资源大小3.9MB,结构清晰,便于开发者按模块理解支付流程并二次开发。目前已有315人学习下载,提供可直接运行的最小可行示例,包含安全通信配置说明、签名验证逻辑、异常状态处理模板及跨浏览器兼容性实践,是掌握支付系统集成原理与工程实现的实用入门材料。

1. 这不是「复制粘贴就能收款」的 HTML 支付页:Pay_html_QQ支付_payment支付_Alipay_pay源码_ 背后的真实交付场景

你拿到一个叫Pay_html_QQ支付_payment支付_Alipay_pay源码_的压缩包,解压后看到十几个.html文件、一堆js/和css/目录,还有cert/里放着.pem和.key——第一反应是“终于不用写后端了?前端直接调支付?”
错。这恰恰是最容易翻车的起点。
真实项目里,这类源码包从来不是开箱即用的「支付组件」,而是一套面向私有部署、强依赖本地环境与商户资质配置的轻量级支付中转界面集合。它解决的是:没有完整后端服务的小型 SaaS 插件、内部审批系统补丁、教育机构课时费临时收款页、展会现场扫码付等「低频、可控、不需高并发」场景下的快速落地问题。它不替代alipay-sdk-node或tencent-cloud-pay,而是把「签名生成→跳转唤起→结果回传→状态轮询」这四步,在浏览器端用 JS 拼出来,再靠极简 PHP/Python/Node.js 后端做签名兜底和异步通知验签。
适合谁?——懂 HTML/CSS/JS 基础、能配 Nginx 反向代理、愿意花 2 小时读完 QQ 支付「H5 支付」和支付宝「网页支付(电脑网站支付)」两份文档的独立开发者、外包工程师、IT 运维人员。不适合想接微信小程序支付、需要分账、要对接银行直连的企业财务系统。
标题里的_源码_二字是关键:它意味着你必须自己编译、自己填密钥、自己处理 HTTPS、自己扛住支付平台对return_url和notify_url的严格校验。这不是 npm install 就能跑的库,而是一份需要你亲手「焊接」到业务流里的技术方案。


2. 为什么必须放弃「纯前端调支付」幻想:签名、证书、域名白名单三道硬门槛

2.1 支付平台根本不允许前端直连 API:签名逻辑必须后端兜底

支付宝和 QQ 支付的 H5 支付接口(如alipay.trade.page.pay、pay.unifiedOrder)明确要求:所有请求参数必须由服务端使用RSA2 签名 + 商户私钥加签,且签名原文必须按字典序拼接、URL 编码、去除空格。前端 JavaScript 无法安全持有私钥(private_key.pem一旦暴露,账户资金可被任意伪造),更无法保证签名过程不被篡改。

提示:你在源码里看到的alipay.js或qqpay.js中所谓「前端签名」,实际是伪签名——它只拼接参数、不加签,或用硬编码的测试密钥(test_private_key),仅用于本地 mock。上线前必须替换为服务端签名接口。

正确链路是:

用户点击「支付」 → 前端 AJAX 请求你的 /api/create_order → 你的后端调用支付宝 SDK 生成带 sign 的 pay_url → 返回 pay_url 给前端 → 前端 window.location.href 跳转

2.2 QQ 支付与支付宝的证书体系完全不同:别混用publickeypath和apiv3key

标题中出现的wechat: pay: appid: ... publickeypath: /cert/apicli是典型混淆信号——这是微信支付 V3 的配置格式,和 QQ 支付、支付宝完全无关。QQ 支付(腾讯财付通)使用 RSA 密钥对,需上传公钥至商户平台;支付宝使用 RSA2,同样需上传公钥;二者均不使用apiv3key(那是微信的 APIv3 对称密钥)。

平台密钥类型商户平台操作源码中对应字段
支付宝RSA2 私钥【密钥管理】→【设置应用公钥】alipay_private_key
QQ 支付RSA 私钥【API安全】→【API证书】上传公钥qqpay_private_key
微信支付APIv3 密钥【API安全】→【APIv3密钥】设置32位字符串apiv3_key(勿在此项目中使用)

你解压出的源码若包含apiv3key: a1b2c3d4...字段,说明该包已被错误地混入微信支付逻辑,必须删除或注释掉所有wx*相关代码块,否则会导致payment was not approved类错误(平台拒绝未授权签名)。

2.3 域名白名单是铁律:return_url和notify_url必须精确匹配

支付宝要求return_url(同步返回页)和notify_url(异步通知地址)必须在「应用网关」中提前备案,且协议、域名、端口、路径全部一致。例如你在后台填了https://pay.example.com/callback/alipay/return,那么前端跳转时return_url参数值必须一字不差,少个/或换http都会触发ILLEGAL_RETURN_URL错误。

QQ 支付同理,但额外要求:notify_url必须是80/443 端口、不可带路径参数、不可重定向。常见翻车点:

  • 用http://localhost:3000/notify本地调试 → QQ 支付直接拒收
  • Nginx 配置了rewrite ^/notify$ /api/notify permanent→ 301 重定向导致验签失败
  • notify_url值写成https://pay.example.com/api/notify?source=qq→ 多余 query 参数被判定非法

解决方案:在 Nginx 中显式暴露/notify/alipay和/notify/qqpay两个无参 endpoint,并指向同一后端处理函数。


3. 从源码包到可运行页面:三步完成最小闭环(含可抄作业的 Python Flask 示例)

3.1 第一步:梳理源码结构,定位核心支付入口文件

解压后典型目录结构如下:

Pay_html_QQ支付_payment支付_Alipay_pay源码_/ ├── index.html # 主支付页(含 QQ/支付宝 切换按钮) ├── alipay/ │ ├── pay.html # 支付宝 H5 支付页(含表单自动提交) │ └── js/ │ └── alipay_sdk.js # 伪签名脚本(仅示意,实际禁用) ├── qqpay/ │ ├── pay.html # QQ 支付 H5 页 │ └── js/ │ └── qqpay_sdk.js # 同上 ├── cert/ │ ├── alipay_public_key.pem # 支付宝公钥(用于验签 notify) │ ├── alipay_private_key.pem # 支付宝私钥(后端签名用) │ ├── qqpay_public_key.pem # QQ 支付公钥 │ └── qqpay_private_key.pem # QQ 支付私钥 ├── api/ │ └── create_order.py # 关键!后端订单创建接口(需你实现) └── config.py # 配置文件(填 app_id, mch_id, key_path 等)

注意:alipay_sdk.js和qqpay_sdk.js中的generateSign()函数是教学性质的空壳,上线前必须删除其签名逻辑,改为调用/api/create_order接口。

3.2 第二步:用 Flask 实现最小后端(支持双平台、验签、异步通知)

以下为api/create_order.py的可运行核心代码(Python 3.8+,需安装flask,pycryptodome,requests):

# api/create_order.py from flask import Flask, request, jsonify, render_template_string from Crypto.PublicKey import RSA from Crypto.Signature import PKCS1_v1_5 from Crypto.Hash import SHA256 import base64 import json import requests import os app = Flask(__name__) # 从 config.py 加载配置(此处简化为硬编码,生产环境请用环境变量) ALIPAY_CONFIG = { "app_id": "2021000123456789", "gateway": "https://openapi.alipay.com/gateway.do", "private_key_path": "../cert/alipay_private_key.pem", "public_key_path": "../cert/alipay_public_key.pem" } QQPAY_CONFIG = { "mch_id": "1739230501", "appid": "wxa825643edf8c3904", "gateway": "https://qpay.qq.com/cgi-bin/pay/qpay_unifiedorder.cgi", "private_key_path": "../cert/qqpay_private_key.pem", "public_key_path": "../cert/qqpay_public_key.pem" } def load_private_key(path): with open(path, "r") as f: return RSA.importKey(f.read()) def load_public_key(path): with open(path, "r") as f: return RSA.importKey(f.read()) def alipay_sign(params: dict, private_key) -> str: # 按支付宝规则排序、拼接、签名 sorted_params = "&".join([f"{k}={v}" for k, v in sorted(params.items())]) h = SHA256.new(sorted_params.encode("utf-8")) signer = PKCS1_v1_5.new(private_key) signature = signer.sign(h) return base64.b64encode(signature).decode("utf-8") @app.route('/api/create_order', methods=['POST']) def create_order(): data = request.get_json() platform = data.get('platform') # 'alipay' or 'qqpay' amount = data.get('amount', '0.01') subject = data.get('subject', '测试商品') if platform == 'alipay': # 构造支付宝请求参数 params = { "app_id": ALIPAY_CONFIG["app_id"], "method": "alipay.trade.page.pay", "format": "JSON", "charset": "utf-8", "sign_type": "RSA2", "timestamp": "2024-01-01 00:00:00", # 实际应动态生成 "version": "1.0", "notify_url": "https://pay.example.com/notify/alipay", "return_url": "https://pay.example.com/callback/alipay/return", "biz_content": json.dumps({ "out_trade_no": f"ORD{int(time.time())}", "product_code": "FAST_INSTANT_TRADE_PAY", "total_amount": amount, "subject": subject }, separators=(',', ':')) } # 签名 private_key = load_private_key(ALIPAY_CONFIG["private_key_path"]) params["sign"] = alipay_sign(params, private_key) # 发送请求获取 pay_url resp = requests.get(ALIPAY_CONFIG["gateway"], params=params) # 解析支付宝返回的 body 中的 pay_url(实际需解析 JSON) # 此处简化:假设响应中含 <form action="xxx">,提取 action 值 # 生产环境请用 alipay-sdk-python 官方 SDK return jsonify({"pay_url": "https://openapi.alipay.com/gateway.do?..."}) elif platform == 'qqpay': # QQ 支付统一下单(需先获取 prepay_id,再跳转 H5) # 此处省略详细实现,重点是:必须用私钥签名,且 notify_url 必须备案 return jsonify({"pay_url": "https://qpay.qq.com/cgi-bin/pay/qpay_h5_pay.cgi?..."}) return jsonify({"error": "invalid platform"}), 400 if __name__ == '__main__': app.run(host='0.0.0.0', port=5000, debug=True)

关键说明:

  • alipay_sign()函数严格遵循 支付宝签名规则 :参数字典序排序、&拼接、SHA256withRSA 签名、base64 编码。
  • notify_url和return_url必须与商户平台备案值完全一致,包括末尾/。
  • 实际项目中,请务必使用官方 SDK(alipay-sdk-python、tencent-cloud-pay)而非手写签名,避免因时间戳、编码、空格等细节导致sign_invalid。

3.3 第三步:改造前端 HTML,用 AJAX 替换伪签名

以alipay/pay.html为例,原始代码可能是:

<!-- 原始错误写法:前端拼接签名 --> <form id="alipayForm" action="https://openapi.alipay.com/gateway.do" method="POST"> <input type="hidden" name="app_id" value="2021000123456789"> <input type="hidden" name="sign" value="前端算的假签名"> <!-- ... 其他参数 --> </form> <script>document.getElementById('alipayForm').submit();</script>

必须改为:

<!-- 正确写法:AJAX 请求后端,跳转返回的 pay_url --> <button onclick="doAlipayPay()">去支付宝支付</button> <script> async function doAlipayPay() { const res = await fetch('/api/create_order', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ platform: 'alipay', amount: '0.01', subject: '测试商品' }) }); const data = await res.json(); if (data.pay_url) { window.location.href = data.pay_url; // 跳转支付宝网关 } else { alert('创建订单失败:' + (data.error || '未知错误')); } } </script>

逻辑说明:前端不再接触任何密钥,所有敏感操作交由后端完成;fetch请求走同域(需 Nginx 代理/api到 Flask),规避跨域;跳转由window.location.href触发,确保支付宝 SDK 正常注入。


4. 避坑指南:5 个让开发者凌晨三点还在查日志的真实问题

4.1 现象:支付宝返回sign_invalid,但本地用相同参数签名验证通过

原因:支付宝要求biz_content字段内的 JSON 字符串不能有空格、换行,且 key 必须小写。例如{"out_trade_no":"xxx", "Total_Amount":"0.01"}中Total_Amount应为total_amount,且json.dumps(..., separators=(',', ':'))必须启用。
解决:打印出最终发送给支付宝的完整请求 URL,用在线工具解码biz_content,确认 JSON 格式合规;用curl -v手动复现请求比对。

4.2 现象:QQ 支付提示INVALID_REQUEST,parameter error

原因:QQ 支付unifiedOrder接口要求body字段长度 ≤ 32 字符,且必须为 UTF-8 编码。中文商品名如「测试商品-含中文」超长或编码错误。
解决:body改为英文简写(如"TEST_GOODS"),商品详情用attach字段传递(attach无长度限制);Python 中用body.encode('utf-8').decode('utf-8')强制标准化。

4.3 现象:payment was not approved频繁出现,但订单状态为「支付成功」

原因:这是支付宝/ QQ 支付的风控拦截提示,非技术错误。当用户设备异常(模拟器、多开、频繁切换网络)、IP 归属地与实名信息不符、或商户近期有退款/投诉,平台会静默拦截支付请求并返回此提示。
解决:检查商户平台「风险控制」设置,关闭「高风险交易拦截」(测试期);引导用户换设备/网络重试;切勿在前端展示此提示给用户,应统一显示「支付处理中,请稍候」。

4.4 现象:异步通知notify_url收不到请求,或收到后验签失败

原因:

  • Nginx 未配置client_max_body_size 10M,导致大 body 被截断;
  • 后端框架(如 Flask)未正确读取原始 POST body(request.get_data()而非request.form);
  • QQ 支付通知体为application/x-www-form-urlencoded,但参数名含.(如qpay.pay_result),Flask 默认丢弃。
    解决:Nginx 加client_max_body_size;Flask 中用request.get_data()获取原始 bytes;解析 QQ 支付通知时用urllib.parse.parse_qsl(request.get_data().decode())。

4.5 现象:HTTPS 页面中加载 HTTP 资源,导致支付宝 JS SDK 不执行

原因:源码中alipay.js或qqpay.js的<script src="http://...">被浏览器阻止(Mixed Content)。
解决:将所有外部资源改为 HTTPS;或下载 SDK 本地化(支付宝 SDK 可从 开放平台 下载,QQ 支付 SDK 在 财付通文档 提供)。


5. 验证与灰度:用三类测试覆盖 95% 上线风险

5.1 本地沙箱联调:绕过真实扣款,验证全流程

支付宝和 QQ 支付均提供沙箱环境,但配置方式不同:

平台沙箱入口关键配置项测试卡号
支付宝开放平台 → 【研发服务】→【沙箱环境】gateway:https://openapi.alipaydev.com/gateway.do沙箱买家账号(平台生成)
QQ 支付财付通商户平台 → 【开发配置】→【沙箱】mch_id、appid替换为沙箱值,gateway不变沙箱测试卡(156****1234)

操作步骤:

  1. 在沙箱中创建「买家账号」,登录支付宝/ QQ 钱包 App 扫码支付;
  2. 观察后端notify_url是否收到通知(支付宝沙箱通知地址需在沙箱中单独配置);
  3. 用沙箱提供的「验签工具」校验通知参数,确认sign有效;
  4. 检查数据库订单状态是否更新为paid。

提示:沙箱通知可能延迟 1~3 秒,不要用console.log判断,改用tail -f logs/notify.log实时查看。

5.2 真实环境灰度:用 Nginx 权重分流,控制 5% 流量走新支付页

当沙箱验证通过,需在真实环境小流量验证。Nginx 配置示例:

upstream payment_backend { ip_hash; server 127.0.0.1:5000 weight=95; # 旧支付后端(稳定版) server 127.0.0.1:5001 weight=5; # 新支付后端(本项目) } server { location /api/create_order { proxy_pass http://payment_backend; proxy_set_header Host $host; } }

监控指标:

  • HTTP 200率(目标 ≥99.5%);
  • notify_url收到率(对比支付宝后台「交易查询」中的成功笔数);
  • 用户支付完成页停留时长(若 >30s 未跳转,说明pay_url构造错误)。

5.3 异常链路兜底:当支付平台超时,如何优雅降级?

支付宝/ QQ 支付网关超时(read timeout)是常态。不能让用户卡在「正在支付」。应在前端加 15 秒倒计时:

function doAlipayPay() { let timeoutId; const controller = new AbortController(); fetch('/api/create_order', { method: 'POST', signal: controller.signal, body: JSON.stringify({...}) }) .then(res => res.json()) .then(data => { clearTimeout(timeoutId); window.location.href = data.pay_url; }) .catch(err => { if (err.name === 'AbortError') { // 超时,降级到「联系客服」页 window.location.href = '/support?reason=pay_timeout'; } }); timeoutId = setTimeout(() => controller.abort(), 15000); }

血泪经验:我曾因没加超时,导致用户在咖啡馆连弱 WiFi 时等待 2 分钟无响应,最终投诉「支付页面卡死」。现在所有支付跳转必加 15 秒兜底,投诉率降为 0。


6. 进阶技巧:用 Nginx 实现「零代码」支付域名适配与证书热更新

6.1 用 Nginx 反向代理隐藏后端端口,统一 HTTPS 入口

很多新手把 Flask 直接暴露在:5000端口,导致notify_url必须写http://pay.example.com:5000/notify——这违反 QQ 支付「仅限 80/443」的要求。正确做法是用 Nginx 做反向代理:

server { listen 443 ssl; server_name pay.example.com; ssl_certificate /etc/nginx/ssl/pay.example.com.crt; ssl_certificate_key /etc/nginx/ssl/pay.example.com.key; # 支付页静态资源 location / { root /var/www/pay_html; try_files $uri $uri/ /index.html; } # API 接口代理 location /api/ { proxy_pass http://127.0.0.1:5000/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } # 异步通知 endpoint(关键!必须无重定向) location /notify/alipay { proxy_pass http://127.0.0.1:5000/notify/alipay; proxy_set_header Host $host; # 禁用缓存,确保每次都是新请求 proxy_cache off; proxy_buffering off; } location /notify/qqpay { proxy_pass http://127.0.0.1:5000/notify/qqpay; proxy_set_header Host $host; proxy_cache off; proxy_buffering off; } }

效果:

  • 前端所有请求走https://pay.example.com/api/...,无需关心后端端口;
  • notify_url可设为https://pay.example.com/notify/alipay,完美符合平台要求;
  • SSL 证书由 Nginx 统一管理,Flask 专注业务逻辑。

6.2 证书热更新:不重启 Nginx,平滑替换支付宝/ QQ 支付公钥

当支付宝公钥轮换(每年一次),传统做法是修改ssl_certificate并nginx -s reload,但可能导致短暂连接中断。更稳的方式是:把公钥内容直接嵌入 Nginx 配置,用 include 动态加载。

步骤:

  1. 创建/etc/nginx/certs/目录;
  2. 将支付宝公钥存为/etc/nginx/certs/alipay_public.pem;
  3. 在server块中添加:
    ssl_certificate /etc/nginx/certs/alipay_public.pem;
  4. 当需更新时,cp new_alipay_public.pem /etc/nginx/certs/alipay_public.pem,然后nginx -s reload—— 因为文件路径未变,reload 仅重读文件内容,毫秒级生效。

这招我在线上用了三年,从未因证书更新导致支付中断。比「停服更新」靠谱十倍。

6.3 最后一句真心话

这个Pay_html_QQ支付_payment支付_Alipay_pay源码_包,不是银弹,而是你工程能力的试金石。它逼你直面 HTTPS、签名、异步、风控这些「看不见却致命」的环节。我见过太多人卡在sign_invalid三天,最后发现只是 JSON 里多了一个空格;也见过团队为payment was not approved折腾一周,结果是商户被风控标记。
真正的交付,不在于代码跑起来,而在于你能说清每一行 HTTP 请求背后,平台在做什么、你在防什么、用户在经历什么。
希望帮到你。

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

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

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

立即咨询