141 lines
5.4 KiB
Markdown
141 lines
5.4 KiB
Markdown
# PRD: CREEM Web Payment Integration
|
||
|
||
## Background
|
||
|
||
iOS App 路线已放弃,需改用 CREEM (Merchant of Record) 进行 Web 端支付。现有 Apple IAP 代码保留但不再活跃使用。
|
||
|
||
## Goals
|
||
|
||
1. Web 端用户可通过 CREEM 托管支付页购买积分
|
||
2. 商品价格从 CREEM API 动态获取,不在本地硬编码
|
||
3. 支付成功后通过 webhook 自动发放积分
|
||
4. 保留现有 Apple IAP 架构,不破坏
|
||
|
||
## CREEM Product Mapping
|
||
|
||
| 内部 product_code | CREEM Product ID | 积分 |
|
||
|---|---|---|
|
||
| new_user_pack | prod_2x9LzVlR3ot1HLgbIZALPd | 60 |
|
||
| starter_pack | prod_697ay0pXFXrBYEVC7HS0MR | 100 |
|
||
| popular_pack | prod_5ivxlPnZWN6dIhnOxctThy | 210 |
|
||
| premium_pack | prod_2L13k70jlpPYkdHhexHP2s | 415 |
|
||
|
||
### Pricing Notes
|
||
|
||
- CREEM 最低支持 $1.00,因此新人专享包价格从原 $0.99 调整为 $1.00
|
||
- 价格由 CREEM Dashboard 管理,后端通过 API 动态获取,不硬编码在 mapping.yaml 中
|
||
- mapping.yaml 只存储: creem_product_id, credits, type, sort_order, enabled
|
||
|
||
## Architecture
|
||
|
||
### Payment Flow
|
||
|
||
```
|
||
Frontend Backend CREEM
|
||
| | |
|
||
|-- GET /packages ---->| |
|
||
| |-- GET /products ---->|
|
||
| |<-- products list ----|
|
||
|<-- packages+prices --| |
|
||
| | |
|
||
|-- POST /checkout --->| |
|
||
| |-- POST /checkouts -->|
|
||
| |<-- checkout_url -----|
|
||
|<-- checkout_url -----| |
|
||
| | |
|
||
|-- redirect to CREEM checkout page --------->|
|
||
| | |
|
||
| |<-- webhook ----------|
|
||
| | (checkout.completed)
|
||
| | verify signature
|
||
| | grant credits |
|
||
| | |
|
||
|-- success_url -------| |
|
||
```
|
||
|
||
### API Endpoints
|
||
|
||
| Method | Endpoint | Auth | Description |
|
||
|--------|----------|------|-------------|
|
||
| POST | `/api/v1/payments/creem/checkouts` | User | Create checkout session, return checkout_url |
|
||
| POST | `/api/v1/payments/creem/webhook` | None | CREEM callback (verify signature) |
|
||
|
||
### Existing Endpoint Changes
|
||
|
||
| Method | Endpoint | Change |
|
||
|--------|----------|--------|
|
||
| GET | `/api/v1/points/packages` | Add `priceCents` and `currency` fields from CREEM API |
|
||
|
||
## Implementation Details
|
||
|
||
### Phase 1: Backend Infrastructure
|
||
|
||
1. **settings.py** - Add `CreemSettings` (api_key, webhook_secret, base_url, success_url)
|
||
2. **creem_client.py** - HTTP client for CREEM API (get_products, create_checkout)
|
||
3. **models/creem_transaction.py** - New DB table for CREEM transactions
|
||
4. **alembic migration** - Create `creem_transactions` table
|
||
5. **repository.py** - Add CREEM transaction CRUD methods
|
||
|
||
### Phase 2: Backend Business Logic
|
||
|
||
6. **creem_service.py** - Checkout creation + webhook handling
|
||
7. **service.py** - Update `ProductMapping` to include `creem_product_id`
|
||
8. **schemas.py** - Add `CreateCheckoutRequest/Response`, update `PackageInfo` with price fields
|
||
9. **dependencies.py** - Add `get_creem_service` DI
|
||
10. **router.py** - Add 2 new CREEM endpoints
|
||
11. **points/service.py** - `get_available_packages` merges CREEM product prices
|
||
|
||
### Phase 3: Frontend
|
||
|
||
12. **api.ts** - Add `createCheckout()` function and types
|
||
13. **StorePage.tsx** - Use dynamic price from API, add buy button that redirects to checkout_url
|
||
|
||
## DB Schema: creem_transactions
|
||
|
||
| Column | Type | Description |
|
||
|--------|------|-------------|
|
||
| id | UUID PK | |
|
||
| user_id | UUID FK | |
|
||
| product_code | String(32) | Internal product code |
|
||
| creem_product_id | String(128) | CREEM product ID |
|
||
| checkout_id | String(128) UNIQUE | CREEM checkout session ID |
|
||
| order_id | String(128) nullable | CREEM order ID |
|
||
| customer_id | String(128) nullable | CREEM customer ID |
|
||
| status | enum(pending, completed, failed, refunded) | |
|
||
| credits | BigInt | Points to grant |
|
||
| amount_cents | BigInt | Payment amount in cents |
|
||
| currency | String(8) | Currency code |
|
||
| creem_payload | JSONB | Full CREEM webhook payload |
|
||
| ledger_event_id | String(128) nullable | Points ledger event ID |
|
||
| created_at, updated_at | Timestamp | |
|
||
|
||
## Environment Variables
|
||
|
||
```bash
|
||
ERYAO_CREEM_API_KEY= # CREEM API key
|
||
ERYAO_CREEM_WEBHOOK_SECRET= # Webhook signature verification secret (先占位,实现后配置)
|
||
ERYAO_CREEM_BASE_URL=https://test-api.creem.io # test or production
|
||
ERYAO_CREEM_SUCCESS_URL=https://yourdomain.com/store?payment=success # 支付成功跳转URL
|
||
```
|
||
|
||
## Frontend Behavior
|
||
|
||
1. 支付成功后跳转到 `ERYAO_CREEM_SUCCESS_URL`
|
||
2. 前端检测 URL 参数 `?payment=success`,显示成功提示并刷新积分
|
||
3. 价格从后端 API 动态获取,不再使用翻译文件中的硬编码价格
|
||
4. 翻译文件保留:套餐名称、描述、badge 文字(如"限购一次"、"推荐")
|
||
|
||
## Security
|
||
|
||
- Webhook must verify `creem-signature` HMAC-SHA256 header
|
||
- API key never exposed to frontend
|
||
- Checkout idempotency: prevent duplicate credit grants via checkout_id unique constraint
|
||
- Starter pack eligibility check shared with Apple IAP logic
|
||
|
||
## Out of Scope
|
||
|
||
- CREEM subscription management (one-time purchases only)
|
||
- CREEM license keys
|
||
- Removing Apple IAP code (preserved for potential future use)
|
||
- Customer portal integration
|