checkout_sessions:write), your site’s origin in Allowed domains and in
Return URL domains. See Getting your API keys.
The flow
- Your page creates a session:
POST /checkout-sessionswith the event and your two return URLs. From the browser, use the publishable key. From your server, use a bearer token. - Laces returns
{ id, url, expires_at }, whereurlishttps://laces.run/checkout/{id}. - Redirect the buyer to
url. - On laces.run the buyer sees a registration form for exactly that event,
with your
prefillfilled in and a “Cancel and return to your organization” link. They fill it in and pay. - Cancel sends them to
on_cancel_url. - Success shows a confirmation with a “Return to your organization” button
and redirects automatically after about 5 seconds to
on_success_url, carrying the session id. - Your server verifies the session before fulfilling anything.
What the buyer sees
Your page

A partner's event page with its own Register button. The example site and race are made up.
The Laces checkout form
prefill is already filled in.
The hosted checkout form, with the buyer's details prefilled.
Pay with mobile money

The payment step. This screenshot was taken in test mode, which is why it shows a sandbox banner; live checkouts don't.

Illustrative: the waiting screen while the buyer approves the prompt on their phone.
Confirmation, then back to you
on_success_url
automatically after about 5 seconds.
The confirmation (shown here for a free entry) with its Return to button.
Your success page

The partner's own success page after the return.
1. Create the session
201:
Return URL rules
Both URLs must be absolutehttps URLs, at most 2048 characters, with no
username or password, whitespace or backslashes, and their origin must
exactly match one of your return URL domains. This is what stops Laces
checkout being used as an open redirect.
The one exception is local development: http://localhost:<port> and
http://127.0.0.1:<port> return URLs work, end to end, when you have
registered that exact origin in Return URL domains. See
Local development domains.
Getting the session id back
- Put the literal placeholder
{CHECKOUT_SESSION_ID}anywhere inon_success_url, and Laces substitutes the real id:https://shop.example.com/thanks/{CHECKOUT_SESSION_ID}. The URL-encoded form%7BCHECKOUT_SESSION_ID%7Dworks too, so a URL built withnew URL()orencodeURIComponentis fine. - With no placeholder, Laces adds
session_id=LCSCS-…as a query parameter (replacing anysession_idyou already had there). on_cancel_urlis used as given, with no id added.
2. Verify on your server
On your success page’s route, read the session with a bearer token:200:
order is the most recent order made through the session, so a buyer who
retries after a failed payment shows their latest attempt. Expired sessions
are deleted automatically some time after they expire, after which a read
returns 404 not_found, so verify promptly and keep your own record.
Always send client_reference_id (your cart id) when you create the session,
and check it on return. That stops a session id from someone else’s purchase
being replayed against your buyer’s cart. If you create sessions from your
server, also store the returned session id with the cart.
Errors
payments_enabled from GET /events/{eventId}
to decide whether to show the button at all.
Test it
There is one environment, so you test with your live key and a real checkout. See Testing your integration for the full approach.- Create an unlisted test event in the Laces app with a free or low-price ticket.
- Add your site’s origin to Allowed domains and Return URL domains:
your staging site, or
http://localhost:<port>while you develop. - Click Register, complete the checkout, and land on your success URL.
- Confirm your server’s session read says
complete, and that theorder.paidwebhook arrived. - Refund the order from the Laces app if you paid for it.