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.