Skip to content

Hosted Checkout

The page Wajub renders for your customer, what it handles, and what you control.

Hosted Checkout is the shortest path to a working payment. You create the payment, send the customer to the page Wajub renders, and verify the outcome when they come back. The operator list, the prompt, the retry when it times out and the wording of every failure belong to us, and they keep improving without you touching anything.

What your customer sees: the amount, the operators available in their country, and the prompt waiting for confirmation on their handset. You render none of it.

This page is the behaviour, not the code

What the hosted page does, what it decides on its own, and what you can change about it. The integration code itself, which URL to build and which component to mount for a redirect, an inline frame or an overlay, is in Wajub Components → Hosted checkout.

Create the payment

Everything the page needs comes from the create call. Send it from your server with the secret key: the public key also creates payments, but it exists for the case where the call is made in a browser.

POSThttps://api.wajub.com/payments
amountnumberrequired
Amount in the major unit of the currency. Decimals are accepted, so 10.50 is ten dollars fifty. In XAF and XOF the payment must fall between 25 and 2 000 000.
currencystringrequired
ISO 4217 code: XAF, XOF, NGN, GHS, KES and the rest of the supported list.
customerobjectoptional
Payer identity: email, phone, name, plus address and metadata. At least one of customer, email, phone or customer_id is required, and the object is the form that grows with you.
descriptionstringoptional
A single label shown to the customer on the page. Defaults to "Payment".
itemsarrayoptional
Line items displayed on the page, each with a name, a description and a quantity. They are shown, never summed: amount remains what you charge.
callbackstring (url)optional
Where the customer is sent afterwards, whatever the outcome: success, failure, cancellation or expiry, told apart by status in the query string. Must be HTTPS, and http://localhost or http://127.0.0.1 are accepted in sandbox; any other address answers 422. Omit it and they stay on a Wajub confirmation page, in which case the webhook is your only signal.
referencestringoptional
Your own order number. It comes back on every read and in the return URL. It carries no uniqueness constraint, so it is not what prevents a double charge.
expires.inintegeroptional
How long the page stays usable, in minutes, from 5 to 43 200. Defaults to 1 440, which is 24 hours.
bearerstringoptional
Who pays the Wajub fee: merchant by default, or customer, which adds the fee on top and shows the breakdown on the page.
acceptobjectoptional
Narrows the payment methods offered, by countries, channels, types and currencies. Omit it and every method you can collect on is shown. See [Choosing what a payment accepts](/payments/payment-methods#choosing-what-a-payment-accepts).
metadataobjectoptional
Free key-value pairs, returned on reads and in webhooks. Use callback for the return: one address for every outcome.

A create call that uses the hosted page's own fields looks like this:

curl https://api.wajub.com/payments \
  -H "Authorization: sk_test.xxxxxxxxxxxxxxxx…" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 25000,
    "currency": "XAF",
    "customer": { "email": "[email protected]", "name": "Amina N." },
    "description": "Order #4172",
    "reference": "order-4172",
    "items": [
      { "name": "Wireless earbuds", "quantity": 1 },
      { "name": "Delivery", "quantity": 1 }
    ],
    "expires": { "in": 60 },
    "callback": "https://shop.example.com/order/complete"
  }'

The response carries the payment and the page to send the customer to. Store transaction.id against your order before you redirect:

Response · 201 Created
{
"code": 201,
"status": "Created",
"message": "Payment initiated",
"authorization_url": "https://pay.wajub.com/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"authorization_token": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"transaction": {
"id": "trx_test_CSUGajfv9xh0XQ5wu2lx",
"reference": "order-4172",
"amount": 25000,
"currency": "XAF",
"status": "pending",
"sandbox": true,
"created_at": "2026-09-11T10:24:00+00:00"
}
}

authorization_url carries an opaque session token, not the transaction id. Redirect to it exactly as returned: never parse it, and never build one yourself. The host is not always pay.wajub.com either, since an account on the Scale plan can serve its checkout from its own verified domain.

What the page decides on its own

The customer sees the operators that can actually take their money, not a fixed list. Wajub filters by currency, by environment and by the amount, so a channel that cannot process XAF is never offered rather than failing at the last step.

Mobile Money caps a single debit at 500 000 XAF, and the same in XOF. In live a payment above that is refused, and the page asks the payer for a smaller amount or another method. In sandbox the page splits it into successive tranches and collects them one after another. That is the situation where a payment sits in partial, described in the payment lifecycle.

While the operator prompt is out, the page shows the instruction that matches the channel, the #150# style dialling code for one operator and the in-app confirmation for another. If you set bearer to customer, it also shows the fee on top of your amount and the total actually being debited.

The language is English or French. It follows the Settings → Regional → Language setting, which accepts auto, en or fr. On auto, the page uses the customer's own preferred_language, and opens in French when the customer has none.

Handle the return

Once the attempt is over, the customer comes back to your callback with the payment identified in the query string:

Return redirect
https://shop.example.com/order/complete?reference=trx_test_CSUGajfv9xh0XQ5wu2lx&status=succeeded&trxref=order-4172

reference is the Wajub payment id and trxref is the reference you sent, present only when you set one. Both come back whatever the outcome: the return after a failed payment used to carry status alone. status is succeeded, failed, cancelled or expired. After a success the page sends the customer on by itself; after a failure or an expiry, when they choose to come back. A customer who closes the page without finishing never comes back at all.

The verification call takes the id you stored before redirecting:

curl https://api.wajub.com/payments/trx_test_CSUGajfv9xh0XQ5wu2lx \
  -H "Authorization: sk_test.xxxxxxxxxxxxxxxx…"

Check the amount and the currency as well as the status, and let the webhook close the orders whose customer never came back.

Make the page yours

The look of the page is set once for the whole account, in Settings → Branding, and applies to every payment page you ever create, in sandbox as in production.

What you setWhere it shows
Rectangular logo, square logo, faviconThe header of the page and the browser tab
Primary, secondary and background coloursButtons, links and the page surface
Text and button text coloursContrast against your own palette
Success and error coloursThe confirmation and the failure states
Font and border radiusThe whole page
Appearance modesystem, light or dark
Density and cover styleHow tight the layout is, and how the header is framed

Branding needs a paid plan

Settings → Branding needs the Branding feature, which starts at Growth. On Pay as you go the payment page keeps the default appearance. Serving the checkout from your own domain is a separate feature, Custom domain, that starts at Scale.

You can also collect information the payment itself does not carry, a delivery note or a table number, by adding fields in Settings → Collect → Fields. Each field has a type among text, textarea, select, checkbox, number, email and tel, a label, an optional placeholder, and a required flag. They appear on the hosted page in the order you arrange them.

What the customer types in those fields is not saved on the payment today, so it never comes back on a read or in a webhook. When you need the answer, ask for it on your own site before creating the payment, and send it in metadata.

What did you think of this content?