diff --git a/app/pricing/page.tsx b/app/pricing/page.tsx
index d7ce1c2..6381381 100644
--- a/app/pricing/page.tsx
+++ b/app/pricing/page.tsx
@@ -1,9 +1,12 @@
+import { Suspense } from 'react'
import PricingSection from '@/components/PricingSection'
export default function PricingPage() {
return (
}>
+
+
)
}
diff --git a/app/settings/page.tsx b/app/settings/page.tsx
index 012f348..030ef5c 100644
--- a/app/settings/page.tsx
+++ b/app/settings/page.tsx
@@ -1,4 +1,6 @@
+import { Suspense } from 'react'
import ProfileSettings from '@/components/settings/ProfileSettings'
+import CheckoutSuccessToast from '@/components/checkout/CheckoutSuccessToast'
export const metadata = {
title: 'Settings - Pulse',
@@ -8,6 +10,9 @@ export const metadata = {
export default function SettingsPage() {
return (
)
diff --git a/components/PricingSection.tsx b/components/PricingSection.tsx
index 0d3b003..88c5ac5 100644
--- a/components/PricingSection.tsx
+++ b/components/PricingSection.tsx
@@ -1,11 +1,12 @@
'use client'
import { useState, useEffect } from 'react'
+import { useSearchParams } from 'next/navigation'
import { Button, CheckCircleIcon } from '@ciphera-net/ui'
import { useAuth } from '@/lib/auth/context'
import { initiateOAuthFlow } from '@/lib/api/oauth'
import { toast } from 'sonner'
-import { getClient } from '@/lib/api/client'
+import { createCheckoutSession } from '@/lib/api/billing'
// 1. Define Plans with IDs and Site Limits
const PLANS = [
@@ -100,11 +101,22 @@ const TRAFFIC_TIERS = [
]
export default function PricingSection() {
+ const searchParams = useSearchParams()
const [isYearly, setIsYearly] = useState(false)
const [sliderIndex, setSliderIndex] = useState(2) // Default to 100k (index 2)
const [loadingPlan, setLoadingPlan] = useState(null)
const { user } = useAuth()
-
+
+ // * Show toast when redirected from Stripe Checkout with canceled=true
+ useEffect(() => {
+ if (searchParams.get('canceled') === 'true') {
+ toast.info('Checkout was canceled. You can try again whenever you’re ready.')
+ const url = new URL(window.location.href)
+ url.searchParams.delete('canceled')
+ window.history.replaceState({}, '', url.pathname + url.search)
+ }
+ }, [searchParams])
+
// * Check for pending checkout on mount/auth
useEffect(() => {
if (!user) return
@@ -174,19 +186,18 @@ export default function PricingSection() {
}
// 2. Call backend to create checkout session
- const client = getClient()
const interval = options?.interval || (isYearly ? 'year' : 'month')
const limit = options?.limit || currentTraffic.value
- const res = await client.post('/api/billing/checkout', {
+ const { url } = await createCheckoutSession({
plan_id: planId,
- interval: interval,
- limit: limit
+ interval,
+ limit,
})
// 3. Redirect to Stripe Checkout
- if (res.data.url) {
- window.location.href = res.data.url
+ if (url) {
+ window.location.href = url
} else {
throw new Error('No checkout URL returned')
}
@@ -317,14 +328,14 @@ export default function PricingSection() {
diff --git a/components/checkout/CheckoutSuccessToast.tsx b/components/checkout/CheckoutSuccessToast.tsx
new file mode 100644
index 0000000..3c13698
--- /dev/null
+++ b/components/checkout/CheckoutSuccessToast.tsx
@@ -0,0 +1,26 @@
+'use client'
+
+import { useEffect } from 'react'
+import { useSearchParams } from 'next/navigation'
+import { toast } from 'sonner'
+
+/**
+ * Shows a success toast when redirected from Stripe Checkout with success=true,
+ * then clears the query params from the URL.
+ */
+export default function CheckoutSuccessToast() {
+ const searchParams = useSearchParams()
+
+ useEffect(() => {
+ const success = searchParams.get('success')
+ if (success === 'true') {
+ toast.success('Thank you for subscribing! Your subscription is now active.')
+ const url = new URL(window.location.href)
+ url.searchParams.delete('success')
+ url.searchParams.delete('session_id')
+ window.history.replaceState({}, '', url.pathname + url.search)
+ }
+ }, [searchParams])
+
+ return null
+}
diff --git a/lib/api/billing.ts b/lib/api/billing.ts
index b7da62d..179591b 100644
--- a/lib/api/billing.ts
+++ b/lib/api/billing.ts
@@ -45,3 +45,16 @@ export async function createPortalSession(): Promise<{ url: string }> {
method: 'POST',
})
}
+
+export interface CreateCheckoutParams {
+ plan_id: string
+ interval: string
+ limit: number
+}
+
+export async function createCheckoutSession(params: CreateCheckoutParams): Promise<{ url: string }> {
+ return await billingFetch<{ url: string }>('/api/billing/checkout', {
+ method: 'POST',
+ body: JSON.stringify(params),
+ })
+}