Skip to content

feat(ratewise): 刷卡估算模式 Phase 1(ADR-002) - #665

Merged
s123104 merged 1 commit into
experiment/ratewise-product-2026h2from
feat/651-card-rate-phase1
Jul 7, 2026
Merged

feat(ratewise): 刷卡估算模式 Phase 1(ADR-002)#665
s123104 merged 1 commit into
experiment/ratewise-product-2026h2from
feat/651-card-rate-phase1

Conversation

@s123104

@s123104 s123104 commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Refs #651(Phase 1,不 close epic)

摘要

ADR-002(Accepted)交付 Phase 1 刷卡估算模式:零爬蟲、零新資料源,估算匯率 = 台銀即期賣出 ×(1+手續費),feature flag 控制、flag off 前端零暴露。

審查修正(APPROVE 後三項 Should-fix,PM 裁決隨本 PR 收斂)

S1:card 強制即期基準

  • 根因resolveRateTypeForSource 對 registry 外 provider(getDefaultProvider('card') 為 null)隱式放行 persisted rateType='cash',切刷卡後估算以現金賣出計算,高估 1–2%,偏離 ADR「即期賣出 ×(1+手續費)」
  • 修正resolveRateTypeForSource 補 card 特例固定回傳 'spot'——切換、hydrate sanitize、setRateType 三路徑全部收斂;引擎既有 spot→cash 缺失回落保留(KRW 情境不變)
  • 揭露同判準:新增 resolveCardBasisRateType(availability) 供計算式標籤消費(即期可用標「即期賣出」,缺失誠實標「現金賣出」),與引擎賣出腿 fallback 同一判準,值-標籤耦合有測試鎖定(persisted cash+切 card+即期存在 → 30.97×1.015 非 31.4×1.015)

S2:?cardRate 深連結豁免

  • RememberedHomeRoutehasDeepLink 豁免加入 cardRate(Phase 1 URL 是唯一啟用入口,persisted multi 不得吞掉);RateWiseisDeepLink(偏好寫入豁免)同步同一契約,臨時進入不覆寫 lastConverterView
  • 測試:persisted multi+/?cardRate=on 進站 → 停留單幣別且 isCardRateEnabled() 生效
  • [bug] remembered-view redirect 劫持 ?converter= URL override(QA/深連結失效) #654 通用 query 豁免另案,不在此 PR 擴 scope

S3:v2 變體 Phase 1 禁用 card(誠實最小解)

  • v2 card 態缺估算 badge/計算式/手續費揭露/免責——不做半套:isCardRateAvailableInContext 加入 !isConverterV2Active 條件(與換錢所不可用同一 SSOT 收斂 effect),v2 下 effective source 與 persisted 偏好均收斂回 bank;SingleConverterV2 完整還原 base 狀態(移除「刷卡估算」chip 標籤分支與四語系 key)
  • ADR-002 UI 邊界備註(已載明):「v2 變體 Phase 1 禁用 card(收斂回 bank):v2 的估算 badge/手續費揭露/免責整合併入 E8 wave-B 一次做對,避免半套誠實揭露上線。」
  • 測試:v2+persisted card → effective source 為 bank、chip 無「刷卡」字樣(分支移除鎖定)

Nit

  • 引擎「外幣→外幣兩腿手續費互銷」註解金融語意不成立 → 改為防禦碼語意(UI 層已擋非 TWD 對),測試名稱同步

實作

RateSource 建模

  • RATE_SOURCES 新增 'card'(RateSource 層級,同 exchange-shop;非 RateType)
  • 刷卡為 registry 外虛擬 providercard-estimate):無 API endpoint,不進 RATE_PROVIDERS,公開 API metadata(api/latest.json、OpenData 頁)零污染;getDefaultProviderRefisProviderSupportedForPair 特例支援(僅涉及 TWD 的貨幣對)
  • PublicRateProvider.sourceKind 顯式收斂為 'bank' | 'exchange-shop',型別層防止 card 滲入公開 schema

兩方向估算語意定案(誠實原則)

  • 兩方向共用單一 effective rate R = 銀行即期賣出 ×(1+手續費%),互為倒數:
    • 外幣→TWD(刷卡帳單估算):單位匯率 = sell × (1 + fee)
    • TWD→外幣(預算可刷額度):單位匯率 = (1 / sell) / (1 + fee)
  • 不採用「外幣→TWD 用即期買入 ×(1−手續費)」:刷卡不存在把外幣賣回銀行的反向交易,發卡行清算永遠是「以 TWD 買入外幣消費額」,買入價語意在此情境不成立;單一公式亦與 ADR-002 SSOT 一致
  • 基準固定即期賣出(S1);即期缺失誠實回落現金賣出(如 KRW),計算式標籤同判準同步

手續費

  • 0–3% 可調、步進 0.1%、預設 1.5%;clampCardFeePercent 消除浮點誤差
  • persist 進 ratewise-converter(storage-keys 慣例,不新增 key);hydrate sanitize 修復損毀值

UI(韓系扁平)

  • v1 RateSelector 加「刷卡」pill(CreditCard icon,第四選項;fix(ratewise): RateSelector pills 內容驅動佈局與 44px 觸控熱區 #660 內容驅動佈局直接容納,320px 四 pills 不換行)
  • 選中刷卡:匯率卡顯示估算值+常駐「估算」badge+計算式小字「= 即期賣出 × 1.015(含 1.5% 海外手續費)」(乘數與引擎 getCardFeeMultiplier 同源)
  • 手續費調整採卡片內小型 stepper(−/+,44px 熱區負邊距擴充):點擊深度 1(選刷卡後直接調整),優於設定頁滑桿的跨頁深度 ≥2,且調整結果即時反映在同一張卡的估算值上(所見即所得)
  • 免責一行「實際扣款以發卡行清算日匯率為準」+連 /card-rate-guide/

Feature flag(converter-v2-flag 模式)

  • card-rate-flag.ts:URL ?cardRate=on|off override > store cardRateEnabled > 預設 off
  • server snapshot 恆 off:SSG 輸出零暴露(rg 'card-estimate|切換到刷卡估算' dist --glob '*.html' = 0 hits)
  • flag off 時:pill 不渲染、persisted rateSource='card' 自動收斂回 bank(useCurrencyConverter 單一 fallback effect)

多幣別(Phase 1.5 申報延後)

  • 多幣別列級 rateSource 適配延至 Phase 1.5:MultiConverter 的 UnifiedRateOption 三選項佈局與列級可用性矩陣需獨立設計,Phase 1 先交付單幣別核心
  • 已保證安全:resolveEffectiveRateSourceForConversion 於 multi 模式一律將 card 回落 bank,且進入多幣別時 store 自動收斂,無錯值風險

驗收證據

截圖矩陣(本機 screenshots/,QA 暫存不入 repo)

尺寸 內容 檔名
320 估算卡完整(badge/計算式/stepper/免責) card-320-estimate-card.png
320 四 pills(即期/現金/換錢所/刷卡)不換行 card-320-four-pills.png
320 KRW 現金回落誠實標籤(= 現金賣出 × 1.016) card-320-krw-card-active.png
375 估算卡 card-375-estimate-card.png
390 估算卡+pill 選中態 card-390-estimate-card.png
390 手續費 1.5→1.6% 即時重算 card-390-fee-adjust.png
390 flag off 零暴露(無 pill、自動回落銀行) card-390-flag-off.png
430 估算卡 card-430-estimate-card.png
768 平板雙欄佈局 card-768-estimate-card.png
1024×768 桌面佈局估算卡(ADR 驗收尺寸補齊) card-1024-estimate-card.png
1024×768 S1 驗證:USD 即期基準(= 即期賣出 × 1.016) card-1024-usd-spot-basis.png
1024×768 S3 驗證:v2+persisted card 收斂 bank(chip 標即期賣出、無刷卡) card-1024-v2-converged-bank.png

剩餘範圍(不在本 PR)

  • Phase 1.5:多幣別列級刷卡適配;設定頁 flag 開關入口(現以 URL override 供 QA)
  • E8 wave-B:v2 變體估算 badge/手續費揭露/免責整合(ADR-002 UI 邊界備註)
  • Phase 2(ADR-002):card-rates.json schema+Playwright headed pipeline+canary 14 天 ≥90% 閘門
  • Phase 3:Visa/MC 真實清算價並排卡+降級層(>120h)+seo-metadata 文案同步

⚠️ 本 PR 不 merge——等待維護者審查與明確批准。

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@s123104
s123104 force-pushed the feat/651-card-rate-phase1 branch from b703b14 to 498de92 Compare July 7, 2026 01:03
@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

⚠️ Deprecation Warning: The deny-licenses option is deprecated for possible removal in the next major release. For more information, see issue 997.

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Scanned Files

None

@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

✅ SEO 審計通過!所有 2026 標準驗證項目都符合要求。

  • ✅ Sitemap 2026 標準
  • ✅ Breadcrumb Schema
  • ✅ JSON-LD 結構化數據
  • ✅ 內部連結結構

- card 建模為 RateSource 層級:registry 外虛擬 provider card-estimate,公開 API metadata 零污染
- 估算引擎:銀行賣出腿 ×(1+手續費),兩方向共用單一 effective rate;resolveRateTypeForSource
  將 card 基準固定即期賣出(persisted cash 不再高估),即期缺失誠實回落現金且揭露同判準
- 手續費 0–3% 可調、步進 0.1%、預設 1.5%,persist 進 ratewise-converter(storage-keys 慣例)
- UI:RateSelector 第四 pill+估算卡(估算 badge、計算式揭露、卡片內 stepper、清算日免責連結)
- card-rate-flag:URL override > store > 預設 off;flag off SSG 與 UI 零暴露;?cardRate 列入
  還原導向深連結豁免(persisted multi 不吞掉 Phase 1 唯一啟用入口)
- v2 版面 Phase 1 缺估算揭露收斂回 bank(不做半套,完整揭露併 E8 wave-B);多幣別 Phase 1.5
- 歷史記錄新增 card 分類;i18n 四語系同步

測試:pnpm --filter @app/ratewise exec vitest run 4902 passed(含 spot 正規化、深連結豁免、
v2 收斂與值-標籤耦合);typecheck 通過;SSG 零暴露 grep=0;320–1024 六尺寸截圖驗證

Co-authored-by: Cursor <cursoragent@cursor.com>
@s123104
s123104 force-pushed the feat/651-card-rate-phase1 branch from 498de92 to 7498151 Compare July 7, 2026 01:26
@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

✅ SEO 審計通過!所有 2026 標準驗證項目都符合要求。

  • ✅ Sitemap 2026 標準
  • ✅ Breadcrumb Schema
  • ✅ JSON-LD 結構化數據
  • ✅ 內部連結結構

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant