Examples
Self-contained React components showing common integration patterns. Each builds on the same three-step flow from Getting Started — 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().
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_test_your_publishable_key',
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.
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_test_your_publishable_key',
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.
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_test_your_publishable_key',
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.
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_test_your_publishable_key',
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.
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_test_your_publishable_key',
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>
);
}