Skip to main content

Internal Transaction Status API

Purpose

This endpoint is used to receive transaction status from the QRIS gateway or payment terminal devices, then:

  1. route to the correct merchant and terminal,
  2. forward a transaction notification to the merchant owner when the normalized status is success.

core_api does not write the transaction row itself — merchant.transactions was dropped (mig 073) and payment_service is now the sole writer for transactions. core_api only resolves routing and forwards the notification.

Endpoints:

  • POST /internal/transactions/transaction-status
  • POST /internal/transactions/transaction-status/bulk

Authentication:

  • header X-Internal-API-Key

Request format

The main payload follows the QRIS gateway format as below:

{
"referenceNo": "989242062200",
"responseCode": "2005200",
"responseMessage": "Successful",
"merchantId": "000007100010926",
"terminalId": "72001126",
"partnerReferenceNo": "230218123798000",
"amount": "1000.00",
"approvalCode": "304689",
"status": "paid"
}

Required fields:

  • referenceNo
  • merchantId
  • terminalId
  • amount
  • one of:
    • status
    • responseCode

Field mapping

  • partnerReferenceNo
    • used as transaction_code
    • if empty, falls back to referenceNo
  • referenceNo
    • stored as external_reference
  • merchantId
    • matched against merchant.merchant_outlets.nmid / mid (multi-outlet path)
    • falls back to merchant.merchants.nmid / mid
    • if it is a UUID, resolved directly against merchant.merchants.id (PSP receiver already resolved the merchant)
  • terminalId
    • resolved against the resolved outlet via inventory_service (HTTP) by TID, producing the internal terminal id and device_id
    • optional when merchantId is a UUID; required for legacy gateway callers that only know the QRIS TID/nmid
  • amount
    • parsed into integer rupiah and stored as gross_amount
  • status
    • normalized to internal status:
      • paid, success, successful -> success
      • pending, waiting -> pending
      • failed, fail -> failed
      • cancelled, canceled -> cancelled
      • refund, refunded -> refunded
      • reversed, reverse -> reversed

If status is empty, fallback from responseCode:

  • prefix 200 -> success
  • prefix 202 -> pending
  • prefix 400, 401, 402, 403, 404, 409, 500 -> failed
  • otherwise -> pending

Behavior

  • core_api resolves the merchant/terminal routing and normalizes the status; it does not persist the transaction (payment_service owns that write)
  • a transaction notification (FCM, plus WhatsApp merchant_transaction_alert on success) is forwarded when the normalized status is success

Because core_api no longer reads a previous status from the database, each success callback that reaches this endpoint will forward a notification. Callers (the PSP event receiver / gateway) are responsible for not replaying already-acknowledged success callbacks.

Response

Example success response:

{
"status": "processed",
"reference_no": "989242062200",
"partner_reference_no": "230218123798000",
"transaction_code": "230218123798000",
"transaction_status": "success",
"gross_amount": 1000,
"merchant_id": "d8a7e12d-6b05-4a5f-aa69-a8c1d8fe4a4a",
"merchant_name": "Contoh Merchant",
"terminal_id": "6f4e7d4e-9f6e-4d03-b0e2-f6f1d6f6be1a",
"device_id": "QRIS-PLUS-DEMO-001",
"nmid": "ID1026000000001",
"status_changed": true,
"notification_attempted": true,
"notification": {
"status": "queued",
"sent_count": 1,
"failed_count": 0,
"invalidated_count": 0
}
}

Production curl

curl -X POST "https://kesles.com/merchant/api/internal/transactions/transaction-status" \
-H "Content-Type: application/json" \
-H "X-Internal-API-Key: YOUR_INTERNAL_NOTIFICATION_API_KEY" \
-d '{
"referenceNo": "989242062200",
"responseCode": "2005200",
"responseMessage": "Successful",
"merchantId": "000007100010926",
"terminalId": "72001126",
"partnerReferenceNo": "230218123798000",
"amount": "1000.00",
"approvalCode": "304689",
"status": "paid"
}'

Bulk replay curl

curl -X POST "https://kesles.com/merchant/api/internal/transactions/transaction-status/bulk" \
-H "Content-Type: application/json" \
-H "X-Internal-API-Key: YOUR_INTERNAL_NOTIFICATION_API_KEY" \
-d '{
"items": [
{
"referenceNo": "989242062200",
"responseCode": "2005200",
"responseMessage": "Successful",
"merchantId": "000007100010926",
"terminalId": "72001126",
"partnerReferenceNo": "230218123798000",
"amount": "1000.00",
"approvalCode": "304689",
"status": "paid"
},
{
"referenceNo": "989242062201",
"responseCode": "2025200",
"responseMessage": "Pending",
"merchantId": "000007100010926",
"terminalId": "72001126",
"partnerReferenceNo": "230218123798001",
"amount": "25000.00",
"status": "pending"
}
]
}'

The bulk response returns:

  • processed_count
  • error_count
  • items

Each item carries the per-transaction result, so the gateway or operations team can perform partial replay without losing detail of failed items.

Production implementation notes

  • this endpoint is assumed to be called server-to-server, not from mobile
  • merchantId (nmid/mid or UUID) and terminalId from the gateway must already resolve to a merchant/outlet, with the terminal resolved via inventory_service
  • core_api does not deduplicate against a stored previous status; the caller must avoid replaying already-acknowledged success callbacks
  • the transaction push payload carries amount and gross_amount, so mobile does not need to rely on body text parsing alone
  • the bulk endpoint uses the same logic as the single endpoint, so the routing and notification results remain consistent