14 KiB
14 KiB
微信公众号与微信支付集成使用指南
本文档说明如何配置和使用系统的微信公众号 OAuth 认证和微信支付功能。
目录
概述
系统集成了以下微信功能:
- 微信公众号 OAuth 认证:个人客户可以通过微信授权码登录/绑定账号
- 微信 JSAPI 支付:支持微信内网页支付
- 微信 H5 支付:支持微信外浏览器 H5 支付
- 支付回调处理:自动验证微信支付签名并处理回调
技术实现使用 PowerWeChat v3 SDK。
前置条件
在开始配置之前,您需要:
-
微信公众号(已认证)
- 公众号 AppID
- 公众号 AppSecret
- OAuth 回调域名(需在公众号后台配置)
-
微信商户号(已开通)
- 商户号 MchID
- APIv3 密钥(32位字符串)
- APIv2 密钥(可选,部分接口需要)
- 商户证书(apiclient_cert.pem)
- 商户私钥(apiclient_key.pem)
- 证书序列号
-
服务器环境
- 可访问的 HTTPS 域名(用于接收微信回调)
- Redis(用于缓存 AccessToken)
配置步骤
1. 微信公众号配置
1.1 获取 AppID 和 AppSecret
登录 微信公众平台,在"开发" → "基本配置"中获取:
- AppID(应用ID)
- AppSecret(应用密钥)
1.2 配置 OAuth 回调域名
在"设置与开发" → "公众号设置" → "功能设置" → "网页授权域名"中配置:
your-domain.com
注意:
- 不要带
http://或https:// - 不要带端口号
- 需要验证域名所有权(下载验证文件到网站根目录)
2. 微信支付配置
2.1 获取商户信息
登录 微信支付商户平台:
- 商户号(MchID):在"账户中心" → "商户信息"中查看
- APIv3 密钥:在"账户中心" → "API安全" → "设置APIv3密钥"中设置(32位字符串)
- APIv2 密钥:(可选)同上,设置API密钥(32位字符串)
2.2 下载商户证书
在"账户中心" → "API安全" → "申请API证书":
- 下载证书工具
- 生成证书请求文件
- 上传请求文件
- 下载证书文件:
apiclient_cert.pem(商户证书)apiclient_key.pem(商户私钥)
2.3 获取证书序列号
方法1:使用 OpenSSL
openssl x509 -in apiclient_cert.pem -noout -serial | cut -d= -f2
方法2:从商户平台查看 在"账户中心" → "API安全" → "API证书"中查看证书序列号。
2.4 配置支付回调 URL
在"产品中心" → "开发配置" → "支付配置"中设置:
https://your-domain.com/api/callback/wechat-pay
注意:
- 必须使用 HTTPS
- 确保服务器可以接收微信的 POST 请求
3. 证书文件配置
将下载的证书文件放置到服务器:
# 创建证书目录
mkdir -p /app/certs
# 复制证书文件
cp apiclient_cert.pem /app/certs/
cp apiclient_key.pem /app/certs/
# 设置文件权限(仅所有者可读写)
chmod 600 /app/certs/*
Docker 部署:在 docker-compose.yml 中挂载证书目录:
services:
api:
volumes:
- ./certs:/app/certs:ro # 只读挂载
环境变量配置
在 .env.local 或生产环境中设置以下环境变量:
# ===== 微信公众号配置 =====
export JUNHONG_WECHAT_OFFICIAL_ACCOUNT_APP_ID="wxabcdef1234567890"
export JUNHONG_WECHAT_OFFICIAL_ACCOUNT_APP_SECRET="your_app_secret_here"
export JUNHONG_WECHAT_OFFICIAL_ACCOUNT_TOKEN="" # 可选,服务器配置用
export JUNHONG_WECHAT_OFFICIAL_ACCOUNT_AES_KEY="" # 可选,消息加解密用
export JUNHONG_WECHAT_OFFICIAL_ACCOUNT_OAUTH_REDIRECT_URL="" # 可选,自定义回调URL
# ===== 微信支付配置 =====
export JUNHONG_WECHAT_PAYMENT_APP_ID="wxabcdef1234567890" # 与公众号 AppID 相同
export JUNHONG_WECHAT_PAYMENT_MCH_ID="1234567890"
export JUNHONG_WECHAT_PAYMENT_API_V3_KEY="your_apiv3_key_32_chars_here"
export JUNHONG_WECHAT_PAYMENT_API_V2_KEY="" # 可选,部分接口需要
export JUNHONG_WECHAT_PAYMENT_CERT_PATH="/app/certs/apiclient_cert.pem"
export JUNHONG_WECHAT_PAYMENT_KEY_PATH="/app/certs/apiclient_key.pem"
export JUNHONG_WECHAT_PAYMENT_SERIAL_NO="1234567890ABCDEF"
export JUNHONG_WECHAT_PAYMENT_NOTIFY_URL="https://your-domain.com/api/callback/wechat-pay"
export JUNHONG_WECHAT_PAYMENT_HTTP_DEBUG=false
export JUNHONG_WECHAT_PAYMENT_TIMEOUT="30s"
配置说明:
| 配置项 | 必填 | 说明 |
|---|---|---|
OFFICIAL_ACCOUNT_APP_ID |
✅ | 公众号 AppID |
OFFICIAL_ACCOUNT_APP_SECRET |
✅ | 公众号 AppSecret |
PAYMENT_APP_ID |
✅ | 支付 AppID(通常与公众号相同) |
PAYMENT_MCH_ID |
✅ | 商户号 |
PAYMENT_API_V3_KEY |
✅ | APIv3 密钥(32位) |
PAYMENT_CERT_PATH |
✅ | 商户证书路径 |
PAYMENT_KEY_PATH |
✅ | 商户私钥路径 |
PAYMENT_SERIAL_NO |
✅ | 证书序列号 |
PAYMENT_NOTIFY_URL |
✅ | 支付回调 URL |
PAYMENT_TIMEOUT |
❌ | HTTP 请求超时(默认30s) |
PAYMENT_HTTP_DEBUG |
❌ | 开启 HTTP 调试日志 |
功能说明
微信 OAuth 登录
业务流程
1. 前端引导用户点击"微信登录"
2. 跳转到微信授权页面(微信SDK处理)
3. 用户同意授权后,微信回调到前端
4. 前端获取授权码(code),调用后端登录接口
5. 后端通过 code 获取用户 OpenID/UnionID
6. 后端创建/查找用户,返回 JWT Token
API 端点
POST /api/c/v1/wechat/auth
请求体:
{
"code": "071abc123456789def"
}
响应:
{
"code": 0,
"msg": "登录成功",
"data": {
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"customer_id": 123,
"phone": "138****8888",
"nickname": "微信用户",
"is_new_user": false
},
"timestamp": "2025-01-30T12:00:00Z"
}
前端集成示例
// 1. 构造微信授权 URL(前端处理)
const redirectUri = encodeURIComponent('https://your-domain.com/wechat-callback');
const appId = 'wxabcdef1234567890';
const scope = 'snsapi_userinfo'; // 或 snsapi_base(静默授权)
const state = 'STATE'; // 自定义参数
const authUrl = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appId}&redirect_uri=${redirectUri}&response_type=code&scope=${scope}&state=${state}#wechat_redirect`;
// 跳转到微信授权页面
window.location.href = authUrl;
// 2. 在回调页面获取 code 并调用后端
const urlParams = new URLSearchParams(window.location.search);
const code = urlParams.get('code');
fetch('https://api.your-domain.com/api/c/v1/wechat/auth', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ code })
})
.then(res => res.json())
.then(data => {
if (data.code === 0) {
localStorage.setItem('token', data.data.token);
// 跳转到主页
}
});
微信 JSAPI 支付
业务流程
1. 前端调用后端创建支付订单
2. 后端调用微信支付接口,获取 prepay_id 和支付配置
3. 前端调用微信 JSAPI 唤起支付
4. 用户完成支付后,微信回调后端通知接口
5. 后端验证签名并处理订单状态
API 端点
POST /api/h5/orders/:id/wechat-pay/jsapi
请求体:
{
"open_id": "o6_bmjrPTlm6_2sgVt7hMZOPfL2M"
}
响应:
{
"code": 0,
"msg": "支付订单创建成功",
"data": {
"prepay_id": "wx30123456789012345678901234567890",
"pay_config": {
"appId": "wxabcdef1234567890",
"timeStamp": "1706606400",
"nonceStr": "5K8264ILTKCH16CQ2502SI8ZNMTM67VS",
"package": "prepay_id=wx30123456789012345678901234567890",
"signType": "RSA",
"paySign": "oR9d8PuhnIc+YZ8cBHFCwfgpaK9gd..."
}
},
"timestamp": "2025-01-30T12:00:00Z"
}
前端集成示例(微信内网页)
// 1. 调用后端创建支付订单
const response = await fetch(`/api/h5/orders/${orderId}/wechat-pay/jsapi`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify({
open_id: 'o6_bmjrPTlm6_2sgVt7hMZOPfL2M'
})
});
const result = await response.json();
// 2. 调用微信 JSAPI 唤起支付
if (result.code === 0) {
const payConfig = result.data.pay_config;
wx.chooseWXPay({
...payConfig,
success: function(res) {
// 支付成功,跳转到订单详情页
window.location.href = `/orders/${orderId}`;
},
fail: function(res) {
// 支付失败
alert('支付失败:' + res.err_msg);
}
});
}
微信 H5 支付
业务流程
1. 前端调用后端创建 H5 支付订单
2. 后端调用微信支付接口,获取 H5 支付 URL
3. 前端跳转到 H5 支付 URL
4. 用户完成支付后,微信回调后端通知接口
5. 后端验证签名并处理订单状态
API 端点
POST /api/h5/orders/:id/wechat-pay/h5
请求体:
{
"scene_info": {
"payer_client_ip": "123.12.12.123",
"h5_type": "Wap"
}
}
响应:
{
"code": 0,
"msg": "H5 支付订单创建成功",
"data": {
"h5_url": "https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?prepay_id=wx..."
},
"timestamp": "2025-01-30T12:00:00Z"
}
前端集成示例(浏览器)
// 1. 调用后端创建 H5 支付订单
const response = await fetch(`/api/h5/orders/${orderId}/wechat-pay/h5`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify({
scene_info: {
payer_client_ip: '123.12.12.123',
h5_type: 'Wap'
}
})
});
const result = await response.json();
// 2. 跳转到微信 H5 支付页面
if (result.code === 0) {
const returnUrl = encodeURIComponent(`https://your-domain.com/orders/${orderId}`);
window.location.href = `${result.data.h5_url}&redirect_url=${returnUrl}`;
}
支付回调处理
回调端点
POST /api/callback/wechat-pay
该端点接收微信支付的异步通知。系统会自动:
- 验证微信签名(使用商户证书)
- 解密通知数据
- 更新订单状态
- 处理业务逻辑(分佣、钱包充值等)
- 返回成功响应给微信
回调处理流程
1. 微信发送 POST 请求到回调 URL
2. 系统验证请求签名(PowerWeChat 自动处理)
3. 解析支付结果(交易状态、金额等)
4. 更新订单状态为"已支付"
5. 触发异步任务(分佣计算、套餐分配等)
6. 返回 200 OK 给微信(表示接收成功)
注意:
- 回调接口必须在 10秒内 返回响应,否则微信会重试
- 系统已实现幂等性处理,重复通知不会重复处理
- 如果处理失败,微信会重试多次(最多3次)
常见问题
1. 配置验证失败,服务启动失败
错误日志:
FATAL: 微信配置不完整或无效
解决方法:
- 检查所有必填环境变量是否设置
- 确认证书文件路径正确且文件存在
- 验证 APIv3 密钥是否为 32 位字符串
2. OAuth 授权失败,返回 1044 错误
错误消息:
{
"code": 1044,
"msg": "微信 OAuth 授权失败"
}
可能原因:
- 授权码(code)已过期(5分钟有效期)
- 授权码已被使用过(一次性有效)
- AppID 或 AppSecret 配置错误
- 回调域名未在公众号后台配置
解决方法:
- 重新发起授权流程获取新 code
- 检查公众号配置是否正确
- 查看
logs/app.log获取详细错误信息
3. 支付订单创建失败,返回 1046 错误
错误消息:
{
"code": 1046,
"msg": "微信支付失败"
}
可能原因:
- 商户号配置错误
- 证书文件无效或过期
- APIv3 密钥错误
- 订单金额为0或负数
解决方法:
- 验证商户号和密钥是否正确
- 检查证书文件是否可读(权限问题)
- 确认证书序列号是否匹配
- 查看
logs/app.log获取详细错误信息
4. 支付回调签名验证失败
错误日志:
ERROR: 支付回调签名验证失败
可能原因:
- 证书配置错误
- 证书序列号不匹配
- 证书已过期
解决方法:
- 重新下载最新的商户证书
- 更新证书序列号配置
- 确保证书文件路径正确
5. 如何测试微信支付?
开发环境测试:
- 使用微信测试号(公众号测试账号)
- 使用真实商户号的沙箱环境(需申请)
- 使用 0.01 元测试订单(生产环境)
注意:
- 测试订单需要真实支付
- 可以通过退款功能退回测试金额
- 建议使用沙箱环境进行测试
6. Redis 连接失败,影响微信功能吗?
是的,微信功能依赖 Redis 缓存 AccessToken。
解决方法:
- 确保 Redis 服务正常运行
- 检查 Redis 连接配置(地址、端口、密码)
- 查看
logs/app.log获取 Redis 连接错误
7. 如何调试微信支付问题?
启用 HTTP 调试日志:
export JUNHONG_WECHAT_PAYMENT_HTTP_DEBUG=true
重启服务后,所有微信 API 请求和响应将记录到 logs/app.log。
查看日志:
tail -f logs/app.log | grep -i wechat