Files
vantage/adminsite/components/CheckoutButton.tsx
T
mrhid6andClaude Opus 5 8bbecd2035 feat(adminsite): self-hosted purchase, checkout overlay, billing portal, client line-item builder
Purchase flow: name -> placeholder -> configure via the shipped PlanConfigurator
-> Paddle overlay with custom_data -> paste install UUID to link and issue.
lineItemsFor mirrors the Go catalogue.LineItems/billable exactly (base included
in exactly one place). ManageBillingButton opens the hosted portal. Paddle token
and env are baked into the build, never fetched.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 10:53:21 +01:00

41 lines
1.2 KiB
TypeScript

"use client";
import { useState } from "react";
import { initPaddle } from "@/lib/paddle";
/* Opens the Paddle overlay with the resolved line items and custom_data. The
* items come from the configurator via catalogue pricing; custom_data is what
* lets the webhook route without a lookup table. */
export function CheckoutButton({
items,
customData,
disabled,
label = "Continue to payment",
}: {
items: { priceId: string; quantity: number }[];
customData: { account_id: string; instance_id: string };
disabled?: boolean;
label?: string;
}) {
const [busy, setBusy] = useState(false);
async function open() {
setBusy(true);
const paddle = await initPaddle();
setBusy(false);
paddle?.Checkout.open({
items: items.map((i) => ({ priceId: i.priceId, quantity: i.quantity })),
customData,
});
}
return (
<button
type="button"
disabled={disabled || busy || items.length === 0}
onClick={open}
className="rounded border border-accent/50 px-3 py-1.5 text-[0.85rem] text-accent disabled:opacity-40"
>
{busy ? "Opening…" : label}
</button>
);
}