feat: add invite rewards and redeem codes #10

Merged
qzl merged 4 commits from dev into main 2026-05-21 16:27:39 +08:00
8 changed files with 142 additions and 2 deletions
Showing only changes of commit 3cc5999383 - Show all commits
@@ -0,0 +1,140 @@
# 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
@@ -3,7 +3,7 @@
"name": "audit-and-optimize-web-performance",
"title": "Audit and optimize web performance",
"description": "",
"status": "in_progress",
"status": "completed",
"dev_type": null,
"scope": null,
"package": null,
@@ -11,7 +11,7 @@
"creator": "zl-q",
"assignee": "zl-q",
"createdAt": "2026-05-10",
"completedAt": null,
"completedAt": "2026-05-11",
"branch": null,
"base_branch": "dev",
"worktree_path": null,