-
-
}
- >
- {(subscription) => (
-
- )}
-
+
+ }
+ >
+ {(subscription) => (
+
+ )}
+
-
-
-
-
+
+
+
>
diff --git a/apps/web/app/(dashboard)/home/[account]/billing/server-actions.ts b/apps/web/app/(dashboard)/home/[account]/billing/server-actions.ts
index 766d96cea..b30443992 100644
--- a/apps/web/app/(dashboard)/home/[account]/billing/server-actions.ts
+++ b/apps/web/app/(dashboard)/home/[account]/billing/server-actions.ts
@@ -4,7 +4,6 @@ import { redirect } from 'next/navigation';
import { z } from 'zod';
-import { getLineItemsFromPlanId } from '@kit/billing';
import { getBillingGatewayProvider } from '@kit/billing-gateway';
import { requireUser } from '@kit/supabase/require-user';
import { getSupabaseServerActionClient } from '@kit/supabase/server-actions-client';
diff --git a/apps/web/app/api/database/webhook.ts b/apps/web/app/api/database/webhook.ts
deleted file mode 100644
index efe1e2ecd..000000000
--- a/apps/web/app/api/database/webhook.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-export function POST(request: Request) {
- console.log(request);
-}
diff --git a/apps/web/app/api/database/webhook/route.ts b/apps/web/app/api/database/webhook/route.ts
new file mode 100644
index 000000000..0144f3e73
--- /dev/null
+++ b/apps/web/app/api/database/webhook/route.ts
@@ -0,0 +1,20 @@
+import { z } from 'zod';
+
+import { DatabaseWebhookHandlerService } from '@kit/database-webhooks';
+
+const webhooksSecret = z
+ .string({
+ description: `The secret used to verify the webhook signature`,
+ })
+ .min(1)
+ .parse(process.env.SUPABASE_DB_WEBHOOK_SECRET);
+
+export async function POST(request: Request) {
+ const service = new DatabaseWebhookHandlerService();
+
+ await service.handleWebhook(request, webhooksSecret);
+
+ return new Response(null, {
+ status: 200,
+ });
+}
diff --git a/apps/web/app/layout.tsx b/apps/web/app/layout.tsx
index 2c54f38b7..7c200e984 100644
--- a/apps/web/app/layout.tsx
+++ b/apps/web/app/layout.tsx
@@ -1,5 +1,5 @@
import { Inter as SansFont } from 'next/font/google';
-import { cookies } from 'next/headers';
+import { cookies, headers } from 'next/headers';
import { Toaster } from '@kit/ui/sonner';
import { cn } from '@kit/ui/utils';
@@ -27,6 +27,8 @@ export default async function RootLayout({
return (
+
+
{children}
@@ -70,3 +72,9 @@ export const metadata = {
},
},
};
+
+function CsrfTokenMeta() {
+ const csrf = headers().get('x-csrf-token') ?? '';
+
+ return
;
+}
diff --git a/apps/web/next.config.mjs b/apps/web/next.config.mjs
index 1e23658b5..0710cda7b 100644
--- a/apps/web/next.config.mjs
+++ b/apps/web/next.config.mjs
@@ -16,6 +16,7 @@ const INTERNAL_PACKAGES = [
'@kit/billing-gateway',
'@kit/stripe',
'@kit/email-templates',
+ '@kit/database-webhooks'
];
/** @type {import('next').NextConfig} */
diff --git a/apps/web/package.json b/apps/web/package.json
index e70326a32..98b398fc4 100644
--- a/apps/web/package.json
+++ b/apps/web/package.json
@@ -22,6 +22,7 @@
"@kit/auth": "workspace:^",
"@kit/billing": "workspace:^",
"@kit/billing-gateway": "workspace:^",
+ "@kit/database-webhooks": "workspace:^",
"@kit/email-templates": "workspace:^",
"@kit/i18n": "workspace:^",
"@kit/mailers": "workspace:^",
diff --git a/packages/billing-gateway/src/components/current-plan-card.tsx b/packages/billing-gateway/src/components/current-plan-card.tsx
index a829726f6..a1384f3b6 100644
--- a/packages/billing-gateway/src/components/current-plan-card.tsx
+++ b/packages/billing-gateway/src/components/current-plan-card.tsx
@@ -1,7 +1,11 @@
import { formatDate } from 'date-fns';
import { BadgeCheck, CheckCircle2 } from 'lucide-react';
-import { BillingConfig, getProductPlanPair } from '@kit/billing';
+import {
+ BillingConfig,
+ getBaseLineItem,
+ getProductPlanPair,
+} from '@kit/billing';
import { formatCurrency } from '@kit/shared/utils';
import { Database } from '@kit/supabase/database';
import {
@@ -31,6 +35,7 @@ export function CurrentPlanCard({
config: BillingConfig;
}>) {
const { plan, product } = getProductPlanPair(config, subscription.variant_id);
+ const baseLineItem = getBaseLineItem(config, plan.id);
return (
@@ -62,7 +67,7 @@ export function CurrentPlanCard({
i18nKey="billing:planRenewal"
values={{
interval: subscription.interval,
- price: formatCurrency(product.currency, plan.price),
+ price: formatCurrency(product.currency, baseLineItem.price),
}}
/>
@@ -111,19 +116,6 @@ export function CurrentPlanCard({