OI Payments Docs
Guides

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" }'
FieldRequiredNotes
amountMinoryesInteger minor units, > 0.
customerReferenceyesYour identifier for the customer.
currencynoISO-4217, defaults to BDT.
customerEmail / customerPhone / customerNamenoPre-fill and display on the gateway page.
productNamenoShown on the checkout page.
invoiceIdnoLink 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
StatusMeaning
CREATEDIntent persisted; checkout session not yet opened.
PENDINGCustomer is at the gateway.
SUCCEEDEDCaptured — safe to fulfill.
FAILED / CANCELLED / EXPIREDTerminal, not captured.
PARTIALLY_REFUNDED / REFUNDEDOne 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.

On this page