When implementing a jPOS client for processing Visa and Mastercard transactions, it's crucial to create custom services that handle the specific requirements of each card network. These services will encapsulate the logic for creating and sending ISO-8583 messages tailored to Visa and Mastercard specifications.
Let's create two services: one for Visa and one for Mastercard. Each service will handle common transaction types such as purchases, cash advances, refunds, and balance inquiries.
First, let's create an interface for our Visa transaction service:
package com.example.jpos.service;
import org.jpos.iso.ISOException;
import org.jpos.iso.ISOMsg;
public interface VisaTransactionService {
ISOMsg createPurchaseRequest(String pan, String amount, String merchantId) throws ISOException;
ISOMsg createCashAdvanceRequest(String pan, String amount, String merchantId) throws ISOException;
ISOMsg createRefundRequest(String pan, String amount, String merchantId, String originalRrn) throws ISOException;
ISOMsg createBalanceInquiryRequest(String pan, String merchantId) throws ISOException;
}Now, let's implement this interface:
package com.example.jpos.service.impl;
import com.example.jpos.service.VisaTransactionService;
import org.jpos.iso.ISOException;
import org.jpos.iso.ISOMsg;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
@Service
public class VisaTransactionServiceImpl implements VisaTransactionService {
private static final String VISA_MTI_REQUEST = "0100";
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("MMddHHmmss");
@Override
public ISOMsg createPurchaseRequest(String pan, String amount, String merchantId) throws ISOException {
ISOMsg msg = new ISOMsg();
msg.setMTI(VISA_MTI_REQUEST);
msg.set(2, pan);
msg.set(3, "000000"); // Processing code for purchase
msg.set(4, amount);
msg.set(7, LocalDateTime.now().format(DATE_TIME_FORMATTER));
msg.set(11, generateStan());
msg.set(18, "5999"); // Merchant category code (example)
msg.set(32, "123456"); // Acquiring Institution ID Code
msg.set(41, merchantId);
msg.set(49, "840"); // Currency code (USD)
return msg;
}
@Override
public ISOMsg createCashAdvanceRequest(String pan, String amount, String merchantId) throws ISOException {
ISOMsg msg = new ISOMsg();
msg.setMTI(VISA_MTI_REQUEST);
msg.set(2, pan);
msg.set(3, "010000"); // Processing code for cash advance
msg.set(4, amount);
msg.set(7, LocalDateTime.now().format(DATE_TIME_FORMATTER));
msg.set(11, generateStan());
msg.set(18, "6011"); // Merchant category code for ATM
msg.set(32, "123456"); // Acquiring Institution ID Code
msg.set(41, merchantId);
msg.set(49, "840"); // Currency code (USD)
return msg;
}
@Override
public ISOMsg createRefundRequest(String pan, String amount, String merchantId, String originalRrn) throws ISOException {
ISOMsg msg = new ISOMsg();
msg.setMTI(VISA_MTI_REQUEST);
msg.set(2, pan);
msg.set(3, "200000"); // Processing code for refund
msg.set(4, amount);
msg.set(7, LocalDateTime.now().format(DATE_TIME_FORMATTER));
msg.set(11, generateStan());
msg.set(18, "5999"); // Merchant category code (example)
msg.set(32, "123456"); // Acquiring Institution ID Code
msg.set(37, originalRrn); // Retrieval Reference Number of the original transaction
msg.set(41, merchantId);
msg.set(49, "840"); // Currency code (USD)
return msg;
}
@Override
public ISOMsg createBalanceInquiryRequest(String pan, String merchantId) throws ISOException {
ISOMsg msg = new ISOMsg();
msg.setMTI(VISA_MTI_REQUEST);
msg.set(2, pan);
msg.set(3, "300000"); // Processing code for balance inquiry
msg.set(7, LocalDateTime.now().format(DATE_TIME_FORMATTER));
msg.set(11, generateStan());
msg.set(18, "6011"); // Merchant category code for ATM
msg.set(32, "123456"); // Acquiring Institution ID Code
msg.set(41, merchantId);
msg.set(49, "840"); // Currency code (USD)
return msg;
}
private String generateStan() {
// Generate a unique 6-digit STAN (System Trace Audit Number)
return String.format("%06d", (int) (Math.random() * 999999));
}
}Now, let's create a similar service for Mastercard transactions:
package com.example.jpos.service;
import org.jpos.iso.ISOException;
import org.jpos.iso.ISOMsg;
public interface MastercardTransactionService {
ISOMsg createPurchaseRequest(String pan, String amount, String merchantId) throws ISOException;
ISOMsg createCashAdvanceRequest(String pan, String amount, String merchantId) throws ISOException;
ISOMsg createRefundRequest(String pan, String amount, String merchantId, String originalRrn) throws ISOException;
ISOMsg createBalanceInquiryRequest(String pan, String merchantId) throws ISOException;
}And its implementation:
package com.example.jpos.service.impl;
import com.example.jpos.service.MastercardTransactionService;
import org.jpos.iso.ISOException;
import org.jpos.iso.ISOMsg;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
@Service
public class MastercardTransactionServiceImpl implements MastercardTransactionService {
private static final String MASTERCARD_MTI_REQUEST = "0100";
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("MMddHHmmss");
@Override
public ISOMsg createPurchaseRequest(String pan, String amount, String merchantId) throws ISOException {
ISOMsg msg = new ISOMsg();
msg.setMTI(MASTERCARD_MTI_REQUEST);
msg.set(2, pan);
msg.set(3, "000000"); // Processing code for purchase
msg.set(4, amount);
msg.set(7, LocalDateTime.now().format(DATE_TIME_FORMATTER));
msg.set(11, generateStan());
msg.set(18, "5999"); // Merchant category code (example)
msg.set(32, "654321"); // Acquiring Institution ID Code
msg.set(41, merchantId);
msg.set(49, "840"); // Currency code (USD)
msg.set(61, "0000000000000001"); // Point-of-Service Data (example)
return msg;
}
@Override
public ISOMsg createCashAdvanceRequest(String pan, String amount, String merchantId) throws ISOException {
ISOMsg msg = new ISOMsg();
msg.setMTI(MASTERCARD_MTI_REQUEST);
msg.set(2, pan);
msg.set(3, "010000"); // Processing code for cash advance
msg.set(4, amount);
msg.set(7, LocalDateTime.now().format(DATE_TIME_FORMATTER));
msg.set(11, generateStan());
msg.set(18, "6011"); // Merchant category code for ATM
msg.set(32, "654321"); // Acquiring Institution ID Code
msg.set(41, merchantId);
msg.set(49, "840"); // Currency code (USD)
msg.set(61, "0000000000000002"); // Point-of-Service Data (example)
return msg;
}
@Override
public ISOMsg createRefundRequest(String pan, String amount, String merchantId, String originalRrn) throws ISOException {
ISOMsg msg = new ISOMsg();
msg.setMTI(MASTERCARD_MTI_REQUEST);
msg.set(2, pan);
msg.set(3, "200000"); // Processing code for refund
msg.set(4, amount);
msg.set(7, LocalDateTime.now().format(DATE_TIME_FORMATTER));
msg.set(11, generateStan());
msg.set(18, "5999"); // Merchant category code (example)
msg.set(32, "654321"); // Acquiring Institution ID Code
msg.set(37, originalRrn); // Retrieval Reference Number of the original transaction
msg.set(41, merchantId);
msg.set(49, "840"); // Currency code (USD)
msg.set(61, "0000000000000003"); // Point-of-Service Data (example)
return msg;
}
@Override
public ISOMsg createBalanceInquiryRequest(String pan, String merchantId) throws ISOException {
ISOMsg msg = new ISOMsg();
msg.setMTI(MASTERCARD_MTI_REQUEST);
msg.set(2, pan);
msg.set(3, "300000"); // Processing code for balance inquiry
msg.set(7, LocalDateTime.now().format(DATE_TIME_FORMATTER));
msg.set(11, generateStan());
msg.set(18, "6011"); // Merchant category code for ATM
msg.set(32, "654321"); // Acquiring Institution ID Code
msg.set(41, merchantId);
msg.set(49, "840"); // Currency code (USD)
msg.set(61, "0000000000000004"); // Point-of-Service Data (example)
return msg;
}
private String generateStan() {
// Generate a unique 6-digit STAN (System Trace Audit Number)
return String.format("%06d", (int) (Math.random() * 999999));
}
}To use these services in your application, you can inject them into your controllers or other services. Here's an example of how you might use them in a transaction processing service:
package com.example.jpos.service;
import org.jpos.iso.ISOException;
import org.jpos.iso.ISOMsg;
import org.jpos.iso.MUX;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class TransactionProcessingService {
@Autowired
private VisaTransactionService visaService;
@Autowired
private MastercardTransactionService mastercardService;
@Autowired
private MUX mux;
public ISOMsg processPurchase(String pan, String amount, String merchantId, String cardNetwork) throws ISOException {
ISOMsg request;
if ("visa".equalsIgnoreCase(cardNetwork)) {
request = visaService.createPurchaseRequest(pan, amount, merchantId);
} else if ("mastercard".equalsIgnoreCase(cardNetwork)) {
request = mastercardService.createPurchaseRequest(pan, amount, merchantId);
} else {
throw new IllegalArgumentException("Unsupported card network: " + cardNetwork);
}
return mux.request(request, 30000); // 30 seconds timeout
}
// Similar methods for cash advance, refund, and balance inquiry
}This implementation provides a clean separation of concerns, making it easy to handle the specific requirements of each card network while providing a unified interface for transaction processing.