Skip to content

Latest commit

 

History

History
265 lines (204 loc) · 9.31 KB

File metadata and controls

265 lines (204 loc) · 9.31 KB

14. Advanced Topics

14.1. EMV and chip card transactions

EMV (Europay, Mastercard, and Visa) is a global standard for credit and debit card payments using chip card technology. When implementing ISO-8583 for EMV transactions, additional data elements and processing are required.

14.1.1. EMV data in ISO-8583 messages

EMV data is typically included in ISO-8583 messages using specific fields, most commonly field 55 (ICC System Related Data). This field contains EMV-specific data in Tag-Length-Value (TLV) format.

Here's an example of how to handle EMV data in jPOS:

import org.jpos.iso.ISOMsg;
import org.jpos.iso.ISOException;
import org.jpos.emv.EMVStandardTagType;
import org.jpos.emv.EMVTagSequence;
import org.jpos.emv.EMVTag;

public class EMVHandler {

    public void processEMVData(ISOMsg msg) throws ISOException {
        byte[] emvData = msg.getBytes(55);
        if (emvData != null) {
            EMVTagSequence tagSequence = new EMVTagSequence(emvData);
            for (EMVTag tag : tagSequence) {
                processEMVTag(tag);
            }
        }
    }

    private void processEMVTag(EMVTag tag) {
        EMVStandardTagType tagType = EMVStandardTagType.forCode(tag.getTagNumber());
        if (tagType != null) {
            switch (tagType) {
                case APPLICATION_INTERCHANGE_PROFILE:
                    processAIP(tag.getValueAsHexString());
                    break;
                case TERMINAL_VERIFICATION_RESULTS:
                    processTVR(tag.getValueAsHexString());
                    break;
                // Add more cases for other relevant EMV tags
            }
        }
    }

    private void processAIP(String aip) {
        // Process Application Interchange Profile
        System.out.println("AIP: " + aip);
    }

    private void processTVR(String tvr) {
        // Process Terminal Verification Results
        System.out.println("TVR: " + tvr);
    }
}

14.1.2. Chip card specific fields

In addition to field 55, other fields may be used or modified for chip card transactions:

  • Field 22 (Point of Service Entry Mode): Indicates that the transaction was chip-based
  • Field 23 (Card Sequence Number): Contains the chip card's sequence number
  • Field 26 (Point of Service PIN Capture Code): Indicates how the PIN was captured for chip transactions

Here's an example of how to set these fields for a chip card transaction:

import org.jpos.iso.ISOMsg;
import org.jpos.iso.ISOException;

public class ChipCardFieldSetter {

    public void setChipCardFields(ISOMsg msg) throws ISOException {
        // Set Point of Service Entry Mode for chip card
        msg.set(22, "05"); // '05' typically indicates chip card read

        // Set Card Sequence Number (if available)
        msg.set(23, "001"); // Example sequence number

        // Set Point of Service PIN Capture Code
        msg.set(26, "12"); // '12' might indicate offline PIN verification

        // EMV data would be set in field 55 as shown in the previous example
    }
}

14.2. Tokenization

Tokenization is a security measure that replaces sensitive data (like a PAN) with a non-sensitive equivalent (a token) that can be used in the payment ecosystem.

14.2.1. Token formats

Tokens often maintain the same format as the original PAN to ensure compatibility with existing systems. Here's an example of a simple tokenization service:

import org.springframework.stereotype.Service;
import java.security.SecureRandom;
import java.util.HashMap;
import java.util.Map;

@Service
public class TokenizationService {

    private final Map<String, String> tokenToCardMap = new HashMap<>();
    private final Map<String, String> cardToTokenMap = new HashMap<>();
    private final SecureRandom random = new SecureRandom();

    public String tokenize(String cardNumber) {
        if (cardToTokenMap.containsKey(cardNumber)) {
            return cardToTokenMap.get(cardNumber);
        }

        String token = generateToken(cardNumber);
        tokenToCardMap.put(token, cardNumber);
        cardToTokenMap.put(cardNumber, token);
        return token;
    }

    public String detokenize(String token) {
        return tokenToCardMap.get(token);
    }

    private String generateToken(String cardNumber) {
        StringBuilder token = new StringBuilder();
        for (int i = 0; i < cardNumber.length(); i++) {
            if (i < 6 || i >= cardNumber.length() - 4) {
                token.append(cardNumber.charAt(i));
            } else {
                token.append(random.nextInt(10));
            }
        }
        return token.toString();
    }
}

14.2.2. De-tokenization process

De-tokenization is the process of retrieving the original PAN from a token. This typically happens in a secure environment. Here's how you might use the tokenization service in an ISO-8583 context:

import org.jpos.iso.ISOMsg;
import org.jpos.iso.ISOException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class TokenizationHandler {

    @Autowired
    private TokenizationService tokenizationService;

    public void handleTokenization(ISOMsg msg) throws ISOException {
        String pan = msg.getString(2);
        String token = tokenizationService.tokenize(pan);
        msg.set(2, token);
    }

    public void handleDetokenization(ISOMsg msg) throws ISOException {
        String token = msg.getString(2);
        String pan = tokenizationService.detokenize(token);
        if (pan != null) {
            msg.set(2, pan);
        } else {
            throw new ISOException("Invalid token");
        }
    }
}

14.3. 3-D Secure integration

3-D Secure is a protocol designed to be an additional security layer for online credit and debit card transactions.

14.3.1. 3-D Secure data in ISO-8583

3-D Secure data is typically included in private-use fields of ISO-8583 messages, such as field 48 or field 124. The exact implementation can vary depending on the card network and acquirer requirements.

Here's an example of how you might include 3-D Secure data in an ISO-8583 message:

import org.jpos.iso.ISOMsg;
import org.jpos.iso.ISOException;
import org.jpos.iso.ISOUtil;

public class ThreeDSecureHandler {

    public void add3DSecureData(ISOMsg msg, String eci, String cavv, String xid) throws ISOException {
        StringBuilder sb = new StringBuilder();
        sb.append("3D");
        sb.append(ISOUtil.padleft(eci, 2, '0'));
        sb.append(ISOUtil.padleft(cavv.length() + "", 3, '0')).append(cavv);
        sb.append(ISOUtil.padleft(xid.length() + "", 3, '0')).append(xid);

        msg.set(48, sb.toString());
    }

    public void parse3DSecureData(ISOMsg msg) throws ISOException {
        String data = msg.getString(48);
        if (data != null && data.startsWith("3D")) {
            String eci = data.substring(2, 4);
            int cavvLength = Integer.parseInt(data.substring(4, 7));
            String cavv = data.substring(7, 7 + cavvLength);
            int xidLength = Integer.parseInt(data.substring(7 + cavvLength, 10 + cavvLength));
            String xid = data.substring(10 + cavvLength, 10 + cavvLength + xidLength);

            System.out.println("ECI: " + eci);
            System.out.println("CAVV: " + cavv);
            System.out.println("XID: " + xid);
        }
    }
}

14.3.2. 3-D Secure 2.0 considerations

3-D Secure 2.0 introduces new data elements and flows. While the basic principle remains the same, you may need to handle additional data elements and potentially use different fields or formats.

Here's a simple example of how you might handle some 3-D Secure 2.0 specific data:

import org.jpos.iso.ISOMsg;
import org.jpos.iso.ISOException;
import org.jpos.iso.ISOUtil;

public class ThreeDSecure2Handler {

    public void add3DS2Data(ISOMsg msg, String threeDSServerTransID, String dsTransID, String acsTransID) throws ISOException {
        StringBuilder sb = new StringBuilder();
        sb.append("3DS2");
        sb.append(ISOUtil.padleft(threeDSServerTransID.length() + "", 3, '0')).append(threeDSServerTransID);
        sb.append(ISOUtil.padleft(dsTransID.length() + "", 3, '0')).append(dsTransID);
        sb.append(ISOUtil.padleft(acsTransID.length() + "", 3, '0')).append(acsTransID);

        msg.set(124, sb.toString());
    }

    public void parse3DS2Data(ISOMsg msg) throws ISOException {
        String data = msg.getString(124);
        if (data != null && data.startsWith("3DS2")) {
            int pos = 4;
            int threeDSServerTransIDLength = Integer.parseInt(data.substring(pos, pos + 3));
            pos += 3;
            String threeDSServerTransID = data.substring(pos, pos + threeDSServerTransIDLength);
            pos += threeDSServerTransIDLength;

            int dsTransIDLength = Integer.parseInt(data.substring(pos, pos + 3));
            pos += 3;
            String dsTransID = data.substring(pos, pos + dsTransIDLength);
            pos += dsTransIDLength;

            int acsTransIDLength = Integer.parseInt(data.substring(pos, pos + 3));
            pos += 3;
            String acsTransID = data.substring(pos, pos + acsTransIDLength);

            System.out.println("3DS Server Transaction ID: " + threeDSServerTransID);
            System.out.println("DS Transaction ID: " + dsTransID);
            System.out.println("ACS Transaction ID: " + acsTransID);
        }
    }
}