简介这是一套面向Web开发初学者与中级工程师的多支付网关集成源码聚焦QQ支付与支付宝AlipayH5/扫码支付的前端后端完整实现解决电商类网站或SaaS系统快速接入主流国内支付渠道的技术落地难题。资源共219个文件包含51个PHP服务端逻辑文件处理订单生成、签名验签、异步回调、31个JS交互脚本支付跳转、二维码渲染、状态轮询、27个CSS样式文件含AmazeUI、Bootstrap等响应式框架以及PNG/GIF/JPG等静态资源整体压缩包仅3.9MB轻量易部署。已有315人学习下载适合用于本地调试、教学演示或二次开发。读者可直接获得双支付通道的可运行示例、标准化接口调用封装、安全签名验证逻辑、跨浏览器兼容的支付页模板以及清晰的目录分层结构如/pay/qq/、/pay/alipay/、/static/css/等大幅降低支付模块从零搭建的学习成本与试错周期。1. Pay_html_QQ支付_payment支付_Alipay_pay源码不是“一套万能支付模板”而是三类支付通道在Web端落地的最小可行集成包你在网上搜到这个标题大概率正卡在这样一个真实场景里前端页面要嵌入支付按钮后端已跑通基础业务逻辑但面对 QQ 钱包、支付宝Alipay和通用 payment 接口如 HTML 表单跳转型支付网关你发现——• QQ 支付文档里全是 JS-SDK native bridge 的调用链Web H5 端连个可调试的 demo 都难找• 支付宝官方 SDK 强依赖服务端签名 notify 回调但你手头只有静态 HTML PHP/Node.js 轻量后端根本没上 Spring Cloud 或阿里云函数计算• 更头疼的是“payment was not approved” 这类错误在测试环境反复出现日志里既没 trace_id 也没具体拒付原因查不到是签名错、时间戳超时、还是商户号未开通 PC 网页支付权限。这个标题指向的不是某个开源项目仓库而是一类被大量中小项目实际复用、但从未被系统梳理的 Web 支付集成模式用纯 HTML 少量 JS 极简后端脚本完成 QQ 支付、支付宝网页版非小程序/APP、以及兼容 W3C Payment Request API 的 fallback 支付流程。它不解决高并发清分、对账或风控但能让你在 2 小时内把“立即支付”按钮点通且所有代码可离线运行、无第三方 CDN 依赖。适合独立开发者、SaaS 工具类后台、教育平台课时购买页等轻量级支付场景。提示本文不涉及任何需要申请企业资质、签约支付通道、或对接银行直连的环节。所有方案均基于支付宝开放平台「电脑网站支付」、QQ 钱包「H5 支付」公开接口以及浏览器原生 Payment Request API 的兼容层实现。不碰敏感词、不绕监管、不模拟支付只讲怎么让真实用户能点、能跳、能返回成功页。2. 为什么必须拆成三套逻辑QQ 支付、Alipay、payment API 的底层差异与选型依据2.1 QQ 钱包 H5 支付依赖腾讯系生态但 Web 端最“轻”QQ 钱包 H5 支付官方称“QQ 钱包 H5 支付”本质是微信支付 H5 的镜像逻辑但面向 QQ 用户。它的核心特点是无需用户安装 QQ App 即可唤起支付页通过内置浏览器 UA 识别且签名逻辑比支付宝更简单。关键限制仅支持https域名备案且需在 QQ 开放平台配置「H5 支付域名白名单」不支持 iOS Safari 直接唤起会降级为二维码但 Android 内置浏览器QQ 浏览器、手机 QQ 内置 WebView可直接拉起签名算法为MD5(keyvaluekeyvaluekeyvaluekeyyour_key)无 RSA 密钥对管理负担。常见误判很多人以为 QQ 支付必须接入 QQ 登录 SDK 才能调用其实不然。只要后端能生成合法prepay_id并返回给前端前端用location.href跳转即可完成支付唤起。2.2 支付宝电脑网站支付强签名 双回调但文档最全支付宝「电脑网站支付」是目前 Web 端最稳定、文档最详尽的方案。它要求后端必须使用 RSA2 签名推荐 2048 位私钥必须提供两个回调地址return_url同步跳转用于展示成功页和notify_url异步通知用于更新订单状态前端只需构造一个隐藏表单并自动 submit全程无 JS 依赖。玄学点在于notify_url必须是公网可访问、且不能带端口号如http://localhost:3000/notify无效但return_url可以是本地开发地址如http://127.0.0.1:8080/success.html。很多新手卡在这一步以为两个 URL 要求一致结果notify一直收不到。2.3 Payment Request API浏览器原生能力但兼容性是硬伤W3C 标准的PaymentRequestAPI 是真正的“未来式”方案前端直接调用new PaymentRequest(methodData, details, options)由浏览器聚合 Apple Pay、Google Pay、Samsung Pay 等甚至可扩展自定义支付方式如模拟 Alipay 按钮。但它的问题很现实iOS Safari 完全不支持截至 iOS 17.5Chrome 仅在 HTTPS 用户主动交互如点击按钮后才允许调用微信内置浏览器、QQ 内置浏览器、各安卓厂商定制浏览器基本无视该 APIpayment was not approved错误在此场景下高频出现——不是支付失败而是用户点了“取消”、浏览器拒绝授权、或instrument配置缺失导致无法初始化。所以真实项目中Payment Request API 只能作为降级兜底或体验增强层绝不能作为主通道。我们把它放在第三位不是因为它弱而是因为它的“可用性”取决于用户设备而非代码质量。3. 用三份最小 HTML 文件跑通全部流程Pay_html_QQ支付_payment支付_Alipay_pay源码结构解析3.1 目录结构与文件职责划分无构建工具纯静态可运行/pay_html/ ├── index.html # 主入口支付方式选择页含 QQ/Alipay/Payment API 三个按钮 ├── qq/ │ ├── pay.html # QQ 支付跳转页接收后端返回的 prepay_url自动跳转 │ └── callback.html # QQ 支付回调页接收 redirect_url 参数解析 out_trade_no 展示结果 ├── alipay/ │ ├── pay.html # 支付宝支付页渲染隐藏 form自动 submit 到 https://openapi.alipay.com/gateway.do │ └── return.html # 支付宝同步回调页解析 GET 参数验证 sign 后展示成功/失败 ├── payment/ │ ├── pay.html # Payment Request API 调用页含 JS 初始化 fallback 按钮 │ └── fallback.html # 当 API 不可用时降级到支付宝表单页复用 alipay/pay.html └── api/ # 模拟后端接口PHP/Node.js 任选此处以 PHP 为例 ├── qq_create.php # 生成 QQ 预支付链接含签名、timestamp、nonce_str ├── alipay_create.php # 生成支付宝请求参数含 sign、biz_content JSON └── notify.php # 支付宝异步通知接收与验签关键注意所有.html文件均可双击本地打开除支付宝return.html需 HTTP Serverapi/下的 PHP 文件需部署在支持 PHP 的服务器如 XAMPP、Docker php:8.1-apache。不依赖 Composer、npm、webpack 等任何构建链。3.2 QQ 支付qq/pay.html的自动跳转逻辑与参数透传qq/pay.html是整个 QQ 支付链路中最薄的一环。它不处理签名只做一件事从 URL 获取prepay_url并跳转。!-- qq/pay.html -- !DOCTYPE html html headtitleQQ支付中.../title/head body script // 从 URL 获取 prepay_url 参数如 ?prepay_urlhttps%3A%2F%2F... const urlParams new URLSearchParams(window.location.search); const prepayUrl decodeURIComponent(urlParams.get(prepay_url) || ); if (!prepayUrl) { alert(缺少 prepay_url 参数); history.back(); exit; } // 自动跳转注意必须是 location.href不能用 window.open location.href prepayUrl; /script /body /html逻辑说明QQ 开放平台要求预支付链接有效期为 2 小时且只能使用一次。因此prepay_url必须由后端实时生成并拼接到此页 URLlocation.href是唯一可靠唤起方式window.open在多数安卓 WebView 中会被拦截此页不参与签名但后端生成prepay_url时必须包含appid、mch_id、nonce_str、time_stamp、sign五要素缺一不可。关键参数说明后端qq_create.php必填参数类型说明appidstringQQ 开放平台分配的 AppID非微信 AppIDmch_idstringQQ 钱包商户号nonce_strstring随机字符串32 位以内建议用 time().rand(1000,9999)time_stampstring当前时间戳秒级非毫秒signstringMD5 签名格式为key1val1key2val2keyYOUR_KEY所有参数按 key 字典序排序后拼接3.3 支付宝alipay/pay.html的隐藏表单与自动提交支付宝电脑网站支付不依赖 JS SDK靠一个form提交到其网关即可。这是它稳定的核心原因。!-- alipay/pay.html -- !DOCTYPE html html headtitle支付宝支付中.../title/head body !-- 支付宝网关固定地址 -- form idalipaySubmit actionhttps://openapi.alipay.com/gateway.do methodPOST input typehidden nameapp_id value2021000123456789 / input typehidden namemethod valuealipay.trade.page.pay / input typehidden nameformat valueJSON / input typehidden namecharset valueutf-8 / input typehidden namesign_type valueRSA2 / input typehidden namesign valuekPv...[长签名字符串]... / input typehidden nametimestamp value2024-05-20 14:30:00 / input typehidden nameversion value1.0 / input typehidden namenotify_url valuehttps://yourdomain.com/api/notify.php / input typehidden namereturn_url valuehttps://yourdomain.com/alipay/return.html / input typehidden namebiz_content value{out_trade_no:ORD202405201430001,product_code:FAST_INSTANT_TRADE_PAY,total_amount:0.01,subject:测试商品} / /form script // 页面加载即提交避免用户看到空白页 document.getElementById(alipaySubmit).submit(); /script /body /html逻辑说明所有参数必须严格按支付宝文档顺序排列虽非强制但乱序易导致签名失败biz_content是 JSON 字符串必须用单引号包裹且内部双引号需转义PHP 中用json_encode($arr, JSON_UNESCAPED_UNICODE)sign是整个请求体的 RSA2 签名不是对biz_content单独签名而是对除sign和sign_type外所有参数按 keyvalue 拼接后签名return_url仅用于同步跳转展示绝不用于状态判断真实支付结果以notify_url异步通知为准。关键参数说明后端alipay_create.php必填参数类型说明app_idstring支付宝开放平台创建的应用 APPIDnotify_urlstring必须公网可访问、无端口、HTTPS否则 notify 失败return_urlstring可为 HTTP / 本地地址仅用于跳转展示biz_contentstringJSON 字符串必含out_trade_no唯一订单号、total_amount单位元精度 2 位、subject商品标题signstring对除sign、sign_type外所有参数按字典序拼接后用应用私钥 RSA2 签名3.4 Payment Request APIpayment/pay.html的渐进式降级设计!-- payment/pay.html -- !DOCTYPE html html headtitle支付方式选择/title/head body button idpayBtn使用浏览器支付/button div idfallbackArea styledisplay:none; p当前浏览器不支持 Payment Request API已自动降级至支付宝支付/p iframe src/alipay/pay.html?auto_submit1 width0 height0 styledisplay:none;/iframe /div script const payBtn document.getElementById(payBtn); const fallbackArea document.getElementById(fallbackArea); payBtn.addEventListener(click, async () { if (!window.PaymentRequest) { showFallback(); return; } try { // 定义支付方式此处仅启用支付宝实际可加 apple-pay、basic-card const supportedInstruments [{ supportedMethods: https://alipay.com, data: { merchantId: your_merchant_id } // 实际需支付宝分配 }]; const details { total: { label: 商品费用, amount: { currency: CNY, value: 0.01 } }, displayItems: [ { label: 测试商品, amount: { currency: CNY, value: 0.01 } } ] }; const request new PaymentRequest(supportedInstruments, details); // 检查是否支持不弹窗 const canMakePayment await request.canMakePayment(); if (!canMakePayment) { showFallback(); return; } // 弹出支付界面 const response await request.show(); // 此处应发送 response 信息到后端验证略见后文 response.complete(success); alert(支付成功模拟); } catch (err) { console.error(PaymentRequest error:, err); if (err.name NotAllowedError) { // 用户点击取消或浏览器拒绝 alert(支付已被取消); } else { showFallback(); } } }); function showFallback() { fallbackArea.style.display block; payBtn.disabled true; } /script /body /html逻辑说明canMakePayment()是静默检测不触发 UI用于判断是否可进入show()show()调用后若用户点击“取消”会抛出NotAllowedError而非AbortError后者是页面卸载导致response对象包含加密的支付凭证response.details必须传给后端由支付宝验签前端不可解密降级逻辑写死为 iframe 加载/alipay/pay.html因 iframe 不会阻塞主页面且可复用已有逻辑。提示https://alipay.com是支付宝注册的 payment method identifierPMI需在支付宝开放平台申请开通。未开通时canMakePayment()永远返回false直接走降级。4. 避坑指南QQ支付、Alipay、payment API 三大通道的 5 个血泪经验4.1 QQ 支付prepay_url生成后立即失效但错误提示永远是“系统繁忙”现象调用qq_create.php返回prepay_url前端跳转后显示“系统繁忙请稍后再试”控制台无报错QQ 开放平台日志查不到请求。原因prepay_url生成后 2 分钟内未被使用即过期更隐蔽的是QQ 服务端会对time_stamp做 ±15 分钟校验若你的服务器时间比 NTP 时间慢 16 分钟签名永远验不过。解决在qq_create.php开头加入date_default_timezone_set(Asia/Shanghai);用curl -I抓包确认服务器时间curl -I https://graph.qq.com/查看响应头Date字段prepay_url生成后前端必须location.href立即跳转禁止任何中间页、埋点 JS 或 setTimeout。4.2 支付宝notify_url收不到请求但return_url能正常跳转现象用户支付成功浏览器跳转到return.html显示“支付成功”但订单状态始终为“待支付”notify.php日志为空。原因支付宝异步通知要求notify_url必须满足① 公网可访问不能是localhost② 响应必须在 5 秒内返回success纯文本无 HTML③ 不能重定向302④ 不能有 CORS 头。解决用ngrok http 8080临时暴露本地端口测试notify_url是否可达notify.php开头必须header(Content-Type: text/plain; charsetutf-8);结尾echo success; die();在notify.php开头写日志file_put_contents(/tmp/alipay_notify.log, print_r($_POST, true), FILE_APPEND);确认是否收到数据。4.3 支付宝payment was not approved出现在return.html但实际已扣款现象用户看到return.html显示“支付失败”但支付宝账单里已扣款订单状态混乱。原因return.html解析GET参数时未验证sign。支付宝return_url的sign是对return参数的签名不验证则无法区分是用户主动跳转还是恶意伪造。解决return.html不应直接展示结果而应重定向到一个 PHP 脚本如check_return.phpcheck_return.php中调用支付宝 SDK 的verifyReturn()方法验证sign和sign_type仅当验签通过才更新前端展示并记录日志。4.4 Payment Request APIiOS Safari 下完全不触发但控制台无报错现象在 iPhone Safari 打开payment/pay.html点击按钮毫无反应DevTools Console 空空如也。原因iOS Safari 完全不支持PaymentRequestwindow.PaymentRequest为undefined但canMakePayment()不会报错而是直接 resolvefalse。解决必须显式判断if (!window.PaymentRequest) { showFallback(); return; }不要依赖try/catch捕获PaymentRequest构造错误因为构造函数本身不抛错错在后续show()在showFallback()中优先加载qq/pay.htmlAndroid 用户多其次才是alipay/pay.html。4.5 三通道共性坑out_trade_no重复导致“重复下单”现象用户点一次支付按钮后端生成两个相同out_trade_noQQ/支付宝均返回“订单已存在”。原因前端防抖缺失 后端未做幂等。用户快速连点两次请求几乎同时到达后端都生成了相同订单号。解决前端按钮点击后立即disabledtrue并显示 loading后端生成out_trade_no时用uniqid(ORD.date(ymd), true)PHP或Date.now() Math.random().toString(36).substr(2, 9)JS确保全局唯一在数据库订单表建唯一索引ALTER TABLE orders ADD UNIQUE KEY uk_out_trade_no (out_trade_no);插入失败即返回“请勿重复提交”。5. 验证与上线前必做的 4 项实操检查从本地到生产环境5.1 本地开发环境验证清单双击 HTML 即可检查项操作预期结果QQ 支付跳转修改index.html中 QQ 按钮 href 为qq/pay.html?prepay_urlhttps%3A%2F%2Ftest.qq.com%2Fpay%3FxxxURL 编码后的测试链接页面自动跳转不报错支付宝表单提交打开alipay/pay.html查看浏览器 Network 面板Filter 输入gateway.do出现 POST 请求Status 200Response 为支付宝 HTML 重定向页Payment API 降级在 Chrome 无痕窗口打开payment/pay.html禁用PaymentRequestDevTools → Application → Service Workers → Unregister点击按钮立即显示fallbackAreaiframe 加载alipay/pay.htmlreturn.html参数解析手动访问alipay/return.html?out_trade_noORD123trade_statusTRADE_SUCCESSsignxxx控制台输出解析后的对象无 JS 报错提示以上四步全部通过代表前端链路 100% 可用。后端只需保证api/下脚本能返回合法参数即可交付。5.2 生产环境部署前的 3 个硬性配置检查1HTTPS 与域名白名单QQ 钱包登录 QQ 开放平台 → 应用管理 → 配置「H5 支付域名」必须是https://yourdomain.com不能带路径不能是子域名泛解析支付宝登录 支付宝开放平台 → 应用管理 → 设置「授权回调地址」和「网关地址」notify_url必须是https://yourdomain.com/api/notify.phpPayment RequestChrome 要求页面必须为 HTTPSHTTP 下PaymentRequest构造即失败。2服务器时间与证书用ntpdate -q pool.ntp.org校准服务器时间误差必须 15 秒SSL 证书必须由可信 CA 签发Let’s Encrypt 可自签名证书会导致支付宝notify拒绝连接。3后端接口安全加固api/notify.php必须校验$_SERVER[HTTP_USER_AGENT]是否为AlipayNotify支付宝固定 UAapi/qq_create.php必须校验请求来源 IP 是否在 QQ 开放平台配置的「IP 白名单」内可先关掉上线前开启所有api/*.php必须加header(Content-Type: application/json; charsetutf-8);避免中文乱码导致签名失败。5.3 上线后第一周监控重点盯住这 3 个日志文件日志文件关键字段异常信号/tmp/alipay_notify.logtrade_status、out_trade_no、sign出现大量TRADE_CLOSED或sign check fail/tmp/qq_notify.logresult_code、err_code、out_trade_noresult_codeFAIL且err_codeSYSTEMERROR时间不同步浏览器 Console生产环境PaymentRequest is not defined、NotAllowedErroriOS 用户占比 30%需提前准备降级文案我上线过 7 个类似项目最深的教训是永远不要相信“文档说支持”的兼容性一定要用真机真网络测。曾有个项目开发时用 Chrome 模拟 iPhone一切正常上线后发现 iOS 用户 100% 走降级因为PaymentRequest在 Safari 里根本不存在——而模拟器不会告诉你这点。现在我的标准动作是上线前借同事的 iPhone、华为、小米各一台连公司 Wi-Fi 和 4G 网络把三个支付按钮各点 5 次录屏存档。这套Pay_html_QQ支付_payment支付_Alipay_pay源码结构不是银弹但它把 Web 支付最痛的“首次打通”压缩到了 2 小时。它不承诺高可用但承诺你照着做一定能点、能跳、能返回。剩下的是业务逻辑、风控策略、对账系统——那些就该交给专业支付中台去扛了。希望帮到你。本文还有配套的精品资源点击获取
