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. Detecting the payment provider (Stripe, Chargebee, Recurly)
  3. Extracting the publishable key from gatewayData
  4. Initializing FloPay with the correct key
  5. Rendering a SplitCardForm with card fields, Apple Pay, Google Pay, and PayPal
  6. Injecting session data (email, userId, amount, currency) automatically

Props

PropTypeDefaultDescription
sessionIdstringCheckout session UUID from the billing API. Required unless createSession is provided.
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.
showPayPalbooleantrueWhether to show the PayPal button.
showApplePaybooleantrueWhether to show Apple Pay (only renders on supported devices).
showGooglePaybooleantrueWhether to show Google Pay (only renders on supported devices).
layout'default' | 'buttons''default'Layout mode. 'buttons' shows PayPal, wallets, and a "Credit / Debit Card" button that expands into the card form.
buttonsThemeButtonsLayoutTheme'default'Theme preset for buttons layout: 'default', 'minimal', 'rounded', or 'dark'.
buttonsStylesButtonsLayoutStylesCustom style overrides for the buttons layout. Merged on top of the theme preset.
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.
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, Apple Pay, Google Pay, 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}
/>

Buttons Layout Themes

Four built-in theme presets control the visual style of the buttons layout:

// Minimal — borderless, subtle backgrounds
<FloPayCheckout
  sessionId={sessionId}
  layout="buttons"
  buttonsTheme="minimal"
  onComplete={handleSuccess}
/>
 
// Rounded — large border radius, soft shadows
<FloPayCheckout
  sessionId={sessionId}
  layout="buttons"
  buttonsTheme="rounded"
  onComplete={handleSuccess}
/>
 
// Dark — dark backgrounds, light text
<FloPayCheckout
  sessionId={sessionId}
  layout="buttons"
  buttonsTheme="dark"
  onComplete={handleSuccess}
/>

Custom Buttons Styles

Use buttonsStyles to override individual parts of the buttons layout. Styles are merged on top of the selected theme preset:

<FloPayCheckout
  sessionId={sessionId}
  layout="buttons"
  buttonsTheme="default"
  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 Presets

You can also import and use theme presets directly:

import {
  BUTTONS_LAYOUT_DEFAULT,
  BUTTONS_LAYOUT_MINIMAL,
  BUTTONS_LAYOUT_ROUNDED,
  BUTTONS_LAYOUT_DARK,
  resolveButtonsLayoutTheme,
} from '@flopay/shared';
 
// Resolve by name
const styles = resolveButtonsLayoutTheme('rounded');
 
// Or merge a preset with custom overrides
const customStyles = {
  ...BUTTONS_LAYOUT_ROUNDED,
  cardButton: {
    ...BUTTONS_LAYOUT_ROUNDED.cardButton,
    backgroundColor: '#f0f0ff',
  },
};

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',
    items: [{
      providerItemId: 'omni-ai-booster',
      providerItemName: 'AI Supercharger Pack',
      totalAmount: 8000,
      overrideAmount: 3900,
      currency: 'EUR',
    }],
    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).

InlineSessionDraft

FieldTypeRequiredDescription
clientIdstringYesClient identifier
itemsCheckoutItem[]NoOne-time purchase items
subscriptionsCheckoutSubscription[]NoRecurring plans
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',
    items: [{ providerItemId: 'product-1', totalAmount: 29.99, currency: 'EUR' }],
    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}
/>

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.