# 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