FloPay
Guides

Payment Flow

End-to-end card payment flow from element submission to 3DS retry.

Payment Flow

This guide explains the full card payment lifecycle that CheckoutForm and SplitCardForm handle internally. Understanding these steps is useful when building custom flows or debugging issues.

The 5 Steps

1. Submit Elements

Validate the mounted payment elements (card number, expiry, CVC, or the combined PaymentElement):

const { error } = await flopay.submitElements();
if (error) {
  // validation failed -- show error to user
  return;
}

2. Create Payment Method

Tokenize the card details into a reusable payment method ID:

const { paymentMethodId, error } = await flopay.createPaymentMethod();
if (error) {
  return;
}
// paymentMethodId = "pm_1abc..."

3. Create Payment Intent

Send the tokenized payment method to the billing API to create a payment intent:

const response = await fetch(`${billingApiUrl}/v1/payments/intent`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    sessionId,
    paymentMethodId,
    email,
  }),
});
const { clientSecret, type } = await response.json();

4. Confirm Card Payment

Use the client secret to confirm the payment with the provider (Stripe):

const { paymentIntent, error } = await flopay.confirmCardPayment({
  clientSecret,
  paymentMethodId,
});

5. Process Payment

Finalize the payment via the billing API:

const result = await fetch(`${billingApiUrl}/v1/payments/process`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    sessionId,
    paymentIntentId: paymentIntent.id,
  }),
});

3D Secure Retry Loop

When the billing API returns type: '3ds_required' with a threeDSecureToken, the form enters a retry loop:

processPayment response
  |
  |--> type: 'success' --> onComplete
  |
  |--> type: '3ds_required'
        |
        |--> confirmCardPayment(threeDSecureToken)
        |      |
        |      |--> 3DS modal shown to user
        |      |--> user authenticates
        |
        |--> processPayment again (retry)
              |
              |--> type: 'success' --> onComplete
              |--> type: '3ds_required' --> retry again (max attempts)

The built-in forms handle this loop automatically. The handleNextAction method on the form ref gives you manual control in delegated mode.

Both CheckoutForm and SplitCardForm handle all five steps plus the 3DS retry loop automatically. You only need to understand these steps if you are building a custom payment flow or using the delegated onTokenizedBody mode.

Delegated Mode

When you provide onTokenizedBody, the form handles steps 1-2 and then calls your callback with the tokenized body. You are responsible for steps 3-5 and 3DS handling. See CheckoutForm and 3D Secure for details.

On this page