# 韩国 NaverPay 支付 API · 商务对接文档

> **服务地址**: `https://cha.nerver.cc`  
> **协议**: HTTPS / JSON  
> **字符编码**: UTF-8

---

## 端点

```
POST https://cha.nerver.cc/api/v1/naver-pay            # 同步 JSON
POST https://cha.nerver.cc/api/v1/naver-pay/stream     # SSE 实时进度流
```

输入用户的 ChatGPT JWT, 返回**韩国 NaverPay 启动页 URL**。  
内置 approve POOL (50 并发持续打 × 累计 2000 次抢风控通过) + 整轮失败可重试。

| 场景 | 典型耗时 |
|---|---|
| approve 第一轮就通过 | 60~90 秒 |
| 用户 token 已失效 | 3~5 秒 |
| approve 全部 blocked / 网络异常 | 120~300 秒 |

> KakaoPay 请改用 [/api/v1/kakao-pay](/docs/kakao-pay.md). 两个端点共用同一份 `kr_*` 后台配置 (token / 代理 / 并发池).

---

## 鉴权

```
Authorization: Bearer <kr_business_token>
```

或使用 query 参数 `?key=<kr_business_token>`。

> 跟 KakaoPay 共用 `kr_service_token` / `kr_business_token`, 任一匹配即可。两者全空 = 不鉴权。

---

## 请求

`Content-Type: application/json`

```json
{
  "token": "eyJhbGciOiJSUzI1NiIs...(用户的 ChatGPT JWT)",
  "funding": "card",
  "promoId": "plus-1-month-free",
  "poolKeep": 50,
  "poolMax": 2000,
  "retryCount": 1
}
```

| 字段 | 类型 | 必填 | 默认 | 说明 |
|---|---|---|---|---|
| `token` | string | ✅ | — | 用户的 ChatGPT AC token (JWT, 以 `eyJ` 开头) |
| `funding` | string | ❌ | 后台 / `card` | NaverPay 资金源, `card` 或 `points` (也接受旧字段名 `naverFunding`) |
| `promoId` | string | ❌ | 后台 `promo_id` | 优惠 ID |
| `poolKeep` | number | ❌ | 后台 `kr_pool_keep` (50) | approve POOL 持续并发槽数, 1~50 |
| `poolMax` | number | ❌ | 后台 `kr_pool_max` (2000) | approve POOL 累计尝试上限, 1~2000 |
| `retryCount` | number | ❌ | 后台 `kr_retry_count` (1) | 总体失败重试次数, 0~5 |

---

## 成功响应 (HTTP 200)

```json
{
  "ok": true,
  "funding": "card",
  "naverpay_bridge_url": "https://m.pay.naver.com/payments/...",
  "stripe_redirect_url": "https://pm-redirects.stripe.com/return/...",
  "nicepay_url": "https://web.nicepay.co.kr/v3/webstd/auth/easypay/naverpay/verify_naverpay.jsp?token=...",
  "qr_url": "https://m.pay.naver.com/payments/...",
  "request_id": "req_xxx",
  "duration_ms": 71234
}
```

| 字段 | 说明 |
|---|---|
| `ok` | `true` 表示成功 |
| `funding` | 实际使用的资金源 (`card` / `points`) |
| `naverpay_bridge_url` | **★ NaverPay 启动页 URL** — PC 浏览器跳转或手机点击都能进入 NaverPay app/网页支付流程 |
| `stripe_redirect_url` | Stripe pm-redirects 中间链 (最保鲜, 备选) |
| `nicepay_url` | NicePay 启动页 (PC 浏览器完整 8-hop 跳转) |
| `qr_url` | 兼容字段, 当前 = `naverpay_bridge_url` |
| `request_id` | 请求追踪 ID |
| `duration_ms` | 服务端处理耗时 (毫秒) |

> **使用建议**: 直接把 `naverpay_bridge_url` 给用户跳转 (PC 在浏览器里完成 NaverPay 选择页, 手机自动唤起 NaverPay app)。NaverPay 不像 KakaoPay 那样有 PC 二维码扫码流程, 主要靠浏览器跳转与移动 app 唤起。

---

## 失败响应

#### token 失效 (HTTP 200, body.ok=false)

```json
{ "ok": false, "reason": "token-invalidated", "message": "TOKEN 已失效, 请重新获取", "request_id": "req_xxx", "duration_ms": 3534 }
```

#### approve 风控 blocked

```json
{ "ok": false, "reason": "approve-blocked", "message": "该账号已被 OpenAI 风控, 请更换账号重试", "error": "approve attempts exhausted", "attempts": 2 }
```

#### funding 参数非法

```json
{ "ok": false, "reason": "invalid-funding", "error": "funding 必须是 card 或 points (got xxx)" }
```

其余 `unauthorized` / `rate-limited` / `concurrency-busy` / `feature-disabled` 同 KakaoPay。

---

## reason 字段全集

| reason | 含义 | 是否值得换 token 重试 |
|---|---|---|
| `ok` | 成功 | — |
| `empty-token` | 没传 token | 否 |
| `invalid-token-format` | token 不是 JWT 格式 | 否 |
| `jwt-expired` | JWT 本地校验已过期 | 是 |
| `token-invalidated` | OAI 返回 401 / token 被撤销 | 是 |
| `already-paid` | 用户已是付费用户 | 否 |
| `risk-blocked` | OpenAI 账号风控 | 是 |
| `checkout-failed` | OAI checkout 接口非 200 | 重试 / 换 token |
| `region-failed` | Stripe tax_region 失败 | 重试 |
| `pm-unavailable` | Stripe 没暴露 NaverPay | 否 |
| `snapshot-failed` | OAI snapshot 接口失败 | 重试 |
| `confirm-error` / `confirm-failed` | Stripe confirm 失败 | 重试 |
| `approve-blocked` | approve POOL 全部 blocked | 换 token |
| `no-redirect` | approve 后 Stripe 未返回 redirect | 重试 |
| `redirect-chain-failed` | NicePay 8 跳链任一失败 | 重试 |
| `invalid-funding` | funding 参数非法 (不是 card/points) | 否 |
| `concurrency-busy` | 服务端并发已满 | 5~10 秒后重试 |
| `feature-disabled` | NaverPay 后台禁用 | 联系运营 |
| `unauthorized` | 鉴权失败 | 检查 token |
| `rate-limited` | 触发 RPM | retry-after 后重试 |
| `server-error` | 内部异常 | 重试 |

---

## SSE 流式接口

```
POST /api/v1/naver-pay/stream
```

请求体 / 鉴权同 `/api/v1/naver-pay`, 返回 `text/event-stream`:

```
event: hello
data: {"request_id":"req_xxx","funding":"card","pool_keep":50,"pool_max":2000,"retry_count":1,"queue_length":0,"inflight":1,"capacity":3}

event: progress
data: {"type":"step","attempt":1,"step":"warmup"}

...

event: progress
data: {"type":"approve-tick","slot":50,"max":2000,"blocked":42,"exception":1,"other":0,"elapsed_ms":12345}

event: progress
data: {"type":"approve-hit","slot":127}

event: progress
data: {"type":"attempt-end","attempt":1,"ok":true,"reason":""}

event: done
data: {完整响应对象, 同 /api/v1/naver-pay 同步返回}
```

step 与 progress 事件枚举与 [/api/v1/kakao-pay/stream](/docs/kakao-pay.md) 完全一致。

---

## 接入示例 (Node.js)

```js
const res = await fetch("https://cha.nerver.cc/api/v1/naver-pay", {
  method: "POST",
  headers: {
    "content-type": "application/json",
    "authorization": "Bearer " + process.env.KR_BUSINESS_TOKEN,
  },
  body: JSON.stringify({ token: userJwt, funding: "card" }),
});
const data = await res.json();
if (!data.ok) {
  console.error(`NaverPay 失败 (${data.reason}): ${data.message}`);
  return;
}
console.log("跳转链接:", data.naverpay_bridge_url);
```

---

## 接入示例 (Python)

```python
import requests
res = requests.post(
    "https://cha.nerver.cc/api/v1/naver-pay",
    headers={
        "content-type": "application/json",
        "authorization": f"Bearer {KR_BUSINESS_TOKEN}",
    },
    json={"token": user_jwt, "funding": "card"},
    timeout=300,
).json()

if not res["ok"]:
    raise RuntimeError(f"NaverPay fail: {res['reason']} {res.get('message')}")

print("跳转链接:", res["naverpay_bridge_url"])
```
