Skip to main content

Dashboard Admin API

Admin endpoints for the internal dashboard — merchant lifecycle, audit, feature flags.

Auth: JWT Bearer (role admin or superadmin) issued by the dashboard login.

Base URL: https://kesles.com/merchant/dashboard-api/admin (production) / http://localhost:8082/admin (local).

Merchant Lifecycle​

Suspend Merchant​

POST /admin/merchants/{id}/suspend

Suspend a merchant — transactions are rejected, but data is not deleted.

curl -X POST https://kesles.com/merchant/dashboard-api/admin/merchants/{id}/suspend \
-H "Authorization: Bearer $ADMIN_JWT" \
-H "Content-Type: application/json" \
-d '{"reason":"pending KYC re-verification","suspended_by":"admin-uuid"}'

Response​

{ "merchant_id": "...", "status": "suspended", "suspended_at": "..." }

Reactivate Merchant​

POST /admin/merchants/{id}/reactivate

Re-activate a suspended/inactive merchant.

Terminate Merchant​

POST /admin/merchants/{id}/terminate

Terminate a merchant — status deleted, disconnected from the PSP.

warning

Termination cannot be undone. 2-eyes approval is required.

KYC Review​

List Pending Review​

GET /admin/merchants/pending-review

List merchants with status pending_review (KYC submitted, not yet approved).

Approve KYC​

POST /admin/merchants/{id}/kyc/approve

Approve merchant KYC. Promotes from pending_review → active.

Reject KYC​

POST /admin/merchants/{id}/kyc/reject

Reject KYC with a reason.

PSP Merchant Management​

PSP merchant lookup sekarang dilakukan via dashboard_api sebagai proxy ke merchant_core_api /internal/psp/merchants/* (Phase 3 reader cutover). Tidak ada SQL langsung ke main DB.

List PSP Merchants​

GET /api/psp/v1/merchants

List semua PSP merchant yang terdaftar.

Lookup by ID​

GET /api/psp/v1/merchants/{id}

Lookup by NMID​

GET /api/psp/v1/merchants/by-nmid/{nmid}

NMID korporat untuk QRIS Plus adalah ID9999999999999 (singleton di psp.qris_config).

Lookup by MID​

GET /api/psp/v1/merchants/by-mid/{mid}

Lookup by Merchant Code​

GET /api/psp/v1/merchants/by-code/{code}

GET /api/psp/v1/merchants/search?q={keyword}

List Devices (per merchant)​

GET /api/psp/v1/merchants/{id}/devices

Lookup by Serial Number​

GET /api/psp/v1/merchants/by-serial/{serial}

Format SN Aisino Q161: 11 digit pure numeric (mis. 00087000668).

QRIS Config​

GET /api/dashboard/settings/qris

Singleton konfigurasi QRIS korporat. Data bersumber dari payment_service /internal/qris-config (Phase 3 cutover).

Feature Flags​

List Flags​

GET /admin/flags

List all feature flags + their current state.

Toggle Flag​

POST /admin/flags/{flag_name}

Toggle a feature flag.

curl -X POST https://kesles.com/merchant/dashboard-api/admin/flags/new_transaction_flow \
-H "Authorization: Bearer $ADMIN_JWT" \
-d '{"enabled": false, "reason": "rollback due to incident"}'

Audit Log​

Query Audit Log​

GET /admin/audit-logs

Query the audit log with filters: actor, action, resource_type, from, to.

curl -G https://kesles.com/merchant/dashboard-api/admin/audit-logs \
-H "Authorization: Bearer $ADMIN_JWT" \
-d actor=admin-uuid \
-d from=2026-04-01 \
-d to=2026-04-24

Transactions​

Recent Transactions​

GET /api/dashboard/transactions

Recent transactions. Data bersumber dari payment_service /internal/transactions (Phase 3 reader cutover — tidak lagi query SQL langsung ke merchant.transactions).

Query params: merchant_id, limit, page, from, to, status.

Sales — Orders​

Base path: /api/dashboard/sales/sales-orders. Read roles: super_admin, admin, operations, finance, support. Write actions (mark-paid/accept/start-processing/complete/cancel) require role operations or super_admin.

List Sales Orders​

GET /api/dashboard/sales/sales-orders?status=&search=&sort=&limit=10&page=1

Query params:

  • status: awaiting_activation / pending / accepted / pending_no_proof (virtual: pending without proof) / pending_with_proof (virtual: pending with proof) / payment_received / processing / completed / cancelled
  • search: substring on order_number, merchant_name, merchant_code, contact_phone, contact_email
  • sort: oldest / total_desc / total_asc (default: newest)

Response (per row):

{
"id": "uuid",
"order_number": "SO-0000000000000067",
"status": "pending",
"merchant_id": "uuid|''",
"merchant_registration_id": "uuid|''",
"merchant_code": "MRC-...",
"merchant_name": "Coffe Mappasitujue",
"device_name": "QRIS Plus",
"device_quantity": 1,
"items": [
{
"line_no": 1,
"device_name": "QRIS Plus",
"quantity": 1,
"unit_price_amount": 1200000,
"promo_amount": 850000,
"line_total_amount": 388500
}
],
"bank_verification": {
"status": "success",
"provider_name": "BCA Virtual Account",
"reference_number": "BCA20260502091234567",
"payment_method": "BANK_TRANSFER",
"amount": 466200,
"paid_at": "2026-05-02T17:11:25Z",
"payer_name": "Kopi Udin",
"payer_account": "8201xxxxxxxxxx",
"raw_note": "Settled via BCA VA"
},
"subtotal_amount": 420000,
"shipping_fee_amount": 30000,
"promo_amount": 850000,
"total_amount": 466200,
"payment_proof_url": "...",
"created_at": "2026-05-02T16:58:00Z",
"updated_at": "..."
}

The items array is the per-line breakdown — used by the Order Detail dialog in the dashboard to render the list of purchased items, not just the total. Empty when the order has no lines yet.

bank_verification is the payment verification result from the acquiring bank / virtual account / PSP API. Optional (omitted from JSON when the backend is not yet integrated with the bank or verification has not been performed). When present, the Review Payment dialog in the dashboard auto-renders the "Bank Verification" section with a status badge (green/red/amber) + cross-checks the amount vs the order total. Status enum: success / paid / pending / failed / rejected / not_found / refunded.

Order State Transitions​

POST /api/dashboard/sales/sales-orders/{id}/{action} — action ∈ mark-paid, accept, start-processing, complete, cancel. State machine: pending → accepted → payment_received → processing → completed. cancel is valid from any non-terminal status.

Catatan mark-paid: Endpoint ini adalah Jalur A (manual) untuk menandai order sebagai sudah dibayar. Jalur B (otomatis) dilakukan oleh payment_service via order_service /internal/orders/{id}/mark-paid saat PSP webhook masuk. Lihat Sales Order State Machine §4.

Sales — Shipping Orders​

Base path: /api/dashboard/sales/shipping-orders.

Create Shipping​

POST /api/dashboard/sales/shipping-orders

Body: {sales_order_id, courier, tracking_number, notes} — courier & tracking are optional (can be filled later via the dispatch action). The backend auto-promotes the sales_order to processing and reserves the terminal stock.

Shipping Actions​

POST /api/dashboard/sales/shipping-orders/{id}/{action} — action ∈ mark-packed, dispatch, mark-delivered. mark-delivered triggers a Goods Received notification in the Notification Center.

Notification Center​

List Notifications​

GET /api/dashboard/notifications?limit=25

Aggregate event stream for the top-bar bell. Polling-based from the frontend (default interval 60s). Read roles are the same as sales-orders.

Source events (window: last 7 days):

  • new_order — sales_orders with status pending/accepted + payment_proof_url=''
  • payment_sent — sales_orders with status pending/accepted + payment_proof_url<>''
  • goods_received — shipping_orders with delivered_at IS NOT NULL

Response:

{
"items": [
{
"id": "so:uuid",
"type": "new_order",
"title": "New Order",
"message": "Order SO-... received from Merchant X.",
"occurred_at": "2026-05-02T19:30:00Z",
"ref_id": "uuid",
"ref_number": "SO-0000000000000067"
}
],
"meta": { "total": 25 }
}

Sort: occurred_at DESC. The frontend resolves type → icon + tint:

  • new_order → blue #1F7AE0
  • payment_sent → amber #D97706
  • goods_received → green #0E9F6E

Rate Limit​

  • admin/* endpoints: 120 req/min per admin user
  • admin/merchants/*/terminate: 5 req/day per admin (hard limit, for the 2-eyes guard)
  • dashboard/notifications: no specific hard limit, polled every 60s from the UI

OpenAPI Spec (Draft)​

Sales Orders + Shipping Orders + Notification Center have a formal OpenAPI 3.0.3 schema in admin-openapi-draft.yaml (di repo, di luar Docusaurus) — import into Postman / Stoplight or use it for client code-gen.