Modular Checkout
Overview
The justifi-modular-checkout wrapper component serves as a container for checkout-related sub components. It manages the tokenization of payment methods, billing information, insurance, and overall form submission to complete the checkout. It also supports saving a payment method to a payment method group for future use.
Supported sub components
Required for checkout completion (add at least one of the following sub components):
- justifi-card-form
- justifi-bank-account-form
- justifi-saved-payment-methods
- justifi-sezzle-payment-method
- justifi-plaid-payment-method
Optional:
- justifi-checkout-summary
- justifi-season-interruption-insurance
- justifi-billing-form-full
- justifi-card-billing-form-simple
- justifi-bank-account-billing-form-simple
- justifi-apple-pay
- justifi-google-pay
Note: In addition to the supported sub components, you can add any custom HTML elements or markup inside the justifi-modular-checkout. This allows you to render headings, descriptions, disclaimers, or other layout elements as needed.
Providing Billing Information
Billing information is required when collecting a new card or bank account and can be provided in one of the following ways:
- Using the
<justifi-billing-form-full />sub component for complete billing address collection - Using the
<justifi-card-billing-form-simple />sub component for ZIP code only (card payments) - Using the
<justifi-bank-account-billing-form-simple />sub component for account owner name only (ACH payments) - Passing a
billingInformationobject directly to thesubmitCheckoutmethod
For checkouts that use a saved payment method or a wallet/BNPL sub component (for example, <justifi-saved-payment-methods />, <justifi-apple-pay />, <justifi-sezzle-payment-method />, or <justifi-plaid-payment-method />), billing information is optional and is not validated by the modular checkout flow.
The billingInformation object can take one of two forms:
Full billing address:
{
name: string;
address_line1: string;
address_line2?: string;
address_city: string;
address_state: string;
address_postal_code: string;
}
Only postal code:
{
address_postal_code: string;
}
Card Form Billing Information
When using <justifi-card-form />, billing information must be provided through either:
<justifi-billing-form-full />sub component for complete billing address<justifi-card-billing-form-simple />sub component for ZIP code only- Passing the full
billingInformationobject directly to thesubmitCheckoutmethod - Passing the
billingInformationobject with onlyaddress_postal_codeto thesubmitCheckoutmethod
Bank Account Form Billing Information
When using <justifi-bank-account-form />, billing information must be provided through either:
<justifi-billing-form-full />sub component for complete billing address<justifi-bank-account-billing-form-simple />sub component for account owner name only- Passing the full
billingInformationobject directly to thesubmitCheckoutmethod
Pre-filling Billing Information
Use fillBillingForm() to programmatically pre-populate billing fields, e.g. from saved customer data. Values persist when the user switches between payment methods.
const modularCheckout = document.querySelector('justifi-modular-checkout');
modularCheckout.fillBillingForm({
name: 'John Doe',
address_line1: '123 Main St',
address_city: 'Anytown',
address_state: 'NY',
address_postal_code: '12345',
});
All fields are optional except address_postal_code:
interface BillingFormFields {
name?: string;
address_line1?: string;
address_line2?: string;
address_city?: string;
address_state?: string;
address_postal_code: string; // required
}
Completing Checkout
To finalize the checkout process, include a submit button that calls the submitCheckout method on the justifi-modular-checkout wrapper component. This method:
- handles validation, form submission and payment method tokenization
- optionally accepts a
billingInformationobject (useful when not using a billing component) - executes
preCompleteHook(if provided) after validation and tokenization (including Plaid exchange) and before checkout completion - emits a
submit-eventwhen the submission is successful - returns a promise (
Promise<void>) and you should listen forsubmit-eventto handle completion
If you passed a
payment_method_group_idto the create checkout API and you want to give the user the option to save a newly entered payment method to that payment method group for future use, set thesavePaymentMethodprop to true on thejustifi-modular-checkout. This will automatically save the payment method to that payment method group after successful checkout.
Props, Events & Methods
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
auth-token | string | Yes | — | |
checkout-id | string | Yes | — | |
preCompleteHook | (data: CheckoutState, resolve: (data: CheckoutState) => void, reject: () => void) => void | No | — |
Events
error-event: Emitted when validation fails or an error occurs. Detail shape:{ message: string; errorCode: string; severity?: 'info' | 'warning' | 'error' }. When the error originates fromjustifi-apple-pay,errorCodewill be prefixed withAPPLE_PAY_(e.g.APPLE_PAY_NOT_SUPPORTED,APPLE_PAY_PAYMENT_FAILED).submit-event: Emitted when checkout completes successfully. Detail shape:{ checkout: any; message: string }.checkout-changed: Emitted when checkout state changes. Detail shape:{ availablePaymentMethodTypes: PAYMENT_METHODS[]; selectedPaymentMethod: { id?: string; type: PAYMENT_METHODS }; savedPaymentMethods: SavedPaymentMethod[] }.
Public methods
fillBillingForm(fields)– Pre-populate billing form fields from saved customer data.submitCheckout(billingInformation?)– Validate, tokenize, and complete the checkout. Optionally accepts abillingInformationobject.validate()– Validate all sub-component form fields; returns a promise resolving toboolean.setSelectedPaymentMethod(method)– Programmatically set the active payment method.
Pre-Complete Hook
You can provide a preCompleteHook function to inspect the checkout state before submission proceeds. This is useful for implementing custom validation, user confirmation dialogs, or conditional payment restrictions based on business rules.
The hook runs after validation and payment method tokenization (including Plaid exchange when applicable), and before the checkout is completed, so the state includes the latest values such as paymentToken when available.
The hook receives three parameters:
state: ACheckoutStateobject containing the current checkout informationresolve: A function to call to proceed with submissionreject: A function to call to stop submission
const checkout = document.querySelector('justifi-modular-checkout');
/**
* CheckoutState example (shape):
* {
* selectedPaymentMethod: { id?: string, type: 'new_card' | 'apple_pay' | 'google_pay' | ... } | undefined,
* paymentAmount: 5000,
* totalAmount: 5000,
* paymentCurrency: 'USD',
* paymentDescription: 'Order #123',
* savedPaymentMethods: [],
* savePaymentMethod: false,
* bnplEnabled: false,
* applePayEnabled: true,
* insuranceEnabled: false,
* disableBankAccount: false,
* disableCreditCard: false,
* disablePaymentMethodGroup: false,
* paymentToken: 'pm_123' // tokenized payment method id; Apple Pay, Google Pay and Plaid set this too (paymentMethodId)
* }
*/
checkout.preCompleteHook = (state, resolve, reject) => {
// Example: Require confirmation for large payments
if (state.totalAmount > 100000) {
const confirmed = confirm(
`Confirm payment of $${(state.totalAmount / 100).toFixed(2)}?`,
);
if (confirmed) {
resolve(state);
} else {
reject();
}
} else {
resolve(state);
}
};
Important: Assign the hook as a JavaScript property on the element (e.g.,
checkout.preCompleteHook = fn). Do not pass it as an HTML attribute (e.g.,pre-complete-hook="..."); functions must be set on properties, not attributes.
See the CheckoutState type definition in the Public Types section below for the complete structure of the state object.
Validation
When submitCheckout is called, the justifi-modular-checkout wrapper component automatically validates all included sub components. If any required fields are missing or invalid, submission is blocked and validation errors will appear inline.
You can also trigger validation manually by calling the validate method on the justifi-modular-checkout wrapper component. This returns a promise that resolves to a boolean indicating whether the form is valid.
Setting Payment Method Programmatically
You can programmatically set the selected payment method by calling the setSelectedPaymentMethod method on the justifi-modular-checkout wrapper component. This method accepts either a saved payment method object or an object with a type field.
Important: The wrapper does not automatically select a default payment method. If you want a default to be selected (for example, the first saved method or a new card), explicitly call
setSelectedPaymentMethodafter initialization (e.g., on the firstcheckout-changedevent).
import { PAYMENT_METHODS } from '@justifi/webcomponents';
const modularCheckout = document.querySelector('justifi-modular-checkout');
// Set to new card payment method
modularCheckout.setSelectedPaymentMethod({ type: PAYMENT_METHODS.NEW_CARD });
// Set to new bank account payment method
modularCheckout.setSelectedPaymentMethod({
type: PAYMENT_METHODS.NEW_BANK_ACCOUNT,
});
// Set to a saved payment method by id
modularCheckout.setSelectedPaymentMethod({
id: 'pm_123',
type: PAYMENT_METHODS.SAVED_CARD,
});
Checkout Changed Event
The justifi-modular-checkout wrapper component emits a checkout-changed event whenever any internal checkout state changes. The event detail includes availablePaymentMethodTypes, selectedPaymentMethod, and savedPaymentMethods.
const modularCheckout = document.querySelector('justifi-modular-checkout');
modularCheckout.addEventListener('checkout-changed', (event) => {
const {
availablePaymentMethodTypes,
selectedPaymentMethod,
savedPaymentMethods,
} = event.detail;
console.log('Available:', availablePaymentMethodTypes);
console.log('Selected:', selectedPaymentMethod);
console.log('Saved methods:', savedPaymentMethods);
});
availablePaymentMethodTypes includes:
saved_card,saved_bank_account: Saved methods frompayment_method_group(respects disabling flags).new_card,new_bank_account: Entry forms for new payment methods (respects disabling flags).sezzle: BNPL is available when enabled by the account and not disabled.plaid: Plaid is available when bank account verification is enabled for the checkout.
Insurance Integration
The justifi-modular-checkout wrapper component automatically integrates the justifi-season-interruption-insurance component.
- Automatic Refresh: When insurance values change the checkout data is automatically refreshed when insurance values are updated
- Feature Flag: If the checkout's
payment_settings.insurance_paymentsisfalse, the wrapper will skip validating the insurance component. Thejustifi-season-interruption-insurancecomponent will also render nothing and log a console warning indicating that insurance is disabled for the checkout.
<justifi-modular-checkout auth-token="123xyz" checkout-id="cho_1234567890">
<justifi-checkout-summary />
<justifi-card-form />
<justifi-billing-form-full />
<justifi-season-interruption-insurance />
</justifi-modular-checkout>
Public Types
import { PAYMENT_METHODS } from '@justifi/webcomponents';
import type {
SelectedPaymentMethod,
SavedPaymentMethod,
CheckoutChangedEventDetail,
CheckoutState,
} from '@justifi/webcomponents';
Authorization
Authorization is performed by passing a web component token as auth-token.
- Web Component Token: These tokens are generated by your backend services using the Web Component Tokens API. Each token can be scoped to perform a set number of actions and is active for 60 minutes. When creating a web component token for this specific component you'll need to use the roles:
write:checkout:checkout_idandwrite:tokenize:account_id. Make sure the value foraccount_idmatches the account associated with the checkout.
<!DOCTYPE html>
<html dir="ltr" lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=5.0" />
<title>justifi-modular-checkout</title>
<script
type="module"
src="https://cdn.jsdelivr.net/npm/@justifi/webcomponents@>=6.12.3/dist/webcomponents/webcomponents.esm.js"
></script>
<script
nomodule
src="https://cdn.jsdelivr.net/npm/@justifi/webcomponents@>=6.12.3/dist/webcomponents/webcomponents.js"
></script>
<style>
::part(font-family) {
font-family: georgia;
}
::part(color) {
color: darkslategray;
}
::part(background-color) {
background-color: transparent;
}
</style>
</head>
<body>
<justifi-modular-checkout auth-token="authToken" checkout-id="cho_1234567890">
<justifi-checkout-summary />
<justifi-card-form />
<justifi-billing-form-full />
</justifi-modular-checkout>
</body>
<script>
(function () {
const modularCheckout = document.querySelector('justifi-modular-checkout');
modularCheckout.addEventListener('error-event', (event) => {
// this event is emitted when validation fails or an error occurs during processing
console.error('error-event', event.detail);
});
modularCheckout.addEventListener('submit-event', (event) => {
// this event is emitted when checkout is successfully completed
console.log('submit-event', event.detail);
});
modularCheckout.addEventListener('checkout-changed', (event) => {
// this event is emitted when the checkout state changes
// includes availablePaymentMethodTypes, selectedPaymentMethod, and savedPaymentMethods
const {
availablePaymentMethodTypes,
selectedPaymentMethod,
savedPaymentMethods,
} = event.detail;
console.log('Available payment methods:', availablePaymentMethodTypes);
console.log('Selected payment method:', selectedPaymentMethod);
console.log('Saved payment methods:', savedPaymentMethods);
});
})();
</script>
</html>