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` ✓
85 lines
2.4 KiB
TypeScript
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 }),
|
|
};
|
|
}
|