220 lines
6.5 KiB
Markdown
220 lines
6.5 KiB
Markdown
# 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 页面展示与登录流程一致。
|