> ## Documentation Index
> Fetch the complete documentation index at: https://waffo.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Checkout Forms, Prefill & Error Handling

> What the customer actually has to fill in on the Waffo checkout: whether the details you sent are prefilled, when the payment-method selection page is skipped, card form validation rules and lengths, how a saved card behaves, QR-code lifetime, and which checkout errors are retried versus sent straight to the failure page.

This page is about what the customer has to type and what happens when
something is wrong. Which payment methods appear and in what order is covered
by the Checkout Payment Method Display Rules page; page-level branding is
covered by the Customizing the Checkout Page docs; reopened links, processing
states and result redirects are covered by the Checkout Page Lifecycle &
Receipt page.

## I send the customer's email and name at order create. Why does the checkout still ask for them?

Because re-using what you sent is an **account-level setting that is off by
default**, and it is set per field. Until it is turned on for your account, the
checkout treats your values as data for the payment, not as something to show
back to the customer.

Why it works that way: a meaningful share of integrations send placeholder or
synthetic contact details. Displaying an obviously fake email back to the buyer
costs trust at the worst possible moment, and some of those values are rejected
downstream anyway. Opt-in per field keeps that out of the buyer's face.

These are the fields that can be re-used, selected individually:

| Field |
| - |
| `userInfo.userEmail` |
| `userInfo.userPhone` |
| `userInfo.userFirstName` |
| `addressInfo.postcode` |
| `addressInfo.address` |
| `addressInfo.city` |
| `addressInfo.region` |
| `addressInfo.addressCountryCode` |

There is a mutually exclusive **none** option: either you list fields, or you
declare that nothing is re-used. You cannot combine "none" with a field list.

Note that first name carries the full name — if you send a first name without a
last name, the last name is stored empty rather than being guessed.

To change this for your account, contact your Waffo contact with the list of
fields you want re-used.

## If the customer edits the email I sent, which value do you use?

The customer's. A value the buyer changes on the checkout page replaces what
you sent for the rest of that payment, including what is passed on to the
payment method. This is deliberate: the buyer is the one who knows their own
contact details, and for methods that verify the address or send a
confirmation, using a stale value would fail the payment.

Your original order data is unchanged — check the order record if you need to
know what you originally sent.

## Do returning customers have to type everything again?

No. Two separate mechanisms exist, and they are easy to confuse:

| Mechanism | Whose data | Controlled by |
| - | - | - |
| **Saved information** | What the customer typed on a previous purchase | The customer, via a "save my information" option on the form |
| **Merchant prefill** | What you sent on the order | Your account setting above |

## When does the checkout skip the payment-method selection page?

Three rules, evaluated in this order:

| Situation | What the customer sees first |
| - | - |
| The order resolves to exactly **one group, and it is the card group** | The card form directly |
| The order resolves to exactly **one group with exactly one method**, and that method needs a form | That method's form directly |
| Anything else | The selection page |

On top of that, if you passed a `payMethodId` on the order and that method
needs input, the checkout opens that method's form directly rather than the
selection page. If you passed a `payMethodId` that needs no input, its group is
preselected on the selection page.

When no `payMethodId` is passed, the preselection is the **first available**
method in the first group that has one — an unavailable method is never
preselected.

## What are the exact validation rules on the card form?

| Field | Rule |
| - | - |
| Card number | Required. **17–23 characters** as displayed, including the spaces the field inserts as you type |
| Expiry | Required. Exactly **5 characters** (`MM/YY`), and the date must be in the future |
| CVV | Required. Length depends on the card brand — see below |
| Cardholder name | Required. Up to **100 characters**. Latin letters only |

CVV length is resolved from the brand as soon as the card number identifies it:

| Brand | CVV length |
| - | - |
| American Express | Exactly 4 |
| Visa | Exactly 3 |
| Mastercard | Exactly 3 |
| Brand not yet identified, or other | 3–4 accepted |

So a CVV that was accepted while the number was incomplete can become invalid
once the brand is known. That is expected.

## Why do I only see one error message when several fields are wrong?

Each field shows **at most one message** — the first rule it fails, in the
order required → format → too short → too long. Messages appear once a field
has been interacted with; pressing the pay button marks every field as
interacted, so on submit each failing field shows its own single message.

One case behaves differently. **"This card is currently not supported"** is
evaluated live against the card number as it is typed, so it can appear before
you leave the field. But it is still a message on the card-number field, which
means a length or format problem on the number itself is reported first — fix
the number, and the unsupported-card message is what remains.

That message means the card itself is out of scope (its type, brand or issuing
region is not accepted), not that the form is malformed. Related codes you may
see on the same field:

| Code | Meaning |
| - | - |
| `008001B027` | This card is currently not supported |
| `008001B028` | The card's type is not supported |
| `008001B029` | The card's issuing region is not accepted |
| `008001B030` | The card's brand is not supported |
| `008001B125` | Cardholder name must contain only English letters |

## What does a customer with a saved card see?

If you pass a `userPaymentAccessToken` on the order, the checkout goes straight
to a short completion step: the card is shown masked, and the **only** field
the customer has to enter is the **CVV**. The card lookup step is skipped
entirely, so there is no per-keystroke card check on that page.

If you do **not** specify a payment method, the selection page lists the cards
already associated with that customer under your account, alongside the option
to add a new card. Each customer can have one default card per merchant, and
the checkout preselects it. You can also restrict, via the API, which saved
cards are offered on a specific order.

Removed or expired tokens are rejected rather than silently ignored — code
`008001B113` means the card has been removed.

## Can the customer choose to save their card for next time?

A save option is offered on the form, and whether it appears depends on the
method and on your account:

| Method type | The save option appears when |
| - | - |
| Credit / debit card | Card saving is enabled for your account for that method |
| Everything else | Saving the customer's entered details is enabled for your account |

You can also drive it from the order: `paymentInfo.bindForFutureUse` (`0` = no,
`1` = yes) asks for the payment method to be bound for future use on that
payment. Nothing is saved silently — the customer sees the option on the form.

Where saved cards then show up, and how to charge them later, is covered by the
tokenization documentation.

## I pass a fixed card prefix on the order. Does that change the form?

Yes. When the order fixes the leading digits of the card number, the checkout
renders those digits as a non-editable prefix in front of the input, and the
number field's accepted length shrinks by exactly the number of digits it
already shows. The customer types only the remainder, and the total accepted
length is unchanged.

## Are there test cards on the sandbox checkout?

Yes. On sandbox, the card form shows quick-fill card chips above the fields;
clicking one populates number, expiry, CVV and name. They are rendered only in
the sandbox environment — there is no code path that shows them in production,
so nothing needs to be switched off before go-live.

## Can a customer save a QR code and pay it later?

No, and this is worth putting in your own support macros.

* The QR code is valid for **that payment attempt only**. It is not a reusable
  payment address and cannot be re-presented for a later order.
* Once the order has expired or succeeded, reopening the checkout link no
  longer shows a QR code at all — the link resolves to the failure page or the
  success page, according to the real order state.
* The **Save QR Code image** button is offered on **mobile only**. On desktop
  it is not rendered, because a saved desktop screenshot is exactly the artefact
  that leads customers to try to pay an expired code.

## The checkout page is showing an error but keeps spinning. Is it stuck?

No — that is the retryable path. Every error that reaches the checkout carries
a retryable flag, and the page behaves differently for each:

| Kind of error | What the customer sees |
| - | - |
| **Not retryable** — declines, limits, validation, expired links | The result is final: the page goes to the **failure page** with the reason |
| **Retryable** — timeouts, system errors, concurrent-operation conflicts | The page stays on the **processing view**, shows the reason, and Waffo keeps retrying in the background. When a retry produces a result, the page advances on its own |

The customer never has to resubmit on the retryable path, and you should not
build a client-side retry on top of it. Treat your payment notification or an
order inquiry as the authority for the final state — not what the page showed
at any moment.

## Which checkout error codes should I recognize?

The full list is long and mostly issuer-driven. These are the ones that come up
in integration testing, where the cause is on your side or the customer's:

| Code | Message | Usually means |
| - | - | - |
| `008000S006` | Timeout. Please try again later | Transient; retryable |
| `008001B003` | Payment link expired. Please create another payment | The checkout link's validity window has passed |
| `008001B007` | You have submitted, please refresh the page | Duplicate submission of the same step |
| `008001B011` | Please input the correct phone number | Phone number format rejected |
| `008001B025` | Sorry your payment order has expired | The order, not just the link, has expired |
| `008001B049` | Order has expired, please place a new order | Same, from the payment step |
| `008001B058` | OTP has expired. Please request a new one | One-time code timed out |
| `008001B059` | OTP verification failed. Please try again | Wrong one-time code |
| `008001B092` | No payment methods are available for this region | Nothing under your agreements matches the resolved country |
| `008001B099` | The transaction currency is not supported | Currency is not enabled for the resolved method |
| `008001B101` | Invalid postcode | Postcode failed the payment method's format check |
| `008001B116` | Receipt link expired | The receipt link is past its window; see the receipt docs |
| `008001B124` | Today's verification attempts have reached the limit | Daily verification cap hit; the customer must retry the next day |

Codes in the `008001B03x`–`008001B09x` range are almost all issuer or wallet
decisions relayed verbatim. They are shown to the customer as-is; you cannot
convert them into a success by retrying with the same instrument.

## Some payments ask the customer for an email or an email code. Why?

Certain payment methods require a verified email address before they will
accept the payment, so the checkout collects it and, where required, verifies
it with a one-time code sent to that address. It is a requirement of the
routed payment method, not something Waffo adds per merchant.

What to expect:

* If you sent a usable `userInfo.userEmail`, it can prefill the field. For some
  methods the address is fixed for that payment and the customer cannot replace
  it — in that case sending a different address on the same step is rejected.
* The one-time code is short-lived. A successful verification is remembered
  for a limited window on that order, so the customer does not have to repeat
  it while completing the payment.
* Email addresses are capped at 50 characters on this step.
* Once the customer has chosen a payment method in a checkout session, that
  choice is fixed for the session.

***

Seeing a validation message you cannot reproduce?
[Contact support](mailto:support@waffo.com) with the order ID, the exact code
shown, and whether the customer was on mobile or desktop.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.