AgentDock
模板web-nextjs

微信支付

在 web-nextjs 中集成微信 Native 扫码支付和 H5 手机网站支付,含内网穿透调试、API v3 安全配置和常见问题。

微信支付

技术架构图

支付时序图

准备工作

步骤 1:注册微信商户

前往 微信支付商户平台 注册。注意:微信支付要求企业或个体工商户资质,个人暂不支持

步骤 2:开通 Native 支付

商户平台 → 产品中心 → 我的产品 → Native 支付 → 开通。

步骤 3:配置 API v3 密钥

商户平台 → 账户中心 → API 安全 → 设置 APIv3 密钥。

生成 32 位随机字符串:

openssl rand -hex 16

将此密钥牢记保存,后续不可查看。此密钥用于回调数据解密。

步骤 4:下载商户 API 证书

商户平台 → 账户中心 → API 安全 → 申请 API 证书:

  1. 按步骤使用证书工具(certutil)生成
  2. 下载 apiclient_cert.pem(证书)和 apiclient_key.pem(私钥)
  3. 记录「证书序列号」(大写十六进制字符串)

步骤 5:获取微信支付平台证书公钥

可通过以下方式获取:

  • 调用 API:GET /v3/certificates
  • 在商户平台下载

此证书用于验证微信支付的回调签名。

步骤 6:准备环境变量

apiclient_key.pem 的内容(完整 PEM 格式,含 header/footer)准备为环境变量。

环境变量配置

WECHAT_PAY_APP_ID=wx1234567890abcdef    # 公众号/小程序/移动应用 AppID
WECHAT_PAY_MCH_ID=1234567890           # 商户号
WECHAT_PAY_API_V3_KEY=your32charkey... # APIv3 密钥(32 位)
WECHAT_PAY_PRIVATE_KEY="-- 你的商户私钥(PKCS8 格式,含 PEM header/footer) --"
WECHAT_PAY_SERIAL_NO=证书序列号(大写十六进制字符串)
WECHAT_PAY_PLATFORM_PUBLIC_KEY="-- 微信支付平台公钥(含 PEM header/footer) --"
WECHAT_PAY_BASE_URL=https://api.mch.weixin.qq.com
WECHAT_PAY_NOTIFY_URL=https://你的域名/api/payments/wechat/notify

开发环境调试

重要提醒

⚠️ 微信支付没有官方沙箱环境(不同于支付宝),必须使用真实商户账号进行调试。建议先用小额(0.01 元)测试。

内网穿透配置

微信需要向本地发送回调,必须使用内网穿透工具:

  1. 推荐 natappcpolar
  2. 操作步骤:注册 → 创建 HTTP 隧道(指向 localhost:3000)→ 获得公网域名 → 配置到 WECHAT_PAY_NOTIFY_URL

Native 支付调试流程

  1. 启动 natapp + pnpm dev
  2. 进入 /pricing → 选择微信支付
  3. 页面显示二维码
  4. 使用真实微信扫码 → 完成支付
  5. 观察 notify 回调日志
  6. 检查数据库

H5 支付无法本地测试

微信要求 H5 支付域名必须在商户平台白名单中,本地 localhost 无法通过验证。开发阶段只调试 Native 支付。

官方调试工具

商户平台 → 开发调试 → API 接口调试

生产环境配置

  1. 在商户平台配置「支付授权目录」(H5 支付):产品中心 → H5 支付 → 申请域名白名单
  2. 回调通知 URL 无需单独配置白名单,但必须是公网可访问的 HTTPS URL
  3. 商户 API 证书有效期约 5 年,到期前商户平台会发送提醒邮件,届时需重新申请并更新环境变量

官方上线指引:https://pay.weixin.qq.com/wiki/doc/apiv3/open/pay/chapter2_8_1.shtml

安全注意事项

  • ⚠️ 私钥绝不能泄露到客户端WECHAT_PAY_PRIVATE_KEY 仅在服务端使用,永远不要以 NEXT_PUBLIC_ 开头
  • ⚠️ 回调验签必须执行:在 notify/route.ts 中,必须先用 await req.text() 读取原始请求体(不能解析为 JSON,否则破坏签名),再验证请求头中的 Wechatpay-SignatureWechatpay-TimestampWechatpay-NonceWechatpay-Serial;验签不通过直接返回 HTTP 400
  • ⚠️ 回调数据必须解密:微信支付通知中的 resource 字段使用 AEAD_AES_256_GCM 加密,必须调用解密函数后才能获取真实支付状态,不可直接信任明文字段
  • ⚠️ 幂等性处理:根据 out_trade_no 检查 DB,已 paid 则直接返回 { code: 'SUCCESS' }
  • ⚠️ API v3 密钥强度:32 位随机字符串,定期轮换,不与其他系统共享同一密钥
  • ⚠️ 时间戳有效期:微信支付的时间戳验证窗口为 5 分钟,服务器时间必须准确(建议启用 NTP 同步)

微信签名规范:https://pay.weixin.qq.com/doc/global/v3/zh/4012354988.md

常见问题(FAQs)

Q: 收不到微信回调? → 检查 WECHAT_PAY_NOTIFY_URL 是否公网可访问,是否 HTTPS;用 natapp/cpolar 内网穿透。

Q: 验签失败? → 确认用的是「微信支付平台公钥」(不是商户证书中的公钥),检查 Wechatpay-Serial 是否与平台证书序列号一致。

Q: Native 支付返回 SIGNERROR → 检查私钥格式:微信 API v3 要求 PKCS8 格式(-----BEGIN PRIVATE KEY-----),而非 PKCS1(-----BEGIN RSA PRIVATE KEY-----)。

Q: 二维码扫了没有反应? → 检查 code_url 是否是 weixin://wxpay/bizpayurl?pr=... 格式,确认微信 App 已更新到最新版。

Q: 轮询一直是 pending? → 检查 /api/payments/wechat/query 是否调用了正确的微信查单 API(需要传入 out_trade_no)。

On this page