export type BillingCatalogTier = 'pro' | 'pro_plus' export type BillingCatalogProvider = 'payple' | 'stripe' export interface BillingCatalogPrice { provider: BillingCatalogProvider unit_amount: number currency: string interval: 'day' | 'week' | 'month' | 'year' interval_count: number } export interface BillingCatalogPlan { tier: BillingCatalogTier prices: BillingCatalogPrice[] } export interface BillingCatalogResponse { schema_version: '1' plans: BillingCatalogPlan[] } interface StripePriceRecord { id?: unknown active?: unknown type?: unknown unit_amount?: unknown currency?: unknown recurring?: { interval?: unknown interval_count?: unknown } | null } const CURRENCY_PATTERN = /^[a-z]{3}$/ const INTERVALS = new Set(['day', 'week', 'month', 'year']) export function parseStripeCatalogPrice( value: unknown, expectedId: string, ): Omit | null { if (!value || typeof value !== 'object' || Array.isArray(value)) return null const price = value as StripePriceRecord if ( price.id !== expectedId || price.active !== true || price.type !== 'recurring' || !Number.isSafeInteger(price.unit_amount) || (price.unit_amount as number) < 1 || typeof price.currency !== 'string' || !CURRENCY_PATTERN.test(price.currency) || !price.recurring || typeof price.recurring.interval !== 'string' || !INTERVALS.has(price.recurring.interval) || !Number.isSafeInteger(price.recurring.interval_count) || (price.recurring.interval_count as number) < 1 || (price.recurring.interval_count as number) > 12 ) return null return { unit_amount: price.unit_amount as number, currency: price.currency.toUpperCase(), interval: price.recurring.interval as BillingCatalogPrice['interval'], interval_count: price.recurring.interval_count as number, } } export function createBillingCatalog(input: { payple?: Record | null stripe?: Partial>> | null }): BillingCatalogResponse { const tiers: BillingCatalogTier[] = ['pro', 'pro_plus'] return { schema_version: '1', plans: tiers.map((tier) => { const prices: BillingCatalogPrice[] = [] const paypleAmount = input.payple?.[tier] if (Number.isSafeInteger(paypleAmount) && (paypleAmount as number) > 0) { prices.push({ provider: 'payple', unit_amount: paypleAmount as number, currency: 'KRW', interval: 'month', interval_count: 1, }) } const stripePrice = input.stripe?.[tier] if (stripePrice) prices.push({ provider: 'stripe', ...stripePrice }) return { tier, prices } }), } }