> ## 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.

# Building My Own Checkout Page Instead of Using Waffo's

> What a merchant-built checkout can and cannot get from Waffo: when orderAction.actionData is populated and when it is empty, which payment instruments come back as raw data, who validates the required customer fields, how to query payment-method availability and amount ranges yourself, whether hosted and self-built flows can be mixed order by order, and the parts of the flow that only the Waffo-hosted checkout performs.

[Choosing an integration approach](/docs/en/developer-docs/use-cases/checkout-solution)
compares the two modes and tells you which request parameters each one needs.
This page answers the questions that come up **after** you have decided to build
your own page: what data you actually receive, what you have to do yourself, and
which parts of the flow have no merchant-side equivalent.

The field-by-field reference for `orderAction.actionData` lives in
[API reference tables](/docs/en/support/api-reference-tables).
This page does not repeat it.

## What do I have to receive and handle if I build my own payment page?

Four things, in this order:

| Step | What you do | Where the data comes from |
| - | - | - |
| 1. Decide the payment method | Your page collects the customer's choice | Your own UI, built from a list you query (see below) |
| 2. Create the order | `POST /api/v1/order/create`, passing `payMethodType` and — for a specific wallet — `payMethodName` | [Checkout customization](/docs/en/developer-docs/integration/checkout/customization) |
| 3. Present the payment instrument | Read `orderAction`. Either redirect the customer (`actionType` = `WEB` / `DEEPLINK`) or render the raw instrument from `actionData` | [Redirects and `actionType`](/docs/en/support/cashier-redirect-and-action-type-faq) |
| 4. Confirm the result | Your payment notification (webhook), or `POST /api/v1/order/inquiry` | [Payment results](/docs/en/support/payment-results-faq) |

Nothing about step 4 changes when you build your own page. A customer arriving
back on your page is not proof of payment, and a rendered QR code is not proof
of payment. The notification and the inquiry remain the only authoritative
sources.

## When is `orderAction.actionData` actually populated?

**Only when the order already resolved to one specific payment method at
create time.** This is the single most important rule for a merchant-built
checkout, and it is easy to miss:

| What you sent on `/api/v1/order/create` | What you get back |
| - | - |
| A specific payment method (`payMethodType`, plus `payMethodName` where the type has several sub-methods) | `orderAction` with the redirect fields **and** `actionData` where the method has an instrument to hand over |
| No payment method — you left the choice to the customer | `orderAction` pointing at the Waffo-hosted checkout. `actionData` is **empty** |

The reason is mechanical: when the customer has not chosen yet, no payment has
been submitted to the payment method, so there is no account number, no QR
payload and no expiry to return. They come into existence only after a method
is picked. If you leave the choice to the customer, the choice happens on the
Waffo-hosted page — and at that point the data has nowhere to go except that
page.

So if `actionData` is empty and you expected content, check the payment-method
parameters on your create request first.

## Which payment instruments come back as raw data, and which force a redirect?

`actionData` carries whatever the routed payment method produces. Broadly:

| Kind of payment method | What you can expect |
| - | - |
| QR-code methods | A QR payload in `paymentQr` — a **string**, not an image |
| Over-the-counter and code-based methods | `paymentCode` and/or `paymentBarCode` |
| Bank transfer and virtual account | `bankAccount`, `bankName`, `bankBeneficiary`, `referenceNumber` |
| Wallets that authorize on their own surface | No instrument — a redirect URL instead, or an app push with no URL at all |
| Cards | No instrument. Card data collection cannot be handed to `actionData` |

Two consequences worth planning for:

* **You render the QR yourself.** `paymentQr` is the raw payload string. Encode
  it into a scannable code client-side, at a size that survives a phone camera.
* **A method can populate several of these, or none.** Which fields arrive is a
  property of the payment method the transaction routed to, not of the country
  or of your account. Build your page to render whichever fields are present
  and to fall back to the redirect when none are.

## Can I get a local bank account number — a CLABE, an IBAN, a virtual account?

Yes, through the same field: `bankAccount`. It carries the account the customer
must transfer to, in whatever form the local system uses — a CLABE in Mexico, an
IBAN elsewhere, a routing plus account number, or a virtual account generated
for that one order. `bankName`, `bankBeneficiary` and `referenceNumber` come
alongside it.

Two caveats:

* It is populated for **bank-transfer and virtual-account style methods that
  return one**. It is not a field you can request for an arbitrary payment
  method, and asking for "the CLABE" in the abstract has no answer — the
  question is always whether the specific method you are integrating returns a
  `bankAccount`. Verify per method in sandbox before you build the screen.
* The rule above still applies: you get it only if you pinned the payment
  method on the order.

When you ask Waffo support whether a given method returns bank-transfer data,
include the method you intend to use and the `userInfo.userTerminal` you will
send — terminal type affects which methods are eligible at all, so the answer
can differ between a PC and a mobile session.

For how these fields behave once the customer transfers — expiry, wrong amount,
reconciliation on `referenceNumber` — see
[Bank transfers and virtual accounts](/docs/en/support/bank-transfer-and-virtual-account-faq).

## `apiMethod` says `POST`. Do I have to build and submit a form myself?

On a merchant-built checkout, yes. `orderAction` describes the redirect
completely, and a `POST` redirect needs all three parts:

| Field | What to do with it |
| - | - |
| `actionType` | `WEB` → use `webUrl`; `DEEPLINK` → use `deeplinkUrl` |
| `apiMethod` | `GET` → navigate to the URL. `POST` → submit a form to it |
| `formBody` | The form fields, in `a=1&b=2` form. Split it and emit one hidden input per pair |

Do not drop or re-encode parameters while doing this — several payment methods
carry required values in the URL or the body, and a rewritten URL fails at the
payment method rather than at Waffo.

If you use the Waffo-hosted checkout instead, this is handled for you: the page
builds and submits the form itself.

## Do I have to query payment-method availability and amount limits myself?

Yes. This is the main ongoing cost of a merchant-built checkout, and it is the
one most often underestimated.

| What you must maintain | How to get it |
| - | - |
| Which payment methods your account can offer, per country and currency | `POST /api/v1/paymethodconfig/inquiry` — see [Payment method configuration and limits](/docs/en/support/payment-method-config-and-limits-faq) |
| The amount range each method accepts | The same response, before you create the order |
| Whether a method is temporarily unavailable | The status fields in the same response |
| Newly signed payment methods | Nothing appears on your page until you add it |

On the Waffo-hosted checkout all four are automatic: unavailable methods are
grayed out, out-of-range methods are not offered, and a newly signed method
shows up without a code change. On your own page, an out-of-date list turns
into a failed order the customer already committed to.

## Who validates the customer fields a payment method requires?

You do, for the fields you send. Several payment methods have fields that are
documented as optional on `/api/v1/order/create` but are **mandatory for that
method** — a national ID, a phone number, a postcode, an email address. If you
collect them on your own page, you own the format validation, and Waffo's only
feedback is a rejected order-create call.

The list of which fields become mandatory for which method type is in
[Conditionally required fields](/docs/en/support/conditional-required-fields-faq),
and the field paths that can appear in a missing-field error are in the same
place.

On the Waffo-hosted checkout these fields are collected on the page, with the
payment method's own format rules applied inline, before the payment is
submitted.

## Can I mix the two — self-built for some payment methods, hosted for others?

Yes, and it is decided per order rather than per account. The same
`/api/v1/order/create` endpoint gives you either behaviour:

* Pass a specific payment method → the customer never sees a Waffo selection
  page, and you receive the instrument or the redirect to drive your own screen.
* Pass no payment method → the customer lands on the Waffo-hosted checkout and
  chooses there.

A common shape is to run your own page for the two or three methods that
dominate your volume, and fall back to the hosted checkout for the long tail —
which also means new payment methods work with no change on your side.

What you cannot do is switch mid-order. Once a checkout session has resolved to
a payment method, that choice is fixed for the session.

## Can I build the over-the-counter store selection page myself?

Not as a self-serve integration. For over-the-counter methods where the
customer picks a specific store or outlet, the store list is presented by the
Waffo-hosted checkout during the payment step; `actionData` returns the payment
code the customer takes to the counter, not the list of places that accept it.

So a merchant-built page can present the code and the instructions, but the
selection step it comes from has no merchant-facing equivalent. If your product
needs that screen inside your own UI, raise it with your Waffo contact as a
product request rather than looking for a parameter.

## Can I build my own page for card payments?

Only if you are prepared to be in PCI DSS scope. That is the trade-off in the
[integration comparison](/docs/en/developer-docs/use-cases/checkout-solution#comparison):
with the Waffo-hosted checkout, card data never touches your systems.

Two intermediate options exist that keep you out of scope while still letting
you control the page:

* **Card tokenization**, where the card fields are rendered by the Waffo
  front-end SDK inside your page — see
  [Tokenization](/docs/en/support/tokenization-faq).
* **The embedded checkout**, where the Waffo checkout is rendered inside your
  page rather than replacing it — see
  [Checkout integration steps](/docs/en/developer-docs/integration/checkout/steps#iframe-embedding-considerations).

## Is Apple Pay available to a merchant-built checkout?

Yes — a direct Apple Pay integration against your own Apple merchant identity
is one of the supported options, alongside redirecting to the Waffo checkout,
loading it in an App WebView, and loading it in an iframe with the Waffo
front-end SDK. The four options and what each one requires are set out in
[Apple Pay integration](/docs/en/developer-docs/integration/apple-pay/overview).

What is not available is a plain iframe of the checkout URL for Apple Pay: that
path requires the front-end SDK plus domain verification arranged in advance.

## Does a self-built checkout change my reporting, receipts or refunds?

No. Everything after the payment is identical, because the order is the same
order:

| Area | Behaviour |
| - | - |
| Payment notification | Same events, same payload |
| Order inquiry | Same endpoint, same statuses |
| Refunds | Same `/api/v1/order/refund` and the same per-method refund rules |
| Portal transaction lists and exports | The transaction appears the same way |
| Receipts | Governed by your receipt configuration, not by which page the customer paid on |

The parts that differ are all on the presentation side: payment-method
selection, required-field collection, instrument rendering, status polling and
the result screen.

## What does the hosted checkout do that I would have to rebuild?

Worth reading as a checklist before committing:

* Payment-method selection, grouping and per-country ranking, plus hiding or
  graying out methods that are unavailable right now
* Collecting and validating each method's required customer fields
* Rendering QR codes and barcodes, and offering copy and save actions
* Polling for the payment result and advancing the page when it lands, so the
  customer is not left on a dead screen
* The retryable-error path, where a transient failure is retried in the
  background and the page moves on by itself
* Letting the customer pick a different payment method after a failure, without
  a new order
* The amount breakdown — price, discount, subtotal with the FX rate, customer
  fee, tax, total
* The display language, including selecting one when you did not send
  `cashierLanguage`
* The expired-link and already-paid screens for a link a customer reopens later

***

Still deciding? [Choosing an integration approach](/docs/en/developer-docs/use-cases/checkout-solution)
has the side-by-side comparison. If you have picked the merchant-built route and
need to confirm what one specific payment method returns,
[contact support](mailto:support@waffo.com) with the payment method, the country
and the `userInfo.userTerminal` you will send.


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