Skip to content
Lockerverse SDK
Esc
↑↓navigate↵open⌘Jpreview
On this page

Payment primitives

Use shared checkout UI with your own Core SDK payment flow.

Use LockerversePayment for the complete checkout. Use these controlled components when your website needs its own layout and payment flow. The ready-made checkout uses the same fields and PaymentSummary; this entry does not import Stripe or the ready-made payment controller.

import {
  buildCustomFieldAnswers,
  createCustomFieldValues,
  CustomField,
  CustomFields,
  EmailField,
  getCheckoutCustomFields,
  PaymentSummary,
  reconcileCustomFieldValues,
  validateCustomFields,
} from "@lockerverse/react/payment-primitives";
import "@lockerverse/react/payment.css";

See the live composition example. It loads an authoritative quote with the Core SDK and lets a website input and an SDK field share the same React state. It previews submission fields and does not charge.

Controlled custom fields

const fields = getCheckoutCustomFields(catalog, checkout.items);
const [values, setValues] = useState(() =>
  createCustomFieldValues(fields, { [itemNameFieldId]: "Autographed basketball" })
);
const [errors, setErrors] = useState<Record<string, string>>({});

<CustomFields
  errors={errors}
  fields={fields}
  onChange={setValues}
  values={values}
/>

CustomFields renders all selected fields. Use CustomField for one field at a time, so you can put your own UI between them. Pass its field, value, onChange, and optional error. The caller owns state; a website input can change the same state at any time. The components supply labels, control IDs, required states, and error associations.

Use field IDs as keys. Text uses strings, a single checkbox uses a boolean, single-choice fields use an option ID, and multiple-choice fields use an array of option IDs. createCustomFieldValues filters initial values against the field definitions. Call reconcileCustomFieldValues(fields, values) when the selected products or field definitions change. This removes fields and options that no longer apply. These helpers do not change the input object.

Before submission, call validateCustomFields. It returns messages keyed by field ID for missing required values. Render those messages and focus the first invalid field. Then use buildCustomFieldAnswers(fields, values) for the Core SDK submission shape. The backend remains the final validation authority.

Submit through the Core SDK

The UI components do not submit or approve payments. Keep the existing Core SDK flow: obtain an authoritative quote for the final selection, email, fees, tip, and promo code; show any changed total; configure Stripe for that quote; and complete Stripe Elements validation and confirmation-token creation.

At the submission step, add the shared field values to the normal request:

const fieldErrors = validateCustomFields(fields, values);
if (Object.keys(fieldErrors).length > 0) {
  setErrors(fieldErrors);
  return;
}

// finalCheckout comes from client.calculateTotal() for the final pricing inputs.
// confirmationToken comes from Stripe after Elements validation. Omit it only
// for a free checkout. Include required phone/shipping fields in contactDetails.
const result = await client.submitPayment({
  checkout: finalCheckout,
  submission: {
    ...contactDetails,
    email,
    confirmationToken,
    customFieldAnswers: buildCustomFieldAnswers(fields, values),
  },
});

This is the submission step, not a complete payment controller. Keep Stripe authentication, payment-status recovery, duplicate-submit protection, and confirmed completion handling in your host flow. A returned result can need more action; do not show success merely because this promise resolves. If an error has recoveryRecommended: true, call client.getPaymentStatus(finalCheckout.paymentReference) before allowing a new payment attempt.

Public components and styles

Export Purpose
FormField, FieldError Label, content wrapper, and associated error message
TextField, EmailField, Checkbox Standard controlled form inputs
PhoneInput, getInternationalPhone Country-aware phone input and normalization before submission
CustomField, CustomFields Widget-defined text and choice fields
PaymentSummary The same order summary used by ready-made checkout

Each component has a named ...Props type. CustomFieldValues and PhoneInputValue describe the controlled state.

Import @lockerverse/react/payment.css and use a lockerverse-payment wrapper with data-lockerverse-theme="light" or "dark" to retain the checkout appearance. For a custom design, omit that stylesheet and style the components in your host. Their labels, input behavior, and error associations remain.

PaymentSummary needs only quote: <PaymentSummary quote={checkout} />. Its optional displayedBaseCents, displayedTipCents, and displayedTotalCents properties let a host show pending price changes, as the ready-made checkout does. They default to the quote’s total minus tip, tip, and total. Display overrides affect the UI only; they never change the payable amount.

Last updated on October 15, 2026

Was this page helpful?