This commit is contained in:
eric
2026-03-13 03:43:50 -05:00
parent e5d080c202
commit 857380aa01
29 changed files with 3595 additions and 580 deletions

View File

@@ -0,0 +1,219 @@
# 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 页面展示与登录流程一致。