diff --git a/apps/web/.env.development b/apps/web/.env.development
index bd96eb1c2..f17125765 100644
--- a/apps/web/.env.development
+++ b/apps/web/.env.development
@@ -15,7 +15,7 @@ NEXT_PUBLIC_AUTH_PASSWORD=true
NEXT_PUBLIC_AUTH_MAGIC_LINK=false
# BILLING
-NEXT_PUBLIC_BILLING_PROVIDER=lemon-squeezy
+NEXT_PUBLIC_BILLING_PROVIDER=stripe
# SUPABASE
NEXT_PUBLIC_SUPABASE_URL=http://127.0.0.1:54321
diff --git a/apps/web/config/billing.sample.config.ts b/apps/web/config/billing.sample.config.ts
index ebc105f62..295406d34 100644
--- a/apps/web/config/billing.sample.config.ts
+++ b/apps/web/config/billing.sample.config.ts
@@ -16,28 +16,6 @@ export default createBillingSchema({
provider,
// products configuration
products: [
- {
- id: 'lifetime',
- name: 'Lifetime',
- description: 'The perfect plan for a lifetime',
- currency: 'USD',
- features: ['Feature 1', 'Feature 2', 'Feature 3'],
- plans: [
- {
- name: 'Lifetime',
- id: 'lifetime',
- paymentType: 'one-time',
- lineItems: [
- {
- id: '324643',
- name: 'Base',
- cost: 999.99,
- type: 'flat',
- },
- ],
- },
- ],
- },
{
id: 'starter',
name: 'Starter',
@@ -56,42 +34,7 @@ export default createBillingSchema({
id: '324646',
name: 'Addon 2',
cost: 9.99,
- type: 'metered',
- unit: 'GBs',
- tiers: [
- {
- upTo: 5,
- cost: 0,
- },
- {
- upTo: 10,
- cost: 6.99,
- },
- {
- upTo: 'unlimited',
- cost: 0.49,
- },
- ],
- },
- {
- id: '324645',
- name: 'Addon 2',
- cost: 9.99,
- type: 'per-seat',
- tiers: [
- {
- upTo: 5,
- cost: 0,
- },
- {
- upTo: 10,
- cost: 6.99,
- },
- {
- upTo: 'unlimited',
- cost: 0.49,
- },
- ],
+ type: 'flat',
},
],
},
diff --git a/packages/billing/gateway/src/components/line-item-details.tsx b/packages/billing/gateway/src/components/line-item-details.tsx
index 0c3d9051b..42e3278bf 100644
--- a/packages/billing/gateway/src/components/line-item-details.tsx
+++ b/packages/billing/gateway/src/components/line-item-details.tsx
@@ -38,114 +38,154 @@ export function LineItemDetails(
);
}
- const BaseFee = () => (
-
-
+ const SetupFee = () => (
+
+
+
+ );
+
+ const FlatFee = () => (
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+ }
+ >
+
+
+
+
+
+
+ {formatCurrency(props?.currency.toLowerCase(), item.cost)}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+
+ const PerSeat = () => (
+
+
-
+
-
-
+
+
+ {formatCurrency(props.currency.toLowerCase(), item.cost)}
+
+
+
-
- }
- >
-
-
+
+
+
+
+
+
+ );
+
+ const Metered = () => (
+
+
+
+
+
+
+
+
+
+
+
+
-
-
- {formatCurrency(props?.currency.toLowerCase(), item.cost)}
-
+ {/* If there are no tiers, there is a flat cost for usage */}
+
+
+ {formatCurrency(props?.currency.toLowerCase(), item.cost)}
+
+
+
+
+
+
+ {/* If there are tiers, we render them as a list */}
+
+
+
);
switch (item.type) {
- case 'base':
- return ;
+ case 'flat':
+ return ;
case 'per-seat':
- return (
-
-
-
-
-
-
-
-
-
-
-
-
- {formatCurrency(props.currency.toLowerCase(), item.cost)}
-
-
-
-
-
-
-
-
- );
+ return ;
case 'metered': {
- return (
-
-
-
-
-
-
-
-
-
-
-
-
- {(fee) => (
-
-
-
- )}
-
-
-
-
-
- {/* If there are no tiers, there is a flat cost for usage */}
-
-
- {formatCurrency(props?.currency.toLowerCase(), item.cost)}
-
-
-
-
- {/* If there are tiers, we render them as a list */}
-
-
-
-
- );
+ return ;
}
}
})}
@@ -160,6 +200,8 @@ function Tiers({
currency: string;
item: z.infer;
}) {
+ const unit = item.unit;
+
const tiers = item.tiers?.map((tier, index) => {
const previousTier = item.tiers?.[index - 1];
const isNoLimit = tier.upTo === 'unlimited';
@@ -173,7 +215,6 @@ function Tiers({
const upTo = tier.upTo;
const isIncluded = tier.cost === 0;
- const unit = item.unit;
return (
{formatCurrency(
product.currency.toLowerCase(),
- baseLineItem.cost,
+ primaryLineItem.cost,
)}
@@ -420,19 +420,21 @@ function PlanDetails({
-
+ 0}>
+
-
+
diff --git a/packages/billing/gateway/src/components/pricing-table.tsx b/packages/billing/gateway/src/components/pricing-table.tsx
index 27f81bc98..457d9ec92 100644
--- a/packages/billing/gateway/src/components/pricing-table.tsx
+++ b/packages/billing/gateway/src/components/pricing-table.tsx
@@ -386,13 +386,16 @@ function PlanIntervalSwitcher(
{props.intervals.map((plan, index) => {
const selected = plan === props.interval;
- const className = cn('focus:!ring-0 !outline-none', {
- 'rounded-r-none border-r-transparent': index === 0,
- 'rounded-l-none': index === props.intervals.length - 1,
- ['hover:bg-muted']: !selected,
- ['font-semibold cursor-default bg-muted hover:bg-muted hover:text-initial']:
- selected,
- });
+ const className = cn(
+ 'focus:!ring-0 !outline-none animate-in transition-all fade-in',
+ {
+ 'rounded-r-none border-r-transparent': index === 0,
+ 'rounded-l-none': index === props.intervals.length - 1,
+ ['hover:text-current hover:bg-muted']: !selected,
+ ['font-semibold cursor-default hover:text-initial hover:bg-background border-primary']:
+ selected,
+ },
+ );
return (
-
+