feat(server+web+desktop): Phase 3.2-B Payple 결제 연동 + 대시보드 Premium 상태 표시

Payple PG 연동:
- _shared/payple.ts: Payple API 래퍼 (auth/billing/cancel/deleteBillingKey)
- payple-checkout Edge Function: 빌링키 결제 + 구독 활성화
- payple-webhook Edge Function: 결제완료/취소 이벤트
- payple-manage Edge Function: 구독 취소 (빌링키 해지)
- DB migration: payment_provider + payple_payer_id + payple_pay_oid
- 웹 billing 페이지: Payple JS SDK 결제창 + 관리 버튼 (Stripe 대체)
- Electron LicenseModal: shell.openExternal → 웹 결제 페이지

대시보드 Premium 상태:
- CrtDisplay services에 PREMIUM LLM LED 추가
- 백엔드 인디케이터 카드 (Local/Premium) + 티어 카드
- 사용량 섹션: 전 티어 표시 + 모델별 Premium 쿼터
- 12개 locale × 14개 i18n 키
This commit is contained in:
윤찬 2026-04-12 19:18:40 +09:00
parent b8cb665264
commit c9baf031c9
27 changed files with 1318 additions and 98 deletions

View file

@ -83,9 +83,9 @@ export function LicenseModal({ open, onClose }: LicenseModalProps): React.ReactE
const isFree = currentTier === 'free'
const isPro = currentTier === 'pro'
const handleUpgrade = useCallback(() => {
alert(t('license.paymentPending'))
}, [t])
const handleUpgrade = useCallback((tier: 'pro' | 'pro_plus' = 'pro') => {
window.electronAPI.license.openBilling({ tier })
}, [])
return (
<Dialog
@ -141,7 +141,7 @@ export function LicenseModal({ open, onClose }: LicenseModalProps): React.ReactE
{isFree && (
<>
<PhysicalButton onClick={handleUpgrade} sx={{ width: '100%' }}>
<PhysicalButton onClick={() => handleUpgrade('pro')} sx={{ width: '100%' }}>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<Led color="green" size={8} />
<PhosphorText variant="compact">
@ -149,7 +149,7 @@ export function LicenseModal({ open, onClose }: LicenseModalProps): React.ReactE
</PhosphorText>
</Box>
</PhysicalButton>
<PhysicalButton onClick={handleUpgrade} sx={{ width: '100%' }}>
<PhysicalButton onClick={() => handleUpgrade('pro_plus')} sx={{ width: '100%' }}>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<Led color="green" size={8} />
<PhosphorText variant="compact">
@ -168,7 +168,7 @@ export function LicenseModal({ open, onClose }: LicenseModalProps): React.ReactE
{t('license.currentPlan')} {t('license.proPlan')}
</PhosphorText>
</Box>
<PhysicalButton onClick={handleUpgrade} sx={{ width: '100%' }}>
<PhysicalButton onClick={() => handleUpgrade('pro_plus')} sx={{ width: '100%' }}>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<Led color="green" size={8} />
<PhosphorText variant="compact">