FloPayFloPay
API Reference@flopay/react

FloPayCheckout

All-in-one checkout component that handles session fetching, provider initialization, and payment form rendering automatically.

FloPayCheckout

Recommended. FloPayCheckout is the simplest way to add payments to your app. It replaces the manual FloPayProvider + loadFloPay + PaymentAPI setup with a single component.

Basic Usage

import { FloPayCheckout } from '@flopay/react';

function CheckoutPage({ sessionId }: { sessionId: string }) {
  return (
    <FloPayCheckout
      sessionId={sessionId}
      onComplete={(result) => {
        window.location.href = '/success';
      }}
      onError={(err) => {
        console.error(err.type, err.message);
      }}
    />
  );
}

That's it. The component handles:

  1. Fetching the checkout session from the billing API
  2. Reading the gateways map to discover which gateways the client has provisioned
  3. Extracting the Stripe publishable key from gateways.stripe for the Stripe-driven payment methods
  4. Choosing the PayPal path from gateways.paypal: direct PayPal when present (see the Direct PayPal guide), otherwise the Stripe-rendered PayPal via Stripe fallback
  5. Initializing FloPay with the correct per-gateway publishable key and environment
  6. Rendering a SplitCardForm with card fields, the Stripe-enabled payment methods configured on the connected Stripe account (Apple Pay, Google Pay, and Stripe-enabled local methods such as Cash App, Klarna, Afterpay), and PayPal
  7. Injecting session data (email, userId, amount, currency) automatically

Props

PropTypeDefaultDescription
sessionIdstringCheckout session UUID from the billing API. Required unless createSession is provided.
noncestringSession-bound checkout token (the nonce returned when sessionId was created). Pass it with a backend-created sessionId so the SDK can authenticate continuation calls — without it post-#640 backends reject the session read and /process with 401 "Missing checkout session token.". Not needed with createSession, where the SDK mints and forwards the nonce itself. See Checkout Session Token.
createSessionInlineSessionDraftCreate a session inline — no separate API route needed. Alternative to sessionId. For embedded checkout, pass createSession.account.email up front.
billingApiUrlstringAuto-resolvedOverride the billing API URL. By default resolved from configureFlopay(), with staging as the final fallback. See Configuration.
appearanceFloPayAppearanceVisual theme for payment elements.
localestring'auto'Locale for payment elements.
onComplete(result: PaymentResult) => voidCalled when payment succeeds.
onError(error: FloPayError) => voidCalled when payment fails.
onDecline(decline: DeclineEvent) => voidCalled when a payment is declined, authentication fails, PayPal is cancelled, or a wallet sheet is dismissed.
onFullNameChange(value: string) => voidCalled when the cardholder name input changes.
onCountryChange(country: string) => voidCalled when the AVS country dropdown changes.
onZipChange(zip: string) => voidCalled when the AVS ZIP/postcode input changes.
showStripebooleantrueWhether to render the Stripe gateway. When false, the entire Stripe panel is hidden — card fields, Apple Pay, Google Pay, and every other Stripe-enabled payment method. When true (default), each payment method enabled on the connected Stripe account renders dynamically. See Stripe payment methods.
showPayPalbooleantrueWhether to render the PayPal gateway. When false, both the direct PayPal and PayPal via Stripe paths are hidden.
layout'default' | 'buttons''default'Layout mode. 'buttons' shows PayPal, wallets, and a "Credit / Debit Card" button that expands into the card form.
themeThemeIdSingle-prop theming. Styles both the Stripe-side appearance and the React-rendered wrapper, submit, and inputs from one value. See Theme Bundles.
buttonsStylesButtonsLayoutStylesPer-field style overrides merged on top of the resolved theme bundle.
buttonsThemeButtonsLayoutThemeDeprecated. Legacy preset ('default' | 'minimal' | 'rounded' | 'dark') still resolved via resolveButtonsLayoutTheme() for back-compat. Prefer theme. Silently ignored when theme is also supplied.
cardButtonContentReactNodeReplaces the default content inside the card button when layout="buttons".
cardBackButtonContentReactNodeReplaces the default "Go back" label in the expanded card form when layout="buttons". Pass '' to remove the text and keep only the icon.
cardTitleContentReactNodeReplaces the default "Secure card checkout" title. Pass '' to remove the title text entirely.
onButtonClick(method: CheckoutButtonMethod) => voidCalled when a payment method button is clicked. method: 'card', 'paypal', 'apple_pay', or 'google_pay'.
onBeforeButtonClick(event: BeforeButtonClickEvent) => void | false | InlineSessionPatch | Promise<...>Card only. Runs before the "Credit / Debit Card" button continues in layout="buttons". Returning false cancels the click. Returning a patch refreshes the card flow with merged createSession data.
enableAVSboolean | AVSFieldConfigfalseEnable Address Verification. true → country dropdown + ZIP/postcode (legacy default). Pass an AVSFieldConfig object for per-field, per-country control of country, postal code, street address, city, and state. See AVS guide.
avsLayout'row' | 'column''row'Layout for the country / postal code pair: 'row' (side-by-side) or 'column' (stacked). Address line, city, and state always render on their own rows.
submitLabelstring'CONFIRM PAYMENT'Custom submit button text.
loadingReactNodeSpinnerCustom loading UI while session loads.
error(err: FloPayError) => ReactNodeError messageCustom error UI if session fails to load.
debugbooleanfalseRender extra on-screen diagnostic panels (gateway/PayPal lifecycle, available payment methods). Intended for local development. See Debug mode.
classNamestringCSS class for the form wrapper.
childrenReactNodeSplitCardFormOverride the default form (see below).
checkoutModeCheckoutModeSession value or 'full'Checkout mode: 'full', 'auto', or 'confirm'.
confirmLabelstring'Confirm Purchase'Label for the confirm button in confirm mode.
renderConfirmButton(props) => ReactNodeCustom confirm button renderer.
onSessionCompleted(successUrl: string) => voidCalled when session is already completed.

Checkout Modes

Full Mode (default)

Standard checkout — shows card fields, the Stripe-enabled payment methods configured on the connected Stripe account (Apple Pay, Google Pay, Cash App, Klarna, etc.), and PayPal. User enters payment details and submits.

Confirm Mode

Shows a "Confirm Purchase" button instead of the payment form. Uses a saved payment method on the backend (vault). Falls back to full mode if payment fails.

<FloPayCheckout
  sessionId={sessionId}
  checkoutMode="confirm"
  confirmLabel="Complete Purchase"
  onComplete={(result) => router.push('/success')}
/>

You can customize the confirm button:

<FloPayCheckout
  sessionId={sessionId}
  checkoutMode="confirm"
  renderConfirmButton={({ onConfirm, isProcessing }) => (
    <button onClick={onConfirm} disabled={isProcessing}>
      {isProcessing ? 'Working...' : 'Buy Now'}
    </button>
  )}
  onComplete={handleSuccess}
/>

Auto Mode

Automatically submits with a saved payment method after the session loads. No user interaction required. Falls back to full mode if auto-checkout fails.

<FloPayCheckout
  sessionId={sessionId}
  checkoutMode="auto"
  onComplete={(result) => router.push('/success')}
  onSessionCompleted={(successUrl) => router.push(successUrl)}
/>

The onSessionCompleted callback fires when the session is already completed (e.g., from a previous auto-checkout). Use it to redirect the user.

Layout Modes

Default Layout

Shows all payment methods and the card form together — PayPal, Apple Pay, Google Pay above a divider, then the card fields below.

Buttons Layout

Shows payment methods as stacked buttons: PayPal, wallets (Apple/Google Pay), and a "Credit / Debit Card" button. Clicking the card button expands into the card form with a "Go back" button and title. Both text regions can be replaced via slot props.

onBeforeButtonClick is credit card only. It runs only for the "Credit / Debit Card" button in layout="buttons". It does not run for PayPal, Apple Pay, or Google Pay.

<FloPayCheckout
  sessionId={sessionId}
  layout="buttons"
  onComplete={handleSuccess}
/>

Theme Bundles

Pass a ThemeId to the theme prop. The value styles the Stripe-rendered fields and the React-rendered wrapper, submit button, and inputs from one source.

theme valueAesthetic
'classic'No-op marker — preserves the historic FloPay look (#EDEDFF wrapper, #4A49FF indigo submit)
'modern-light' / 'modern-dark'Inter, soft shadows, generous spacing, FloPay-blue accents
'bold-light' / 'bold-dark'Saturated FloPay blue with gradient pill submit and heavy borders
'glass-light' / 'glass-dark'Translucent surfaces with backdrop blur over a blue gradient
<FloPayCheckout
  sessionId={sessionId}
  layout="buttons"
  theme="modern-light"
  onComplete={handleSuccess}
/>

<FloPayCheckout
  sessionId={sessionId}
  layout="buttons"
  theme="bold-dark"
  onComplete={handleSuccess}
/>

<FloPayCheckout
  sessionId={sessionId}
  layout="buttons"
  theme="glass-dark"
  onComplete={handleSuccess}
/>

Resolution Precedence

When multiple styling props are present, the SDK resolves them from highest priority to lowest:

  1. Explicit appearance — overrides the resolved bundle's appearance (palette + Stripe rules).
  2. Explicit buttonsStyles — merges per-field on top of the resolved bundle's button-layout styles.
  3. theme bundle.
  4. Legacy buttonsTheme preset (back-compat).
  5. Hardcoded defaults.

Custom Buttons Styles

Use buttonsStyles to override individual fields on top of the resolved theme bundle:

<FloPayCheckout
  sessionId={sessionId}
  layout="buttons"
  theme="modern-light"
  buttonsStyles={{
    cardButton: {
      borderRadius: '12px',
      border: '2px solid #4A49FF',
      fontWeight: 700,
    },
    cardFormContainer: {
      backgroundColor: '#f8f7ff',
      borderRadius: '12px',
    },
    cardInputBorder: '#c4c3ff',
    submitButton: {
      backgroundColor: '#2d2ccc',
      borderRadius: '12px',
    },
    backButton: {
      color: '#4A49FF',
    },
    backButtonIcon: {
      backgroundColor: '#ededff',
    },
    title: {
      color: '#2d2ccc',
      fontSize: '1.2rem',
    },
  }}
  onComplete={handleSuccess}
/>

Custom Card Button Content

Use cardButtonContent when you want to replace the default "Credit / Debit Card" button body with your own React content while keeping the Flo button behavior and outer styling.

<FloPayCheckout
  sessionId={sessionId}
  layout="buttons"
  cardButtonContent={
    <div
      style={{
        display: 'flex',
        alignItems: 'center',
        gap: '0.75rem',
        width: '100%',
      }}
    >
      <span style={{ fontWeight: 700 }}>Pay by card</span>
      <span style={{ fontSize: '0.75rem', opacity: 0.7 }}>
        Visa, Mastercard, Amex
      </span>
      <span style={{ marginLeft: 'auto', fontSize: '0.7rem' }}>Secure</span>
    </div>
  }
  onComplete={handleSuccess}
/>

Use cardButtonContent for the inner React content and buttonsStyles.cardButton for the outer button container styles.

Buttons Layout Header Slots

Use cardBackButtonContent and cardTitleContent when you want to replace the default text in the expanded card form header. These props accept any ReactNode, and passing '' removes the text completely.

<FloPayCheckout
  sessionId={sessionId}
  layout="buttons"
  cardBackButtonContent=""
  cardTitleContent="Enter card details"
  onComplete={handleSuccess}
/>

cardTitleContent also applies to the standalone title in the default card form layout.

ButtonsLayoutStyles Reference

Layout & Container Styles:

PropertyTypeDescription
cardButtonRecord<string, string | number>Style for the "Credit / Debit Card" button
cardButtonFontSizestringFont size for the card button label (default: '0.95rem')
cardFormContainerRecord<string, string | number>Style for the expanded card form wrapper
backButtonRecord<string, string | number>Style for the "Go back" button text
backButtonFontSizestringFont size for the "Go back" label (default: '0.85rem')
backButtonIconRecord<string, string | number>Style for the circular back button icon
submitButtonRecord<string, string | number>Style for the submit/confirm button
submitButtonFontSizestringFont size for the submit button label (default: '1rem')
titleRecord<string, string | number>Style for the "Secure card checkout" title
titleFontSizestringFont size for the title (default: '1.05rem')
errorBannerRecord<string, string | number>Style for the error message banner

Card Input Styles:

These control the Stripe Elements appearance (rendered inside iframes):

PropertyTypeDescription
cardInputBorderstringBorder color for card number, expiry, CVC, and name fields
cardInputColorstringText color inside Stripe card input fields
cardInputPlaceholderColorstringPlaceholder text color for Stripe card input fields
cardInputFontSizestringFont size for Stripe card input fields
cardInputBackgroundstringBackground color for card input field containers
nameInputRecord<string, string | number>Style for the "Full Name on Card" text input

AVS Field Styles:

PropertyTypeDescription
countrySelectRecord<string, string | number>Style for the country select dropdown container
zipInputRecord<string, string | number>Style for the ZIP/postcode input container
addressLine1InputRecord<string, string | number>Style for the street address input (address_line_1)
addressLine2InputRecord<string, string | number>Style for the apt/suite input (address_line_2)
cityInputRecord<string, string | number>Style for the city input
stateInputRecord<string, string | number>Style for the state/province input or dropdown

The cardInputColor, cardInputPlaceholderColor, and cardInputFontSize properties are passed to Stripe Elements via their style option, which controls text appearance inside the cross-origin iframe. This is how the dark theme gets light text on dark backgrounds. AVS fields (countrySelect, zipInput) inherit from the card input styles by default — override them individually for custom theming.

Importing Theme Bundles

You can also import the bundle map or individual constants directly when you need to merge two themes or render a preview swatch outside the SDK:

import {
  THEMES,
  resolveTheme,
  MODERN_LIGHT_APPEARANCE,
  BUTTONS_LAYOUT_MODERN_LIGHT,
} from '@flopay/shared';

// Resolve by id
const bundle = resolveTheme('modern-light');

// Or merge a bundle's buttonsLayout with custom overrides
const customLayout = {
  ...BUTTONS_LAYOUT_MODERN_LIGHT,
  cardButton: {
    ...BUTTONS_LAYOUT_MODERN_LIGHT.cardButton,
    backgroundColor: '#f0f0ff',
  },
};

See Constants → Themes for every exported constant.

The legacy BUTTONS_LAYOUT_DEFAULT / _MINIMAL / _ROUNDED / _DARK presets and resolveButtonsLayoutTheme() continue to work for back-compat. Prefer the new THEMES map and resolveTheme in new code.

Inline Session Creation

Instead of creating a session via a backend API route and passing the sessionId, you can create the session directly from the component — zero backend code needed:

<FloPayCheckout
  layout="buttons"
  createSession={{
    clientId: '18bff186-284c-483f-acee-e712f21d2b8d',
    currency: 'EUR',
    products: [{
      code: 'omni-ai-booster',
      name: 'AI Supercharger Pack',
      totalAmount: 8000,
    }],
    account: {
      userId: 'user_123',
      email: 'customer@example.com',
      firstName: 'John',
      lastName: 'Doe',
    },
    successUrl: '/success',
    cancelUrl: '/cancel',
  }}
  onComplete={(result) => window.location.href = '/success'}
  onError={(err) => console.error(err.message)}
/>

The component POSTs to the billing API, receives the full session data, initializes Stripe, and renders the payment form — all in one step.

Backend requirement: The billing API must support ?expand=true on POST /v1/checkouts/sessions to return full session data. If not supported, the component falls back to the two-step flow (create + GET).

The create response also carries a nonce that authenticates every subsequent /v1/checkouts/* call as the x-checkout-session-token header. When FloPayCheckout creates the session (createSession) it handles this automatically; when you pass a backend-created sessionId, supply the matching nonce prop so the SDK can forward it. Direct HTTP integrators should read the Checkout session token guide.

InlineSessionDraft

FieldTypeRequiredDescription
clientIdstringYesClient identifier
currencystringRequiredSession-level ISO 4217 currency code. The SDK throws FloPayError({ type: 'validation_error', code: 'CurrencyRequired' }) before any network call when nothing can be resolved from this field or per-line currency.
productsCheckoutProduct[]NoUnified products array. Each entry is a catalog entry identified by code and is sent to the backend verbatim.
accountCheckoutAccount with optional emailYesBuyer info. For embedded checkout, send account.email up front because the session is created with accountData.email. If needed, seed a temporary email and replace it in onBeforeButtonClick before the card flow continues.
successUrlstringYesRedirect URL after success
cancelUrlstringYesRedirect URL on cancel
checkoutMode'full' | 'auto' | 'confirm'NoDefault: 'full'
couponCodesstring[]NoDiscount codes
tagsDataTagsDataNoAnalytics tags
utmMetadataRecord<string, string | null | undefined>[]NoUTM / funnel metadata

Button Click Events

Track when users interact with payment methods using onButtonClick:

<FloPayCheckout
  sessionId={sessionId}
  layout="buttons"
  onButtonClick={(method) => {
    // method: 'card' | 'paypal' | 'apple_pay' | 'google_pay'
    window.dataLayer?.push({
      event: 'initiate_checkout',
      payment_method: method,
    });
  }}
  onComplete={handleSuccess}
/>

The event fires at these moments:

Actionmethod value
"Credit / Debit Card" button clicked (buttons layout)'card'
Card form submitted (default layout)'card'
PayPal button clicked'paypal'
Apple Pay button clicked'apple_pay'
Google Pay button clicked'google_pay'

For PayPal and wallets, the event fires when the buyer clicks the button to initiate the payment — before the PayPal redirect or the wallet authorization sheet. If onBeforeButtonClick returns false for that method, onButtonClick is not fired (the click is cancelled). Use onComplete to track successful payment confirmation.

onBeforeButtonClick

Use onBeforeButtonClick when you need to do async work before the credit card button continues, such as confirming checkout details or replacing a temporary email address:

<FloPayCheckout
  layout="buttons"
  createSession={{
    clientId: 'your-client-id',
    currency: 'EUR',
    products: [{ code: 'product-1', totalAmount: 29.99 }],
    account: { userId: 'user_1', email: 'test@email.com' },
    successUrl: '/success',
    cancelUrl: '/cancel',
  }}
  onBeforeButtonClick={async ({ method, createSession }) => {
    if (method !== 'card') return;

    const email = await openEmailCaptureModal({
      initialEmail: createSession?.account.email ?? '',
    });
    if (!email) return false;

    return {
      account: { email },
      tagsData: { sessionId: 'email_captured' },
    };
  }}
  onComplete={handleSuccess}
/>

onBeforeButtonClick only runs for the buttons-layout credit card button. It does not run for PayPal, Apple Pay, Google Pay, or default-layout card submission. Returning false cancels the click. Throwing routes the error to onError. Returned patches may include account, couponCodes, tagsData, and utmMetadata. FloPayCheckout still bootstraps PayPal and wallet buttons normally when this hook is present; only the card path is enriched by the returned patch. createSession.account.email should already be present when the embedded checkout renders; use this hook to update it, not to omit it from the initial session payload.

onDecline

Use onDecline when you need structured failure/cancellation events:

<FloPayCheckout
  sessionId={sessionId}
  onDecline={(decline) => {
    // { method, message, code?, declineCode? }
    window.dataLayer?.push({
      event: 'checkout_decline',
      ...decline,
    });
  }}
  onComplete={handleSuccess}
/>

onDecline fires for provider declines, failed 3DS, PayPal cancellations, and wallet dismissals.

Custom Loading State

<FloPayCheckout
  sessionId={sessionId}
  loading={<MySkeletonLoader />}
  onComplete={handleSuccess}
/>

Custom Error State

<FloPayCheckout
  sessionId={sessionId}
  error={(err) => (
    <div className="error-banner">
      <p>Failed to load checkout: {err.message}</p>
      <button onClick={() => window.location.reload()}>Retry</button>
    </div>
  )}
  onComplete={handleSuccess}
/>

Debug mode

Pass debug to render extra on-screen diagnostic panels while developing locally. When debug is omitted or false, all diagnostic state is no-op'd and nothing extra is rendered, so it's safe to leave the prop wired up in shared code.

<FloPayCheckout sessionId={sessionId} debug onComplete={handleSuccess} />

With debug enabled, the component currently surfaces:

  • Which payment methods resolved as available for the session (card, PayPal, Apple Pay, Google Pay) and which gateway each is routed through.
  • The Direct PayPal button lifecycle (script load, button render, order creation, approval, capture, failure) — including the failure panel that would otherwise return null in production.

debug is intended for local development only. Do not enable it in production builds — the diagnostic panels are not styled for end users and expose internal state. We will expand the information shown by debug in later SDK versions; the rendered output is not a stable contract.

Custom Form (Children Override)

By default, FloPayCheckout renders a SplitCardForm. To use a different form component, pass it as children:

import { FloPayCheckout, CheckoutForm } from '@flopay/react';

<FloPayCheckout sessionId={sessionId}>
  <CheckoutForm
    onComplete={handleSuccess}
    layout="accordion"
    showAddress="billing"
  />
</FloPayCheckout>

When you provide children, FloPayCheckout auto-injects sessionId, billingApiUrl, email, userId, firstName, and lastName from the session data. You don't need to pass them manually. Explicit props on the child take precedence.

Comparison with Manual Setup

import { FloPayCheckout } from '@flopay/react';

<FloPayCheckout sessionId={sessionId} onComplete={handleSuccess} />

Manual Setup (advanced)

import { loadFloPay, PaymentAPI } from '@flopay/js';
import { FloPayProvider, SplitCardForm } from '@flopay/react';

const api = new PaymentAPI('https://api.stage.flopay.com');
const session = await api.getUnifiedCheckoutSession(sessionId);
const flopay = await loadFloPay(session.data.stripe?.publishableKey);

<FloPayProvider
  flopay={flopay}
  options={{ paymentMethodCreation: 'manual', amount: 4999, currency: 'usd' }}
>
  <SplitCardForm
    sessionId={sessionId}
    email="user@example.com"
    userId="user_123"
    onComplete={handleSuccess}
  />
</FloPayProvider>

Use the manual setup when you need full control over provider initialization, element options, or want to use the FloPay SDK outside of React.

On this page