> ## Documentation Index
> Fetch the complete documentation index at: https://funnelfox.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Checkout customization

> Customize the checkout UI: payment method order, card form options, button styling, input theming, and CSS overrides.

When you call
[`createCheckout()`](/develop/create-checkout), the SDK
renders a complete payment UI inside your container. Learn what the default checkout includes and how
to customize it.

## Default styles

Out of the box, `createCheckout()` applies the [default CSS styles](https://github.com/adaptyteam/funnelfox-billing-js/blob/main/src/skins/default/styles.css) and renders:

* An accordion with radio-button selection between available
  payment methods.
* A card form with number, expiry, and CVV fields.
* PayPal, Google Pay, and Apple Pay buttons in their own
  accordion sections.
* Loading spinners, error containers, and a success screen.
* A responsive layout with a mobile breakpoint at 768 px.

The accordion auto-selects the first available payment
method and expands it.

To customize the default UI, follow the sections below.

## Override CSS

The default checkout renders elements with `.ff-*` CSS
classes you can target for visual overrides:

| Class                              | Purpose                                          |
| ---------------------------------- | ------------------------------------------------ |
| `.ff-skin-default`                 | Top-level container (max-width: 400px, centered) |
| `.ff-payment-method-card`          | Payment method card (hidden until `.visible`)    |
| `.ff-payment-method-card.expanded` | Accordion expanded state                         |
| `.ff-payment-method-radio`         | Custom radio button                              |
| `.ff-payment-method-label`         | Row with radio button and header                 |
| `.ff-payment-method-content`       | Animated accordion panel                         |
| `.ff-card-form-container`          | Card form wrapper                                |
| `.ff-card-form-submit-button`      | Submit button                                    |
| `.ff-card-form-label`              | Input labels                                     |
| `.ff-card-form-text-input`         | Native inputs (cardholder, email)                |
| `.ff-card-form-text-input.error`   | Error state for native inputs                    |
| `.ff-security-message`             | Security message below forms                     |
| `.ff-payment-features`             | Checkmark list per payment method                |
| `.payment-errors-container`        | Error message area                               |
| `.loader-container`                | Full overlay with loading spinner                |

For example, to change the submit button border radius:

```css theme={null}
.ff-card-form-submit-button {
  border-radius: 8px;
}
```

## Customize checkout

For full control over the checkout UI, provide both
`cardSelectors` and `paymentButtonSelectors` to
`createCheckout()`. The SDK skips the default UI entirely
and injects payment inputs into your own HTML elements.

<Warning>
  The [`paymentMethodOrder`](/develop/checkout-customization#payment-method-order) option is ignored when using a
  custom layout.
</Warning>

### Card selectors

Card selectors are resolved relative to the `container`
element.

<ParamField body="cardNumber" type="string" required>
  An empty `div` where the hosted card number input is
  injected.
</ParamField>

<ParamField body="expiryDate" type="string" required>
  An empty `div` for the hosted expiry input.
</ParamField>

<ParamField body="cvv" type="string" required>
  An empty `div` for the hosted CVV input.
</ParamField>

<ParamField body="button" type="string" required>
  A `button` element. The SDK hooks its click event for
  form submission.
</ParamField>

<ParamField body="cardholderName" type="string">
  An `input` element. The SDK reads its value directly.
</ParamField>

<ParamField body="emailAddress" type="string">
  An `input` element. The SDK reads its value directly.
</ParamField>

<Accordion title="Card selectors example">
  ```html theme={null}
  <!-- Card form inside the container -->
  <div id="checkout-container" style="position: relative;
    min-height: 200px;">
    <div id="my-card-number"></div>
    <div id="my-expiry"></div>
    <div id="my-cvv"></div>
    <input id="my-cardholder" placeholder="Cardholder name" />
    <input id="my-email" placeholder="Email" />
    <button id="my-submit-button">Pay</button>
  </div>
  ```

  ```javascript theme={null}
  cardSelectors: {
    cardNumber: '#my-card-number',
    expiryDate: '#my-expiry',
    cvv: '#my-cvv',
    cardholderName: '#my-cardholder',
    emailAddress: '#my-email',
    button: '#my-submit-button',
  },
  ```
</Accordion>

### Payment button selectors

Payment button selectors are resolved relative to
`document`, not the container.

<ParamField body="paypal" type="string" required>
  A `div` for the PayPal button.
</ParamField>

<ParamField body="googlePay" type="string" required>
  A `div` for the Google Pay button.
</ParamField>

<ParamField body="applePay" type="string" required>
  A `div` for the Apple Pay button.
</ParamField>

All three payment button selectors are required. Omitting
any of them throws a `CheckoutError`.

<Accordion title="Payment button selectors example">
  ```html theme={null}
  <!-- Payment buttons outside the container -->
  <div id="my-paypal-container"></div>
  <div id="my-gpay-container"></div>
  <div id="my-applepay-container"></div>
  ```

  ```javascript theme={null}
  paymentButtonSelectors: {
    paypal: '#my-paypal-container',
    googlePay: '#my-gpay-container',
    applePay: '#my-applepay-container',
  },
  ```
</Accordion>

## Payment method order

The `paymentMethodOrder` array controls the display order
of payment methods in the checkout accordion.

By default, it shows: `APPLE_PAY` > `GOOGLE_PAY` > `PAYPAL` > `PAYMENT_CARD`.

```javascript theme={null}
await Billing.createCheckout({
  // ...required options
  paymentMethodOrder: [
    'APPLE_PAY',
    'GOOGLE_PAY',
    'PAYPAL',
    'PAYMENT_CARD',
  ],
});
```

Omit a method from the array to exclude it from the
checkout. For example, to show only card and PayPal:

```javascript theme={null}
paymentMethodOrder: ['PAYMENT_CARD', 'PAYPAL'],
```

## Card form

Customize card form fields visibility.

### Cardholder name field

The cardholder name field visibility is controlled by the
server response by default. Override it using the `required` parameter:

* **true**: Show and require the field.
* **false or unset**: Hide the field.

Your explicit configuration takes precedence
over the server default.

```javascript theme={null}
await Billing.createCheckout({
  // ...required options
  card: {
    cardholderName: {
      required: true,
    },
  },
});
```

### Email address field

The email field is also server-controlled by default.
Override it to show an email input in the card form:

Set `visible: true` to render an email input
pre-populated with the `customer.email` value.

Use `template` to optionally format the email before
sending it as the payment email address. Any occurrence
of `{{email}}` is replaced with the actual email.

```javascript theme={null}
await Billing.createCheckout({
  // ...required options
  card: {
    emailAddress: {
      visible: true,
      template: '{{email}}',
    },
  },
});
```

## Payment button

Each payment method button accepts styling options that
are passed to the underlying payment provider SDK.

### Apple Pay

By default, `buttonStyle` is set to `black`, but you can optionally use `white` or `white-outline`.

```javascript theme={null}
await Billing.createCheckout({
  // ...required options
  applePay: {
    buttonStyle: 'black', // 'black' | 'white' | 'white-outline'
  },
});
```

### PayPal

Customize the PayPal button appearance and payment flow.

| Option         | Values                                               | Default          |
| -------------- | ---------------------------------------------------- | ---------------- |
| `buttonColor`  | `'gold'`, `'blue'`, `'silver'`, `'black'`, `'white'` | `'gold'`         |
| `buttonShape`  | `'pill'`, `'rect'`                                   | `'pill'`         |
| `buttonLabel`  | `'pay'`, `'checkout'`, `'buynow'`, `'paypal'`        | `'pay'`          |
| `buttonSize`   | `'small'`, `'medium'`, `'large'`                     | `'large'`        |
| `buttonHeight` | Pixel value                                          | `54`             |
| `paymentFlow`  | `'PREFER_VAULT'`, `'DEFAULT'`                        | `'PREFER_VAULT'` |

```javascript theme={null}
await Billing.createCheckout({
  // ...required options
  paypal: {
    buttonColor: 'gold',
    buttonShape: 'pill',
    buttonLabel: 'pay',
    buttonSize: 'large',
    buttonHeight: 54,
    paymentFlow: 'PREFER_VAULT',
  },
});
```

### Google Pay

Customize the Google Pay button appearance and sizing.

| Option           | Values                                                                                    | Default   |
| ---------------- | ----------------------------------------------------------------------------------------- | --------- |
| `buttonColor`    | `'black'`, `'white'`                                                                      | `'black'` |
| `buttonSizeMode` | `'fill'`, `'static'`                                                                      | `'fill'`  |
| `buttonType`     | `'pay'`, `'book'`, `'buy'`, `'checkout'`, `'donate'`, `'order'`, `'plain'`, `'subscribe'` | `'pay'`   |

```javascript theme={null}
await Billing.createCheckout({
  // ...required options
  googlePay: {
    buttonColor: 'black',
    buttonSizeMode: 'fill',
    buttonType: 'pay',
  },
});
```

## Card input

Card number, expiry, and CVV fields are rendered inside
iframes by the payment provider, so regular CSS cannot reach
them. Use the `style` option to control their appearance:

```javascript theme={null}
await Billing.createCheckout({
  // ...required options
  style: {
    input: {
      base: {
        borderWidth: '1px',
        borderStyle: 'solid',
        borderColor: 'rgb(0 0 0 / 10%)',
        height: '36px',
        paddingHorizontal: 10,
        borderRadius: '6px',
      },
      error: {
        borderColor: 'rgb(227, 47, 65)',
      },
    },
  },
});
```

The `base` object also supports `fontSize`, `color`,
`fontFamily`, and `fontWeight`.

By default, SDK applies the following styles:

| Property            | Base state           | Error state          |
| ------------------- | -------------------- | -------------------- |
| `borderWidth`       | `'1px'`              | —                    |
| `borderStyle`       | `'solid'`            | —                    |
| `borderColor`       | `'rgb(0 0 0 / 10%)'` | `'rgb(227, 47, 65)'` |
| `height`            | `'36px'`             | —                    |
| `paddingHorizontal` | `10`                 | —                    |
| `borderRadius`      | `'6px'`              | —                    |

<Info>The cardholder name and email inputs are native HTML `input` elements. Style them with CSS using the `.ff-card-form-text-input` class, not through the `style`
option.</Info>

## Single payment method

Use `Billing.initMethod()` to render a single payment
method into your own container:

```javascript theme={null}
import { Billing, PaymentMethod } from '@funnelfox/billing';

const container = document.getElementById('payment-container');

const method = await Billing.initMethod(
  PaymentMethod.PAYMENT_CARD,
  container,
  {
    orgId: 'your-org-id',
    priceId: 'price_123',
    externalId: 'user_456',
    email: 'user@example.com',

    // Optional styling
    style: { /* CheckoutStyle */ },
    card: { /* BillingCardOptions */ },

    // Callbacks
    onRenderSuccess: () => {
      console.log('Payment method rendered');
    },
    onPaymentSuccess: () => {
      console.log('Payment completed');
    },
    onPaymentFail: (error) => {
      console.error('Payment failed:', error.message);
    },
  }
);
```

Available payment methods: `PAYMENT_CARD`, `PAYPAL`,
`GOOGLE_PAY`, `APPLE_PAY`.

For `PAYMENT_CARD`, the SDK renders a card form (number,
expiry, CVV, optional cardholder name, optional email)
inside the container. For `PAYPAL`, `GOOGLE_PAY`, and
`APPLE_PAY`, the payment button is rendered directly.

The returned interface provides:

* **`setDisabled(disabled)`**: Enable or disable the
  payment method.
* **`submit()`**: Programmatically trigger form
  submission (card payments only).
* **`destroy()`**: Clean up and remove the payment
  method.
