feat/lithic-m3-onboarding-division → feat/lithic-m3
· 31 files · +3126 / −488 · OPEN · Generated 2026-06-29
LITHIC 卡專案(Continental Bank 承作)需符合 FinCEN CDD,要收集「多名受益人 +
獨立控制人 + 分數持股」,舊的單一 representative KYB 流程無法滿足。本 PR 重寫
onboarding,並以 organization.productCard.program 把流程分岔:
LITHIC 走 Beneficial Owner / Control Person 流程;STRIPE(及 null / legacy)
維持原本單一 representative 流程,作為安全預設。改動幾乎全集中於 src/pages/OnboardingPages/。
feat/lithic-m3 之上的子分支;
用 main 比 diff 會看到一堆無關檔案。正確 diff:origin/feat/lithic-m3...origin/feat/lithic-m3-onboarding-division。
isLithicFlow = organization.value.productCard?.program === CARD_PROGRAM.LITHIC
— 後端設定、非使用者選擇;任何非 LITHIC 值(含 null / legacy)fall-through 到 STRIPE。
onboardingPanels computed 依此 fork。
兩流程在有資格時都會 append Pay Over Time。
helpers/stripePerson.js 新增 STRIPE_ROLES preset(只改列出的 flag,可組合)。
| Flow | 角色 | 持有 relationship flags |
|---|---|---|
| STRIPE | Representative(單人) | representative + executive + owner(三合一) |
| LITHIC | Beneficial Owner step | owner |
| LITHIC | Control Person step | representative + executive |
currentControlPerson 與 currentRepresentativePerson
都指向 relationship.representative === true 那筆,用 alias 讓兩流程各讀自己的詞彙。
ControlPersonKey(injectionSymols.js)OnboardingPageKey 多 provide isLithicFlowStripePersonsKey:currentRepresentativePerson → currentControlPerson,多 export queryStripePersonscomposables/beneficialOwner.js — 多受益人清單:addBo/removeBo(上限 4 人 FinCEN)、totalOwnership、100% gate、<25% 提示、Promise.allSettled 並行送出 + partial-failure 回報composables/controlPersonPrefill.js — Control Person 的「Pre-fill from」下拉(My account / 各 BO),以及 control-person swap:送出新 CP 前先 demote(舊人也是 BO)或 delete 舊 CPhelpers/stripePerson.js 純函式:buildPrefillFromMember、convertStripePersonToForm、pickStripePersonSubmitErrorKey(BE error code → curated i18n)、ownership regex / keystroke guardhelpers/onboarding.js — isOnboardingTermsAccepted(org):OR 兩個 sentinel,讓 route guard 與 page 兩端 lock-step| Flow | 寫入的 sentinel |
|---|---|
| LITHIC | acceptedOrganizationInfoCertificationTermsAt |
| STRIPE | acceptedStripeFinancialConnectionsTermsAt |
auth.js route guard 與 OnboardingPage 的 termsAccepted 都改用 isOnboardingTermsAccepted()(OR 兩者),對齊後端 V1Update.js per-program gate。
router.push('/')」→ interval 只刷新 organization,改用 watch(onboardingCompleted) 觸發導頁onUnmounted → onBeforeUnmountBeneficialOwnerPanel.vue(641 行)— 多受益人卡片、分數持股(999.99 上限)、<25% 送出阻擋 + spec toast、self-prefillControlPersonPanel.vue(315 行)— 控制人,Pre-fill 下拉 + swap on submitIndividualRolesSection.vue(336 行)— role-edit UI,依 flow 分岔KybPanel.vue(+279)— 同一面板用 isLithicFlow 參數化:LITHIC 顯示 certification(+ sole-prop credit-pull)checkbox、CP/BO 標籤、缺 CP 警示;STRIPE 維持原 rep+owner/exec gate;role tag 改 personRoleTags()EditIndividualDialog.vue(+126)、RepresentativePanel.vue、StripePersonForm.vue(大量抽到 helper)、PayOverTimePanel.vuecontrol_person_*、prefill_from_*、kyb_certification_checkbox、credit-pull 授權文案、BO 相關 toastownership_percentage_equal_zero_error 等舊 keyOnboardingPages/docs/ 4 份(README / development-guide / integrations / panel-navigation)同步更新(符合 doc-code 鐵律)CLAUDE.md(repo 根 + LoginPages / RegisterPages / OnboardingPages / TwoFactorAuth / NitraMart),並從 .gitignore 移出開始追蹤OnboardingPages/ 模組沒有任何 Cypress / spec 檔,本 PR 也未新增。
最高價值的邏輯 — BO 持股數學、25 / 100 邊界、prefill、CP swap、program fork — 全部未被自動化覆蓋。
isOnboardingTermsAccepted(OR 雙 sentinel)後,既有用戶「已完成 → 導回首頁」行為不變program === null org 不會誤入 LITHIC 流程hasBeneficialOwner / wantsRedoBo)watch 觸發導頁(取代原 polling-redirect)onBeforeUnmount)ONBOARDING_PANEL 常數 + onboardingPanels computed + template v-if switch,缺一即靜默壞掉導覽onboardingPanels 是導覽唯一真相來源;「落在哪一步」初始判定只在 onMounted(getStripePersons() resolve 後),不可下放給各 panelisOnboardingTermsAccepted():route guard 與 banner 必須 lock-step;改 submitOnboardingAcknowledgements 寫入內容會同時影響導頁與 bannerisRepresentativeSet + 「你是 representative 嗎」自動 dialog 只屬 STRIPE flow;LITHIC 改用 Pre-fill 下拉 + BO/CP 拆分STRIPE_ROLES preset 只改列出的 flag(可組合不清空);keys 必須與 defaultStripePersonForm / BO_FORM_DEFAULTS 同步,漂移會靜默翻轉 touched 判定socialSecurityNumberSubmitted 為 true 後,該欄位從 validation / changedFields / payload 一律剔除(Stripe 第一次寫入後鎖 id_number)pickStripePersonSubmitErrorKey 映射成 curated i18n