Accept a Payment
An end-to-end walkthrough of the payment lifecycle, from creating a request to confirming it was paid.
1. Create a payment intent
curl -X POST https://dev.gladys.the-all.io/api/v1/payments \
-H "Authorization: Bearer <your access token>" \
-H "Idempotency-Key: <a unique key for this request>" \
-H "Content-Type: application/json" \
-d '{
"amount": 150000,
"currency": "KES",
"description": "Order #4471",
"merchant_reference": "order-4471",
"capture_method": "AUTOMATIC",
"return_url": "https://yourapp.example/orders/4471/complete"
}'
Amounts are in cents — 150000 is KES 1,500.00. The response includes a reference (e.g. pay_...) that identifies this payment intent everywhere from here on.
Always send an Idempotency-Key — see Idempotent Requests. It's what makes it safe to retry a create call after a timeout without risking a duplicate charge.
2. Send the payer to check out
Redirect the payer's browser to:
https://dev.gladys.the-all.io/pay/{reference}
That's the whole integration on your side — a redirect (or a popup pointed at the same URL), not an API call. It's a Gladys-hosted page, not part of your application: your server never sees it render, and your frontend never sees the payer's PIN.
The page handles everything from there:
- Signs the payer in, if they aren't already — phone number and their Gladys login PIN. This is a different PIN from the one that approves the payment; a payer with an existing Gladys session (for instance, already signed into another Gladys surface in the same browser) skips this step entirely.
- Shows the payment — who's being paid (your business's verified name, not your app's own name), the amount, and your
description. It never shows a fee breakdown, because the payer isn't charged one. - Takes their transaction PIN and lets them confirm or cancel.
- Shows the result — paid, cancelled, or (for manual capture) approved and waiting on you to capture.
If capture_method is AUTOMATIC (the default), confirming captures the funds immediately. If it's MANUAL, confirming only places a hold — you capture separately (step 4).
Returning to your site
Set return_url when you create the intent (step 1) and the page redirects there once the payment resolves — paid, cancelled, or expired. It has to be one of the redirect URIs registered on your OAuth client, matched exactly; anything else is rejected when you create the intent, not silently ignored at checkout. If you don't set one, the payer sees a standalone result screen and no redirect happens.
Treat the redirect as a convenience for the payer, not as your source of truth — a closed tab or a lost connection means it may never fire. Confirm what actually happened with a webhook or a status check (step 3), the same as you would regardless of how the payer got there.
:::note Don't embed this page
The checkout page sends frame-ancestors 'none', so it will not render inside an iframe — that's deliberate, not a bug to work around. Use the redirect above.
:::
3. Find out what happened
The recommended way is to register a webhook endpoint once — Gladys pushes payment.authorized, payment.captured, payment.cancelled, and payment.expired events as they happen, so you don't have to poll.
If you'd rather poll (or want a reconciliation check alongside webhooks), GET /payments/:reference always reflects the current state:
curl https://dev.gladys.the-all.io/api/v1/payments/pay_abc123 \
-H "Authorization: Bearer <your access token>"
Watch the status field:
| Status | Meaning |
|---|---|
CREATED | Waiting on the payer |
AUTHORIZED | Payer approved, funds held (manual capture only) |
CAPTURED | Funds moved — this is a completed payment |
CANCELLED | Cancelled by the merchant or declined by the payer |
EXPIRED | The payer didn't act before expires_at |
If you're polling, a sensible starting point is every few seconds after you redirect the payer, with backoff, until you see a terminal status (CAPTURED, CANCELLED, or EXPIRED).
4. Capture (manual capture only)
If you created the intent with capture_method: "MANUAL", capture it once you're ready to actually take the funds — for example, after you've confirmed you can fulfil the order:
curl -X POST https://dev.gladys.the-all.io/api/v1/payments/pay_abc123/capture \
-H "Authorization: Bearer <your access token>" \
-H "Idempotency-Key: <a unique key for this request>"
An authorized hold that's never captured is released automatically after 7 days.
5. Cancel, if needed
curl -X POST https://dev.gladys.the-all.io/api/v1/payments/pay_abc123/cancel \
-H "Authorization: Bearer <your access token>" \
-H "Content-Type: application/json" \
-d '{"reason": "Order cancelled by customer"}'
Only CREATED and AUTHORIZED intents can be cancelled — once a payment is CAPTURED, money needs to go back through a refund instead. See Refund a Payment.