Merge branch 'emdash/wechat-official-account-payment-integration-30g'
# Conflicts: # README.md # cmd/api/main.go # internal/bootstrap/dependencies.go # pkg/config/config.go # pkg/config/defaults/config.yaml
This commit is contained in:
@@ -33,6 +33,40 @@
|
||||
|---------|------|------|
|
||||
| `JUNHONG_JWT_SECRET_KEY` | JWT 签名密钥(生产环境必须修改) | `your-secret-key` |
|
||||
|
||||
### 微信配置
|
||||
|
||||
#### 微信公众号
|
||||
|
||||
| 环境变量 | 说明 | 示例 |
|
||||
|---------|------|------|
|
||||
| `JUNHONG_WECHAT_OFFICIAL_ACCOUNT_APP_ID` | 公众号 AppID(必填) | `wxabcdef1234567890` |
|
||||
| `JUNHONG_WECHAT_OFFICIAL_ACCOUNT_APP_SECRET` | 公众号 AppSecret(必填) | `abcdef1234567890` |
|
||||
| `JUNHONG_WECHAT_OFFICIAL_ACCOUNT_TOKEN` | 服务器配置Token(可选) | `your_token` |
|
||||
| `JUNHONG_WECHAT_OFFICIAL_ACCOUNT_AES_KEY` | 消息加解密Key(可选) | `` |
|
||||
| `JUNHONG_WECHAT_OFFICIAL_ACCOUNT_OAUTH_REDIRECT_URL` | OAuth回调URL(可选) | `https://your-domain.com/callback` |
|
||||
|
||||
#### 微信支付
|
||||
|
||||
| 环境变量 | 说明 | 示例 |
|
||||
|---------|------|------|
|
||||
| `JUNHONG_WECHAT_PAYMENT_APP_ID` | 支付 AppID(必填,通常与公众号相同) | `wxabcdef1234567890` |
|
||||
| `JUNHONG_WECHAT_PAYMENT_MCH_ID` | 商户号(必填) | `1234567890` |
|
||||
| `JUNHONG_WECHAT_PAYMENT_API_V3_KEY` | APIv3 密钥(必填,32位字符串) | `your_apiv3_key_32_chars_here` |
|
||||
| `JUNHONG_WECHAT_PAYMENT_API_V2_KEY` | APIv2 密钥(可选,部分接口需要) | `` |
|
||||
| `JUNHONG_WECHAT_PAYMENT_CERT_PATH` | 商户证书路径(必填) | `/app/certs/apiclient_cert.pem` |
|
||||
| `JUNHONG_WECHAT_PAYMENT_KEY_PATH` | 商户私钥路径(必填) | `/app/certs/apiclient_key.pem` |
|
||||
| `JUNHONG_WECHAT_PAYMENT_SERIAL_NO` | 证书序列号(必填) | `1234567890ABCDEF` |
|
||||
| `JUNHONG_WECHAT_PAYMENT_NOTIFY_URL` | 支付回调URL(必填) | `https://api.your-domain.com/api/callback/wechat-pay` |
|
||||
| `JUNHONG_WECHAT_PAYMENT_HTTP_DEBUG` | HTTP调试日志(可选) | `false` |
|
||||
| `JUNHONG_WECHAT_PAYMENT_TIMEOUT` | HTTP请求超时(可选) | `30s` |
|
||||
|
||||
**配置说明**:
|
||||
- 微信公众号和支付配置缺失时,服务启动会失败(FATAL 错误)
|
||||
- 证书文件必须可读(权限 600 或 644)
|
||||
- APIv3 密钥必须是 32 位字符串
|
||||
- 证书序列号可通过 `openssl x509 -in apiclient_cert.pem -noout -serial` 获取
|
||||
- 详细配置指南参见 [微信集成使用指南](wechat-integration/使用指南.md)
|
||||
|
||||
## 可选配置
|
||||
|
||||
以下配置有合理的默认值,可按需覆盖:
|
||||
|
||||
564
docs/wechat-integration/API文档.md
Normal file
564
docs/wechat-integration/API文档.md
Normal file
@@ -0,0 +1,564 @@
|
||||
# 微信集成 API 文档
|
||||
|
||||
本文档详细说明微信 OAuth 登录和微信支付相关的 API 接口。
|
||||
|
||||
## 目录
|
||||
|
||||
- [认证说明](#认证说明)
|
||||
- [错误码](#错误码)
|
||||
- [API 接口](#api-接口)
|
||||
- [1. 微信 OAuth 登录](#1-微信-oauth-登录)
|
||||
- [2. 绑定微信账号](#2-绑定微信账号)
|
||||
- [3. 微信 JSAPI 支付](#3-微信-jsapi-支付)
|
||||
- [4. 微信 H5 支付](#4-微信-h5-支付)
|
||||
- [5. 微信支付回调](#5-微信支付回调)
|
||||
|
||||
---
|
||||
|
||||
## 认证说明
|
||||
|
||||
### 公开接口
|
||||
|
||||
以下接口无需认证,可直接调用:
|
||||
|
||||
- `POST /api/c/v1/wechat/auth` - 微信 OAuth 登录
|
||||
- `POST /api/callback/wechat-pay` - 微信支付回调
|
||||
|
||||
### 需要认证的接口
|
||||
|
||||
以下接口需要在请求头中携带 JWT Token:
|
||||
|
||||
```
|
||||
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
|
||||
```
|
||||
|
||||
- `POST /api/c/v1/bind-wechat` - 绑定微信账号(个人客户)
|
||||
- `POST /api/h5/orders/:id/wechat-pay/jsapi` - 微信 JSAPI 支付(H5认证)
|
||||
- `POST /api/h5/orders/:id/wechat-pay/h5` - 微信 H5 支付(H5认证)
|
||||
|
||||
---
|
||||
|
||||
## 错误码
|
||||
|
||||
微信集成相关的错误码:
|
||||
|
||||
| 错误码 | 说明 | HTTP 状态码 |
|
||||
|--------|------|-------------|
|
||||
| 1044 | 微信 OAuth 授权失败 | 400 |
|
||||
| 1045 | 获取微信用户信息失败 | 400 |
|
||||
| 1046 | 微信支付失败 | 400 |
|
||||
| 1047 | 微信支付回调数据无效 | 400 |
|
||||
| 1003 | 参数无效 | 400 |
|
||||
| 1020 | 手机号已被使用 | 400 |
|
||||
| 1021 | 个人客户不存在 | 404 |
|
||||
| 1035 | 订单不存在 | 404 |
|
||||
|
||||
---
|
||||
|
||||
## API 接口
|
||||
|
||||
### 1. 微信 OAuth 登录
|
||||
|
||||
通过微信授权码登录或创建账号。如果用户首次登录,系统会自动创建账号。
|
||||
|
||||
**接口地址**
|
||||
|
||||
```
|
||||
POST /api/c/v1/wechat/auth
|
||||
```
|
||||
|
||||
**请求参数**
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| code | string | ✅ | 微信授权码(5分钟有效期,一次性使用) |
|
||||
|
||||
**请求示例**
|
||||
|
||||
```json
|
||||
{
|
||||
"code": "071abc123456789def"
|
||||
}
|
||||
```
|
||||
|
||||
**响应参数**
|
||||
|
||||
| 参数 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| code | integer | 响应码(0表示成功) |
|
||||
| msg | string | 响应消息 |
|
||||
| data | object | 响应数据 |
|
||||
| data.token | string | JWT Token(用于后续请求认证) |
|
||||
| data.customer_id | integer | 个人客户ID |
|
||||
| data.phone | string | 手机号(未绑定时为空) |
|
||||
| data.nickname | string | 昵称(微信昵称) |
|
||||
| data.is_new_user | boolean | 是否新用户 |
|
||||
| timestamp | string | 响应时间戳(RFC3339格式) |
|
||||
|
||||
**响应示例**
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"msg": "登录成功",
|
||||
"data": {
|
||||
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjdXN0b21lcl9pZCI6MTIzLCJleHAiOjE3MDY2OTI4MDB9.abc123def456",
|
||||
"customer_id": 123,
|
||||
"phone": "138****8888",
|
||||
"nickname": "微信用户",
|
||||
"is_new_user": false
|
||||
},
|
||||
"timestamp": "2025-01-30T12:00:00+08:00"
|
||||
}
|
||||
```
|
||||
|
||||
**错误响应**
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 1044,
|
||||
"msg": "微信 OAuth 授权失败",
|
||||
"timestamp": "2025-01-30T12:00:00+08:00"
|
||||
}
|
||||
```
|
||||
|
||||
**业务逻辑**
|
||||
|
||||
1. 验证授权码是否有效
|
||||
2. 调用微信API获取用户 OpenID 和 UnionID
|
||||
3. 查找数据库是否存在该微信用户:
|
||||
- **存在**:返回已有账号的 Token
|
||||
- **不存在**:创建新账号,返回新账号的 Token
|
||||
4. 新用户状态为"未绑定手机号",后续需要绑定手机号才能使用完整功能
|
||||
|
||||
**注意事项**
|
||||
|
||||
- 授权码(code)只能使用一次,重复使用会失败
|
||||
- 授权码有效期为5分钟
|
||||
- Token 有效期为7天
|
||||
- 新用户首次登录时 `phone` 字段为空,需要引导绑定手机号
|
||||
|
||||
---
|
||||
|
||||
### 2. 绑定微信账号
|
||||
|
||||
将当前登录的个人客户账号绑定到微信。
|
||||
|
||||
**接口地址**
|
||||
|
||||
```
|
||||
POST /api/c/v1/bind-wechat
|
||||
```
|
||||
|
||||
**认证方式**
|
||||
|
||||
需要携带 JWT Token(个人客户)。
|
||||
|
||||
**请求参数**
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| code | string | ✅ | 微信授权码 |
|
||||
|
||||
**请求示例**
|
||||
|
||||
```json
|
||||
{
|
||||
"code": "071abc123456789def"
|
||||
}
|
||||
```
|
||||
|
||||
**响应参数**
|
||||
|
||||
| 参数 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| code | integer | 响应码(0表示成功) |
|
||||
| msg | string | 响应消息 |
|
||||
| timestamp | string | 响应时间戳 |
|
||||
|
||||
**响应示例**
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"msg": "绑定成功",
|
||||
"timestamp": "2025-01-30T12:00:00+08:00"
|
||||
}
|
||||
```
|
||||
|
||||
**错误响应**
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 1020,
|
||||
"msg": "该微信号已被其他账号绑定",
|
||||
"timestamp": "2025-01-30T12:00:00+08:00"
|
||||
}
|
||||
```
|
||||
|
||||
**业务逻辑**
|
||||
|
||||
1. 验证授权码是否有效
|
||||
2. 获取微信 OpenID 和 UnionID
|
||||
3. 检查该微信号是否已被其他账号绑定
|
||||
4. 更新当前账号的微信绑定信息
|
||||
|
||||
**注意事项**
|
||||
|
||||
- 一个微信号只能绑定一个账号
|
||||
- 绑定后无法解绑(需联系管理员)
|
||||
- 绑定成功后,可以使用微信登录
|
||||
|
||||
---
|
||||
|
||||
### 3. 微信 JSAPI 支付
|
||||
|
||||
创建微信 JSAPI 支付订单(微信内网页支付)。
|
||||
|
||||
**接口地址**
|
||||
|
||||
```
|
||||
POST /api/h5/orders/:id/wechat-pay/jsapi
|
||||
```
|
||||
|
||||
**认证方式**
|
||||
|
||||
需要携带 H5 Token。
|
||||
|
||||
**路径参数**
|
||||
|
||||
| 参数 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| id | integer | 订单ID |
|
||||
|
||||
**请求参数**
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| open_id | string | ✅ | 用户的微信 OpenID(在公众号内获取) |
|
||||
|
||||
**请求示例**
|
||||
|
||||
```json
|
||||
{
|
||||
"open_id": "o6_bmjrPTlm6_2sgVt7hMZOPfL2M"
|
||||
}
|
||||
```
|
||||
|
||||
**响应参数**
|
||||
|
||||
| 参数 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| code | integer | 响应码(0表示成功) |
|
||||
| msg | string | 响应消息 |
|
||||
| data | object | 响应数据 |
|
||||
| data.prepay_id | string | 预支付交易会话标识 |
|
||||
| data.pay_config | object | 支付配置(直接传给微信JSAPI) |
|
||||
| data.pay_config.appId | string | 公众号AppID |
|
||||
| data.pay_config.timeStamp | string | 时间戳 |
|
||||
| data.pay_config.nonceStr | string | 随机字符串 |
|
||||
| data.pay_config.package | string | 订单详情扩展字符串 |
|
||||
| data.pay_config.signType | string | 签名方式(RSA) |
|
||||
| data.pay_config.paySign | string | 签名 |
|
||||
| timestamp | string | 响应时间戳 |
|
||||
|
||||
**响应示例**
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"msg": "支付订单创建成功",
|
||||
"data": {
|
||||
"prepay_id": "wx30123456789012345678901234567890",
|
||||
"pay_config": {
|
||||
"appId": "wxabcdef1234567890",
|
||||
"timeStamp": "1706606400",
|
||||
"nonceStr": "5K8264ILTKCH16CQ2502SI8ZNMTM67VS",
|
||||
"package": "prepay_id=wx30123456789012345678901234567890",
|
||||
"signType": "RSA",
|
||||
"paySign": "oR9d8PuhnIc+YZ8cBHFCwfgpaK9gd7JS..."
|
||||
}
|
||||
},
|
||||
"timestamp": "2025-01-30T12:00:00+08:00"
|
||||
}
|
||||
```
|
||||
|
||||
**错误响应**
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 1035,
|
||||
"msg": "订单不存在",
|
||||
"timestamp": "2025-01-30T12:00:00+08:00"
|
||||
}
|
||||
```
|
||||
|
||||
**业务逻辑**
|
||||
|
||||
1. 验证订单是否存在且状态为"待支付"
|
||||
2. 验证订单归属(只能支付自己的订单)
|
||||
3. 调用微信支付API创建预支付订单
|
||||
4. 生成支付配置(包含签名)
|
||||
5. 返回支付配置给前端
|
||||
|
||||
**前端调用示例**
|
||||
|
||||
```javascript
|
||||
// 获取支付配置
|
||||
const res = await fetch(`/api/h5/orders/${orderId}/wechat-pay/jsapi`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${h5Token}`
|
||||
},
|
||||
body: JSON.stringify({ open_id: openId })
|
||||
});
|
||||
|
||||
const result = await res.json();
|
||||
|
||||
// 调用微信JSAPI支付
|
||||
wx.chooseWXPay({
|
||||
...result.data.pay_config,
|
||||
success: function(res) {
|
||||
console.log('支付成功', res);
|
||||
},
|
||||
fail: function(res) {
|
||||
console.log('支付失败', res);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
**注意事项**
|
||||
|
||||
- 只能在微信内网页中使用
|
||||
- OpenID 需要通过公众号 OAuth 获取
|
||||
- 支付有效期为2小时
|
||||
- 订单只能支付一次
|
||||
|
||||
---
|
||||
|
||||
### 4. 微信 H5 支付
|
||||
|
||||
创建微信 H5 支付订单(微信外浏览器支付)。
|
||||
|
||||
**接口地址**
|
||||
|
||||
```
|
||||
POST /api/h5/orders/:id/wechat-pay/h5
|
||||
```
|
||||
|
||||
**认证方式**
|
||||
|
||||
需要携带 H5 Token。
|
||||
|
||||
**路径参数**
|
||||
|
||||
| 参数 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| id | integer | 订单ID |
|
||||
|
||||
**请求参数**
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| scene_info | object | ❌ | 场景信息 |
|
||||
| scene_info.payer_client_ip | string | ❌ | 用户客户端IP |
|
||||
| scene_info.h5_type | string | ❌ | H5类型(Wap/IOS/Android) |
|
||||
|
||||
**请求示例**
|
||||
|
||||
```json
|
||||
{
|
||||
"scene_info": {
|
||||
"payer_client_ip": "123.12.12.123",
|
||||
"h5_type": "Wap"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**响应参数**
|
||||
|
||||
| 参数 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| code | integer | 响应码(0表示成功) |
|
||||
| msg | string | 响应消息 |
|
||||
| data | object | 响应数据 |
|
||||
| data.h5_url | string | H5 支付跳转链接 |
|
||||
| timestamp | string | 响应时间戳 |
|
||||
|
||||
**响应示例**
|
||||
|
||||
```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"
|
||||
}
|
||||
```
|
||||
|
||||
**前端调用示例**
|
||||
|
||||
```javascript
|
||||
// 创建 H5 支付订单
|
||||
const res = await fetch(`/api/h5/orders/${orderId}/wechat-pay/h5`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${h5Token}`
|
||||
},
|
||||
body: JSON.stringify({
|
||||
scene_info: {
|
||||
payer_client_ip: clientIp,
|
||||
h5_type: 'Wap'
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
const result = await res.json();
|
||||
|
||||
// 跳转到微信 H5 支付页面
|
||||
if (result.code === 0) {
|
||||
const returnUrl = encodeURIComponent(`https://your-domain.com/orders/${orderId}`);
|
||||
window.location.href = `${result.data.h5_url}&redirect_url=${returnUrl}`;
|
||||
}
|
||||
```
|
||||
|
||||
**注意事项**
|
||||
|
||||
- 适用于微信外浏览器
|
||||
- 支付完成后会跳转到 `redirect_url`(需URL编码)
|
||||
- 支付有效期为5分钟
|
||||
- 需要在微信商户平台配置 H5 支付域名
|
||||
|
||||
---
|
||||
|
||||
### 5. 微信支付回调
|
||||
|
||||
接收微信支付的异步通知。
|
||||
|
||||
**接口地址**
|
||||
|
||||
```
|
||||
POST /api/callback/wechat-pay
|
||||
```
|
||||
|
||||
**认证方式**
|
||||
|
||||
无需认证(由微信签名验证)。
|
||||
|
||||
**请求说明**
|
||||
|
||||
该接口由微信支付系统调用,开发者无需主动调用。
|
||||
|
||||
**请求头**
|
||||
|
||||
| 参数 | 说明 |
|
||||
|------|------|
|
||||
| Wechatpay-Serial | 微信支付平台证书序列号 |
|
||||
| Wechatpay-Signature | 微信签名 |
|
||||
| Wechatpay-Timestamp | 微信时间戳 |
|
||||
| Wechatpay-Nonce | 微信随机串 |
|
||||
|
||||
**请求体**
|
||||
|
||||
微信发送的加密数据(JSON格式)。
|
||||
|
||||
**响应**
|
||||
|
||||
成功处理返回 HTTP 200:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": "SUCCESS",
|
||||
"message": "成功"
|
||||
}
|
||||
```
|
||||
|
||||
失败返回 HTTP 500:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": "FAIL",
|
||||
"message": "失败原因"
|
||||
}
|
||||
```
|
||||
|
||||
**处理流程**
|
||||
|
||||
1. 验证微信签名(PowerWeChat 自动处理)
|
||||
2. 解密通知数据
|
||||
3. 提取支付结果(交易状态、金额、订单号等)
|
||||
4. 更新订单状态为"已支付"
|
||||
5. 触发异步任务:
|
||||
- 分佣计算
|
||||
- 套餐分配
|
||||
- 钱包充值
|
||||
6. 返回成功响应给微信
|
||||
|
||||
**幂等性保证**
|
||||
|
||||
系统会检查订单状态,避免重复处理:
|
||||
|
||||
- 如果订单已支付,直接返回成功
|
||||
- 如果订单不存在,返回失败
|
||||
- 使用数据库事务确保原子性
|
||||
|
||||
**重试机制**
|
||||
|
||||
微信会在以下情况重试:
|
||||
|
||||
- 商户系统未返回响应
|
||||
- 返回 HTTP 状态码不是 200
|
||||
- 返回结果为 FAIL
|
||||
|
||||
重试规则:
|
||||
- 15秒后第1次重试
|
||||
- 30秒后第2次重试
|
||||
- 3分钟后第3次重试
|
||||
- 最多重试3次
|
||||
|
||||
**注意事项**
|
||||
|
||||
- 接口必须在 **10秒内** 返回响应
|
||||
- 必须返回正确的 JSON 格式
|
||||
- 签名验证失败会记录日志但不影响服务
|
||||
- 处理失败会自动重试,无需手动干预
|
||||
|
||||
---
|
||||
|
||||
## 测试建议
|
||||
|
||||
### 开发环境测试
|
||||
|
||||
1. **OAuth 登录测试**
|
||||
- 使用微信测试号(公众号测试账号)
|
||||
- 在本地配置内网穿透(ngrok、frp等)
|
||||
- 测试授权流程和账号创建
|
||||
|
||||
2. **支付功能测试**
|
||||
- 使用 0.01 元小额订单测试
|
||||
- 验证支付流程和回调处理
|
||||
- 测试完成后可通过退款功能退回
|
||||
|
||||
3. **回调测试**
|
||||
- 使用微信支付沙箱环境(需申请)
|
||||
- 或者使用 Postman 模拟回调请求
|
||||
- 验证幂等性和重试机制
|
||||
|
||||
### 生产环境测试
|
||||
|
||||
1. 使用真实商户号和公众号
|
||||
2. 配置正确的 HTTPS 域名
|
||||
3. 小额订单测试(建议 0.01 元)
|
||||
4. 监控日志确认回调正常
|
||||
|
||||
---
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [使用指南](./使用指南.md) - 详细的配置和部署说明
|
||||
- [环境变量配置](../environment-variables.md) - 所有环境变量说明
|
||||
- [README](../../README.md) - 项目整体说明
|
||||
562
docs/wechat-integration/使用指南.md
Normal file
562
docs/wechat-integration/使用指南.md
Normal file
@@ -0,0 +1,562 @@
|
||||
# 微信公众号与微信支付集成使用指南
|
||||
|
||||
本文档说明如何配置和使用系统的微信公众号 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)
|
||||
675
docs/wechat-integration/验证指南.md
Normal file
675
docs/wechat-integration/验证指南.md
Normal file
@@ -0,0 +1,675 @@
|
||||
# 微信集成功能验证指南
|
||||
|
||||
本文档提供微信公众号 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) - 所有环境变量说明
|
||||
Reference in New Issue
Block a user