La page tarifs reprend la barre du site public. En annuel, Pro affiche 8,25 € par mois (99 € l’année), plus le 99 € comme gros chiffre. Co-authored-by: Cursor <cursoragent@cursor.com>
48 lines
1.7 KiB
TypeScript
48 lines
1.7 KiB
TypeScript
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.9, currency: 'EUR' },
|
||
year: { display: '99,00 €', amount: 99, currency: 'EUR' },
|
||
},
|
||
BUSINESS: {
|
||
month: { display: '29,90 €', amount: 29.9, currency: 'EUR' },
|
||
year: { display: '299,00 €', amount: 299, currency: 'EUR' },
|
||
},
|
||
}
|
||
|
||
export function formatBillingAmount(amount: number, currency = 'EUR'): string {
|
||
const c = currency.toUpperCase()
|
||
if (c === 'EUR') {
|
||
return `${amount.toLocaleString('fr-FR', { minimumFractionDigits: 2, maximumFractionDigits: 2 })} €`
|
||
}
|
||
if (c === 'USD') {
|
||
return `$${amount.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`
|
||
}
|
||
if (c === 'GBP') {
|
||
return `£${amount.toLocaleString('en-GB', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`
|
||
}
|
||
return `${amount.toLocaleString('fr-FR', { minimumFractionDigits: 2, maximumFractionDigits: 2 })} ${c}`
|
||
}
|
||
|
||
/** Prix annuel ramené au mois (99 € / an → 8,25 € / mois). */
|
||
export function yearToMonthlyEquivalent(yearAmount: number): number {
|
||
return Math.round((yearAmount / 12) * 100) / 100
|
||
}
|
||
|
||
/** Remise réelle : 9,90 € × 12 vs 99 € / an → 17 %. */
|
||
export function annualDiscountPercent(monthlyAmount: number, yearlyAmount: number): number {
|
||
const paidMonthlyForYear = monthlyAmount * 12
|
||
if (paidMonthlyForYear <= 0) return 0
|
||
const raw = (1 - yearlyAmount / paidMonthlyForYear) * 100
|
||
if (!Number.isFinite(raw) || raw <= 0) return 0
|
||
return Math.round(raw)
|
||
}
|