Skip to main content

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:

StatusMeaning
CREATEDWaiting on the payer
AUTHORIZEDPayer approved, funds held (manual capture only)
CAPTUREDFunds moved — this is a completed payment
CANCELLEDCancelled by the merchant or declined by the payer
EXPIREDThe 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.