# 网页支付技术文档 ## 1. 概述 网页支付指在没有原生 App(或 App 内嵌 H5)场景下,通过**浏览器收银台**完成收款。本文档覆盖四种主流网页支付方式: | 方式 | 接口/产品 | 适用浏览器 | 用户交互 | | --- | --- | --- | --- | | **微信 Native(扫码)** | `/v3/pay/transactions/native` | 任意(PC/移动) | 网页展示二维码,微信 App 扫码支付 | | **支付宝 PC / 手机网站** | `alipay.trade.page.pay` / `alipay.trade.wap.pay` | 任意 | 跳转支付宝收银台(扫码或登录支付) | | **Stripe Checkout** | `checkout.sessions.create` | 任意 | 跳转 Stripe 托管收银台(多卡种/Apple Pay/Google Pay) | | **浏览器原生 PaymentRequest** | Web Payments API | Chrome/Safari | 系统级支付面板 | > 建议后端统一实现一个**聚合收银台**:`POST /v1/checkout/session` 根据 `channel` 参数分发到对应渠道。 ## 2. 方案选择 | 场景 | 推荐方案 | | --- | --- | | 中国大陆用户 + 微信 | 微信 Native 扫码 | | 中国大陆用户 + 支付宝 | 支付宝 PC/WAP 支付 | | 海外用户 / 多卡种 | Stripe Checkout(或 PaymentRequest) | | 无法接入微信/支付宝直连 | 聚合支付服务商(如 PayerMax、Adyen)或 Stripe(收支付宝/微信) | | 通用免卡支付 | Web Payment Request API | ## 3. 聚合收银台架构 ```mermaid flowchart LR U[用户浏览器] -->|访问收银台页面| C[Web 收银台前端] C -->|POST /v1/checkout/session| B[业务后端] B -->|分发| W[微信Native: code_url→二维码] B -->|分发| A[支付宝PC: 跳转form/URL] B -->|分发| S[Stripe Checkout: 跳转URL] B -->|分发| P[PaymentRequest: 返回支持性] W -->|异步通知| B A -->|异步通知| B S -->|Webhook| B B -->|轮询/回调| C[收银台展示结果] ``` ## 4. 后端集成 ### 4.1 统一收银台接口 ```javascript // src/controllers/checkout_controller.js const express = require('express'); const router = express.Router(); const { createWechatNative, createAlipayPage, createStripeCheckout } = require('../services/pay_gateway_service'); /** * 创建收银台会话(聚合分发) * body: { channel: 'wechat'|'alipay'|'stripe', product_id, return_url } */ router.post('/v1/checkout/session', async (req, res) => { const { channel, product_id, return_url } = req.body; const order = await orderService.createOrder(product_id, { source: 'web' }); let payload; switch (channel) { case 'wechat': // 微信 Native:返回 code_url 用于渲染二维码 payload = await createWechatNative(order); break; case 'alipay': // 支付宝 PC/WAP:返回跳转 form 或 URL payload = await createAlipayPage(order, return_url); break; case 'stripe': // Stripe Checkout:返回托管收银台 URL payload = await createStripeCheckout(order, return_url); break; default: return res.status(400).json({ error: 'unsupported channel' }); } res.json({ channel, payment_id: order.id, ...payload, }); }); // 查询收银台支付状态 router.get('/v1/checkout/session/:id', async (req, res) => { const status = await orderService.getStatus(req.params.id); res.json({ payment_id: req.params.id, success: status === 'PAID', note: status }); }); ``` ### 4.2 微信 Native(扫码) ```javascript // src/services/pay_gateway_service.js // 复用 wechatpay-node-v3(详见 wechat_pay.md 4.2) async function createWechatNative(order) { const result = await pay.transactions_native({ description: order.name, out_trade_no: order.id, notify_url: process.env.WX_NOTIFY_URL, amount: { total: order.amount }, // 分 scene_info: { payer_client_ip: order.clientIp }, }); return { code_url: result.code_url }; // weixin://wxpay/bizpayurl?pr=... } ``` ### 4.3 支付宝 PC / 手机网站 ```javascript // 复用 alipay-sdk(详见 alipay.md 4.2) async function createAlipayPage(order, returnUrl) { if (isMobile) { // 手机网站支付 const url = await alipaySdk.pageExecute('alipay.trade.wap.pay', { returnUrl, notifyUrl: process.env.ALIPAY_NOTIFY_URL, bizContent: { out_trade_no: order.id, total_amount: (order.amount / 100).toFixed(2), subject: order.name, product_code: 'QUICK_WAP_WAY', }, }); return { redirect_url: url }; // 302 跳转 } // PC 支付:返回自动提交的 form const form = await alipaySdk.pageExecute('alipay.trade.page.pay', { returnUrl, notifyUrl: process.env.ALIPAY_NOTIFY_URL, bizContent: { out_trade_no: order.id, total_amount: (order.amount / 100).toFixed(2), subject: order.name, product_code: 'FAST_INSTANT_TRADE_PAY', }, }); return { html_form: form }; } ``` ### 4.4 Stripe Checkout(托管收银台) ```javascript // Node.js + stripe SDK async function createStripeCheckout(order, returnUrl) { const session = await stripe.checkout.sessions.create({ mode: 'payment', line_items: [{ price_data: { currency: order.currency, product_data: { name: order.name }, unit_amount: order.amount, // 分 }, quantity: 1, }], success_url: `${returnUrl}?result=success&session_id={CHECKOUT_SESSION_ID}`, cancel_url: `${returnUrl}?result=cancel`, metadata: { out_trade_no: order.id }, }); return { redirect_url: session.url }; } ``` > 支付结果通过 Webhook `checkout.session.completed` 通知(验签方式见 [stripe.md](stripe.md))。 ### 4.5 浏览器原生 PaymentRequest(可选) 当浏览器不支持跳转收银台时,可用 Web Payments API。后端需额外提供两个端点: ```javascript // 1. 支付意向确认(校验金额后创建订单) router.post('/v1/payment/paymentrequest', async (req, res) => { const { amount, currency, methodData } = req.body; // methodData 含令牌 // 走 Stripe/收单渠道验证令牌并扣款 const result = await acquiringGateway.charge({ orderId: req.body.orderId, amount, currency, token: methodData.token, }); res.json({ success: result.success, paymentId: req.body.orderId }); }); ``` ## 5. 前端集成 ### 5.1 收银台页面(Web) ```html
``` ### 5.2 Flutter Web / WebView 复用 Flutter 项目的 `web/` 目录可直接托管上述收银台页面,或在 App 内用 `url_launcher` 跳转: ```dart // Flutter App 内跳转网页收银台 import 'package:url_launcher/url_launcher.dart'; Future