Files
2026-01-30 17:25:30 +08:00

563 lines
14 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 微信公众号与微信支付集成使用指南
本文档说明如何配置和使用系统的微信公众号 OAuth 认证和微信支付功能。
## 目录
- [概述](#概述)
- [前置条件](#前置条件)
- [配置步骤](#配置步骤)
- [1. 微信公众号配置](#1-微信公众号配置)
- [2. 微信支付配置](#2-微信支付配置)
- [3. 证书文件配置](#3-证书文件配置)
- [环境变量配置](#环境变量配置)
- [功能说明](#功能说明)
- [微信 OAuth 登录](#微信-oauth-登录)
- [微信 JSAPI 支付](#微信-jsapi-支付)
- [微信 H5 支付](#微信-h5-支付)
- [支付回调处理](#支付回调处理)
- [常见问题](#常见问题)
---
## 概述
系统集成了以下微信功能:
1. **微信公众号 OAuth 认证**:个人客户可以通过微信授权码登录/绑定账号
2. **微信 JSAPI 支付**:支持微信内网页支付
3. **微信 H5 支付**:支持微信外浏览器 H5 支付
4. **支付回调处理**:自动验证微信支付签名并处理回调
技术实现使用 [PowerWeChat v3 SDK](https://github.com/ArtisanCloud/PowerWeChat)。
---
## 前置条件
在开始配置之前,您需要:
1. **微信公众号**(已认证)
- 公众号 AppID
- 公众号 AppSecret
- OAuth 回调域名(需在公众号后台配置)
2. **微信商户号**(已开通)
- 商户号 MchID
- APIv3 密钥32位字符串
- APIv2 密钥(可选,部分接口需要)
- 商户证书apiclient_cert.pem
- 商户私钥apiclient_key.pem
- 证书序列号
3. **服务器环境**
- 可访问的 HTTPS 域名(用于接收微信回调)
- Redis用于缓存 AccessToken
---
## 配置步骤
### 1. 微信公众号配置
#### 1.1 获取 AppID 和 AppSecret
登录 [微信公众平台](https://mp.weixin.qq.com/),在"开发" → "基本配置"中获取:
- AppID应用ID
- AppSecret应用密钥
#### 1.2 配置 OAuth 回调域名
在"设置与开发" → "公众号设置" → "功能设置" → "网页授权域名"中配置:
```
your-domain.com
```
**注意**
- 不要带 `http://``https://`
- 不要带端口号
- 需要验证域名所有权(下载验证文件到网站根目录)
### 2. 微信支付配置
#### 2.1 获取商户信息
登录 [微信支付商户平台](https://pay.weixin.qq.com/)
1. **商户号MchID**:在"账户中心" → "商户信息"中查看
2. **APIv3 密钥**:在"账户中心" → "API安全" → "设置APIv3密钥"中设置32位字符串
3. **APIv2 密钥**可选同上设置API密钥32位字符串
#### 2.2 下载商户证书
在"账户中心" → "API安全" → "申请API证书"
1. 下载证书工具
2. 生成证书请求文件
3. 上传请求文件
4. 下载证书文件:
- `apiclient_cert.pem`(商户证书)
- `apiclient_key.pem`(商户私钥)
#### 2.3 获取证书序列号
**方法1使用 OpenSSL**
```bash
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. 证书文件配置
将下载的证书文件放置到服务器:
```bash
# 创建证书目录
mkdir -p /app/certs
# 复制证书文件
cp apiclient_cert.pem /app/certs/
cp apiclient_key.pem /app/certs/
# 设置文件权限(仅所有者可读写)
chmod 600 /app/certs/*
```
**Docker 部署**:在 `docker-compose.yml` 中挂载证书目录:
```yaml
services:
api:
volumes:
- ./certs:/app/certs:ro # 只读挂载
```
---
## 环境变量配置
`.env.local` 或生产环境中设置以下环境变量:
```bash
# ===== 微信公众号配置 =====
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`**
请求体:
```json
{
"code": "071abc123456789def"
}
```
响应:
```json
{
"code": 0,
"msg": "登录成功",
"data": {
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"customer_id": 123,
"phone": "138****8888",
"nickname": "微信用户",
"is_new_user": false
},
"timestamp": "2025-01-30T12:00:00Z"
}
```
#### 前端集成示例
```javascript
// 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`**
请求体:
```json
{
"open_id": "o6_bmjrPTlm6_2sgVt7hMZOPfL2M"
}
```
响应:
```json
{
"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"
}
```
#### 前端集成示例(微信内网页)
```javascript
// 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`**
请求体:
```json
{
"scene_info": {
"payer_client_ip": "123.12.12.123",
"h5_type": "Wap"
}
}
```
响应:
```json
{
"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"
}
```
#### 前端集成示例(浏览器)
```javascript
// 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. 验证微信签名(使用商户证书)
2. 解密通知数据
3. 更新订单状态
4. 处理业务逻辑(分佣、钱包充值等)
5. 返回成功响应给微信
#### 回调处理流程
```
1. 微信发送 POST 请求到回调 URL
2. 系统验证请求签名PowerWeChat 自动处理)
3. 解析支付结果(交易状态、金额等)
4. 更新订单状态为"已支付"
5. 触发异步任务(分佣计算、套餐分配等)
6. 返回 200 OK 给微信(表示接收成功)
```
**注意**
- 回调接口必须在 **10秒内** 返回响应,否则微信会重试
- 系统已实现幂等性处理,重复通知不会重复处理
- 如果处理失败微信会重试多次最多3次
---
## 常见问题
### 1. 配置验证失败,服务启动失败
**错误日志**
```
FATAL: 微信配置不完整或无效
```
**解决方法**
- 检查所有必填环境变量是否设置
- 确认证书文件路径正确且文件存在
- 验证 APIv3 密钥是否为 32 位字符串
### 2. OAuth 授权失败,返回 1044 错误
**错误消息**
```json
{
"code": 1044,
"msg": "微信 OAuth 授权失败"
}
```
**可能原因**
- 授权码code已过期5分钟有效期
- 授权码已被使用过(一次性有效)
- AppID 或 AppSecret 配置错误
- 回调域名未在公众号后台配置
**解决方法**
- 重新发起授权流程获取新 code
- 检查公众号配置是否正确
- 查看 `logs/app.log` 获取详细错误信息
### 3. 支付订单创建失败,返回 1046 错误
**错误消息**
```json
{
"code": 1046,
"msg": "微信支付失败"
}
```
**可能原因**
- 商户号配置错误
- 证书文件无效或过期
- APIv3 密钥错误
- 订单金额为0或负数
**解决方法**
- 验证商户号和密钥是否正确
- 检查证书文件是否可读(权限问题)
- 确认证书序列号是否匹配
- 查看 `logs/app.log` 获取详细错误信息
### 4. 支付回调签名验证失败
**错误日志**
```
ERROR: 支付回调签名验证失败
```
**可能原因**
- 证书配置错误
- 证书序列号不匹配
- 证书已过期
**解决方法**
- 重新下载最新的商户证书
- 更新证书序列号配置
- 确保证书文件路径正确
### 5. 如何测试微信支付?
**开发环境测试**
1. 使用微信测试号(公众号测试账号)
2. 使用真实商户号的沙箱环境(需申请)
3. 使用 0.01 元测试订单(生产环境)
**注意**
- 测试订单需要真实支付
- 可以通过退款功能退回测试金额
- 建议使用沙箱环境进行测试
### 6. Redis 连接失败,影响微信功能吗?
**是的**,微信功能依赖 Redis 缓存 AccessToken。
**解决方法**
- 确保 Redis 服务正常运行
- 检查 Redis 连接配置(地址、端口、密码)
- 查看 `logs/app.log` 获取 Redis 连接错误
### 7. 如何调试微信支付问题?
**启用 HTTP 调试日志**
```bash
export JUNHONG_WECHAT_PAYMENT_HTTP_DEBUG=true
```
重启服务后,所有微信 API 请求和响应将记录到 `logs/app.log`
**查看日志**
```bash
tail -f logs/app.log | grep -i wechat
```
---
## 相关文档
- [微信公众号官方文档](https://developers.weixin.qq.com/doc/offiaccount/Getting_Started/Overview.html)
- [微信支付官方文档](https://pay.weixin.qq.com/wiki/doc/apiv3/index.shtml)
- [PowerWeChat SDK 文档](https://github.com/ArtisanCloud/PowerWeChat)
- [API 文档](./API文档.md)
- [环境变量配置](../environment-variables.md)