Checkout & payments

One-click checkout with @hyperyai/sdk — BuyButton and useCheckout run login → charge → add-card → retry as a popup or redirect, for marketplace purchases and AI-credit top-ups.

@hyperyai/sdk ships a seamless auth + charge flow: a single action drives the whole chain — press buy → log in (if needed) → charge → add a card (if needed) → retry → done. An unauthenticated visitor can click Buy and be walked through sign-in, card entry, and payment without you writing any of that glue.

It comes in two forms:

  • <BuyButton> — a drop-in "buy with Hypery" button for one-click marketplace purchases.
  • useCheckout() — the hook underneath, for programmatic checkout (purchases and AI-credit top-ups) on your own UI.
press buy
user action
log in
if signed out
charge
card on file
add card
if no card (402)
retry → done
success

Each interactive step (login, card entry) runs as a popup or a redirect, chosen by interactionMode — so the flow completes in-page on desktop and stays robust on mobile.

Setup

Checkout reads everything it needs from HyperyProvider — no extra provider. The only new option is interactionMode:

// app/layout.tsx
import { HyperyProvider } from "@hyperyai/sdk";
 
export default function RootLayout({ children }) {
  return (
    <HyperyProvider
      config={{
        clientId: process.env.NEXT_PUBLIC_HYPERY_CLIENT_ID!,
        redirectUri: "https://yourapp.com/callback",
        gatewayUrl: "https://hypery.ai",
        interactionMode: "auto", // 'auto' | 'popup' | 'redirect'
      }}
    >
      {children}
    </HyperyProvider>
  );
}

interactionMode defaults to auto: popup on desktop, redirect on mobile or when a popup is blocked. For popup mode, redirectUri must be same-origin as your app (the popup posts the result back to the opener).

BuyButton

Drop-in checkout for a marketplace purchase. On click it runs the full flow via useCheckout and credits the sale to the seller's app (a Stripe Connect direct charge; your platform fee is applied automatically).

import { BuyButton } from "@hyperyai/sdk";
 
<BuyButton
  appId="app_123"
  amountCents={499}
  description="Pro upgrade"
  onSuccess={(r) => console.log("paid", r.paymentIntentId)}
  onError={(e) => toast.error(e.message)}
/>;

By default the button is two-click — the first click arms it (Confirm · $4.99 + Cancel), the second charges — because the card is charged off-session and the buyer should give a clear "yes". Set requireConfirmation={false} to charge on a single click.

PropTypeDescription
appIdstringSeller app the purchase is credited to. Required.
amountCentsnumberAmount to charge, in cents. Required.
descriptionstringHuman-readable purchase description.
labelReactNodeButton label. Defaults to Buy · $X.XX.
requireConfirmationbooleanTwo-click confirm (default true) vs one-click.
onSuccess(r: BuySuccess) => voidFires on a completed charge.
onError(e: ParsedError) => voidFires on failures other than needs-a-card.
brandingBrandingConfigprimaryColor is used as the button color.

onSuccess receives { ok, paymentIntentId, status, amountCents, applicationFeeCents }. The button never throws — a redirecting (navigating away) or cancelled (user closed a popup) result stays quiet, so no callback fires.

useCheckout

The hook behind BuyButton. Use it directly when you want checkout on your own button, or for AI-credit top-ups (which BuyButton doesn't cover).

import { useCheckout } from "@hyperyai/sdk";
 
function AddCredits() {
  const { checkout, status, isRunning } = useCheckout();
 
  const buy = async () => {
    const result = await checkout({ kind: "topup", usdAmount: 5 });
    if (result.status === "success") {
      // balance is funded
    }
  };
 
  return (
    <button onClick={buy} disabled={isRunning}>
      {status === "charging" ? "charging…" : "add $5 of credits"}
    </button>
  );
}

useCheckout() returns:

FieldTypeDescription
checkout(input) => Promise<CheckoutResult>Runs the flow; resolves when it finishes (or is redirecting away).
statusCheckoutStatusidle · authenticating · charging · adding-card · redirecting · success · error · cancelled.
isRunningbooleanTrue while a checkout is in flight — bind to your button's disabled.
errorParsedError | nullThe last classified error, if any.

checkout(input) takes one of two shapes:

// marketplace purchase (Stripe Connect)
checkout({ kind: "purchase", appId: "app_123", amountCents: 499, description: "Pro" });
 
// buy AI credits for the signed-in user's balance
checkout({ kind: "topup", usdAmount: 5 });

Purchases are idempotent: a stable idempotencyKey is generated (or pass your own) so a resumed or retried charge is never double-billed.

interactionMode controls how the login and add-card steps appear.

Each step opens a centered popup and the chain completes in-page — no navigation (like PayPal / Firebase signInWithPopup). Best on desktop. Requires a same-origin redirectUri.

In redirect mode the flow can navigate away mid-checkout and resume on return — so treat checkout() returning redirecting as "in progress", not "done". Resume runs automatically once per page load.

Handling the result

checkout() resolves to a CheckoutResult:

const result = await checkout({ kind: "topup", usdAmount: 5 });
 
switch (result.status) {
  case "success":     /* result.data has the charge */ break;
  case "error":       /* result.error is a ParsedError */ break;
  case "cancelled":   /* user closed a popup — no-op */ break;
  case "redirecting": /* navigating away; resumes on return */ break;
}

Errors are already classified (ParsedError) — the same shape RestrictionModal consumes — so a payment decline or a spending-limit hit can drive a modal without extra parsing.

Next steps