From 3cc5999383278b5e590f088c05ab3bfb14dabb91 Mon Sep 17 00:00:00 2001 From: zl-q Date: Mon, 11 May 2026 19:46:35 +0800 Subject: [PATCH 1/4] chore(task): archive 05-10-audit-and-optimize-web-performance --- .../05-11-creem-payment-integration/prd.md | 140 ++++++++++++++++++ .../check.jsonl | 0 .../implement.jsonl | 0 .../prd.md | 0 .../refactor-plan.md | 0 .../request-audit.md | 0 ...b-performance-bottlenecks-after-1e4871e.md | 0 .../task.json | 4 +- 8 files changed, 142 insertions(+), 2 deletions(-) create mode 100644 .trellis/tasks/archive/05-11-creem-payment-integration/prd.md rename .trellis/tasks/{ => archive/2026-05}/05-10-audit-and-optimize-web-performance/check.jsonl (100%) rename .trellis/tasks/{ => archive/2026-05}/05-10-audit-and-optimize-web-performance/implement.jsonl (100%) rename .trellis/tasks/{ => archive/2026-05}/05-10-audit-and-optimize-web-performance/prd.md (100%) rename .trellis/tasks/{ => archive/2026-05}/05-10-audit-and-optimize-web-performance/refactor-plan.md (100%) rename .trellis/tasks/{ => archive/2026-05}/05-10-audit-and-optimize-web-performance/request-audit.md (100%) rename .trellis/tasks/{ => archive/2026-05}/05-10-audit-and-optimize-web-performance/research/remaining-web-performance-bottlenecks-after-1e4871e.md (100%) rename .trellis/tasks/{ => archive/2026-05}/05-10-audit-and-optimize-web-performance/task.json (90%) 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, -- 2.43.7 From 2b8984edbc181d69cd131a379ab15186cec7854f Mon Sep 17 00:00:00 2001 From: zl-q Date: Mon, 11 May 2026 20:07:36 +0800 Subject: [PATCH 2/4] fix(web): validate question input before divination and fix logout flow - Add validation for question text in ManualDivinationPage and AutoDivinationPage - Fix logout flow to call backend API before clearing local auth - Show error message when question is empty before starting interpretation --- web/src/components/AutoDivinationPage.tsx | 4 ++++ web/src/components/ManualDivinationPage.tsx | 4 ++++ web/src/components/SettingsPage.tsx | 11 +++++++---- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/web/src/components/AutoDivinationPage.tsx b/web/src/components/AutoDivinationPage.tsx index 3fd8abb..2f6a95a 100644 --- a/web/src/components/AutoDivinationPage.tsx +++ b/web/src/components/AutoDivinationPage.tsx @@ -422,6 +422,10 @@ export default function AutoDivinationPage({ locale, divination: d }: Props) { const handleSubmit = () => { if (!done) return; + if (!question.trim()) { + setErrorMessage(locale === 'en' ? 'Please enter your question before starting the interpretation.' : '请输入您要占卜的问题后再开始解卦。'); + return; + } setShowConfirm(true); }; diff --git a/web/src/components/ManualDivinationPage.tsx b/web/src/components/ManualDivinationPage.tsx index b54e571..119e35d 100644 --- a/web/src/components/ManualDivinationPage.tsx +++ b/web/src/components/ManualDivinationPage.tsx @@ -440,6 +440,10 @@ export default function ManualDivinationPage({ locale, divination: d }: Props) { const showNextGuide = () => setGuideStep((step) => (step === null ? 0 : Math.min(step + 1, text.guideSteps.length - 1))); const handleSubmit = () => { + if (!question.trim()) { + setErrorMessage(locale === 'en' ? 'Please enter your question before starting the interpretation.' : '请输入您要占卜的问题后再开始解卦。'); + return; + } setShowConfirm(true); }; diff --git a/web/src/components/SettingsPage.tsx b/web/src/components/SettingsPage.tsx index 07c0c54..2334ca5 100644 --- a/web/src/components/SettingsPage.tsx +++ b/web/src/components/SettingsPage.tsx @@ -21,11 +21,14 @@ export default function SettingsPage({ locale, settings: s }: Props) { if (!confirm(s.logoutConfirm)) return; setLogoutLoading(true); - // Clear local auth immediately and redirect + try { + // Call backend logout first (needs auth) + await logout(); + } catch { + // Ignore logout API errors + } + // Clear local auth and redirect clearAuth(); - // Fire backend logout in background (don't wait) - logout().catch(() => {}); - // Redirect to login redirectToLogin(); }; -- 2.43.7 From d7126457548be434c6d81d3fab938157541ea1cd Mon Sep 17 00:00:00 2001 From: zl-q Date: Tue, 12 May 2026 17:51:10 +0800 Subject: [PATCH 3/4] Update web compliance disclosures --- web/design/assets/legal/en/about_us.md | 6 +++ .../assets/legal/en/terms_of_service.md | 1 + web/design/assets/legal/zh/about_us.md | 6 +++ .../assets/legal/zh/terms_of_service.md | 1 + web/design/assets/legal/zh_Hant/about_us.md | 6 +++ .../assets/legal/zh_Hant/terms_of_service.md | 1 + web/src/components/AppShell.tsx | 2 +- web/src/components/Footer.astro | 6 +-- web/src/components/LoginForm.tsx | 2 +- web/src/components/Navbar.astro | 2 +- web/src/components/PricingPage.astro | 6 +-- web/src/i18n/utils.ts | 26 ++++++------- web/src/lib/auth.ts | 39 ++++++++++++++----- 13 files changed, 72 insertions(+), 32 deletions(-) diff --git a/web/design/assets/legal/en/about_us.md b/web/design/assets/legal/en/about_us.md index 7ecd245..5a60573 100644 --- a/web/design/assets/legal/en/about_us.md +++ b/web/design/assets/legal/en/about_us.md @@ -8,6 +8,12 @@ MeeYao Divination is designed based on traditional oriental culture. Our core go --- +## AI Model Disclosure + +MeeYao Divination's AI Analysis feature is powered by DeepSeek's deepseek-v4-flash model. + +--- + ## Company Info **Developer:** Ann Lee diff --git a/web/design/assets/legal/en/terms_of_service.md b/web/design/assets/legal/en/terms_of_service.md index 213713f..dbb28e3 100644 --- a/web/design/assets/legal/en/terms_of_service.md +++ b/web/design/assets/legal/en/terms_of_service.md @@ -25,6 +25,7 @@ You represent and warrant that you are at least 13 years of age to use this App. This App provides AI-assisted cultural interpretation content related to traditional I Ching and Six-Line culture, for daily reference and cultural appreciation only. +- The AI Analysis feature is powered by DeepSeek's deepseek-v4-flash model. - All AI-generated content and cultural reference materials are for entertainment and personal reference purposes solely. - Content shall not be regarded as professional advice, including without limitation finance, investment, law, medical treatment, career or business decision-making. - I do not guarantee the accuracy, completeness or practicality of any AI-generated content within the App. diff --git a/web/design/assets/legal/zh/about_us.md b/web/design/assets/legal/zh/about_us.md index 85d2226..3ffa323 100644 --- a/web/design/assets/legal/zh/about_us.md +++ b/web/design/assets/legal/zh/about_us.md @@ -8,6 +8,12 @@ --- +## AI 模型披露 + +觅爻 MeeYao 的 AI 解卦分析功能由 DeepSeek 的 deepseek-v4-flash 模型提供支持。 + +--- + ## 开发者信息 **开发者**:Ann Lee diff --git a/web/design/assets/legal/zh/terms_of_service.md b/web/design/assets/legal/zh/terms_of_service.md index 68e3aa6..2ccccf9 100644 --- a/web/design/assets/legal/zh/terms_of_service.md +++ b/web/design/assets/legal/zh/terms_of_service.md @@ -25,6 +25,7 @@ 本应用提供与传统易经和六爻文化相关的 AI 辅助文化解读内容,仅供日常参考和文化赏析。 +- AI 解卦分析功能由 DeepSeek 的 deepseek-v4-flash 模型提供支持。 - 所有 AI 生成内容和文化参考资料仅供娱乐和个人参考目的。 - 内容不得视为专业建议,包括但不限于金融、投资、法律、医疗、职业或商业决策。 - 我不保证本应用内任何 AI 生成内容的准确性、完整性或实用性。 diff --git a/web/design/assets/legal/zh_Hant/about_us.md b/web/design/assets/legal/zh_Hant/about_us.md index 218fddd..dc941d0 100644 --- a/web/design/assets/legal/zh_Hant/about_us.md +++ b/web/design/assets/legal/zh_Hant/about_us.md @@ -8,6 +8,12 @@ --- +## AI 模型披露 + +覓爻 MeeYao 的 AI 解卦分析功能由 DeepSeek 的 deepseek-v4-flash 模型提供支持。 + +--- + ## 開發者信息 **開發者**:Ann Lee diff --git a/web/design/assets/legal/zh_Hant/terms_of_service.md b/web/design/assets/legal/zh_Hant/terms_of_service.md index 7e33a7f..5f8c677 100644 --- a/web/design/assets/legal/zh_Hant/terms_of_service.md +++ b/web/design/assets/legal/zh_Hant/terms_of_service.md @@ -25,6 +25,7 @@ 本應用提供與傳統易經和六爻文化相關的 AI 輔助文化解讀內容,僅供日常參考和文化賞析。 +- AI 解卦分析功能由 DeepSeek 的 deepseek-v4-flash 模型提供支持。 - 所有 AI 生成內容和文化參考資料僅供娛樂和個人參考目的。 - 內容不得視為專業建議,包括但不限於金融、投資、法律、醫療、職業或商業決策。 - 我不保證本應用內任何 AI 生成內容的準確性、完整性或實用性。 diff --git a/web/src/components/AppShell.tsx b/web/src/components/AppShell.tsx index 4fe0d91..d2065f5 100644 --- a/web/src/components/AppShell.tsx +++ b/web/src/components/AppShell.tsx @@ -153,7 +153,7 @@ export default function AppShell({ locale, brandName, navItems, userName, userEm