Accept a payment
The end-to-end hosted-checkout flow, the payment lifecycle, and how to handle each outcome.
A payment is taken through the gateway's hosted checkout — your app never sees card data. You create an intent, send the customer to the returned URL, and react to the confirmed result.
The flow
Create the payment
POST /payments with the amount in minor units and your
customerReference. Send an Idempotency-Key so a
retry can't double-create.
curl -X POST http://localhost:8080/api/v1/payments \
-H "X-Api-Key: oi_test_xxx" -H "X-Api-Secret: your-secret" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 9b1c…" \
-d '{ "amountMinor": 10000, "currency": "BDT",
"customerReference": "cust_123", "customerEmail": "[email protected]",
"productName": "Pro plan" }'| Field | Required | Notes |
|---|---|---|
amountMinor | yes | Integer minor units, > 0. |
customerReference | yes | Your identifier for the customer. |
currency | no | ISO-4217, defaults to BDT. |
customerEmail / customerPhone / customerName | no | Pre-fill and display on the gateway page. |
productName | no | Shown on the checkout page. |
invoiceId | no | Link this payment to an invoice. |
Redirect to checkout
The response contains a reference and a checkoutUrl. Redirect the customer's
browser to checkoutUrl; they complete payment on the hosted page. In TEST mode
this is the sandbox.
React to the confirmed result
The gateway calls the service back server-to-server. The service verifies the
callback, finalizes the payment, posts to the ledger, and emits a signed
payment.succeeded (or payment.failed / payment.expired)
webhook. Treat the webhook as the source of truth.
If you need to check synchronously, poll GET /payments/{id} or
GET /payments?customerReference=cust_123.
Never grant value off the browser redirect alone. The redirect can be lost,
spoofed, or interrupted. Fulfill only on a confirmed SUCCEEDED status — from a
verified webhook or a status read.
Payment lifecycle
stateDiagram-v2
[*] --> CREATED
CREATED --> PENDING: checkout opened
PENDING --> SUCCEEDED: gateway confirmed
PENDING --> FAILED: gateway rejected
PENDING --> CANCELLED: customer cancelled
PENDING --> EXPIRED: timed out
SUCCEEDED --> PARTIALLY_REFUNDED: partial refund
SUCCEEDED --> REFUNDED: fully refunded
PARTIALLY_REFUNDED --> REFUNDED| Status | Meaning |
|---|---|
CREATED | Intent persisted; checkout session not yet opened. |
PENDING | Customer is at the gateway. |
SUCCEEDED | Captured — safe to fulfill. |
FAILED / CANCELLED / EXPIRED | Terminal, not captured. |
PARTIALLY_REFUNDED / REFUNDED | One or more refunds issued. |
Receipts
If receipts are enabled for your app, a settled payment exposes a receipt at
GET /payments/{id}/receipt.
Hosted checkout context (advanced)
The checkout page itself reads GET /checkout/{reference} — an unauthenticated,
reference-based endpoint that returns the amount, status, and the gateway page URL.
You normally don't call this directly; the service-hosted checkout page does.