Internal Transaction Status API
Purpose
This endpoint is used to receive transaction status from the QRIS gateway or payment terminal devices, then:
- route to the correct merchant and terminal,
- 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-statusPOST /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:
referenceNomerchantIdterminalIdamount- one of:
statusresponseCode
Field mapping
partnerReferenceNo- used as
transaction_code - if empty, falls back to
referenceNo
- used as
referenceNo- stored as
external_reference
- stored as
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)
- matched against
terminalId- resolved against the resolved outlet via
inventory_service(HTTP) by TID, producing the internal terminal id anddevice_id - optional when
merchantIdis a UUID; required for legacy gateway callers that only know the QRIS TID/nmid
- resolved against the resolved outlet via
amount- parsed into integer rupiah and stored as
gross_amount
- parsed into integer rupiah and stored as
status- normalized to internal status:
paid,success,successful->successpending,waiting->pendingfailed,fail->failedcancelled,canceled->cancelledrefund,refunded->refundedreversed,reverse->reversed
- normalized to internal status:
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_alerton success) is forwarded when the normalized status issuccess
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_counterror_countitems
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) andterminalIdfrom the gateway must already resolve to a merchant/outlet, with the terminal resolved viainventory_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
amountandgross_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