279 lines
9.3 KiB
Markdown
279 lines
9.3 KiB
Markdown
# 支付宝付费开通会员 — 部署指南
|
||
|
||
本文档涵盖支付宝电脑网站支付功能的完整部署流程。用户注册后通过支付宝付费自动激活会员,有效期 1 年。
|
||
|
||
---
|
||
|
||
## 前置条件
|
||
|
||
- 支付宝企业/个体商户账号
|
||
- 已在 [支付宝开放平台](https://open.alipay.com) 创建应用并获取 APPID
|
||
- 应用已开通 **「电脑网站支付」** 产品权限(`alipay.trade.page.pay` 接口)
|
||
- 服务器域名已配置 HTTPS(支付宝回调要求公网可达)
|
||
|
||
---
|
||
|
||
## 第一部分:支付宝开放平台配置
|
||
|
||
### 1. 创建应用
|
||
|
||
登录 https://open.alipay.com → 控制台 → 创建应用(或使用已有应用)。
|
||
|
||
### 2. 开通「电脑网站支付」产品
|
||
|
||
进入应用详情 → 产品绑定/产品管理 → 添加 **「电脑网站支付」** → 提交审核。
|
||
|
||
> **注意**:未开通此产品会导致 `ACQ.ACCESS_FORBIDDEN` 错误。
|
||
|
||
### 3. 生成密钥对
|
||
|
||
进入应用详情 → 开发设置 → 接口加签方式 → 选择 **RSA2(SHA256)**:
|
||
|
||
1. 使用支付宝官方密钥工具生成 RSA2048 密钥对
|
||
2. 将 **应用公钥** 上传到开放平台
|
||
3. 上传后平台会显示 **支付宝公钥**(`alipayPublicKey_RSA2`)
|
||
|
||
最终你会得到两样东西:
|
||
- **应用私钥**:你本地保存,代码用来签名请求
|
||
- **支付宝公钥**:平台返回给你,代码用来验证回调签名
|
||
|
||
> 应用公钥只是上传用的中间产物,代码中不需要。
|
||
|
||
---
|
||
|
||
## 第二部分:服务器配置
|
||
|
||
### 1. 放置密钥文件
|
||
|
||
将密钥保存为标准 PEM 格式,放到 `backend/keys/` 目录:
|
||
|
||
```bash
|
||
mkdir -p /home/rongye/ProgramFiles/ViGent2/backend/keys
|
||
```
|
||
|
||
**`backend/keys/app_private_key.pem`**(应用私钥):
|
||
|
||
```
|
||
-----BEGIN PRIVATE KEY-----
|
||
MIIEvQIBADANBgkqhkiG9w0BAQEFAASC...(你的私钥内容)
|
||
...
|
||
-----END PRIVATE KEY-----
|
||
```
|
||
|
||
**`backend/keys/alipay_public_key.pem`**(支付宝公钥):
|
||
|
||
```
|
||
-----BEGIN PUBLIC KEY-----
|
||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A...(支付宝公钥内容)
|
||
...
|
||
-----END PUBLIC KEY-----
|
||
```
|
||
|
||
#### PEM 格式要求
|
||
|
||
支付宝密钥工具导出的是一行纯文本,需要转换为标准 PEM 格式:
|
||
|
||
- 必须有头尾标记(`-----BEGIN/END ...-----`)
|
||
- 密钥内容每 64 字符换行
|
||
- 私钥头标记为 `-----BEGIN PRIVATE KEY-----`(PKCS#8 格式)
|
||
- 公钥头标记为 `-----BEGIN PUBLIC KEY-----`
|
||
|
||
如果你拿到的是一行裸密钥,用以下命令转换:
|
||
|
||
```bash
|
||
# 私钥格式化(假设裸密钥在 raw_private.txt 中)
|
||
echo "-----BEGIN PRIVATE KEY-----" > app_private_key.pem
|
||
cat raw_private.txt | fold -w 64 >> app_private_key.pem
|
||
echo "-----END PRIVATE KEY-----" >> app_private_key.pem
|
||
|
||
# 公钥格式化
|
||
echo "-----BEGIN PUBLIC KEY-----" > alipay_public_key.pem
|
||
cat raw_public.txt | fold -w 64 >> alipay_public_key.pem
|
||
echo "-----END PUBLIC KEY-----" >> alipay_public_key.pem
|
||
```
|
||
|
||
> `backend/keys/` 目录已加入 `.gitignore`,不会被提交到仓库。
|
||
|
||
### 2. 配置环境变量
|
||
|
||
在 `backend/.env` 中添加:
|
||
|
||
```ini
|
||
# =============== 支付宝配置 ===============
|
||
ALIPAY_APP_ID=你的应用APPID
|
||
ALIPAY_PRIVATE_KEY_PATH=/home/rongye/ProgramFiles/ViGent2/backend/keys/app_private_key.pem
|
||
ALIPAY_PUBLIC_KEY_PATH=/home/rongye/ProgramFiles/ViGent2/backend/keys/alipay_public_key.pem
|
||
ALIPAY_NOTIFY_URL=https://vigent.hbyrkj.top/api/payment/notify
|
||
ALIPAY_RETURN_URL=https://vigent.hbyrkj.top/pay
|
||
```
|
||
|
||
| 变量 | 说明 |
|
||
|------|------|
|
||
| `ALIPAY_APP_ID` | 支付宝开放平台应用 APPID |
|
||
| `ALIPAY_PRIVATE_KEY_PATH` | 应用私钥 PEM 文件绝对路径 |
|
||
| `ALIPAY_PUBLIC_KEY_PATH` | 支付宝公钥 PEM 文件绝对路径 |
|
||
| `ALIPAY_NOTIFY_URL` | 异步回调地址(服务器间通信),必须公网 HTTPS 可达 |
|
||
| `ALIPAY_RETURN_URL` | 同步跳转地址(用户支付完成后浏览器跳转回的页面) |
|
||
|
||
`config.py` 中还有几个可调参数(已有默认值,一般不需要加到 .env):
|
||
|
||
| 变量 | 默认值 | 说明 |
|
||
|------|--------|------|
|
||
| `ALIPAY_SANDBOX` | `false` | 是否使用沙箱环境 |
|
||
| `PAYMENT_AMOUNT` | `999.00` | 会员价格(元) |
|
||
| `PAYMENT_EXPIRE_DAYS` | `365` | 会员有效天数 |
|
||
|
||
### 3. 创建数据库表
|
||
|
||
通过 Docker 在本地 Supabase 中执行:
|
||
|
||
```bash
|
||
docker exec -i supabase-db psql -U postgres -c "
|
||
CREATE TABLE IF NOT EXISTS orders (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
|
||
out_trade_no TEXT UNIQUE NOT NULL,
|
||
amount DECIMAL(10, 2) NOT NULL DEFAULT 999.00,
|
||
status TEXT DEFAULT 'pending' CHECK (status IN ('pending', 'paid', 'failed')),
|
||
trade_no TEXT,
|
||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||
paid_at TIMESTAMP WITH TIME ZONE
|
||
);
|
||
|
||
CREATE INDEX IF NOT EXISTS idx_orders_user_id ON orders(user_id);
|
||
CREATE INDEX IF NOT EXISTS idx_orders_out_trade_no ON orders(out_trade_no);
|
||
"
|
||
```
|
||
|
||
### 4. 安装依赖
|
||
|
||
```bash
|
||
# 后端(在 venv 中)
|
||
cd /home/rongye/ProgramFiles/ViGent2/backend
|
||
venv/bin/pip install python-alipay-sdk
|
||
```
|
||
|
||
> 前端无额外依赖需要安装。
|
||
|
||
### 5. Nginx 配置
|
||
|
||
确保 Nginx 将 `/api/payment/notify` 代理到后端。如果现有配置已覆盖 `/api/` 前缀,则无需额外修改:
|
||
|
||
```nginx
|
||
location /api/ {
|
||
proxy_pass http://localhost:8006;
|
||
# ... 现有配置
|
||
}
|
||
```
|
||
|
||
### 6. 重启服务
|
||
|
||
```bash
|
||
# 构建前端
|
||
cd /home/rongye/ProgramFiles/ViGent2/frontend
|
||
npx next build
|
||
|
||
# 重启
|
||
pm2 restart vigent2-backend
|
||
pm2 restart vigent2-frontend
|
||
```
|
||
|
||
---
|
||
|
||
## 第三部分:正式上线
|
||
|
||
测试通过后,将 `backend/app/core/config.py` 中的测试金额改为正式价格:
|
||
|
||
```python
|
||
PAYMENT_AMOUNT: float = 999.00 # 正式价格
|
||
```
|
||
|
||
或在 `backend/.env` 中添加覆盖:
|
||
|
||
```ini
|
||
PAYMENT_AMOUNT=999.00
|
||
```
|
||
|
||
然后重启后端:
|
||
|
||
```bash
|
||
pm2 restart vigent2-backend
|
||
```
|
||
|
||
---
|
||
|
||
## 支付流程说明
|
||
|
||
```
|
||
用户注册 → 登录(密码正确但 is_active=false)
|
||
→ 后端返回 403 + payment_token
|
||
→ 前端跳转 /pay 页面
|
||
→ POST /api/payment/create-order → 返回支付宝收银台 URL
|
||
→ 前端重定向到支付宝收银台页面(支持扫码、账号登录、余额等多种支付方式)
|
||
→ 用户完成支付
|
||
→ 支付宝异步回调 POST /api/payment/notify
|
||
→ 后端验签 → 更新订单 → 激活用户(is_active=true, expires_at=+365天)
|
||
→ 支付宝同步跳转回 /pay?out_trade_no=xxx
|
||
→ 前端轮询 GET /api/payment/status/{out_trade_no}
|
||
→ 轮询到 paid → 提示成功 → 跳转登录页
|
||
→ 用户重新登录 → 成功进入系统
|
||
```
|
||
|
||
**电脑网站支付 vs 当面付**:电脑网站支付(`alipay.trade.page.pay`)会跳转到支付宝官方收银台页面,用户可以选择扫码、支付宝账号登录、余额等多种方式支付,体验更好。当面付(`alipay.trade.precreate`)仅生成一个二维码,只能扫码支付。
|
||
|
||
会员到期续费同流程:登录时检测到过期 → 返回 PAYMENT_REQUIRED → 跳转 /pay。
|
||
|
||
管理员手动激活功能不受影响,两种方式并存。
|
||
|
||
---
|
||
|
||
## 涉及文件
|
||
|
||
| 文件 | 变更类型 | 说明 |
|
||
|------|---------|------|
|
||
| `backend/requirements.txt` | 修改 | 添加 `python-alipay-sdk` |
|
||
| `backend/database/schema.sql` | 修改 | 新增 `orders` 表 |
|
||
| `backend/app/core/config.py` | 修改 | 支付宝配置项 |
|
||
| `backend/app/core/security.py` | 修改 | payment_token 函数 |
|
||
| `backend/app/core/deps.py` | 修改 | is_active 安全兜底 |
|
||
| `backend/app/repositories/orders.py` | 新建 | orders 数据层 |
|
||
| `backend/app/modules/payment/__init__.py` | 新建 | 模块初始化 |
|
||
| `backend/app/modules/payment/schemas.py` | 新建 | 请求/响应模型 |
|
||
| `backend/app/modules/payment/service.py` | 新建 | 支付业务逻辑(电脑网站支付) |
|
||
| `backend/app/modules/payment/router.py` | 新建 | 3 个 API 端点 |
|
||
| `backend/app/modules/auth/router.py` | 修改 | 登录返回 PAYMENT_REQUIRED |
|
||
| `backend/app/main.py` | 修改 | 注册 payment_router |
|
||
| `backend/.env` | 修改 | 支付宝环境变量 |
|
||
| `backend/keys/` | 新建 | PEM 密钥文件 |
|
||
| `frontend/src/shared/lib/auth.ts` | 修改 | login() 处理 paymentToken |
|
||
| `frontend/src/shared/api/axios.ts` | 修改 | PUBLIC_PATHS 加 /pay |
|
||
| `frontend/src/app/login/page.tsx` | 修改 | paymentToken 跳转 |
|
||
| `frontend/src/app/register/page.tsx` | 修改 | 注册成功提示文案 |
|
||
| `frontend/src/app/pay/page.tsx` | 新建 | 付费页面(重定向到支付宝收银台) |
|
||
|
||
---
|
||
|
||
## 常见问题
|
||
|
||
### RSA key format is not supported
|
||
|
||
密钥文件缺少 PEM 头尾标记或未按 64 字符换行。参考「PEM 格式要求」重新格式化。
|
||
|
||
### ACQ.ACCESS_FORBIDDEN
|
||
|
||
应用未开通「电脑网站支付」产品。在支付宝开放平台 → 应用详情 → 产品管理中添加并开通。
|
||
|
||
### 支付宝回调不到
|
||
|
||
1. 检查 `ALIPAY_NOTIFY_URL` 是否公网 HTTPS 可达
|
||
2. 检查 Nginx 是否将 `/api/payment/notify` 代理到后端
|
||
3. 支付宝回调超时(15s 未响应)会重试,共重试 8 次,持续 24 小时
|
||
|
||
### 支付完成后页面未跳转回来
|
||
|
||
检查 `ALIPAY_RETURN_URL` 配置是否正确,必须是前端 `/pay` 页面的完整 URL(如 `https://vigent.hbyrkj.top/pay`)。支付宝会在用户支付完成后将浏览器重定向到此地址,并附带 `out_trade_no` 等参数。
|
||
|
||
### 前端显示"网络错误"而非具体错误
|
||
|
||
API 函数缺少 try/catch 捕获 axios 异常。已在 `auth.ts` 的 `register()` 和 `login()` 中修复。
|