676 lines
14 KiB
Markdown
676 lines
14 KiB
Markdown
# 微信集成功能验证指南
|
||
|
||
本文档提供微信公众号 OAuth 认证和微信支付功能的完整验证流程。
|
||
|
||
## 目录
|
||
|
||
- [前置准备](#前置准备)
|
||
- [配置验证](#配置验证)
|
||
- [功能测试](#功能测试)
|
||
- [1. 微信 OAuth 登录](#1-微信-oauth-登录)
|
||
- [2. 微信账号绑定](#2-微信账号绑定)
|
||
- [3. 微信 JSAPI 支付](#3-微信-jsapi-支付)
|
||
- [4. 微信 H5 支付](#4-微信-h5-支付)
|
||
- [5. 支付回调验证](#5-支付回调验证)
|
||
- [常见问题排查](#常见问题排查)
|
||
|
||
---
|
||
|
||
## 前置准备
|
||
|
||
### 1. 微信配置准备
|
||
|
||
确保已获取以下信息:
|
||
|
||
**公众号配置**:
|
||
- [ ] AppID
|
||
- [ ] AppSecret
|
||
- [ ] OAuth 回调域名已配置(在公众号后台)
|
||
|
||
**支付配置**:
|
||
- [ ] 商户号
|
||
- [ ] APIv3 密钥(32位)
|
||
- [ ] 商户证书文件(apiclient_cert.pem)
|
||
- [ ] 商户私钥文件(apiclient_key.pem)
|
||
- [ ] 证书序列号
|
||
- [ ] 支付回调 URL 已配置(在商户平台)
|
||
|
||
### 2. 环境准备
|
||
|
||
```bash
|
||
# 创建证书目录
|
||
mkdir -p /app/certs
|
||
|
||
# 复制证书文件
|
||
cp apiclient_cert.pem /app/certs/
|
||
cp apiclient_key.pem /app/certs/
|
||
|
||
# 设置文件权限
|
||
chmod 600 /app/certs/*
|
||
|
||
# 加载环境变量
|
||
source .env.local
|
||
```
|
||
|
||
### 3. 启动服务
|
||
|
||
```bash
|
||
# 编译并启动
|
||
go run cmd/api/main.go
|
||
|
||
# 或使用 Docker
|
||
docker-compose up -d api
|
||
```
|
||
|
||
---
|
||
|
||
## 配置验证
|
||
|
||
### 自动验证脚本
|
||
|
||
运行配置验证脚本:
|
||
|
||
```bash
|
||
# 加载环境变量
|
||
source .env.local
|
||
|
||
# 运行验证脚本
|
||
bash scripts/verify-wechat.sh
|
||
```
|
||
|
||
**预期输出**(所有检查通过):
|
||
|
||
```
|
||
========================================
|
||
微信配置验证脚本
|
||
========================================
|
||
|
||
1. 检查微信公众号配置
|
||
----------------------------------------
|
||
✓ JUNHONG_WECHAT_OFFICIAL_ACCOUNT_APP_ID
|
||
✓ JUNHONG_WECHAT_OFFICIAL_ACCOUNT_APP_SECRET
|
||
✓ JUNHONG_WECHAT_OFFICIAL_ACCOUNT_TOKEN
|
||
✓ JUNHONG_WECHAT_OFFICIAL_ACCOUNT_AES_KEY
|
||
✓ JUNHONG_WECHAT_OFFICIAL_ACCOUNT_OAUTH_REDIRECT_URL
|
||
|
||
2. 检查微信支付配置
|
||
----------------------------------------
|
||
✓ JUNHONG_WECHAT_PAYMENT_APP_ID
|
||
✓ JUNHONG_WECHAT_PAYMENT_MCH_ID
|
||
✓ JUNHONG_WECHAT_PAYMENT_API_V3_KEY
|
||
✓ JUNHONG_WECHAT_PAYMENT_CERT_PATH
|
||
✓ JUNHONG_WECHAT_PAYMENT_KEY_PATH
|
||
✓ JUNHONG_WECHAT_PAYMENT_SERIAL_NO
|
||
✓ JUNHONG_WECHAT_PAYMENT_NOTIFY_URL
|
||
|
||
3. 检查证书文件
|
||
----------------------------------------
|
||
✓ 文件存在: /app/certs/apiclient_cert.pem
|
||
✓ 文件存在: /app/certs/apiclient_key.pem
|
||
|
||
4. 验证配置格式
|
||
----------------------------------------
|
||
✓ 支付回调 URL 使用 HTTPS
|
||
|
||
5. 检查证书有效性(可选)
|
||
----------------------------------------
|
||
✓ 证书有效期至: Jan 30 12:00:00 2026 GMT
|
||
✓ 证书序列号匹配
|
||
|
||
========================================
|
||
验证结果
|
||
========================================
|
||
错误: 0
|
||
警告: 0
|
||
|
||
✅ 配置验证通过,所有配置正确
|
||
```
|
||
|
||
### 查看服务启动日志
|
||
|
||
```bash
|
||
# 查看实时日志
|
||
tail -f logs/app.log
|
||
|
||
# 或使用 Docker
|
||
docker logs -f junhong-api
|
||
```
|
||
|
||
**预期日志**(成功初始化):
|
||
|
||
```json
|
||
{
|
||
"level": "info",
|
||
"ts": "2025-01-30T12:00:00.000+0800",
|
||
"msg": "微信公众号服务初始化成功"
|
||
}
|
||
{
|
||
"level": "info",
|
||
"ts": "2025-01-30T12:00:00.001+0800",
|
||
"msg": "微信支付服务初始化成功"
|
||
}
|
||
{
|
||
"level": "info",
|
||
"ts": "2025-01-30T12:00:00.002+0800",
|
||
"msg": "服务启动成功",
|
||
"address": ":3000"
|
||
}
|
||
```
|
||
|
||
**错误日志**(配置问题):
|
||
|
||
```json
|
||
{
|
||
"level": "fatal",
|
||
"ts": "2025-01-30T12:00:00.000+0800",
|
||
"msg": "微信配置不完整或无效",
|
||
"error": "证书文件不存在: /app/certs/apiclient_cert.pem"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 功能测试
|
||
|
||
### 1. 微信 OAuth 登录
|
||
|
||
#### 前端测试步骤
|
||
|
||
**步骤 1:构造授权 URL**
|
||
|
||
```javascript
|
||
const appId = 'wxabcdef1234567890';
|
||
const redirectUri = encodeURIComponent('https://your-domain.com/wechat-callback');
|
||
const state = Math.random().toString(36).substring(7);
|
||
|
||
const authUrl = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appId}&redirect_uri=${redirectUri}&response_type=code&scope=snsapi_userinfo&state=${state}#wechat_redirect`;
|
||
|
||
// 跳转到微信授权页面
|
||
window.location.href = authUrl;
|
||
```
|
||
|
||
**步骤 2:处理回调**
|
||
|
||
在回调页面(`https://your-domain.com/wechat-callback`):
|
||
|
||
```javascript
|
||
// 获取 URL 参数中的 code
|
||
const urlParams = new URLSearchParams(window.location.search);
|
||
const code = urlParams.get('code');
|
||
const state = urlParams.get('state');
|
||
|
||
if (!code) {
|
||
alert('授权失败:未获取到授权码');
|
||
return;
|
||
}
|
||
|
||
// 调用后端 OAuth 登录接口
|
||
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) {
|
||
console.log('登录成功', data.data);
|
||
localStorage.setItem('token', data.data.token);
|
||
// 跳转到主页
|
||
window.location.href = '/';
|
||
} else {
|
||
alert(`登录失败: ${data.msg}`);
|
||
}
|
||
})
|
||
.catch(err => {
|
||
console.error('请求失败', err);
|
||
alert('登录失败,请重试');
|
||
});
|
||
```
|
||
|
||
#### 后端日志验证
|
||
|
||
```bash
|
||
# 查看 OAuth 请求日志
|
||
tail -f logs/app.log | grep -i oauth
|
||
```
|
||
|
||
**成功日志**:
|
||
|
||
```json
|
||
{
|
||
"level": "debug",
|
||
"ts": "2025-01-30T12:00:00.000+0800",
|
||
"msg": "微信 OAuth 授权成功",
|
||
"open_id": "o6_bmjrPTlm6_2sgVt7hMZOPfL2M",
|
||
"union_id": "oGfRjwX..."
|
||
}
|
||
{
|
||
"level": "info",
|
||
"ts": "2025-01-30T12:00:00.001+0800",
|
||
"msg": "个人客户创建成功",
|
||
"customer_id": 123
|
||
}
|
||
```
|
||
|
||
**失败日志**:
|
||
|
||
```json
|
||
{
|
||
"level": "error",
|
||
"ts": "2025-01-30T12:00:00.000+0800",
|
||
"msg": "微信 OAuth 授权失败",
|
||
"code": "071abc123...",
|
||
"error": "invalid code"
|
||
}
|
||
```
|
||
|
||
#### 使用 curl 测试
|
||
|
||
```bash
|
||
# 替换为真实的授权码(5分钟有效)
|
||
CODE="071abc123456789def"
|
||
|
||
curl -X POST http://localhost:3000/api/c/v1/wechat/auth \
|
||
-H "Content-Type: application/json" \
|
||
-d "{\"code\":\"$CODE\"}"
|
||
```
|
||
|
||
**成功响应**:
|
||
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"msg": "登录成功",
|
||
"data": {
|
||
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
||
"customer_id": 123,
|
||
"phone": "",
|
||
"nickname": "微信用户",
|
||
"is_new_user": true
|
||
},
|
||
"timestamp": "2025-01-30T12:00:00+08:00"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 2. 微信账号绑定
|
||
|
||
#### 前提条件
|
||
|
||
- 已有个人客户账号
|
||
- 已获取 JWT Token
|
||
|
||
#### 测试步骤
|
||
|
||
```bash
|
||
# 替换为真实的 Token 和授权码
|
||
TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
|
||
CODE="071abc123456789def"
|
||
|
||
curl -X POST http://localhost:3000/api/c/v1/bind-wechat \
|
||
-H "Authorization: Bearer $TOKEN" \
|
||
-H "Content-Type: application/json" \
|
||
-d "{\"code\":\"$CODE\"}"
|
||
```
|
||
|
||
**成功响应**:
|
||
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"msg": "绑定成功",
|
||
"timestamp": "2025-01-30T12:00:00+08:00"
|
||
}
|
||
```
|
||
|
||
**失败响应**(微信号已被绑定):
|
||
|
||
```json
|
||
{
|
||
"code": 1020,
|
||
"msg": "该微信号已被其他账号绑定",
|
||
"timestamp": "2025-01-30T12:00:00+08:00"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 3. 微信 JSAPI 支付
|
||
|
||
#### 前提条件
|
||
|
||
- 已创建订单(状态为"待支付")
|
||
- 在微信内网页中调用
|
||
- 已获取用户 OpenID
|
||
|
||
#### 测试步骤
|
||
|
||
**步骤 1:创建支付订单**
|
||
|
||
```bash
|
||
# 替换为真实的 Token、订单ID 和 OpenID
|
||
H5_TOKEN="your_h5_token_here"
|
||
ORDER_ID=1
|
||
OPEN_ID="o6_bmjrPTlm6_2sgVt7hMZOPfL2M"
|
||
|
||
curl -X POST "http://localhost:3000/api/h5/orders/$ORDER_ID/wechat-pay/jsapi" \
|
||
-H "Authorization: Bearer $H5_TOKEN" \
|
||
-H "Content-Type: application/json" \
|
||
-d "{\"open_id\":\"$OPEN_ID\"}"
|
||
```
|
||
|
||
**成功响应**:
|
||
|
||
```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:00+08:00"
|
||
}
|
||
```
|
||
|
||
**步骤 2:前端唤起支付**
|
||
|
||
```javascript
|
||
// 获取支付配置后,调用微信 JSAPI
|
||
wx.chooseWXPay({
|
||
...payConfig,
|
||
success: function(res) {
|
||
console.log('支付成功', res);
|
||
alert('支付成功');
|
||
// 跳转到订单详情页
|
||
window.location.href = `/orders/${orderId}`;
|
||
},
|
||
fail: function(res) {
|
||
console.error('支付失败', res);
|
||
alert('支付失败:' + res.err_msg);
|
||
}
|
||
});
|
||
```
|
||
|
||
#### 后端日志验证
|
||
|
||
```bash
|
||
# 查看支付请求日志
|
||
tail -f logs/app.log | grep -i jsapi
|
||
```
|
||
|
||
**成功日志**:
|
||
|
||
```json
|
||
{
|
||
"level": "info",
|
||
"ts": "2025-01-30T12:00:00.000+0800",
|
||
"msg": "创建 JSAPI 支付订单成功",
|
||
"order_no": "ORDER_20250130_001",
|
||
"prepay_id": "wx30123456789012345678901234567890"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 4. 微信 H5 支付
|
||
|
||
#### 前提条件
|
||
|
||
- 已创建订单(状态为"待支付")
|
||
- 在浏览器中调用(微信外)
|
||
|
||
#### 测试步骤
|
||
|
||
**步骤 1:创建 H5 支付订单**
|
||
|
||
```bash
|
||
# 替换为真实的 Token 和订单ID
|
||
H5_TOKEN="your_h5_token_here"
|
||
ORDER_ID=1
|
||
|
||
curl -X POST "http://localhost:3000/api/h5/orders/$ORDER_ID/wechat-pay/h5" \
|
||
-H "Authorization: Bearer $H5_TOKEN" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{
|
||
"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=wx30123456789012345678901234567890&package=3583359058"
|
||
},
|
||
"timestamp": "2025-01-30T12:00:00+08:00"
|
||
}
|
||
```
|
||
|
||
**步骤 2:前端跳转支付**
|
||
|
||
```javascript
|
||
// 跳转到微信 H5 支付页面
|
||
const returnUrl = encodeURIComponent(`https://your-domain.com/orders/${orderId}`);
|
||
window.location.href = `${h5Url}&redirect_url=${returnUrl}`;
|
||
```
|
||
|
||
#### 后端日志验证
|
||
|
||
```bash
|
||
# 查看 H5 支付日志
|
||
tail -f logs/app.log | grep -i "h5"
|
||
```
|
||
|
||
**成功日志**:
|
||
|
||
```json
|
||
{
|
||
"level": "info",
|
||
"ts": "2025-01-30T12:00:00.000+0800",
|
||
"msg": "创建 H5 支付订单成功",
|
||
"order_no": "ORDER_20250130_001",
|
||
"h5_url": "https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?..."
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 5. 支付回调验证
|
||
|
||
#### 验证方法1:查看日志
|
||
|
||
支付成功后,微信会自动调用回调接口。查看日志验证:
|
||
|
||
```bash
|
||
# 查看支付回调日志
|
||
tail -f logs/app.log | grep -i "支付通知"
|
||
```
|
||
|
||
**成功日志**:
|
||
|
||
```json
|
||
{
|
||
"level": "info",
|
||
"ts": "2025-01-30T12:00:00.000+0800",
|
||
"msg": "支付通知处理成功",
|
||
"out_trade_no": "ORDER_20250130_001",
|
||
"transaction_id": "4200001234202501301234567890"
|
||
}
|
||
```
|
||
|
||
#### 验证方法2:查询订单状态
|
||
|
||
```bash
|
||
# 查询订单状态(使用数据库或 API)
|
||
curl -X GET "http://localhost:3000/api/h5/orders/$ORDER_ID" \
|
||
-H "Authorization: Bearer $H5_TOKEN"
|
||
```
|
||
|
||
**预期响应**(订单已支付):
|
||
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"data": {
|
||
"id": 1,
|
||
"order_no": "ORDER_20250130_001",
|
||
"status": "paid",
|
||
"total_amount": 100,
|
||
"paid_amount": 100,
|
||
"paid_at": "2025-01-30T12:00:00+08:00",
|
||
"payment_method": "wechat"
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 验证方法3:使用 Postman 模拟回调
|
||
|
||
**注意**:真实环境中由微信服务器调用,本地测试需要跳过签名验证。
|
||
|
||
```bash
|
||
# 模拟支付回调(仅测试环境)
|
||
curl -X POST http://localhost:3000/api/callback/wechat-pay \
|
||
-H "Content-Type: application/json" \
|
||
-d '{
|
||
"id": "test_id",
|
||
"create_time": "2025-01-30T12:00:00+08:00",
|
||
"resource_type": "encrypt-resource",
|
||
"event_type": "TRANSACTION.SUCCESS",
|
||
"summary": "支付成功",
|
||
"resource": {
|
||
"ciphertext": "...",
|
||
"nonce": "...",
|
||
"associated_data": "..."
|
||
}
|
||
}'
|
||
```
|
||
|
||
---
|
||
|
||
## 常见问题排查
|
||
|
||
### 1. 配置验证失败
|
||
|
||
**问题**:脚本报错 "缺失必填配置"
|
||
|
||
**解决方法**:
|
||
```bash
|
||
# 检查环境变量是否加载
|
||
env | grep JUNHONG_WECHAT
|
||
|
||
# 重新加载环境变量
|
||
source .env.local
|
||
|
||
# 重新运行验证脚本
|
||
bash scripts/verify-wechat.sh
|
||
```
|
||
|
||
### 2. 服务启动失败
|
||
|
||
**问题**:日志显示 "微信配置不完整或无效"
|
||
|
||
**解决方法**:
|
||
1. 查看详细错误日志
|
||
2. 检查证书文件路径是否正确
|
||
3. 验证证书文件权限(600 或 644)
|
||
4. 确认 APIv3 密钥长度为 32 位
|
||
|
||
### 3. OAuth 授权失败
|
||
|
||
**问题**:返回错误码 1044
|
||
|
||
**可能原因**:
|
||
- 授权码已过期(5分钟有效期)
|
||
- 授权码已被使用过
|
||
- AppID 或 AppSecret 配置错误
|
||
- 回调域名未在公众号后台配置
|
||
|
||
**解决方法**:
|
||
1. 重新发起授权流程获取新 code
|
||
2. 检查公众号配置
|
||
3. 查看详细日志:`tail -f logs/app.log | grep -i oauth`
|
||
|
||
### 4. 支付订单创建失败
|
||
|
||
**问题**:返回错误码 1046
|
||
|
||
**可能原因**:
|
||
- 商户号配置错误
|
||
- 证书文件无效或过期
|
||
- APIv3 密钥错误
|
||
- 订单金额为0或负数
|
||
|
||
**解决方法**:
|
||
1. 验证商户号和密钥
|
||
2. 检查证书有效期:`openssl x509 -in /app/certs/apiclient_cert.pem -noout -dates`
|
||
3. 确认证书序列号匹配
|
||
4. 查看详细日志:`tail -f logs/app.log | grep -i payment`
|
||
|
||
### 5. 支付回调签名验证失败
|
||
|
||
**问题**:日志显示 "支付回调签名验证失败"
|
||
|
||
**可能原因**:
|
||
- 证书配置错误
|
||
- 证书序列号不匹配
|
||
- 证书已过期
|
||
|
||
**解决方法**:
|
||
1. 重新下载最新的商户证书
|
||
2. 更新证书序列号配置
|
||
3. 确保证书文件路径正确
|
||
4. 验证证书:`bash scripts/verify-wechat.sh`
|
||
|
||
### 6. 启用调试日志
|
||
|
||
如需查看详细的 HTTP 请求日志:
|
||
|
||
```bash
|
||
# 设置环境变量
|
||
export JUNHONG_WECHAT_PAYMENT_HTTP_DEBUG=true
|
||
|
||
# 重启服务
|
||
go run cmd/api/main.go
|
||
|
||
# 查看调试日志
|
||
tail -f logs/app.log | grep -i wechat
|
||
```
|
||
|
||
---
|
||
|
||
## 验证清单
|
||
|
||
完成以下清单后,微信集成功能验证完成:
|
||
|
||
- [ ] 配置验证脚本通过(0 错误)
|
||
- [ ] 服务启动成功,微信服务初始化日志正常
|
||
- [ ] 微信 OAuth 登录成功,返回 Token
|
||
- [ ] 微信账号绑定成功
|
||
- [ ] JSAPI 支付订单创建成功,返回支付配置
|
||
- [ ] H5 支付订单创建成功,返回支付 URL
|
||
- [ ] 支付回调处理成功,订单状态更新为"已支付"
|
||
- [ ] 日志中无错误或警告信息
|
||
|
||
---
|
||
|
||
## 相关文档
|
||
|
||
- [使用指南](./使用指南.md) - 详细的配置和部署说明
|
||
- [API 文档](./API文档.md) - 接口说明和示例
|
||
- [环境变量配置](../environment-variables.md) - 所有环境变量说明
|