Files
zclaw_openfang/desktop/src/store/saas/billing.ts
iven e3b6003be2 refactor(store): saasStore 拆分为子模块 (Phase 2B)
1025行单文件 → 5个文件 + barrel re-export:
- saas/types.ts (103行) — 类型定义
- saas/shared.ts (93行) — Device ID、常量、recovery probe
- saas/auth.ts (362行) — 登录/注册/登出/恢复/TOTP
- saas/billing.ts (84行) — 计划/订阅/支付
- saas/index.ts (309行) — Store 组装 + 连接/模板/配置
- saasStore.ts (15行) — re-export barrel(外部零改动)

所有 25+ 消费者 import 路径不变,`tsc --noEmit` ✓
2026-04-17 20:05:43 +08:00

85 lines
2.4 KiB
TypeScript

/**
* SaaS Billing Slice
*
* Plans, subscription, and payment actions.
*/
import { saasClient } from '../../lib/saas-client';
import { createLogger } from '../../lib/logger';
import type { SaaSStore } from './types';
const log = createLogger('SaaSStore:Billing');
type SetFn = (partial: Partial<SaaSStore> | ((state: SaaSStore) => Partial<SaaSStore>)) => void;
type GetFn = () => SaaSStore;
export function createBillingSlice(set: SetFn, _get: GetFn) {
return {
plans: [],
subscription: null,
billingLoading: false,
billingError: null,
fetchPlans: async () => {
try {
const plans = await saasClient.listPlans();
set({ plans });
} catch (err: unknown) {
log.warn('Failed to fetch plans:', err);
}
},
fetchSubscription: async () => {
try {
const sub = await saasClient.getSubscription();
set({ subscription: sub });
} catch (err: unknown) {
log.warn('Failed to fetch subscription:', err);
set({ subscription: null });
}
},
fetchBillingOverview: async () => {
set({ billingLoading: true, billingError: null });
try {
const [plans, sub] = await Promise.all([
saasClient.listPlans(),
saasClient.getSubscription(),
]);
set({ plans, subscription: sub, billingLoading: false });
} catch (err: unknown) {
const msg = err instanceof Error ? err.message : String(err);
set({ billingLoading: false, billingError: msg });
}
},
createPayment: async (planId: string, method: 'alipay' | 'wechat') => {
set({ billingLoading: true, billingError: null });
try {
const result = await saasClient.createPayment({ plan_id: planId, payment_method: method });
set({ billingLoading: false });
return result;
} catch (err: unknown) {
const msg = err instanceof Error ? err.message : String(err);
set({ billingLoading: false, billingError: msg });
return null;
}
},
pollPaymentStatus: async (paymentId: string) => {
try {
const status = await saasClient.getPaymentStatus(paymentId);
if (status.status === 'succeeded') {
await _get().fetchSubscription();
}
return status;
} catch (err: unknown) {
log.warn('Failed to poll payment status:', err);
return null;
}
},
clearBillingError: () => set({ billingError: null }),
};
}