perf: memo GridCard, fuse save fns, fix slash tab active color
This commit is contained in:
@@ -31,7 +31,7 @@ export async function cancelSubscription(userId: string): Promise<CancelSubscrip
|
||||
// Direct call to Stripe API with period end cancellation set to true
|
||||
const updatedSub = await stripe.subscriptions.update(subscription.stripeSubscriptionId, {
|
||||
cancel_at_period_end: true,
|
||||
});
|
||||
}) as any;
|
||||
|
||||
// Local database synchronization
|
||||
await prisma.subscription.update({
|
||||
|
||||
@@ -1,8 +1,81 @@
|
||||
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: {
|
||||
|
||||
@@ -45,13 +45,13 @@ export async function syncSubscriptionFromStripe(
|
||||
const status = mapStripeStatus(subscription.status);
|
||||
|
||||
const currentPeriodStartTimestamp =
|
||||
subscription.current_period_start ??
|
||||
(subscription as any).current_period_start ??
|
||||
(subscription as any).items?.data?.[0]?.current_period_start ??
|
||||
(subscription as any).start_date ??
|
||||
Math.floor(Date.now() / 1000);
|
||||
|
||||
const currentPeriodEndTimestamp =
|
||||
subscription.current_period_end ??
|
||||
(subscription as any).current_period_end ??
|
||||
(subscription as any).items?.data?.[0]?.current_period_end ??
|
||||
(currentPeriodStartTimestamp + 30 * 24 * 3600);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user