Skip to main content
The Bonum admin surface gives Droplinked operators two capabilities:
  1. Manual reconciliation — one-shot recovery for orders stuck in PENDING because the sandbox (or, occasionally, prod) failed to fire a settlement webhook.
  2. Per-merchant configuration — onboard a Bonum-MoR or Bonum-Direct merchant by writing their terminal credentials directly, without an env-flag round-trip.
All admin endpoints below require:
  • JWT with role = SUPER_ADMIN
  • IpAllowlistGuard — caller IP must be in the operator allowlist
  • GeoBlockGuard — caller geo must be permitted (GCC/US/EU by default)
Calls that miss any of the three return 403.

When to use

SituationEndpoint
Order is PENDING in dev because Bonum sandbox did not POST /bonum/webhookPOST /admin/bonum/reconcile/:transactionId
Tugsjargal (or operator partner) sent new prod creds for a merchantPUT /admin/bonum/config/:merchantId
Need to confirm the active config for a merchant before debugging a failed chargeGET /admin/bonum/config/:merchantId
Move a merchant back to env-default fallbackDELETE /admin/bonum/config/:merchantId

POST /admin/bonum/reconcile/:transactionId

Manually reconciles a single Bonum transaction. Bypasses the BONUM_RECONCILIATION_ENABLED env flag — this endpoint is intended for stuck-order recovery, so the flag check is intentionally skipped. Accepts either:
  • The Bonum invoice ID (24-char alphanumeric returned by POST /orders/v2/create-payment-intent)
  • Our internal order ObjectId (24-char hex)
The handler resolves the supplied ID to the canonical BonumTransaction, calls Bonum’s /api/payment-log/read to refetch settlement state, then writes through to the order and unified-transaction projection.

Authentication

GuardRequirement
JWTRequired, role = SUPER_ADMIN
IP allowlistCaller IP in ADMIN_IP_ALLOWLIST
GeoCountry in ADMIN_GEO_ALLOWLIST

Path parameters

ParamTypeRequiredDescription
transactionIdstringYesBonum invoiceId or our order ObjectId

Request body

Empty — POST with no body.

Response — 200 OK

{
  "transactionId": "65f8a1b2c3d4e5f6a7b8c9d0",
  "invoiceId": "ABC123DEF456GHI789JKL012",
  "merchantId": "65f8a1b2c3d4e5f6a7b8c9aa",
  "previousStatus": "PENDING",
  "currentStatus": "SETTLED",
  "settledAt": "2026-06-04T12:34:56.789Z",
  "amount": 100000,
  "currency": "MNT",
  "reconciledVia": "manual-admin",
  "orderUpdated": true,
  "unifiedTransactionUpdated": true
}

Error responses

StatusWhen
400transactionId is neither a Bonum invoice ID nor a valid order ObjectId
403JWT missing / wrong role / IP or geo guard failed
404No BonumTransaction matches the supplied ID
502Bonum /api/payment-log/read returned a non-2xx
503Bonum API breaker is open

Example

curl -X POST \
  https://apiv3.droplinked.com/admin/bonum/reconcile/65f8a1b2c3d4e5f6a7b8c9d0 \
  -H "Authorization: Bearer <SUPER_ADMIN_JWT>"
The Bonum sandbox at testpsp.bonum.mn does not fire webhooks reliably. The reconcile endpoint is the canonical recovery path for sandbox testing — keep its URL bookmarked next to your Bonum sandbox test script.

PUT /admin/bonum/config/:merchantId

Upserts the per-merchant Bonum configuration. When a BonumConfig row exists for a merchant, the BonumPaymentStrategy uses it instead of the global env defaults. checksumKey is encrypted at rest with the platform KMS key. The plaintext value is required on write — it cannot be recovered after storage. mode and apiBaseUrl are validated together: mode = production requires a production-origin apiBaseUrl (no testpsp.* host); mode = sandbox rejects production hosts.

Authentication

GuardRequirement
JWTRequired, role = SUPER_ADMIN
IP allowlistCaller IP in ADMIN_IP_ALLOWLIST
GeoCountry in ADMIN_GEO_ALLOWLIST

Path parameters

ParamTypeRequiredDescription
merchantIdstring (ObjectId)YesDroplinked merchant ID

Request body

{
  "terminalId": "TERM-MN-0042",
  "checksumKey": "raw-checksum-key-from-bonum",
  "apiBaseUrl": "https://psp.bonum.mn",
  "mode": "production"
}
FieldTypeRequiredDescription
terminalIdstringYesBonum terminal identifier issued by Bonum / MCredit
checksumKeystringYesPlaintext checksum key — encrypted server-side before persist
apiBaseUrlstring (URL)Yeshttps://testpsp.bonum.mn for sandbox, https://psp.bonum.mn for prod
modeenum (sandbox | production)YesMust match the host class of apiBaseUrl

Response — 200 OK

{
  "merchantId": "65f8a1b2c3d4e5f6a7b8c9aa",
  "terminalId": "TERM-MN-0042",
  "checksumKey": "***",
  "apiBaseUrl": "https://psp.bonum.mn",
  "mode": "production",
  "createdAt": "2026-06-04T10:00:00.000Z",
  "updatedAt": "2026-06-04T10:00:00.000Z"
}

Error responses

StatusWhen
400Missing field; mode/apiBaseUrl mismatch (e.g. mode=production + testpsp.bonum.mn)
403JWT / IP / geo guard failed
404Merchant does not exist

Operator playbook — “Tugsjargal sent prod creds, how do I onboard them?”

1

Confirm the receipt

Verify the merchantId and terminalId in 1Password / Slack DM with Tugsjargal. Do not accept creds via email plaintext.
2

Write the config

curl -X PUT \
  https://apiv3.droplinked.com/admin/bonum/config/<merchantId> \
  -H "Authorization: Bearer <SUPER_ADMIN_JWT>" \
  -H "Content-Type: application/json" \
  -d '{
    "terminalId": "<terminalId>",
    "checksumKey": "<plaintext-checksumKey>",
    "apiBaseUrl": "https://psp.bonum.mn",
    "mode": "production"
  }'
3

Verify storage

GET /admin/bonum/config/<merchantId> — confirm mode=production, apiBaseUrl=https://psp.bonum.mn, and checksumKey=***. No operator action elsewhere is required; the next intent created for this merchant will route through the prod Bonum endpoint with the new terminal.
4

Smoke test

Create a 100 MNT test intent for the merchant, complete the payment via Bonum, confirm via GET /admin/bonum/config/<merchantId> does not change, and check the order moves to SETTLED within one webhook cycle.

GET /admin/bonum/config/:merchantId

Reads the per-merchant Bonum configuration. checksumKey is always masked to the literal string "***" in the response — it is never returned in plaintext.

Authentication

GuardRequirement
JWTRequired, role = SUPER_ADMIN
IP allowlistCaller IP in ADMIN_IP_ALLOWLIST
GeoCountry in ADMIN_GEO_ALLOWLIST

Path parameters

ParamTypeRequiredDescription
merchantIdstring (ObjectId)YesDroplinked merchant ID

Response — 200 OK

{
  "merchantId": "65f8a1b2c3d4e5f6a7b8c9aa",
  "terminalId": "TERM-MN-0042",
  "checksumKey": "***",
  "apiBaseUrl": "https://psp.bonum.mn",
  "mode": "production",
  "createdAt": "2026-06-04T10:00:00.000Z",
  "updatedAt": "2026-06-04T10:00:00.000Z"
}

Response — 404 Not Found

Returned when no BonumConfig row exists for the merchant. Means the merchant is on env defaults (BONUM_API_BASE_URL, BONUM_MERCHANT_KEY, etc.).

Example

curl https://apiv3.droplinked.com/admin/bonum/config/65f8a1b2c3d4e5f6a7b8c9aa \
  -H "Authorization: Bearer <SUPER_ADMIN_JWT>"

DELETE /admin/bonum/config/:merchantId

Removes the per-merchant Bonum configuration. From the next intent onward, the merchant falls back to env defaults.

Authentication

GuardRequirement
JWTRequired, role = SUPER_ADMIN
IP allowlistCaller IP in ADMIN_IP_ALLOWLIST
GeoCountry in ADMIN_GEO_ALLOWLIST

Path parameters

ParamTypeRequiredDescription
merchantIdstring (ObjectId)YesDroplinked merchant ID

Response — 200 OK

{
  "merchantId": "65f8a1b2c3d4e5f6a7b8c9aa",
  "deleted": true,
  "fallback": "env"
}

Error responses

StatusWhen
403JWT / IP / geo guard failed
404No BonumConfig row to delete (idempotent caller should treat as success)

Example

curl -X DELETE \
  https://apiv3.droplinked.com/admin/bonum/config/65f8a1b2c3d4e5f6a7b8c9aa \
  -H "Authorization: Bearer <SUPER_ADMIN_JWT>"