FloPayFloPay
API Reference@flopay/react

SplitCardForm

Split card checkout form with separate card number, expiry, CVC fields, name input, and optional PayPal button.

SplitCardForm

Split card checkout form that renders individual card fields (CardNumber, CardExpiry, CardCVC), a full name input, and an optional PayPal button. Matches the checkout/StripeCardForm layout.

const SplitCardForm: React.ForwardRefExoticComponent<
  SplitCardFormProps & React.RefAttributes<SplitCardFormRef>
>

Props

NameTypeRequiredDefaultDescription
sessionIdstringYes--Checkout session ID (UUID from billing API).
billingApiUrlstringYes--Billing API base URL.
emailstringNo--User's email (required for creating payment intents).
userIdstringNo--User ID (required for processing payments).
dataAttachmentPendingbooleanNofalseTrue while a session's background claim is still in flight. The hosted card widget is mounted and the buyer can fill it in, but nothing may be submitted yet. While true the card submit is gated and the non-card surfaces stay hidden; all of them enable together the moment the claim lands. FloPayCheckout plumbs this automatically — set it yourself only when you create the session and drive the claim. See Session Creation.
onComplete(result: PaymentResult) => voidNo--Called when payment completes successfully.
onError(error: FloPayError) => voidNo--Called when a payment error occurs.
onDecline(decline: DeclineEvent) => voidNo--Called when a payment is declined, authentication fails, PayPal is cancelled, or a wallet sheet is dismissed.
onTokenizedBody(tokenizedBody: TokenizedBody) => voidNo--Override: delegates backend submission to the caller.
firstNamestringNo--First name for billing.
lastNamestringNo--Last name for billing.
chvstringNo--Checkout version for A/B tracking.
submitLabelstringNo'CONFIRM PAYMENT'Label for the submit button.
classNamestringNo--Additional CSS class for the form wrapper.
childrenReact.ReactNodeNo--Custom children (overrides default submit button).
isProcessingbooleanNo--External processing state.
errorstring | nullNo--External error message.
onErrorChange(error: string | null) => voidNo--Called when internal error state changes.
onFullNameChange(value: string) => voidNo--Callback when the full cardholder name input changes.
onFirstNameChange(value: string) => voidNo--Callback when first name changes (parsed from full name input).
onLastNameChange(value: string) => voidNo--Callback when last name changes (parsed from full name input).
showStripebooleanNotrueRender 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 method enabled on the connected Stripe account renders dynamically. See Stripe payment methods.
showPayPalbooleanNotrueRender the PayPal gateway above card fields. When false, both the direct PayPal and PayPal via Stripe paths are hidden.
enabledPaymentMethodsstring[]NoSession listNarrows the wallets/APMs the session advertises via gateways.stripe.enabledPaymentMethods. Omit to use the backend's per-session list. Pass [] to render the hosted card form on its own — an empty array means "no methods enabled", not "fall back to the session list". Narrowing only: naming a method the backend has not enabled for the session will not turn it on. Prefer this over showStripe={false} for a card-only checkout, since showStripe also gates the card fields. See Stripe payment methods.
layout'default' | 'buttons'No'default'Layout mode. 'buttons' shows payment method buttons with an expandable card form.
themeThemeIdNo--Single-prop theming. Styles both the Stripe-side appearance and the React-rendered wrapper, submit, and inputs from one value. Available across FloPayCheckout, FloPayAutomaticPaymentButton, and SplitCardForm.
appearanceFloPayAppearanceNo--Stripe-side appearance overrides (palette + nested-element rules). Wins over a theme bundle's appearance. When supplied without buttonsStyles, the wrapper, submit, title, input, and font styling derive from appearance.variables.
buttonsStylesButtonsLayoutStylesNo--Per-field style overrides merged on top of the resolved theme bundle. Painted regardless of layout="default" or layout="buttons".
buttonsThemeButtonsLayoutThemeNo--Deprecated. Legacy preset ('default' | 'minimal' | 'rounded' | 'dark') still resolved via resolveButtonsLayoutTheme() for back-compat. Prefer theme.
cardButtonContentReact.ReactNodeNo--Replaces the default content inside the card button when layout="buttons".
cardBackButtonContentReact.ReactNodeNo--Replaces the default "Go back" label in the expanded card form when layout="buttons". Pass '' to remove the text and keep only the icon.
cardTitleContentReact.ReactNodeNo--Replaces the default "Secure card checkout" title. Pass '' to remove the title text entirely.
onButtonClick(method: CheckoutButtonMethod) => voidNo--Called when a payment method button is clicked. Fires on the initial click for 'card' (buttons layout), 'paypal', 'apple_pay', 'google_pay' — before PayPal redirect or wallet authorization. Cancelled if onBeforeButtonClick returns false.
onBeforeButtonClick(event: BeforeButtonClickEvent) => void | false | InlineSessionPatch | Promise<...>No--Card only. Runs before the "Credit / Debit Card" button continues in layout="buttons". Returning false cancels the click.
enableAVSboolean | AVSFieldConfigNofalseEnable AVS. true → country + postal code (legacy default). An AVSFieldConfig object enables per-field, per-country control of country, postal code, street address, city, and state. See AVS guide.
avsLayout'row' | 'column'No'row'Layout for the country / postal code pair. Address line, city, and state always render on their own rows.
countrystringNo'US'Pre-filled country code (ISO 3166-1 alpha-2). Typically from a partner GEO/IP lookup.
zipstringNoPre-filled ZIP/postal code.
citystringNoPre-filled city. Typically from a partner GEO/IP lookup.
statestringNoPre-filled state/province. Typically from a partner GEO/IP lookup.
addressLine1stringNoPre-filled street address (line 1).
addressLine2stringNoPre-filled apt/suite/unit (line 2).
onCountryChange(country: string) => voidNoCallback when country changes.
onZipChange(zip: string) => voidNoCallback when ZIP/postal code changes.
totalAmountnumberNo0Total amount in cents (used for wallet/PayPal Elements config).
currencystringNo'usd'Currency code (used for PayPal Elements config).
debugbooleanNofalseRender diagnostic panels showing resolved payment methods and the Direct PayPal button lifecycle. Forwarded to each DirectPayPalButton render site. Intended for local development — leave off in production.

SplitCardFormRef

Methods exposed via ref for external 3DS handling.

MethodSignatureDescription
handleNextAction(clientSecret: string) => Promise<void>Trigger 3DS authentication with the given client secret.

Default Layout

The default layout renders all payment methods and card fields together:

  1. Wallet buttons (Apple Pay / Google Pay)
  2. PayPal button
  3. "or pay with card" divider
  4. Card Number field
  5. Card Expiry + CVC fields (side by side)
  6. Full Name input
  7. Error display
  8. Submit button

Buttons Layout

When layout="buttons", payment methods are shown as stacked buttons:

  1. PayPal button
  2. Wallet buttons (Apple Pay / Google Pay)
  3. "Credit / Debit Card" button

Clicking the card button expands into the card form with a configurable back button label and title. PayPal and wallet buttons stay mounted (no re-render) and are hidden via CSS.

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.

Submit gating while a claim is pending

When dataAttachmentPending is true, the hosted widget's submit button is gated. A buyer who clicks anyway has the click cancelled by the widget, which reports it back so the form can show:

Just a moment — we are finalising your order. Please press pay again.

The notice is retracted as soon as the claim lands, so a buyer who never clicked while gated is not left reading a stale instruction. The gate is not silent — nothing is submitted and nothing is swallowed.

<SplitCardForm
  sessionId={sessionId}
  billingApiUrl="https://api.flopay.com"
  email={email}
  layout="buttons"
  theme="modern-light"
/>

You can replace the default card button body with any React content:

<SplitCardForm
  sessionId={sessionId}
  billingApiUrl="https://api.flopay.com"
  email={email}
  layout="buttons"
  cardButtonContent={
    <div style={{ display: 'flex', alignItems: 'center', width: '100%' }}>
      <span style={{ fontWeight: 700 }}>Pay by card</span>
      <span style={{ marginLeft: 'auto', opacity: 0.7 }}>Secure</span>
    </div>
  }
/>

You can also replace or remove the header text in the expanded card form:

<SplitCardForm
  sessionId={sessionId}
  billingApiUrl="https://api.flopay.com"
  email={email}
  layout="buttons"
  cardBackButtonContent=""
  cardTitleContent="Enter card details"
/>

See FloPayCheckout — Layout Modes for theme presets and custom styling options.

Button Hooks

<SplitCardForm
  sessionId={sessionId}
  billingApiUrl="https://api.flopay.com"
  email={email}
  layout="buttons"
  onBeforeButtonClick={async ({ method }) => {
    if (method !== 'card') return;

    const confirmed = await confirmCardCheckoutDetails();
    if (!confirmed) return false;
  }}
  onButtonClick={(method) => {
    console.log('clicked:', method);
  }}
  onDecline={(decline) => {
    console.log('declined:', decline);
  }}
/>

onBeforeButtonClick only runs for the buttons-layout credit card button. Throwing routes the error through onError.

Example

<FloPayProvider
  flopay={flopayPromise}
  options={{ amount: 4999, currency: 'usd', paymentMethodCreation: 'manual' }}
>
  <SplitCardForm
    sessionId="uuid-xxx"
    billingApiUrl="https://api.example.com"
    email="user@example.com"
    userId="user_1"
    totalAmount={49.99}
    currency="usd"
    onComplete={(result) => router.push('/success')}
    onFirstNameChange={setFirstName}
    onLastNameChange={setLastName}
  />
</FloPayProvider>

On this page