# Sub2ApiPay

**语言 / Language**: 中文（当前）｜ [English](./README.en.md)

Sub2ApiPay 是为 [Sub2API](https://sub2api.com) 平台构建的自托管充值支付网关。支持支付宝、微信支付（通过 EasyPay 聚合）和 Stripe，订单支付成功后自动调用 Sub2API 管理接口完成余额到账，无需人工干预。

---

## 目录

- [功能特性](#功能特性)
- [技术栈](#技术栈)
- [快速开始](#快速开始)
- [环境变量](#环境变量)
- [部署指南](#部署指南)
- [集成到 Sub2API](#集成到-sub2api)
- [管理后台](#管理后台)
- [支付流程](#支付流程)
- [开发指南](#开发指南)

---

## 功能特性

- **多支付方式** — 支付宝、微信支付（EasyPay 聚合）、Stripe 信用卡
- **自动到账** — 支付回调验签后自动调用 Sub2API 充值接口，全程无需人工
- **订单全生命周期** — 超时自动取消、用户主动取消、管理员取消、退款
- **限额控制** — 可配置单笔上限与每日累计上限，按用户维度统计
- **安全设计** — Token 鉴权、MD5/Webhook 签名验证、时序安全对比、完整审计日志
- **响应式 UI** — PC + 移动端自适应，支持深色模式，支持 iframe 嵌入
- **管理后台** — 订单列表（分页/筛选）、订单详情、重试充值、退款

---

## 技术栈

| 类别   | 技术                        |
| ------ | --------------------------- |
| 框架   | Next.js 16 (App Router)     |
| 语言   | TypeScript 5 + React 19     |
| 样式   | TailwindCSS 4               |
| ORM    | Prisma 7（adapter-pg 模式） |
| 数据库 | PostgreSQL 16               |
| 容器   | Docker + Docker Compose     |
| 包管理 | pnpm                        |

---

## 快速开始

### 使用 Docker Hub 镜像（推荐）

无需本地安装 Node.js 或 pnpm，服务器上只需 Docker。

```bash
mkdir -p /opt/sub2apipay && cd /opt/sub2apipay

# 下载 Compose 文件和环境变量模板
curl -O https://raw.githubusercontent.com/touwaeriol/sub2apipay/main/docker-compose.hub.yml
curl -O https://raw.githubusercontent.com/touwaeriol/sub2apipay/main/.env.example
cp .env.example .env

# 填写必填环境变量
nano .env

# 启动（含自带 PostgreSQL）
docker compose -f docker-compose.hub.yml up -d
```

### 从源码构建

```bash
git clone https://github.com/touwaeriol/sub2apipay.git
cd sub2apipay
cp .env.example .env
nano .env
docker compose up -d --build
```

---

## 环境变量

完整模板见 [`.env.example`](./.env.example)。

### 核心（必填）

| 变量                    | 说明                                           |
| ----------------------- | ---------------------------------------------- |
| `SUB2API_BASE_URL`      | Sub2API 服务地址，如 `https://sub2api.com`     |
| `SUB2API_ADMIN_API_KEY` | Sub2API 管理 API 密钥                          |
| `ADMIN_TOKEN`           | 管理后台访问令牌（自定义强密码）               |
| `NEXT_PUBLIC_APP_URL`   | 本服务的公网地址，如 `https://pay.example.com` |

> `DATABASE_URL` 使用自带数据库时由 Compose 自动注入，无需手动填写。

### 支付服务商与支付方式

**第一步**：通过 `PAYMENT_PROVIDERS` 声明启用哪些支付服务商（逗号分隔）：

```env
# 仅易支付
PAYMENT_PROVIDERS=easypay
# 仅 Stripe
PAYMENT_PROVIDERS=stripe
# 两者都用
PAYMENT_PROVIDERS=easypay,stripe
```

**第二步**：通过 `ENABLED_PAYMENT_TYPES` 控制向用户展示哪些支付渠道：

```env
# 易支付支持: alipay, wxpay；Stripe 支持: stripe
ENABLED_PAYMENT_TYPES=alipay,wxpay
```

#### EasyPay（支付宝 / 微信支付）

支付提供商只需兼容**易支付（EasyPay）协议**即可接入，例如 [ZPay](https://z-pay.cn/?uid=23808)（`https://z-pay.cn/?uid=23808`）等平台（链接含本项目作者的邀请码，介意可去掉）。

<details>
<summary>ZPay 申请二维码</summary>

![ZPay 预览](./docs/zpay-preview.png)

</details>

> **注意**：支付渠道的安全性、稳定性及合规性请自行鉴别，本项目不对任何第三方支付服务商做担保或背书。

| 变量                  | 说明                                                          |
| --------------------- | ------------------------------------------------------------- |
| `EASY_PAY_PID`        | EasyPay 商户 ID                                               |
| `EASY_PAY_PKEY`       | EasyPay 商户密钥                                              |
| `EASY_PAY_API_BASE`   | EasyPay API 地址                                              |
| `EASY_PAY_NOTIFY_URL` | 异步回调地址，填 `${NEXT_PUBLIC_APP_URL}/api/easy-pay/notify` |
| `EASY_PAY_RETURN_URL` | 支付完成跳转地址，填 `${NEXT_PUBLIC_APP_URL}/pay/result`      |
| `EASY_PAY_CID_ALIPAY` | 支付宝通道 ID（可选）                                         |
| `EASY_PAY_CID_WXPAY`  | 微信支付通道 ID（可选）                                       |

#### Stripe

| 变量                     | 说明                                   |
| ------------------------ | -------------------------------------- |
| `STRIPE_SECRET_KEY`      | Stripe 密钥（`sk_live_...`）           |
| `STRIPE_PUBLISHABLE_KEY` | Stripe 可公开密钥（`pk_live_...`）     |
| `STRIPE_WEBHOOK_SECRET`  | Stripe Webhook 签名密钥（`whsec_...`） |

> Stripe Webhook 端点：`${NEXT_PUBLIC_APP_URL}/api/stripe/webhook`
> 需订阅事件：`payment_intent.succeeded`、`payment_intent.payment_failed`

### 业务规则

| 变量                        | 说明                               | 默认值                     |
| --------------------------- | ---------------------------------- | -------------------------- |
| `MIN_RECHARGE_AMOUNT`       | 单笔最低充值金额（元）             | `1`                        |
| `MAX_RECHARGE_AMOUNT`       | 单笔最高充值金额（元）             | `1000`                     |
| `MAX_DAILY_RECHARGE_AMOUNT` | 每日累计最高充值（元，`0` = 不限） | `10000`                    |
| `ORDER_TIMEOUT_MINUTES`     | 订单超时分钟数                     | `5`                        |
| `PRODUCT_NAME`              | 充值商品名称（显示在支付页）       | `Sub2API Balance Recharge` |

### UI 定制（可选）

在充值页面右侧可展示客服联系方式、说明图片等帮助内容。

| 变量                 | 说明                                                            |
| -------------------- | --------------------------------------------------------------- |
| `PAY_HELP_IMAGE_URL` | 帮助图片地址（支持外部 URL 或本地路径，见下方说明）             |
| `PAY_HELP_TEXT`      | 帮助说明文字，用 `\n` 换行，如 `扫码加微信\n工作日 9-18 点在线` |

**图片地址两种方式：**

- **外部 URL**（推荐，无需改 Compose 配置）：直接填图片的公网地址，如 OSS / CDN / 图床链接。

  ```env
  PAY_HELP_IMAGE_URL=https://cdn.example.com/help-qr.jpg
  ```

- **本地文件**：将图片放到 `./uploads/` 目录，通过 `/uploads/文件名` 引用。
  需在 `docker-compose.app.yml` 中挂载目录（默认已包含）：
  ```yaml
  volumes:
    - ./uploads:/app/public/uploads:ro
  ```
  ```env
  PAY_HELP_IMAGE_URL=/uploads/help-qr.jpg
  ```

> 点击帮助图片可在屏幕中央全屏放大查看。

### Docker Compose 专用

| 变量          | 说明                                | 默认值                       |
| ------------- | ----------------------------------- | ---------------------------- |
| `APP_PORT`    | 宿主机映射端口                      | `3001`                       |
| `DB_PASSWORD` | PostgreSQL 密码（使用自带数据库时） | `password`（**生产请修改**） |

---

## 部署指南

### 方案一：Docker Hub 镜像 + 自带数据库

使用 `docker-compose.hub.yml`，最省事的部署方式：

```bash
docker compose -f docker-compose.hub.yml up -d
```

镜像：[`touwaeriol/sub2apipay:latest`](https://hub.docker.com/r/touwaeriol/sub2apipay)

### 方案二：Docker Hub 镜像 + 外部数据库

适用于已有 PostgreSQL 实例（如与其他服务共用）：

1. 在 `.env` 中填写 `DATABASE_URL`
2. 使用 `docker-compose.app.yml`（仅启动应用，不含 DB）：

```bash
docker compose -f docker-compose.app.yml up -d
```

### 方案三：从源码构建

适用于自定义修改后自行构建：

```bash
# 在构建服务器上
docker compose build
docker tag sub2apipay-app:latest touwaeriol/sub2apipay:latest
docker push touwaeriol/sub2apipay:latest

# 在部署服务器上
docker compose -f docker-compose.hub.yml pull
docker compose -f docker-compose.hub.yml up -d
```

### 端口与反向代理

默认宿主机端口为 `3001`（可通过 `APP_PORT` 修改）。建议使用 Nginx/Caddy 作反向代理并配置 HTTPS：

```nginx
server {
    listen 443 ssl;
    server_name pay.example.com;

    location / {
        proxy_pass http://127.0.0.1:3001;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}
```

### 数据库迁移

容器启动时自动执行 `prisma migrate deploy`，无需手动操作。如需手动执行：

```bash
docker compose exec app npx prisma migrate deploy
```

---

## 集成到 Sub2API

在 Sub2API 管理后台可配置以下页面链接：

| 页面     | 链接                                 | 说明                    |
| -------- | ------------------------------------ | ----------------------- |
| 充值页面 | `https://pay.example.com/pay`        | 用户充值入口            |
| 我的订单 | `https://pay.example.com/pay/orders` | 用户查看自己的充值记录  |
| 管理后台 | `https://pay.example.com/admin`      | 管理后台入口（仅管理员）|

Sub2API **v0.1.88** 及以上版本会自动拼接以下参数，无需手动添加：

| 参数      | 说明                                             |
| --------- | ------------------------------------------------ |
| `user_id` | Sub2API 用户 ID                                  |
| `token`   | 用户登录 Token（有 token 才能查看订单历史）      |
| `theme`   | `light`（默认）或 `dark`                         |
| `ui_mode` | `standalone`（默认）或 `embedded`（iframe 嵌入） |

---

## 管理后台

访问：`https://pay.example.com/admin?token=YOUR_ADMIN_TOKEN`

| 模块     | 路径                   | 说明                                        |
| -------- | ---------------------- | ------------------------------------------- |
| 总览     | `/admin`               | 聚合入口，卡片式导航到各管理模块            |
| 订单管理 | `/admin/orders`        | 按状态筛选、分页浏览、订单详情、重试/取消/退款 |
| 数据概览 | `/admin/dashboard`     | 收入统计、订单趋势、支付方式分布            |
| 渠道管理 | `/admin/channels`      | 配置 API 渠道与倍率，支持从 Sub2API 同步    |
| 订阅管理 | `/admin/subscriptions` | 管理订阅套餐与用户订阅                      |

---

## 支付流程

```
用户提交充值金额
       │
       ▼
  创建订单 (PENDING)
  ├─ 校验用户状态 / 待支付订单数 / 每日限额
  └─ 调用支付提供商获取支付链接
       │
       ▼
  用户完成支付
  ├─ EasyPay → 扫码 / H5 跳转
  └─ Stripe  → Payment Element (PaymentIntent)
       │
       ▼
  支付回调（签名验证）→ 订单 PAID
       │
       ▼
  自动调用 Sub2API 充值接口
  ├─ 成功 → COMPLETED，余额自动到账
  └─ 失败 → FAILED（管理员可重试）
```

---

## 开发指南

### 环境要求

- Node.js 22+
- pnpm
- PostgreSQL 16+

### 本地启动

```bash
pnpm install
cp .env.example .env
# 编辑 .env，填写 DATABASE_URL 和其他必填项
pnpm prisma migrate dev
pnpm dev
```

### 常用命令

```bash
pnpm dev                      # 开发服务器（热重载）
pnpm build                    # 生产构建
pnpm test                     # 运行测试
pnpm typecheck                # TypeScript 类型检查
pnpm lint                     # ESLint 代码检查
pnpm format                   # Prettier 格式化

pnpm prisma generate          # 生成 Prisma 客户端
pnpm prisma migrate dev       # 创建迁移（开发）
pnpm prisma migrate deploy    # 应用迁移（生产）
pnpm prisma studio            # 可视化数据库管理
```

---

## License

MIT
