OI Payments Docs
Admin dashboard

Transactions

Search payments across every application you are scoped to, export the filtered set as CSV, and read per-app dashboard metrics and daily trends.

Operators with payment:read can search payments across every application their grants cover, and export the same filtered set as CSV with payment:export. These are admin, session-authenticated endpoints under /api/v1/admin/payments — distinct from the app-API-key payments API, whose reads are confined to the one authenticating app and mode.

Every admin endpoint on this page is session-authenticated: send your admin session token as Authorization: Bearer SESSION_TOKEN, not an app API key. Results are always narrowed to the apps your RBAC grants cover — you never see another app's transactions (app isolation).

Every response uses the standard envelope — { "data": …, "meta": { … }, "pagination": … }. The examples below show the data payload; see responses and errors for the full shape.

GET /admin/payments (gated by payment:read) returns a paginated list of payment summaries, automatically narrowed to the apps your grants cover.

Query parameters

FieldTypeRequiredNotes
applicationIdintegerNoA specific app within your scope.
modeenum (TEST | LIVE)NoFilter by mode; an invalid value is rejected with 400.
statusenumNoOne of CREATED, PENDING, SUCCEEDED, FAILED, CANCELLED, EXPIRED, PARTIALLY_REFUNDED, REFUNDED.
referencestringNoExact payment reference.
customerRefIdintegerNoThe app's internal customer-reference id.
fromdate (YYYY-MM-DD)NoInclusive start of the createdAt window.
todate (YYYY-MM-DD)NoInclusive end of the createdAt window.
pageintegerNoZero-based page index. Default 0.
sizeintegerNoPage size. Default 20.
sortBystringNoSort field. Default createdAt.
orderenum (ASC | DESC)NoSort direction. Default DESC.
paginatebooleanNoWhen false, returns the full unpaged set. Default true.
curl -G "http://localhost:8080/api/v1/admin/payments" \
  -H "Authorization: Bearer SESSION_TOKEN" \
  --data-urlencode "applicationId=42" \
  --data-urlencode "mode=LIVE" \
  --data-urlencode "status=SUCCEEDED" \
  --data-urlencode "from=2026-06-01" \
  --data-urlencode "to=2026-06-30"
{
  "data": [
    {
      "id": 90142,
      "reference": "PAY-7H2K9Q8M",
      "applicationId": 42,
      "applicationName": "Acme Storefront",
      "mode": "LIVE",
      "status": "SUCCEEDED",
      "amountMinor": 150000,
      "currency": "BDT",
      "customerRefId": 5567,
      "customerExternalId": "cust_8842",
      "customerName": "Rahim Uddin",
      "invoiceId": 3310,
      "gateway": "SSLCOMMERZ",
      "gatewayTxnId": "2406301230ACME",
      "createdAt": "2026-06-28T09:14:05"
    }
  ],
  "meta": { "success": true, "message": null, "errorCode": null, "timestamp": "2026-06-30T12:00:00Z" },
  "pagination": { "page": 0, "size": 20, "totalElements": 42, "totalPages": 3 }
}

Response row — PaymentSummaryDto

Null fields are omitted from the JSON. Fields marked CSV are also written by the export; the customer-contact fields are enrichment for the dashboard and are JSON-only.

FieldTypeRequiredNotes
idintegerYesPayment id. CSV
referencestringYesOpaque payment reference. CSV
applicationIdintegerYesOwning app. CSV
applicationNamestringNoResolved for display; omitted when the owning app can no longer be found. CSV
modestringYesTEST or LIVE. CSV
statusstringYesPayment status. CSV
amountMinorintegerYesAmount in integer minor units (150000 = 1,500.00 BDT). CSV
currencystringYesAlways BDT in v1. CSV
customerRefIdintegerNoThe app's internal customer-reference id. CSV
customerExternalIdstringNoThe app's external customer id (enriched).
customerNamestringNoCustomer display name (enriched).
customerEmailstringNoCustomer email (enriched).
customerPhonestringNoCustomer phone (enriched).
invoiceIdintegerNoSettling invoice, when the payment settles one. CSV
gatewaystringNoPayment gateway (e.g. SSLCOMMERZ). CSV
gatewayTxnIdstringNoGateway transaction handle. CSV
createdAtdatetimeYesCreation timestamp. CSV
updatedAtdatetimeNoLast-update timestamp (JSON only).

Export

GET /admin/payments/export (gated by payment:export) streams the same filtered set as the search — identical query parameters, same app-scope narrowing — as a text/csv attachment named transactions.csv. The export is sorted by id descending and is not paginated: every matching row is written, so you export exactly what you're looking at — handy for finance hand-offs.

The header row is fixed, in this exact column order:

id,reference,applicationId,applicationName,mode,status,amountMinor,currency,customerRefId,invoiceId,gateway,gatewayTxnId,createdAt

Only those 13 columns are exported; the customer-contact fields the search JSON enriches (customerName, customerEmail, customerPhone, customerExternalId) and updatedAt are not in the CSV. amountMinor is in integer minor units (150000 = 1,500.00 BDT).

curl -G "http://localhost:8080/api/v1/admin/payments/export" \
  -H "Authorization: Bearer SESSION_TOKEN" \
  --data-urlencode "applicationId=42" \
  --data-urlencode "mode=LIVE" \
  --data-urlencode "from=2026-06-01" \
  --data-urlencode "to=2026-06-30" \
  -o transactions.csv
id,reference,applicationId,applicationName,mode,status,amountMinor,currency,customerRefId,invoiceId,gateway,gatewayTxnId,createdAt
90142,PAY-7H2K9Q8M,42,Acme Storefront,LIVE,SUCCEEDED,150000,BDT,5567,3310,SSLCOMMERZ,2406301230ACME,2026-06-28T09:14:05

Dashboard metrics

GET /admin/payments/metrics (gated by payment:read) returns the per-app, mode-aware key metrics for the dashboard metrics row. Unlike search and export, both applicationId and mode are required query parameters.

Admin sessions are not bound to a single mode the way an app API key is, so the dashboard reads (/metrics and /timeseries) take mode as an explicit, required query parameter. This is the same exception documented on dashboard analytics — it does not contradict the modes rule that an app-API request's mode is derived from its credential, because an admin session carries no app credential.

Query parameters

FieldTypeRequiredNotes
applicationIdintegerYesThe app to aggregate. Outside your scope → all-zero metrics.
modeenum (TEST | LIVE)YesThe mode to aggregate.
fromdate (YYYY-MM-DD)NoInclusive start of the createdAt window. Defaults to all time.
todate (YYYY-MM-DD)NoInclusive end of the createdAt window. Defaults to all time.
curl -G "http://localhost:8080/api/v1/admin/payments/metrics" \
  -H "Authorization: Bearer SESSION_TOKEN" \
  --data-urlencode "applicationId=42" \
  --data-urlencode "mode=LIVE" \
  --data-urlencode "from=2026-06-01" \
  --data-urlencode "to=2026-06-30"
{
  "data": {
    "totalCount": 318,
    "succeededCount": 297,
    "failedCount": 21,
    "grossSucceededMinor": 44550000,
    "successRate": 0.9339622641509434
  },
  "meta": { "success": true, "message": null, "errorCode": null, "timestamp": "2026-06-30T12:00:00Z" },
  "pagination": null
}

Response — PaymentMetricsDto

FieldTypeRequiredNotes
totalCountintegerYesPayments created in the window.
succeededCountintegerYesHow many reached SUCCEEDED.
failedCountintegerYesHow many reached FAILED.
grossSucceededMinorintegerYesGross succeeded amount in minor units.
successRatenumberYessucceededCount / totalCount as a 01 fraction (0 when there are no payments).

The endpoint always returns concrete zeros (never null) for an app with no payments in the window — and the same all-zero object when you are not scoped to the requested app, so callers can't probe app existence.

Daily time-series

GET /admin/payments/timeseries (gated by payment:read) returns one point per calendar day for the dashboard revenue/volume trend. It takes the same required applicationId + mode and optional from/to window as /metrics. Days with no payments are omitted (the dashboard fills the gaps client-side); an app outside your scope yields an empty list.

curl -G "http://localhost:8080/api/v1/admin/payments/timeseries?applicationId=42&mode=LIVE" \
  -H "Authorization: Bearer SESSION_TOKEN"
{
  "data": [
    { "date": "2026-06-28", "totalCount": 12, "succeededCount": 11, "grossSucceededMinor": 1650000 },
    { "date": "2026-06-29", "totalCount": 9,  "succeededCount": 9,  "grossSucceededMinor": 1350000 }
  ],
  "meta": { "success": true, "message": null, "errorCode": null, "timestamp": "2026-06-30T12:00:00Z" },
  "pagination": null
}

Response row — PaymentDayPointDto

FieldTypeRequiredNotes
datedate (YYYY-MM-DD)YesThe calendar day.
totalCountintegerYesPayments created that day.
succeededCountintegerYesHow many succeeded that day.
grossSucceededMinorintegerYesGross succeeded amount that day, in minor units.

No single-transaction endpoint

There is no GET /admin/payments/{id}. The admin transaction surface is the cross-app search list, export, metrics and time-series above — the only /{id} path under /admin/payments is POST /admin/payments/{id}/refunds, which opens a dashboard-initiated refund (see refunds and refund approval). To inspect one payment, filter the search by its reference or customerRefId. App integrators read a single payment by id through the app-API payments endpoint, not this admin surface.

On this page