# Examples

Self-contained React components showing common integration patterns. Each builds on the same three-step flow from [Getting Started](https://docs.odus.com/docs/guides/checkout/elements/getting-started.md) — the differences are **which elements you mount** and **how you collect customer data**.

## Minimal Checkout

**Use when:** You only need card details and an email — the simplest possible form.

Mounts email, cardholder name, card, and error elements. Unmounted elements (phone, address) are automatically skipped by `validate()` and `authorize()`.

src/components/MinimalCheckout.tsx

```
import { useEffect, useRef, useState } from 'react';

import { OdusElements } from '@odus/checkout/elements';



export function MinimalCheckout() {

  const checkoutRef = useRef<OdusElements | null>(null);

  const emailRef = useRef<HTMLDivElement>(null);

  const cardholderRef = useRef<HTMLDivElement>(null);

  const cardRef = useRef<HTMLDivElement>(null);

  const errorRef = useRef<HTMLDivElement>(null);

  const [isFormValid, setIsFormValid] = useState(false);



  useEffect(() => {

    const checkout = new OdusElements({

      apiKey: 'pk_retired',

      environment: 'test',

      returnUrl: 'https://your-website.com/checkout/return',

      callbacks: {

        onPaymentSucceeded: () => {

          window.location.href = '/success';

        },

        onPaymentFailed: (error) => console.error('Failed', error),

        onActionRequired: (url) => {

          window.location.href = url;

        },

      },

    });



    checkout.elements.contact.email.mount(emailRef.current!);

    checkout.elements.cardholderName.mount(cardholderRef.current!);

    checkout.elements.card.mount(cardRef.current!);

    checkout.elements.error.mount(errorRef.current!);



    checkout.onChange(() => setIsFormValid(checkout.isValid));

    checkoutRef.current = checkout;



    fetch('/api/create-payment', { method: 'POST' })

      .then((res) => res.json())

      .then(({ paymentId, checkoutKey }) => checkout.associatePayment(paymentId, checkoutKey));



    return () => checkoutRef.current?.destroy();

  }, []);



  async function handlePay() {

    const checkout = checkoutRef.current!;

    const { valid, errors } = checkout.validate();

    if (!valid) {

      console.error('Validation errors:', errors);

      return;

    }

    await checkout.authorize();

  }



  return (

    <div>

      <div ref={emailRef} />

      <div ref={cardholderRef} />

      <div ref={cardRef} />

      <div ref={errorRef} />

      <button onClick={handlePay} disabled={!isFormValid}>

        Pay

      </button>

    </div>

  );

}
```

***

## Full Checkout

**Use when:** You need complete customer information — email, phone, full address, and card.

All elements are mounted, giving you full layout control. Demonstrates `update()` to set initial values after mounting.

src/components/FullCheckout.tsx

```
import { useEffect, useRef, useState } from 'react';

import { OdusElements } from '@odus/checkout/elements';



export function FullCheckout() {

  const checkoutRef = useRef<OdusElements | null>(null);

  const emailRef = useRef<HTMLDivElement>(null);

  const phoneRef = useRef<HTMLDivElement>(null);

  const firstNameRef = useRef<HTMLDivElement>(null);

  const lastNameRef = useRef<HTMLDivElement>(null);

  const streetRef = useRef<HTMLDivElement>(null);

  const cityRef = useRef<HTMLDivElement>(null);

  const zipCodeRef = useRef<HTMLDivElement>(null);

  const countryRef = useRef<HTMLDivElement>(null);

  const stateRef = useRef<HTMLDivElement>(null);

  const cardRef = useRef<HTMLDivElement>(null);

  const errorRef = useRef<HTMLDivElement>(null);

  const [isFormValid, setIsFormValid] = useState(false);



  useEffect(() => {

    const checkout = new OdusElements({

      apiKey: 'pk_retired',

      environment: 'test',

      returnUrl: 'https://your-website.com/checkout/return',

      callbacks: {

        onPaymentSucceeded: () => {

          window.location.href = '/success';

        },

        onPaymentFailed: (error) => console.error('Failed', error),

        onActionRequired: (url) => {

          window.location.href = url;

        },

      },

    });



    checkout.elements.contact.email.mount(emailRef.current!);

    checkout.elements.contact.email.update({ initialValue: 'user@example.com' });



    checkout.elements.contact.phone.mount(phoneRef.current!);

    checkout.elements.address.firstName.mount(firstNameRef.current!);

    checkout.elements.address.lastName.mount(lastNameRef.current!);

    checkout.elements.address.street.mount(streetRef.current!);

    checkout.elements.address.city.mount(cityRef.current!);

    checkout.elements.address.zipCode.mount(zipCodeRef.current!);



    checkout.elements.address.country.mount(countryRef.current!);

    checkout.elements.address.country.update({ initialValue: 'US' });



    checkout.elements.address.state.mount(stateRef.current!);

    checkout.elements.card.mount(cardRef.current!);

    checkout.elements.error.mount(errorRef.current!);



    checkout.onChange(() => setIsFormValid(checkout.isValid));

    checkoutRef.current = checkout;



    fetch('/api/create-payment', { method: 'POST' })

      .then((res) => res.json())

      .then(({ paymentId, checkoutKey }) => checkout.associatePayment(paymentId, checkoutKey));



    return () => checkoutRef.current?.destroy();

  }, []);



  async function handlePay() {

    const checkout = checkoutRef.current!;

    const { valid, errors } = checkout.validate();

    if (!valid) {

      console.error('Validation errors:', errors);

      return;

    }

    await checkout.authorize();

  }



  return (

    <form

      onSubmit={(e) => {

        e.preventDefault();

        handlePay();

      }}

    >

      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16 }}>

        <div ref={emailRef} />

        <div ref={phoneRef} />

      </div>

      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16 }}>

        <div ref={firstNameRef} />

        <div ref={lastNameRef} />

      </div>

      <div ref={streetRef} />

      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16 }}>

        <div ref={cityRef} />

        <div ref={zipCodeRef} />

      </div>

      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16 }}>

        <div ref={countryRef} />

        <div ref={stateRef} />

      </div>

      <div ref={cardRef} />

      <div ref={errorRef} />

      <button type="submit" disabled={!isFormValid}>

        Pay

      </button>

    </form>

  );

}
```

***

## Custom Payload

**Use when:** You want to collect some fields (email, name) with your own native inputs while using Elements only for card data.

Mounts only card and error elements. Merchant-collected values are passed to `authorize()` via the `customerData` option — these are deep-merged on top of element-derived values.

src/components/CustomPayloadCheckout.tsx

```
import { useEffect, useRef, useState } from 'react';

import { OdusElements } from '@odus/checkout/elements';



export function CustomPayloadCheckout() {

  const checkoutRef = useRef<OdusElements | null>(null);

  const cardRef = useRef<HTMLDivElement>(null);

  const errorRef = useRef<HTMLDivElement>(null);



  const [email, setEmail] = useState('');

  const [name, setName] = useState('');

  const [isCardValid, setIsCardValid] = useState(false);



  useEffect(() => {

    const checkout = new OdusElements({

      apiKey: 'pk_retired',

      environment: 'test',

      returnUrl: 'https://your-website.com/checkout/return',

      callbacks: {

        onPaymentSucceeded: () => {

          window.location.href = '/success';

        },

        onPaymentFailed: (error) => console.error('Failed', error),

        onActionRequired: (url) => {

          window.location.href = url;

        },

      },

    });



    checkout.elements.card.mount(cardRef.current!);

    checkout.elements.error.mount(errorRef.current!);



    checkout.onChange(() => setIsCardValid(checkout.isValid));

    checkoutRef.current = checkout;



    fetch('/api/create-payment', { method: 'POST' })

      .then((res) => res.json())

      .then(({ paymentId, checkoutKey }) => checkout.associatePayment(paymentId, checkoutKey));



    return () => checkoutRef.current?.destroy();

  }, []);



  async function handlePay() {

    const checkout = checkoutRef.current!;



    if (!email.trim() || !name.trim()) {

      alert('Please fill in all fields');

      return;

    }



    const { valid, errors } = checkout.validate();

    if (!valid) {

      console.error('Validation errors:', errors);

      return;

    }



    await checkout.authorize({

      customerData: {

        email: email.trim(),

        name: name.trim(),

      },

    });

  }



  const isFormValid = isCardValid && email.trim() !== '' && name.trim() !== '';



  return (

    <div>

      <input type="email" placeholder="Email" value={email} onChange={(e) => setEmail(e.target.value)} />

      <input type="text" placeholder="Cardholder Name" value={name} onChange={(e) => setName(e.target.value)} />

      <div ref={cardRef} />

      <div ref={errorRef} />

      <button onClick={handlePay} disabled={!isFormValid}>

        Pay

      </button>

    </div>

  );

}
```

***

## PayPal Only

**Use when:** You want to offer PayPal as the sole payment method — no card inputs needed.

The PayPal button handles authorization and redirect automatically; you don't call `authorize()` manually.

src/components/PayPalCheckout.tsx

```
import { useEffect, useRef } from 'react';

import { OdusElements } from '@odus/checkout/elements';



export function PayPalCheckout() {

  const checkoutRef = useRef<OdusElements | null>(null);

  const paypalRef = useRef<HTMLDivElement>(null);

  const errorRef = useRef<HTMLDivElement>(null);



  useEffect(() => {

    const checkout = new OdusElements({

      apiKey: 'pk_retired',

      environment: 'test',

      returnUrl: 'https://your-website.com/checkout/return',

      callbacks: {

        onPaymentSucceeded: () => {

          window.location.href = '/success';

        },

        onPaymentFailed: (error) => console.error('Failed', error),

        onActionRequired: (url) => {

          window.location.href = url;

        },

      },

    });



    checkout.elements.express.payPal.mount(paypalRef.current!);

    checkout.elements.error.mount(errorRef.current!);

    checkoutRef.current = checkout;



    fetch('/api/create-payment', { method: 'POST' })

      .then((res) => res.json())

      .then(({ paymentId, checkoutKey }) => checkout.associatePayment(paymentId, checkoutKey));



    return () => checkoutRef.current?.destroy();

  }, []);



  return (

    <div>

      <div ref={paypalRef} />

      <div ref={errorRef} />

    </div>

  );

}
```

***

## Apple Pay Only

**Use when:** You want to offer Apple Pay as the sole payment method (Safari / supported devices only).

Apple Pay configuration (display name, countries, required contact fields) is set in `ElementsConfig.additionalPaymentMethods`. The button only renders on supported devices after `associatePayment()` succeeds.

src/components/ApplePayCheckout.tsx

```
import { useEffect, useRef } from 'react';

import { OdusElements } from '@odus/checkout/elements';



export function ApplePayCheckout() {

  const checkoutRef = useRef<OdusElements | null>(null);

  const applePayRef = useRef<HTMLDivElement>(null);

  const errorRef = useRef<HTMLDivElement>(null);



  useEffect(() => {

    const checkout = new OdusElements({

      apiKey: 'pk_retired',

      environment: 'test',

      returnUrl: 'https://your-website.com/checkout/return',

      additionalPaymentMethods: {

        applePay: {

          displayName: 'My Store',

          countries: ['US', 'GB'],

          requiredBillingContactFields: ['postalAddress'],

          requiredShippingContactFields: ['email', 'name'],

        },

      },

      callbacks: {

        onPaymentSucceeded: () => {

          window.location.href = '/success';

        },

        onPaymentFailed: (error) => console.error('Failed', error),

        onActionRequired: (url) => {

          window.location.href = url;

        },

      },

    });



    checkout.elements.express.applePay.mount(applePayRef.current!);

    checkout.elements.error.mount(errorRef.current!);

    checkoutRef.current = checkout;



    fetch('/api/create-payment', { method: 'POST' })

      .then((res) => res.json())

      .then(({ paymentId, checkoutKey }) => checkout.associatePayment(paymentId, checkoutKey));



    return () => checkoutRef.current?.destroy();

  }, []);



  return (

    <div>

      <div ref={applePayRef} />

      <div ref={errorRef} />

    </div>

  );

}
```
