- Checkout
- Features
Integrating the Hosted Payment Page
Create an order, wrap it in a hosted session, get the customer to the page Kustom hosts and read the outcome — the integration model for integrations that can't run any client-side code.
The Hosted Payment Page (HPP) puts Kustom Checkout on a page Kustom hosts, so your integration is server calls only — no snippet, no iframe, no JavaScript of yours anywhere. You create the order exactly as you would for the embedded checkout, create a hosted session on top of it and then get the customer to that session by redirect, e-mail or QR code. This guide covers the whole flow. The field-by-field contract lives in the session reference.
Before you start
Section titled “Before you start”You need a working Checkout integration — HPP doesn’t replace it, it distributes it. Three Kustom services are involved in a full purchase:
| Service | What it does here | Base path |
|---|---|---|
| Checkout | Creates the order — the cart, the totals, the callback URLs | /checkout/v3 |
| Hosted Payment Page | Wraps that order in a session the customer can reach | /hpp/v1 |
| Order Management | Captures and refunds afterwards | /ordermanagement/v1 |
All three take the same HTTP Basic credentials. Playground is
https://api.playground.kustom.co, production is https://api.kustom.co. Credentials
don’t cross between them.
Decide up front which of the three shapes you’re building, because it changes how you distribute the session in step 3:
- E-commerce. The customer is already in a browser on a site you own, so you can redirect them.
- Telesales or e-mail. The customer is on the phone or in their inbox. You have to send them a link.
- Shared device. A kiosk or an in-store screen. Privacy rules mean you must not leave a session open on a shared browser, so you distribute rather than redirect.
Step by step
Section titled “Step by step”1. Create the order
Section titled “1. Create the order”This is the ordinary Checkout call. Nothing about it is HPP-specific:
curl -X POST https://api.playground.kustom.co/checkout/v3/orders \ --header "Authorization: Basic <token>" \ --header "Content-Type: application/json" \ --data @order.jsonSee Create and update an order for what belongs in the body.
The response carries the full order, but for HPP you need only one field from it:
order_id. The html_snippet in the response is for the embedded checkout — with HPP you
never render it yourself, and you should not try to.
2. Create the hosted session on top of it
Section titled “2. Create the hosted session on top of it”POST /hpp/v1/sessions, pointing payment_session_url at the order you just created:
{ "payment_session_url": "https://api.playground.kustom.co/checkout/v3/orders/8cf27b55-53e8-6aba-9fb4-7c692e56ddee", "merchant_urls": { "success": "https://example.com/done?order_id={{order_id}}", "back": "https://example.com/cart", "failure": "https://example.com/declined", "error": "https://example.com/error", "status_update": "https://example.com/hpp-callback?session={{session_id}}&token=<your secret>" }}The response gives you a session_id and a set of URLs — redirect_url for the customer,
session_url for you to poll, distribution_url to send the link by e-mail or SMS and
qr_code_url for a screen. The reference
lists all of them, and every option you can set on the session, including branding.
Use the URLs as returned. Their structure isn’t part of the contract and Kustom
changes it. Only session_id is stable enough to store.
3. Get the customer to the page
Section titled “3. Get the customer to the page”Redirect if the customer is already in a browser you control. Send them to
redirect_url, and set merchant_urls so they come back to you afterwards:
| URL | When the customer lands there |
|---|---|
success | Payment was authorized. You must show your own confirmation — Kustom’s isn’t enough on its own. |
failure | Payment was refused. The customer has already seen why. |
back | The customer pressed Back. The session stays open, so they can return to it. Disabling it is your call. |
cancel | The customer confirmed a cancellation, which moves the session to CANCELLED permanently. |
error | Something went wrong. Without this URL, errors go to failure and you can’t tell a decline from a fault. |
Which of the two exit controls the page draws follows from which of the two URLs you set. A
Back control leaves the session reachable. A Cancel control prompts for confirmation
and then moves the session to CANCELLED permanently. Set both and the page draws both. Set
neither and the customer gets a Cancel leading to Kustom’s own page.
Set only one and the order decides. With just back, you get Back. With just cancel, you get
Cancel when the order declares an options.acquiring_channel and Back when it doesn’t — the
discriminator is whether the field is set at all, not which channel it names.
Both URLs also stand in for each other. back falls back to cancel when it’s missing,
and error falls back to failure — which is why a missing error URL leaves you unable
to tell a decline from a fault rather than losing the customer entirely.
Those five are every redirect merchant_urls carries. The object takes two more, both
callbacks rather than destinations — status_update, in step 4 below, and
distribution_update, which fires once when Kustom has successfully sent a link by e-mail
or SMS and carries session_id, distribution_id, method and the contact information it
was sent to.
Distribute if you can’t redirect. POST to the distribution_url with an e-mail
address or a phone number and Kustom sends a localized message containing the link, or display qr_code_url
on a screen for the customer to scan. Both are covered in
the reference. If an operator — a clerk or a
telesales agent — is driving the flow by hand, use the Distribution Module rather than
building your own interface on the API.
4. Read the outcome
Section titled “4. Read the outcome”Don’t rely on the redirect. The customer can close the window before it happens, and then you have taken a payment you don’t know about. Track the session instead, by one of two mechanisms, ideally both:
- Callbacks. Set
merchant_urls.status_updateat session creation and KustomPOSTs the session to you on every status change. This is the mechanism to build on. - Polling.
GETthesession_urlfrom the create response. Use it as the backstop for callbacks your endpoint missed, not as the primary path.
A callback is attempted three times in total — the first send plus two retries, three seconds apart — and each attempt is given three seconds to answer. That’s the whole budget, about nine seconds from the first attempt to the last, which is precisely why the polling backstop is worth having.
The statuses and the callback body are in
the reference. The one you’re waiting for is
COMPLETED.
5. Capture, or disable the session
Section titled “5. Capture, or disable the session”Unless the order was created with auto_capture, no money moves until you capture it
through Order Management — see Capture an order.
If the purchase is off — the offer expired, the customer paid another way — DELETE the
session. Its status becomes DISABLED, permanently, and the link stops working.
Why the hosted session expires before the order
Section titled “Why the hosted session expires before the order”The session and the order have tied lifetimes. A session never outlives its order: it takes the order’s expiry, capped at 48 hours after the session is created, and then expires up to an hour earlier than that. The early part is a stagger of between 0 and 59 minutes that Kustom applies to spread expiries out, so two sessions created a minute apart on identical orders can expire at different times.
The practical consequence is that expires_at on the session is the deadline that matters
to you, and the order’s own expiry isn’t something to distribute a link against.
You may attach several sessions to one order — for instance to try two brandings — but it’s a bad practice and Kustom treats it as one. Prefer one session per order.
Check that it works
Section titled “Check that it works”Run the whole flow in Playground before you build the production path:
- Create an order, then a session on it. You get a
session_idand aredirect_url. - Open
redirect_urlin a browser. Yourstatus_updateendpoint receivesIN_PROGRESS. - Pay with Playground test data. Your endpoint receives
COMPLETED, carrying theorder_id. GETthesession_urland confirm it agrees with the callback.DELETEa second, unused session and confirm its status isDISABLEDand its link is dead.
Variations
Section titled “Variations”Auto-capture. Create the order with auto_capture and money moves at authorization,
with no Order Management call from you. Use it when goods are handed over immediately.
Place-order mode. A session on a Checkout order always completes with an order_id,
never an authorization_token, so {{order_id}} is usable in success whatever you set.
The one options.place_order_mode value that changes anything here is CAPTURE_ORDER,
which sets options.auto_capture on the order for you — the auto-capture variation above,
from the session side. PLACE_ORDER is a no-op on a Checkout order.
Generic confirmation pages. Leave merchant_urls empty and Kustom shows its own
success, failure and cancellation pages. Acceptable for a pilot. For anything real, send
the customer somewhere you control.
Troubleshooting
Section titled “Troubleshooting”The customer paid but never came back. Expected, and the reason step 4 exists. The redirect is best-effort. The callback and the read endpoint are the record.
A callback never arrived. Kustom gives up after three attempts against an endpoint that
doesn’t answer within three seconds each time. Each callback is retried on its own, so
losing one doesn’t stop the next: a session whose IN_PROGRESS never landed can still
deliver COMPLETED. Poll session_url to recover the state, then fix the endpoint’s
latency — three seconds is the whole budget per attempt, including TLS.
The customer sees “Page Not Found”. The session was disabled while they were on it.
That’s what DISABLED looks like from their side.
You can’t tell declines from faults. You didn’t set error, so both went to
failure. Set both URLs.
Related
Section titled “Related”- Session reference — every endpoint, option, status and error code
- Capture an order — moving the money after
COMPLETED - Integration models — whether HPP is the right integration model at all
- Create and update an order — the call step 1 makes
- In-person payments — the other way to take payment away from your website
- Checkout API reference — the order’s full schema
This page is also available as Markdown — append .md to its URL — and is indexed in llms.txt.