FloPayFloPay
Examples

Automatic Payment Buttons

React upsell example using FloPayAutomaticPaymentButton to keep purchases on the current page.

Automatic Payment Buttons Example

This example shows a same-page upsell on a success screen. The user keeps their original purchase details in view, clicks a single saved-payment button, and successful upsells are appended back into the page state.

app/success/UpsellSection.tsx
'use client';
 
import { useMemo, useState } from 'react';
import { FloPayAutomaticPaymentButton } from '@flopay/react';
 
type PurchaseEntry = {
  name: string;
  amount: number;
  currency: string;
};
 
const upsellItem = {
  providerItemId: 'upsell_ai_pack',
  providerItemName: 'AI Supercharger Pack',
  totalAmount: 249,
  overrideAmount: 80,
  currency: 'USD',
  quantity: 1,
};
 
function formatAmount(amount: number, currency: string) {
  return new Intl.NumberFormat('en-US', {
    style: 'currency',
    currency,
  }).format(amount / 100);
}
 
export default function UpsellSection() {
  const [upsells, setUpsells] = useState<PurchaseEntry[]>([]);
 
  const buttonLabel = useMemo(() => {
    const amount = upsellItem.overrideAmount ?? upsellItem.totalAmount;
    return `Add ${upsellItem.providerItemName} for ${formatAmount(amount, upsellItem.currency)}`;
  }, []);
 
  return (
    <section style={{ display: 'grid', gap: '1rem' }}>
      <div>
        <h2>Original Purchase</h2>
        <p>Core Plan - {formatAmount(4900, 'USD')}</p>
      </div>
 
      <div style={{ display: 'grid', gap: '0.5rem' }}>
        {upsells.map((item, index) => (
          <p key={`${item.name}-${index}`}>
            Upsell purchase {index + 1}: {item.name} - {formatAmount(item.amount, item.currency)}
          </p>
        ))}
      </div>
 
      <FloPayAutomaticPaymentButton
        clientId="client_123"
        account={{
          userId: 'user_123',
          email: 'customer@example.com',
        }}
        items={[upsellItem]}
        successUrl={`${window.location.origin}/success`}
        cancelUrl={`${window.location.origin}/success`}
        buttonsTheme="rounded"
        onSuccess={() => {
          setUpsells((current) => [
            ...current,
            {
              name: upsellItem.providerItemName,
              amount: upsellItem.overrideAmount ?? upsellItem.totalAmount,
              currency: upsellItem.currency,
            },
          ]);
        }}
        onDecline={(decline) => {
          console.log('payment declined', decline.code);
        }}
        onError={(error) => {
          console.error(error.message);
        }}
      >
        {buttonLabel}
      </FloPayAutomaticPaymentButton>
    </section>
  );
}

Why This Pattern Works

  • the page stays on the original success route instead of redirecting to a separate checkout screen
  • the button gets FloPay's shared processing, success, and decline modal states automatically
  • if authentication is required, the SDK opens the fallback checkout modal on the same page
  • successful upsells can update your local React state immediately

Reusing A Session Created On Your Backend

If your backend creates the upsell session first, switch the button to sessionId mode:

<FloPayAutomaticPaymentButton
  sessionId={upsellSessionId}
  onSuccess={() => {
    setUpsells((current) => [
      ...current,
      {
        name: 'AI Supercharger Pack',
        amount: 80,
        currency: 'USD',
      },
    ]);
  }}
>
  Purchase Item
</FloPayAutomaticPaymentButton>

On this page