diff --git a/.trellis/tasks/archive/05-11-creem-payment-integration/prd.md b/.trellis/tasks/archive/05-11-creem-payment-integration/prd.md new file mode 100644 index 0000000..6038e3c --- /dev/null +++ b/.trellis/tasks/archive/05-11-creem-payment-integration/prd.md @@ -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 diff --git a/.trellis/tasks/05-10-audit-and-optimize-web-performance/check.jsonl b/.trellis/tasks/archive/2026-05/05-10-audit-and-optimize-web-performance/check.jsonl similarity index 100% rename from .trellis/tasks/05-10-audit-and-optimize-web-performance/check.jsonl rename to .trellis/tasks/archive/2026-05/05-10-audit-and-optimize-web-performance/check.jsonl diff --git a/.trellis/tasks/05-10-audit-and-optimize-web-performance/implement.jsonl b/.trellis/tasks/archive/2026-05/05-10-audit-and-optimize-web-performance/implement.jsonl similarity index 100% rename from .trellis/tasks/05-10-audit-and-optimize-web-performance/implement.jsonl rename to .trellis/tasks/archive/2026-05/05-10-audit-and-optimize-web-performance/implement.jsonl diff --git a/.trellis/tasks/05-10-audit-and-optimize-web-performance/prd.md b/.trellis/tasks/archive/2026-05/05-10-audit-and-optimize-web-performance/prd.md similarity index 100% rename from .trellis/tasks/05-10-audit-and-optimize-web-performance/prd.md rename to .trellis/tasks/archive/2026-05/05-10-audit-and-optimize-web-performance/prd.md diff --git a/.trellis/tasks/05-10-audit-and-optimize-web-performance/refactor-plan.md b/.trellis/tasks/archive/2026-05/05-10-audit-and-optimize-web-performance/refactor-plan.md similarity index 100% rename from .trellis/tasks/05-10-audit-and-optimize-web-performance/refactor-plan.md rename to .trellis/tasks/archive/2026-05/05-10-audit-and-optimize-web-performance/refactor-plan.md diff --git a/.trellis/tasks/05-10-audit-and-optimize-web-performance/request-audit.md b/.trellis/tasks/archive/2026-05/05-10-audit-and-optimize-web-performance/request-audit.md similarity index 100% rename from .trellis/tasks/05-10-audit-and-optimize-web-performance/request-audit.md rename to .trellis/tasks/archive/2026-05/05-10-audit-and-optimize-web-performance/request-audit.md diff --git a/.trellis/tasks/05-10-audit-and-optimize-web-performance/research/remaining-web-performance-bottlenecks-after-1e4871e.md b/.trellis/tasks/archive/2026-05/05-10-audit-and-optimize-web-performance/research/remaining-web-performance-bottlenecks-after-1e4871e.md similarity index 100% rename from .trellis/tasks/05-10-audit-and-optimize-web-performance/research/remaining-web-performance-bottlenecks-after-1e4871e.md rename to .trellis/tasks/archive/2026-05/05-10-audit-and-optimize-web-performance/research/remaining-web-performance-bottlenecks-after-1e4871e.md diff --git a/.trellis/tasks/05-10-audit-and-optimize-web-performance/task.json b/.trellis/tasks/archive/2026-05/05-10-audit-and-optimize-web-performance/task.json similarity index 90% rename from .trellis/tasks/05-10-audit-and-optimize-web-performance/task.json rename to .trellis/tasks/archive/2026-05/05-10-audit-and-optimize-web-performance/task.json index 3e6bf2e..0667d29 100644 --- a/.trellis/tasks/05-10-audit-and-optimize-web-performance/task.json +++ b/.trellis/tasks/archive/2026-05/05-10-audit-and-optimize-web-performance/task.json @@ -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,