Skip to main content

Overview

The transaction status endpoint allows you to query the current state of a payment transaction using its unique request reference. This is essential for tracking pending transactions, reconciling payments, and providing status updates to users.
Use this endpoint to check the final status of transactions that returned a pending response (code 90009) or to verify historical transactions.

Endpoint

GET /isw/payments/checkStatus?requestReference={requestReference}
Reference: PaymentsController.java:31-35

When to check status

1

Pending transactions

When a payment returns response code 90009 (REQUEST IN PROGRESS)
2

Timeout recovery

When a payment request times out before receiving a response
3

Reconciliation

During end-of-day reconciliation to verify transaction outcomes
4

User inquiry

When users request the status of their previous transactions

Request parameters

requestReference
string
required
The unique request reference used when initiating the payment transaction
The requestReference is the same value you provided in the original payment request. Store this value securely to enable status checks.

Implementation

The status check is implemented in PaymentsService.checkStatus():
public String checkStatus(String requestReference) throws Exception {
    String endpointUrl = Constants.ROOT_LINK + "sente/status";
    String request = endpointUrl 
        + "?terminalId=" + Constants.TERMINAL_ID 
        + "&requestReference=" + requestReference;

    SystemResponse<KeyExchangeResponse> exchangeKeys = keyExchangeService.doKeyExchange();

    if(exchangeKeys.getResponseCode().equals(PhoenixResponseCodes.APPROVED.CODE)) {
        Map<String,String> headers = AuthUtils.generateInterswitchAuth(
            Constants.GET_REQUEST, 
            request, 
            "",
            exchangeKeys.getResponse().getAuthToken(),
            exchangeKeys.getResponse().getTerminalKey()
        );
        return HttpUtil.getHTTPRequest(request, headers);
    }
    else {
        return "Cannot Continue with balance Check,Key Exchange failed";
    }
}
Reference: PaymentsService.java:90-104

Response types

Successful transaction

{
  "responseCode": "90000",
  "responseMessage": "TRANSACTION APPROVED",
  "requestReference": "038938738738",
  "transactionReference": "ISW20240315123456",
  "amount": 100.00,
  "customerId": "12345",
  "customerName": "Alice Smith",
  "transactionDate": "2024-03-15T14:30:00",
  "paymentCode": 56789
}
A response code of 90000 indicates the transaction was successfully processed. Save the transactionReference for your records.

Pending transaction

{
  "responseCode": "90009",
  "responseMessage": "REQUEST IN PROGRESS",
  "requestReference": "038938738738"
}
If the transaction is still pending, wait 30-60 seconds before checking again. Implement exponential backoff for repeated checks.

Failed transaction

{
  "responseCode": "90051",
  "responseMessage": "INSUFFICIENT FUNDS",
  "requestReference": "038938738738",
  "amount": 100.00,
  "customerId": "12345"
}

Transaction not found

{
  "responseCode": "90025",
  "responseMessage": "NON EXISTENT TRANSACTION",
  "requestReference": "038938738738"
}
If you receive code 90025 (NON EXISTENT TRANSACTION), verify that:
  • The request reference is correct
  • The original payment was actually submitted
  • You’re checking the status on the same terminal that initiated the payment

Status codes reference

Common response codes for transaction status:
CodeMessageDescription
90000TRANSACTION APPROVEDPayment completed successfully
90009REQUEST IN PROGRESSTransaction is still being processed
90025NON EXISTENT TRANSACTIONNo transaction found with this reference
90051INSUFFICIENT FUNDSTransaction failed due to insufficient balance
90055WRONG PIN OR OTPTransaction failed due to incorrect credentials
90020TRANSACTION DECLINED BY BILLERBiller rejected the transaction
90059REJECTED AS SUSPECT DUPLICATE/FRAUDTransaction flagged as fraudulent
Reference: PhoenixResponseCodes.java:7-22

Code examples

Basic status check

@Autowired
private PaymentsService paymentsService;

public String checkTransactionStatus(String requestReference) {
    try {
        String response = paymentsService.checkStatus(requestReference);
        return response;
    } catch (Exception e) {
        System.err.println("Status check failed: " + e.getMessage());
        throw new StatusCheckException(e);
    }
}

Polling for completion

public JSONObject waitForTransactionCompletion(
    String requestReference, 
    int maxAttempts,
    int delaySeconds
) throws Exception {
    
    for (int attempt = 0; attempt < maxAttempts; attempt++) {
        String response = paymentsService.checkStatus(requestReference);
        JSONObject json = new JSONObject(response);
        String responseCode = json.getString("responseCode");
        
        // Transaction completed (success or failure)
        if (!"90009".equals(responseCode)) {
            return json;
        }
        
        // Still pending - wait before retry
        Thread.sleep(delaySeconds * 1000);
    }
    
    throw new TimeoutException("Transaction still pending after " + maxAttempts + " attempts");
}

Exponential backoff

public JSONObject checkStatusWithBackoff(String requestReference) throws Exception {
    int[] delays = {5, 10, 30, 60, 120}; // seconds
    
    for (int delay : delays) {
        String response = paymentsService.checkStatus(requestReference);
        JSONObject json = new JSONObject(response);
        String responseCode = json.getString("responseCode");
        
        if (!"90009".equals(responseCode)) {
            return json;
        }
        
        System.out.println("Transaction pending, waiting " + delay + " seconds...");
        Thread.sleep(delay * 1000);
    }
    
    throw new TimeoutException("Transaction still pending after maximum retries");
}

Using the REST endpoint

curl -X GET "http://localhost:8081/isw/payments/checkStatus?requestReference=038938738738"
Reference: README.md:14

Handling different statuses

Success handling

JSONObject response = new JSONObject(responseString);
String responseCode = response.getString("responseCode");

if ("90000".equals(responseCode)) {
    String transactionRef = response.getString("transactionReference");
    String transactionDate = response.getString("transactionDate");
    
    // Update database
    updateTransactionRecord(requestReference, transactionRef, "COMPLETED");
    
    // Notify customer
    sendSuccessNotification(customerId, amount);
    
    return true;
}

Pending handling

if ("90009".equals(responseCode)) {
    // Schedule another status check
    scheduleStatusCheck(requestReference, 60); // Check again in 60 seconds
    return false;
}

Failure handling

List<String> failureCodes = Arrays.asList(
    "90051", // Insufficient funds
    "90055", // Wrong PIN
    "90020", // Declined by biller
    "90059"  // Suspected fraud
);

if (failureCodes.contains(responseCode)) {
    String errorMessage = response.getString("responseMessage");
    
    // Update database
    updateTransactionRecord(requestReference, null, "FAILED");
    
    // Notify customer of failure
    sendFailureNotification(customerId, errorMessage);
    
    return false;
}

Best practices

Don’t poll too frequentlyAvoid checking status more than once every 5-10 seconds. Excessive polling can lead to rate limiting and poor system performance.

Store request references

Always store request references in your database along with transaction details:
public void saveTransaction(PaymentRequest request) {
    Transaction txn = new Transaction();
    txn.setRequestReference(request.getRequestReference());
    txn.setCustomerId(request.getCustomerId());
    txn.setAmount(request.getAmount());
    txn.setStatus("PENDING");
    txn.setCreatedAt(new Date());
    
    transactionRepository.save(txn);
}

Implement timeout limits

Don’t poll indefinitely. Set a maximum number of attempts:
int maxAttempts = 10;
int currentAttempt = 0;
int delaySeconds = 30;

while (currentAttempt < maxAttempts) {
    String status = checkStatus(requestReference);
    if (!isPending(status)) {
        return status;
    }
    currentAttempt++;
    Thread.sleep(delaySeconds * 1000);
}

// Mark as timeout after max attempts
markAsTimeout(requestReference);

Handle network errors

Implement retry logic for network failures:
public String checkStatusWithRetry(String requestReference) {
    int maxRetries = 3;
    
    for (int retry = 0; retry < maxRetries; retry++) {
        try {
            return paymentsService.checkStatus(requestReference);
        } catch (IOException e) {
            if (retry == maxRetries - 1) {
                throw e;
            }
            Thread.sleep(2000 * (retry + 1)); // Exponential backoff
        }
    }
    
    throw new RuntimeException("Failed after " + maxRetries + " retries");
}

Reconciliation process

Run daily reconciliation to check pending transactions:
public void reconcilePendingTransactions() {
    List<Transaction> pending = transactionRepository.findByStatus("PENDING");
    
    for (Transaction txn : pending) {
        try {
            String response = paymentsService.checkStatus(txn.getRequestReference());
            JSONObject json = new JSONObject(response);
            String responseCode = json.getString("responseCode");
            
            if ("90000".equals(responseCode)) {
                txn.setStatus("COMPLETED");
                txn.setTransactionReference(json.getString("transactionReference"));
            } else if (!"90009".equals(responseCode)) {
                txn.setStatus("FAILED");
                txn.setErrorMessage(json.getString("responseMessage"));
            }
            
            transactionRepository.save(txn);
            
        } catch (Exception e) {
            System.err.println("Failed to reconcile: " + txn.getRequestReference());
        }
    }
}

Response time expectations

Typical response times for status checks:
  • Immediate: Completed or failed transactions return instantly
  • Pending: Transactions may take 30 seconds to 5 minutes
  • Offline billers: Some billers may take up to 24 hours
For real-time transactions, check status every 30 seconds for the first 5 minutes, then reduce frequency to every 5 minutes.

Next steps

Wallet balance

Check available balance before processing payments

Payment operations

Return to payment operations overview

Build docs developers (and LLMs) love