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

@@ -69,8 +69,11 @@ payjsapi 不在本仓库,需在 api.hackrobot.cn 侧确认:
支付成功后payjsapi 需:
1. 从订单中读取 `user_id``site_id`
2. 调用 PocketBase 或内部 API 写入/更新 `site_vip`
3. 字段:user_id、site_id、order_id、expires_at(可选
2. **写入 PocketBase `payments` 表**user_id、type、order_id 等)
3. **写入 PocketBase `site_vip` 表**user_id、site_id、order_id、expires_at
4. **创建 Memos 账号**qun.hackrobot.cn账号名=user_id密码 12345678
详见 `docs/PAYJSAPI_NOMADVIP_SPEC.md`
### 3.3 PocketBase site_vip 表
@@ -96,3 +99,5 @@ payjsapi 不在本仓库,需在 api.hackrobot.cn 侧确认:
- [x] digital/cnomadcna auth/me 使用 credentials
- [ ] payjsapi 确认 nomadvip 订单接收并处理 site_id
- [ ] payjsapi 确认支付回调写入 site_vip
- [ ] payjsapi 确认支付回调写入 payments 表
- [ ] payjsapi 确认支付回调创建 Memos 账号user_id 格式,密码 12345678

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 页面展示与登录流程一致。

View File

@@ -0,0 +1,39 @@
# vip_email_link 集合说明
「已有账号登录」按邮箱查库需要 `vip_email_link` 映射表。请在 PocketBase 管理后台创建。
**若出现 404**:检查集合名称是否为 `vip_email_link`(小写+下划线),且必须添加 `anonymous_user_id` 字段。
## 集合名称
`vip_email_link`(必须与此完全一致)
## 字段(全部必建)
| 字段名 | 类型 | 说明 |
|--------|------|------|
| email | Text | 邮箱,建议唯一 |
| user_id | Text | 关联的 PB 用户 ID |
| anonymous_user_id | Text | **必建** 匿名 user_id如 user1773315608560用于已有账号登录查关联 |
## 创建方式
1. 打开 PocketBase 管理后台
2. Collections → New collection
3. 名称:`vip_email_link`(精确匹配)
4. 添加字段:`email` (Text)、`user_id` (Text)、`anonymous_user_id` (Text)
5. 可选:为 `email` 设置 unique 约束
6. API RulesAdmin 需能访问,或留空使用默认
## 数据来源
- **支付成功页**:用户输入邮箱后调用 `POST /api/auth/store-email-link` 写入 `email -> 匿名 user_id`
- **关联邮箱**`POST /api/auth/link-email` 成功后写入 `email -> PB user_id`(覆盖)
## 查询流程
`POST /api/vip/check-by-email` 会:
1. 验证邮箱密码PB 登录)
2. 按 PB id 查 site_vip、payments
3. 若未找到,按 email 查 vip_email_link 得 user_id再查 site_vip、payments