Payment method picker
Selectable cards for Mobile Money operators, card and cash. Choosing a Mobile Money option reveals a phone input for that operator right under the cards.
Preview
- methodId
- null
- e164
- null
- ready
- false
Installation
npx shadcn@latest add @mpui-kit/payment-method-pickerRequires the @mpui-kit namespace. See Getting started.
Usage
import { PaymentMethodPicker } from "@/components/mpui-kit/payment-method-picker"
import { emptySelection, type PaymentSelection } from "@/lib/mpui-kit/payment-methods"
// Inside <MpuiKitProvider country={cm}>
const [selection, setSelection] = useState<PaymentSelection>(emptySelection)
const [ready, setReady] = useState(false)
<PaymentMethodPicker
value={selection}
onChange={(next, resolved) => {
setSelection(next)
setReady(resolved.ready) // card and cash: true. Mobile Money: needs a valid number
console.log(resolved.e164) // "+237651234567" for Mobile Money once valid
}}
/>Props
| Prop | Type | Default | Description |
|---|---|---|---|
| value | PaymentSelection | — | The current choice: { methodId: string | null, phone: string }. phone is the national number as digits. Makes the picker controlled. |
| defaultValue | PaymentSelection | nothing selected | Initial choice for an uncontrolled picker. |
| onChange | (selection: PaymentSelection, resolved: ResolvedPayment) => void | — | Called when the method or the phone number changes. resolved.ready says whether the selection can be paid, and resolved.e164 is the number for Mobile Money. |
| methods | PaymentMethod[] | defaultPaymentMethods(country) | Methods to offer. By default: Mobile Money for each operator with a wallet, then card and cash. |
| country | CountryConfig | MpuiKitProvider's country | Country to use. Throws if neither this nor a provider is set. |
| locale | "fr" | "en" | MpuiKitProvider's locale, then “fr” | Language of the labels and messages. |
| legend | string | “Payment method” | Group label read by screen readers. |
| error | string | — | Error under the cards, for example “Choose a payment method to continue.” |
| operatorLogos | Record<string, ReactNode> | — | Your own logos by operator id, shown instead of the color dot. |
| disabled | boolean | false | Disables every card and the phone field. |
| phoneError | string | — | Error for the phone field of the chosen Mobile Money method. Overrides its built-in error. |
| classNames | { legend, options, option, optionLabel, optionDescription, phone, error } | — | Class names for parts of the picker. Style the selected card with has-[:checked]:... in option. Each part also has a data-slot attribute. |
| ...props | ComponentProps<'fieldset'> | — | Other fieldset props, such as className. |
Choosing the methods
A country offers Mobile Money for every operator that has a wallet in its data. Pass methods to change that.
import { defaultPaymentMethods } from "@/lib/mpui-kit/payment-methods"
// Mobile Money only
<PaymentMethodPicker methods={defaultPaymentMethods(cm, { card: false, cash: false })} />
// Your own list
<PaymentMethodPicker
methods={[
{ id: "mtn", kind: "mobile_money", operatorId: "mtn" },
{ id: "cash", kind: "cash", label: "Pay on arrival", description: "No fees" },
]}
/>Notes
- Native radios. The cards are radio inputs in a labelled fieldset, so arrow keys move the choice, the group is one tab stop, and screen readers announce it as a group. There is no custom key handling to break.
- Focus is not moved when the phone field appears, because that would interrupt arrow-key navigation between cards. The field sits right under the cards.
- The phone number is kept when you switch between Mobile Money operators, in case someone picked the wrong one.
- A number that belongs to a different known operator is rejected (
operator_mismatch). A prefix that is not in the country data is accepted, because the data is community-maintained and may be incomplete. - This component only collects a choice. It never calls a payment API. See the checkout block for the full flow.