119 lines
4.1 KiB
TypeScript
119 lines
4.1 KiB
TypeScript
import type { SubscriptionTier } from '@/lib/entitlements';
|
|
import { stripe } from '@/lib/stripe';
|
|
|
|
export type BillingTier = 'PRO' | 'BUSINESS';
|
|
export type BillingInterval = 'month' | 'year';
|
|
|
|
export interface DynamicPrice {
|
|
display: string;
|
|
amount: number;
|
|
currency: string;
|
|
}
|
|
|
|
export const DEFAULT_PRICES: Record<BillingTier, Record<BillingInterval, DynamicPrice>> = {
|
|
PRO: {
|
|
month: { display: '9,90 €', amount: 9.90, currency: 'EUR' },
|
|
year: { display: '99,00 €', amount: 99.00, currency: 'EUR' },
|
|
},
|
|
BUSINESS: {
|
|
month: { display: '29,90 €', amount: 29.90, currency: 'EUR' },
|
|
year: { display: '299,00 €', amount: 299.00, currency: 'EUR' },
|
|
},
|
|
};
|
|
|
|
export async function getDynamicPrices(): Promise<Record<BillingTier, Record<BillingInterval, DynamicPrice>>> {
|
|
const isMock = !process.env.STRIPE_SECRET_KEY || process.env.STRIPE_SECRET_KEY === 'sk_test_placeholder';
|
|
if (isMock) {
|
|
return DEFAULT_PRICES;
|
|
}
|
|
|
|
const result: Record<BillingTier, Record<BillingInterval, DynamicPrice>> = {
|
|
PRO: {
|
|
month: { ...DEFAULT_PRICES.PRO.month },
|
|
year: { ...DEFAULT_PRICES.PRO.year },
|
|
},
|
|
BUSINESS: {
|
|
month: { ...DEFAULT_PRICES.BUSINESS.month },
|
|
year: { ...DEFAULT_PRICES.BUSINESS.year },
|
|
},
|
|
};
|
|
|
|
const retrieveAndFormatPrice = async (tier: BillingTier, interval: BillingInterval) => {
|
|
try {
|
|
const priceId = resolvePriceId(tier, interval);
|
|
const price = await stripe.prices.retrieve(priceId);
|
|
if (price.unit_amount !== null && price.unit_amount !== undefined) {
|
|
const amount = price.unit_amount / 100;
|
|
const currency = price.currency.toUpperCase();
|
|
|
|
// Format the price nicely
|
|
let display = '';
|
|
if (currency === 'EUR') {
|
|
display = `${amount.toLocaleString('fr-FR', { minimumFractionDigits: 0, maximumFractionDigits: 2 })} €`;
|
|
} else if (currency === 'USD') {
|
|
display = `$${amount.toLocaleString('en-US', { minimumFractionDigits: 0, maximumFractionDigits: 2 })}`;
|
|
} else if (currency === 'GBP') {
|
|
display = `£${amount.toLocaleString('en-GB', { minimumFractionDigits: 0, maximumFractionDigits: 2 })}`;
|
|
} else {
|
|
display = `${amount} ${currency}`;
|
|
}
|
|
|
|
result[tier][interval] = { display, amount, currency };
|
|
}
|
|
} catch (err) {
|
|
console.error(`[stripe-prices] Failed to retrieve price for ${tier}/${interval}:`, err);
|
|
// Fallback to default in case of error
|
|
}
|
|
};
|
|
|
|
await Promise.all([
|
|
retrieveAndFormatPrice('PRO', 'month'),
|
|
retrieveAndFormatPrice('PRO', 'year'),
|
|
retrieveAndFormatPrice('BUSINESS', 'month'),
|
|
retrieveAndFormatPrice('BUSINESS', 'year'),
|
|
]);
|
|
|
|
return result;
|
|
}
|
|
|
|
export function resolvePriceId(tier: BillingTier, interval: BillingInterval): string {
|
|
const map: Record<BillingTier, Record<BillingInterval, string>> = {
|
|
PRO: {
|
|
month: process.env.STRIPE_PRICE_PRO_MONTHLY!,
|
|
year: process.env.STRIPE_PRICE_PRO_ANNUAL!,
|
|
},
|
|
BUSINESS: {
|
|
month: process.env.STRIPE_PRICE_BUSINESS_MONTHLY!,
|
|
year: process.env.STRIPE_PRICE_BUSINESS_ANNUAL!,
|
|
},
|
|
};
|
|
const priceId = map[tier][interval];
|
|
if (!priceId) {
|
|
const isMock = !process.env.STRIPE_SECRET_KEY || process.env.STRIPE_SECRET_KEY === 'sk_test_placeholder';
|
|
if (isMock && process.env.NODE_ENV !== 'test') {
|
|
return `price_mock_${tier.toLowerCase()}_${interval}`;
|
|
}
|
|
throw new Error(`No Stripe price ID configured for ${tier}/${interval}`);
|
|
}
|
|
return priceId;
|
|
}
|
|
|
|
export function priceIdToTier(priceId: string): SubscriptionTier | null {
|
|
if (priceId && priceId.startsWith('price_mock_')) {
|
|
if (priceId.includes('pro')) return 'PRO';
|
|
if (priceId.includes('business')) return 'BUSINESS';
|
|
return 'BASIC';
|
|
}
|
|
|
|
const entries: Array<[string | undefined, SubscriptionTier]> = [
|
|
[process.env.STRIPE_PRICE_PRO_MONTHLY, 'PRO'],
|
|
[process.env.STRIPE_PRICE_PRO_ANNUAL, 'PRO'],
|
|
[process.env.STRIPE_PRICE_BUSINESS_MONTHLY, 'BUSINESS'],
|
|
[process.env.STRIPE_PRICE_BUSINESS_ANNUAL, 'BUSINESS'],
|
|
];
|
|
for (const [envPriceId, tier] of entries) {
|
|
if (envPriceId && envPriceId === priceId) return tier;
|
|
}
|
|
return null;
|
|
}
|