fix stripe configuration
This commit is contained in:
89
web/src/routes/billing/checkout.tsx
Normal file
89
web/src/routes/billing/checkout.tsx
Normal file
@@ -0,0 +1,89 @@
|
||||
import { createSignal, onMount, Show } from "solid-js";
|
||||
import { Title } from "@solidjs/meta";
|
||||
import { useNavigate, useSearchParams } from "@solidjs/router";
|
||||
import EmbeddedCheckout from "~/components/EmbeddedCheckout";
|
||||
import { api } from "~/lib/api";
|
||||
import PageContainer from "~/components/layout/PageContainer";
|
||||
|
||||
const priceMap: Record<string, string> = {
|
||||
basic: process.env.STRIPE_PRICE_BASIC ?? "",
|
||||
plus: process.env.STRIPE_PRICE_PLUS ?? "",
|
||||
premium: process.env.STRIPE_PRICE_PREMIUM ?? "",
|
||||
};
|
||||
|
||||
export default function CheckoutPage() {
|
||||
const navigate = useNavigate();
|
||||
const [searchParams] = useSearchParams();
|
||||
const [clientSecret, setClientSecret] = createSignal("");
|
||||
const [error, setError] = createSignal<string | null>(null);
|
||||
const [loading, setLoading] = createSignal(true);
|
||||
|
||||
onMount(async () => {
|
||||
const plan = Array.isArray(searchParams.plan) ? searchParams.plan[0] : searchParams.plan;
|
||||
const priceIdParam = Array.isArray(searchParams.priceId) ? searchParams.priceId[0] : searchParams.priceId;
|
||||
const priceId = priceIdParam ?? (plan ? priceMap[plan] : "");
|
||||
|
||||
if (!priceId) {
|
||||
setError("No plan selected. Please select a plan to continue.");
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const returnUrl = `${window.location.origin}/billing/return`;
|
||||
const result = await api.billing.createCheckoutSession.mutate({
|
||||
priceId,
|
||||
returnUrl,
|
||||
});
|
||||
setClientSecret(result.clientSecret);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : "Failed to create checkout session");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<main class="min-h-screen py-8 md:py-12">
|
||||
<Title>Checkout — Kordant</Title>
|
||||
<PageContainer>
|
||||
<div class="max-w-2xl mx-auto">
|
||||
<div class="mb-8">
|
||||
<h1 class="text-2xl font-bold text-[var(--color-text-primary)]">Complete your purchase</h1>
|
||||
<p class="text-sm text-[var(--color-text-secondary)] mt-1">
|
||||
Secure payment powered by Stripe
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<Show when={loading()}>
|
||||
<div class="flex items-center justify-center min-h-[300px]">
|
||||
<div class="text-center">
|
||||
<div class="animate-spin h-8 w-8 border-2 border-[var(--color-brand-primary)] border-t-transparent rounded-full mx-auto mb-3" />
|
||||
<p class="text-sm text-[var(--color-text-secondary)]">Preparing checkout...</p>
|
||||
</div>
|
||||
</div>
|
||||
</Show>
|
||||
|
||||
<Show when={error() && !loading()}>
|
||||
<div class="text-center py-8 border border-[var(--color-border)] rounded-xl">
|
||||
<p class="text-[var(--color-error)] mb-4">{error()}</p>
|
||||
<button
|
||||
onClick={() => navigate("/pricing")}
|
||||
class="text-sm text-[var(--color-brand-primary)] hover:underline"
|
||||
>
|
||||
← Back to pricing
|
||||
</button>
|
||||
</div>
|
||||
</Show>
|
||||
|
||||
<Show when={clientSecret() && !loading() && !error()}>
|
||||
<EmbeddedCheckout
|
||||
clientSecret={clientSecret()}
|
||||
onCheckoutComplete={() => navigate("/dashboard")}
|
||||
/>
|
||||
</Show>
|
||||
</div>
|
||||
</PageContainer>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user