FloPay
Guides

CheckoutForm

Use the drop-in CheckoutForm for self-contained or delegated payment flows.

CheckoutForm

CheckoutForm is a drop-in React component that renders a Stripe PaymentElement with a submit button and handles the full payment lifecycle.

Two Modes

Self-Contained Mode (Default)

Provide onComplete and the form handles everything: tokenize the card, create the intent, confirm with Stripe, process via the billing API, and retry on 3DS challenges.

import { loadFloPay } from '@flopay/js';
import { FloPayProvider, CheckoutForm } from '@flopay/react';
 
const flopayPromise = loadFloPay('pk_test_...');
 
function Checkout() {
  return (
    <FloPayProvider flopay={flopayPromise} options={{ paymentMethodCreation: 'manual' }}>
      <CheckoutForm
        sessionId="sess_abc123"
        billingApiUrl="https://billing.example.com"
        email="user@example.com"
        userId="user_1"
        onComplete={(result) => {
          window.location.href = '/success';
        }}
        onError={(error) => {
          console.error(error.type, error.message);
        }}
      />
    </FloPayProvider>
  );
}

Delegated Mode

Provide onTokenizedBody to take control of the backend submission. The form tokenizes the card and confirms the payment, then hands you the TokenizedBody to submit to your own API.

import { useRef } from 'react';
import { CheckoutForm, type CheckoutFormRef } from '@flopay/react';
 
function DelegatedCheckout() {
  const formRef = useRef<CheckoutFormRef>(null);
  const [processing, setProcessing] = useState(false);
 
  const handleTokenized = async (tokenizedBody: TokenizedBody) => {
    setProcessing(true);
    const res = await fetch('/api/my-payment', {
      method: 'POST',
      body: JSON.stringify(tokenizedBody),
    });
    const data = await res.json();
 
    if (data.type === '3ds_required') {
      await formRef.current?.handleNextAction(data.threeDSecureToken);
    }
    setProcessing(false);
  };
 
  return (
    <CheckoutForm
      ref={formRef}
      sessionId="sess_abc123"
      billingApiUrl="https://billing.example.com"
      onTokenizedBody={handleTokenized}
      isProcessing={processing}
    />
  );
}

Props Reference

PropTypeDescription
sessionIdstringCheckout session UUID from the billing API (required)
billingApiUrlstringBilling API base URL (required)
emailstringCustomer email for payment intents
userIdstringCustomer user ID for payment processing
onComplete(result: PaymentResult) => voidCalled on successful payment (self-contained mode)
onError(error: FloPayError) => voidCalled on payment error
onTokenizedBody(body: TokenizedBody) => voidEnables delegated mode
layout'tabs' | 'accordion' | 'auto'PaymentElement layout style
submitLabelstringCustom submit button text
showAddressboolean | 'billing' | 'shipping'Show an address element
classNamestringCSS class for the form wrapper
childrenReactNodeCustom submit button (overrides default)
isProcessingbooleanExternal processing state (delegated mode)
errorstring | nullExternal error message (delegated mode)
onErrorChange(error: string | null) => voidCalled when internal error state changes

When using delegated mode, you are responsible for 3DS handling. Use the handleNextAction method on the form ref. See the 3D Secure guide.

On this page