Files
gitlab-instance-0a899031_no…/docs/PAYJSAPI_NOMADVIP_SPEC.md
2026-03-13 03:43:50 -05:00

220 lines
6.5 KiB
Markdown
Raw 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.
# payjsapi nomadvip 对接规范
**问题**:支付成功后,页面显示账号,但 PocketBase `payments` 表和 Memos 账户均无记录,导致会员验证失败。
**原因**payjsapi 支付回调未正确写入 PocketBase 及创建 Memos 账号。
---
## 一、nomadvip 调用流程
```
用户点击「立即加入」
→ nomadvip POST /api/pay (user_id, site_id: "vip", ...)
→ nomadvip 调用 payjsapi POST /nomadvip/payh5
→ payjsapi 返回支付参数,用户完成支付
→ 支付渠道回调 payjsapi
→ 【payjsapi 必须】写入 PocketBase payments + site_vip创建 Memos 账号
→ nomadvip 轮询 GET /api/pay/status?order_id=xxx
→ nomadvip 调用 payjsapi GET /nomadvip/order_status?order_id=xxx
→ payjsapi 返回 { paid: true }
```
---
## 二、payjsapi 支付回调必须完成
### 2.1 写入 PocketBase `payments` 表
| 字段 | 说明 | 示例 |
|------|------|------|
| user_id | 订单中的 user_id匿名格式 `user1773315608560` 或 PB 用户 ID | `user1773315608560` |
| type | 产品类型 | `vip` |
| order_id | 支付订单号 | 渠道返回的 out_trade_no |
| total_fee | 金额(分) | 8800 |
| channel | 支付渠道 | wxpay / alipay |
| created | 创建时间 | 自动 |
**nomadvip 依赖**`app/page.tsx``checkPaymentFromPB``user_id` 查询 payments 判断是否 VIP。**不写入则会员验证失败**。
### 2.2 写入 PocketBase `site_vip` 表
| 字段 | 说明 | 示例 |
|------|------|------|
| user_id | 同 payments.user_id | `user1773315608560` |
| site_id | 固定 `vip` | `vip` |
| order_id | 支付订单号 | 可选 |
| expires_at | 到期时间null 表示永久 | null |
**nomadvip 依赖**`/api/vip/check``user_id` + `site_id = "vip"` 校验 Cookie 登录用户的 VIP 状态。
### 2.3 创建 Memos 账号
- **Memos 地址**qun.hackrobot.cn
- **账号格式**`user` + 时间戳,如 `user1773315608560`
- **默认密码**`12345678`
payjsapi 在支付成功后,需调用 Memos API 或内部服务创建账号,账号名与订单中的 `user_id` 一致(匿名用户即为 `user1773315608560` 格式)。
**nomadvip 依赖**:首页「星球登录信息」展示的账号即为此格式,用户需用该账号登录 qun.hackrobot.cn。
---
## 三、payjsapi 接口要求
### 3.1 订单创建 `POST /nomadvip/payh5`
**请求体**nomadvip 已传递):
```json
{
"user_id": "user1773315608560",
"site_id": "vip",
"total_fee": 8800,
"type": "vip",
"channel": "wxpay",
"return_url": "https://vip.hackrobot.cn/paid",
"client_ip": "可选"
}
```
**要求**:订单存储时需保存 `user_id``site_id`,供支付回调使用。
### 3.2 订单状态 `GET /nomadvip/order_status?order_id=xxx`
**响应**
```json
{ "paid": true }
```
```json
{ "paid": false }
```
**要求**:支付回调完成写入 payments/site_vip 后,此接口应返回 `paid: true`。nomadvip 据此判断支付成功并刷新页面。
---
## 四、order_status 接口(关键)
**nomadvip 依赖**支付成功后nomadvip 轮询 `order_status`,若返回 `paid: true` 则调用 `/api/pay/confirm` 写入 PocketBase 和 Memos。**若 order_status 返回 false 或接口不存在,则 confirm 失败payments/site_vip/Memos 均无记录**。
payjsapi 必须实现:
```
GET /nomadvip/order_status?order_id=xxx
响应: { "paid": true } 或 { "paid": false }
```
- 参数名必须为 `order_id`(与 nomadvip 传递一致)
- 支付渠道回调完成后,必须返回 `paid: true`
- 若 payh5/redirect 返回 301-308 重定向,建议响应头带上 `x-pay-order-id`,以便 nomadvip 正确轮询
## 五、已有账号登录(可选,优先于 PocketBase 查询)
nomadvip 会优先调用 payjsapi 验证 VIP若接口不存在或失败则回退到 PocketBase。
### 5.1 按 user_id 查 VIP `GET /nomadvip/check_vip?user_id=xxx`
**响应**
```json
{ "vip": true, "email": "user@example.com" }
```
```json
{ "vip": false }
```
**说明**payjsapi 需查 users/payments/site_vip 关联,返回是否 VIP若已关联邮箱可返回 `email` 供前端不再显示绑定表单。
### 5.2 按邮箱+密码查 VIP `POST /nomadvip/check_vip_by_email`
**请求体**
```json
{ "email": "user@example.com", "password": "xxx" }
```
**响应**
```json
{
"vip": true,
"token": "PB token",
"record": { "id": "xxx", "email": "xxx" },
"effectiveUserId": "user1773315608560"
}
```
```json
{ "vip": false }
```
**说明**payjsapi 需验证邮箱密码PB users再查 payments/site_vip 关联,返回 VIP 状态及 PB token/record。
---
## 六、检查清单payjsapi 侧)
- [ ] 支付回调写入 PocketBase `payments`
- [ ] 支付回调写入 PocketBase `site_vip`site_id = "vip"
- [ ] 支付回调创建 Memos 账号user_id 格式,密码 12345678
- [ ] **`order_status` 在支付成功后返回 `paid: true`**(否则 nomadvip confirm 失败)
- [ ] 订单创建时正确保存 `user_id``site_id`
- [ ] (可选)`GET /nomadvip/check_vip?user_id=xxx` 按 user_id 查 VIP
- [ ] (可选)`POST /nomadvip/check_vip_by_email` 按邮箱密码查 VIP
---
## 七、PocketBase 配置
payjsapi 需配置:
- `POCKETBASE_URL`PocketBase 地址(如 https://pocketbase.hackrobot.cn
- `POCKETBASE_EMAIL` / `POCKETBASE_PASSWORD`:管理员账号,用于写入 payments、site_vip
---
## 八、日志要求(排查 VIP 登录问题)
payjsapi 需在以下接口打印日志,便于排查「已有账号登录」失败:
| 接口 | 建议日志 |
|------|----------|
| `GET /nomadvip/check_vip?user_id=xxx` | 请求 user_id、查询结果 vip |
| `POST /nomadvip/check_vip_by_email` | 请求 email、PB 登录结果、vip 查询结果 |
| `GET /nomadvip/order_status?order_id=xxx` | 请求 order_id、返回 paid |
| 支付回调 | 写入 payments/site_vip 的 user_id、order_id |
示例Node/Express
```js
// check_vip
console.log("[payjsapi] check_vip user_id=%s vip=%s", user_id, vip);
// check_vip_by_email
console.log("[payjsapi] check_vip_by_email email=%s pbLogin=%s vip=%s", email, !!token, vip);
// order_status
console.log("[payjsapi] order_status order_id=%s paid=%s", order_id, paid);
```
---
## 九、Memos 集成
Memos 账号创建方式取决于现有架构,可选:
1. 直接调用 Memos API 创建用户
2. 通过内部服务/队列异步创建
3. 与 PocketBase users 表联动(若 Memos 与 PB 共享用户)
账号名必须与 `user_id` 一致,以便 nomadvip 页面展示与登录流程一致。