Skip to content

Latest commit

 

History

History
204 lines (161 loc) · 7.22 KB

File metadata and controls

204 lines (161 loc) · 7.22 KB

9. Error Handling and Response Codes

9.1. Common response codes

Response codes are typically found in Field 39 of the ISO-8583 message. They are usually two-character codes that indicate the result of a transaction. Let's look at some common categories:

9.1.1. Approval codes

  • 00: Approved or completed successfully
  • 10: Partial approval (for partial amount)
  • 85: No reason to decline

9.1.2. Decline codes

  • 05: Do not honor
  • 14: Invalid card number
  • 51: Insufficient funds
  • 54: Expired card
  • 57: Transaction not permitted to cardholder
  • 61: Exceeds withdrawal amount limit
  • 65: Exceeds withdrawal frequency limit

9.1.3. Referral codes

  • 01: Refer to card issuer
  • 02: Refer to card issuer, special condition

9.2. Error scenarios and handling

9.2.1. Network errors

These are typically issues related to connectivity or timeouts. They should be handled gracefully, often by retrying the transaction or notifying the user to try again later.

9.2.2. Format errors

These occur when the message structure is incorrect or contains invalid data. They should be logged for debugging and may require code fixes.

9.2.3. Security errors

These are related to encryption, authentication, or other security measures. They should be treated with high priority and may require immediate action.

Now, let's implement a simple error handling system using jPOS and Spring. We'll create a service that processes transactions and handles various error scenarios.

import org.jpos.iso.ISOException;
import org.jpos.iso.ISOMsg;
import org.springframework.stereotype.Service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Service
public class TransactionService {

    private static final Logger logger = LoggerFactory.getLogger(TransactionService.class);

    public void processTransaction(ISOMsg msg) {
        try {
            // Simulate processing
            String responseCode = processISOMessage(msg);
            handleResponseCode(responseCode);
        } catch (ISOException e) {
            logger.error("ISO Exception occurred", e);
            handleFormatError(e);
        } catch (NetworkException e) {
            logger.error("Network error occurred", e);
            handleNetworkError(e);
        } catch (SecurityException e) {
            logger.error("Security error occurred", e);
            handleSecurityError(e);
        }
    }

    private String processISOMessage(ISOMsg msg) throws ISOException, NetworkException, SecurityException {
        // Simulated processing logic
        String mti = msg.getMTI();
        if ("0100".equals(mti) || "0200".equals(mti)) {
            // Simulate a random response
            return generateRandomResponseCode();
        }
        throw new ISOException("Unsupported MTI: " + mti);
    }

    private String generateRandomResponseCode() {
        String[] codes = {"00", "05", "51", "54", "91"};
        return codes[(int) (Math.random() * codes.length)];
    }

    private void handleResponseCode(String responseCode) {
        switch (responseCode) {
            case "00":
                logger.info("Transaction approved");
                break;
            case "05":
                logger.warn("Transaction declined: Do not honor");
                break;
            case "51":
                logger.warn("Transaction declined: Insufficient funds");
                break;
            case "54":
                logger.warn("Transaction declined: Expired card");
                break;
            default:
                logger.warn("Transaction declined: Unknown reason");
        }
    }

    private void handleFormatError(ISOException e) {
        logger.error("Format error in ISO message", e);
        // Implement logic to notify developers or log for debugging
    }

    private void handleNetworkError(NetworkException e) {
        logger.error("Network error occurred", e);
        // Implement retry logic or notify user to try again later
    }

    private void handleSecurityError(SecurityException e) {
        logger.error("Security error occurred", e);
        // Implement logic to notify security team and possibly halt transactions
    }
}

class NetworkException extends Exception {
    public NetworkException(String message) {
        super(message);
    }
}

Now, let's create a simple test for our TransactionService:

import org.jpos.iso.ISOException;
import org.jpos.iso.ISOMsg;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.slf4j.Logger;

import static org.mockito.Mockito.*;

@ExtendWith(MockitoExtension.class)
public class TransactionServiceTest {

    @InjectMocks
    private TransactionService transactionService;

    @Mock
    private Logger logger;

    private ISOMsg mockMsg;

    @BeforeEach
    void setUp() throws ISOException {
        mockMsg = mock(ISOMsg.class);
        when(mockMsg.getMTI()).thenReturn("0100");
    }

    @Test
    void testProcessTransaction_Approved() throws ISOException {
        transactionService.processTransaction(mockMsg);
        verify(logger, atLeastOnce()).info(contains("Transaction approved"));
    }

    @Test
    void testProcessTransaction_Declined() throws ISOException {
        transactionService.processTransaction(mockMsg);
        verify(logger, atLeastOnce()).warn(contains("Transaction declined"));
    }

    @Test
    void testProcessTransaction_FormatError() throws ISOException {
        when(mockMsg.getMTI()).thenThrow(new ISOException("Invalid MTI"));
        transactionService.processTransaction(mockMsg);
        verify(logger).error(eq("Format error in ISO message"), any(ISOException.class));
    }

    @Test
    void testProcessTransaction_NetworkError() throws ISOException {
        when(mockMsg.getMTI()).thenThrow(new NetworkException("Connection timeout"));
        transactionService.processTransaction(mockMsg);
        verify(logger).error(eq("Network error occurred"), any(NetworkException.class));
    }

    @Test
    void testProcessTransaction_SecurityError() throws ISOException {
        when(mockMsg.getMTI()).thenThrow(new SecurityException("Invalid encryption"));
        transactionService.processTransaction(mockMsg);
        verify(logger).error(eq("Security error occurred"), any(SecurityException.class));
    }
}

This implementation and test suite demonstrate how to handle various error scenarios in an ISO-8583 transaction processing system:

  1. We've created a TransactionService that simulates processing an ISO message and handling different response codes.
  2. The service also handles different types of exceptions that might occur during processing.
  3. We've implemented logging for different scenarios to ensure proper tracking and debugging.
  4. The test suite covers various scenarios including successful transactions, declined transactions, and different types of errors.

In a real-world scenario, you would expand on this basic structure to include more detailed error handling, possibly integrating with a monitoring system, implementing retry mechanisms for certain types of errors, and providing more detailed feedback to the user or calling system.