# 微信公众号与微信支付集成使用指南

本文档说明如何配置和使用系统的微信公众号 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)
