All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.devnied.emvnfccard.parser.impl.AbstractParser Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2019 MILLAU Julien
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.github.devnied.emvnfccard.parser.impl;

import com.github.devnied.emvnfccard.enums.CommandEnum;
import com.github.devnied.emvnfccard.exception.CommunicationException;
import com.github.devnied.emvnfccard.iso7816emv.EmvTags;
import com.github.devnied.emvnfccard.iso7816emv.TagAndLength;
import com.github.devnied.emvnfccard.model.EmvTransactionRecord;
import com.github.devnied.emvnfccard.model.enums.CurrencyEnum;
import com.github.devnied.emvnfccard.parser.EmvTemplate;
import com.github.devnied.emvnfccard.parser.IParser;
import com.github.devnied.emvnfccard.utils.CommandApdu;
import com.github.devnied.emvnfccard.utils.ResponseUtils;
import com.github.devnied.emvnfccard.utils.TlvUtil;
import com.github.devnied.emvnfccard.utils.TrackUtils;
import fr.devnied.bitlib.BytesUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;

/**
 * Abstract Parser with provider attribute
 *
 * @author MILLAU Julien
 *
 */
public abstract class AbstractParser implements IParser {

	/**
	 * Class Logger
	 */
	private static final Logger LOGGER = LoggerFactory.getLogger(AbstractParser.class);

	/**
	 * Unknown response
	 */
	public static final int UNKNOW = -1;

	/**
	 * Card provider used
	 */
	protected final WeakReference template;

	/**
	 * Default protected constructor
	 *
	 * @param pTemplate
	 *            Emv template
	 */
	protected AbstractParser(EmvTemplate pTemplate) {
		template = new WeakReference(pTemplate);
	}

	/**
	 * Select application with AID or RID
	 *
	 * @param pAid
	 *            byte array containing AID or RID
	 * @return response byte array
	 * @throws CommunicationException communication error
	 */
	protected byte[] selectAID(final byte[] pAid) throws CommunicationException {
		if (LOGGER.isDebugEnabled()) {
			LOGGER.debug("Select AID: " + BytesUtils.bytesToString(pAid));
		}
		return template.get().getProvider().transceive(new CommandApdu(CommandEnum.SELECT, pAid, 0).toBytes());
	}

	/**
	 * Method used to extract application label
	 *
	 * @param pData
	 * 			raw response data
	 *
	 * @return decoded application label or null
	 */
	protected String extractApplicationLabel(final byte[] pData) {
		if (LOGGER.isDebugEnabled()) {
			LOGGER.debug("Extract Application label");
		}
		String label = null;
		// Get Preferred name first
		byte[] labelByte = TlvUtil.getValue(pData, EmvTags.APPLICATION_PREFERRED_NAME);
		// Get Application label
		if (labelByte == null) {
			labelByte = TlvUtil.getValue(pData, EmvTags.APPLICATION_LABEL);
		}
		// Convert to String
		if (labelByte != null) {
			label = new String(labelByte);
		}
		return label;
	}

	/**
	 * Extract bank data (BIC and IBAN)
	 *
	 * @param pData
	 *            card data
	 */
	protected void extractBankData(final byte[] pData) {
		// Extract BIC data
		byte[] bic = TlvUtil.getValue(pData, EmvTags.BANK_IDENTIFIER_CODE);
		if (bic != null) {
			template.get().getCard().setBic(new String(bic));
		}
		// Extract IBAN
		byte[] iban = TlvUtil.getValue(pData, EmvTags.IBAN);
		if (iban != null) {
			template.get().getCard().setIban(new String(iban));
		}
	}

	/**
	 * Extract card holder lastname and firstname
	 *
	 * @param pData
	 *            card data
	 */
	protected void extractCardHolderName(final byte[] pData) {
		// Extract Card Holder name (if exist)
		byte[] cardHolderByte = TlvUtil.getValue(pData, EmvTags.CARDHOLDER_NAME);
		if (cardHolderByte != null) {
			String[] name = StringUtils.split(new String(cardHolderByte).trim(), TrackUtils.CARD_HOLDER_NAME_SEPARATOR);
			if (name != null && name.length > 0) {
				template.get().getCard().setHolderLastname(StringUtils.trimToNull(name[0]));
				if (name.length == 2) {
					template.get().getCard().setHolderFirstname(StringUtils.trimToNull(name[1]));
				}
			}
		}
	}

	/**
	 * Method used to extract Log Entry from Select response
	 *
	 * @param pSelectResponse
	 *            select response
	 * @return byte array
	 */
	protected byte[] getLogEntry(final byte[] pSelectResponse) {
		return TlvUtil.getValue(pSelectResponse, EmvTags.LOG_ENTRY, EmvTags.VISA_LOG_ENTRY);
	}

	/**
	 * Method used to get Transaction counter
	 *
	 * @return the number of card transaction
	 * @throws CommunicationException communication error
	 */
	protected int getTransactionCounter() throws CommunicationException {
		int ret = UNKNOW;
		if (LOGGER.isDebugEnabled()) {
			LOGGER.debug("Get Transaction Counter ATC");
		}
		byte[] data = template.get().getProvider().transceive(new CommandApdu(CommandEnum.GET_DATA, 0x9F, 0x36, 0).toBytes());
		if (ResponseUtils.isSucceed(data)) {
			// Extract ATC
			byte[] val = TlvUtil.getValue(data, EmvTags.APP_TRANSACTION_COUNTER);
			if (val != null) {
				ret = BytesUtils.byteArrayToInt(val);
			}
		}
		return ret;
	}

	/**
	 * Method used to get the number of pin try left
	 *
	 * @return the number of pin try left
	 * @throws CommunicationException communication error
	 */
	protected int getLeftPinTry() throws CommunicationException {
		int ret = UNKNOW;
		if (LOGGER.isDebugEnabled()) {
			LOGGER.debug("Get Left PIN try");
		}
		// Left PIN try command
		byte[] data = template.get().getProvider().transceive(new CommandApdu(CommandEnum.GET_DATA, 0x9F, 0x17, 0).toBytes());
		if (ResponseUtils.isSucceed(data)) {
			// Extract PIN try counter
			byte[] val = TlvUtil.getValue(data, EmvTags.PIN_TRY_COUNTER);
			if (val != null) {
				ret = BytesUtils.byteArrayToInt(val);
			}
		}
		return ret;
	}

	/**
	 * Method used to get log format
	 *
	 * @return list of tag and length for the log format
	 * @throws CommunicationException communication error
	 */
	protected List getLogFormat() throws CommunicationException {
		List ret = new ArrayList();
		if (LOGGER.isDebugEnabled()) {
			LOGGER.debug("GET log format");
		}
		// Get log format
		byte[] data = template.get().getProvider().transceive(new CommandApdu(CommandEnum.GET_DATA, 0x9F, 0x4F, 0).toBytes());
		if (ResponseUtils.isSucceed(data)) {
			ret = TlvUtil.parseTagAndLength(TlvUtil.getValue(data, EmvTags.LOG_FORMAT));
		} else {
			LOGGER.warn("No Log format found");
		}
		return ret;
	}

	/**
	 * Method used to extract log entry from card
	 *
	 * @param pLogEntry
	 *            log entry position
	 * @return list of transaction records
	 * @throws CommunicationException communication error
	 */
	protected List extractLogEntry(final byte[] pLogEntry) throws CommunicationException {
		List listRecord = new ArrayList();
		// If log entry is defined
		if (template.get().getConfig().readTransactions && pLogEntry != null) {
			List tals = getLogFormat();
			if (tals != null && !tals.isEmpty()) {
				// read all records
				for (int rec = 1; rec <= pLogEntry[1]; rec++) {
					byte[] response = template.get().getProvider()
							.transceive(new CommandApdu(CommandEnum.READ_RECORD, rec, pLogEntry[0] << 3 | 4, 0).toBytes());
					// Extract data
					if (ResponseUtils.isSucceed(response)) {
						try {
							EmvTransactionRecord record = new EmvTransactionRecord();
							record.parse(response, tals);

							if (record.getAmount() != null) {
								// Fix artifact in EMV VISA card
								if (record.getAmount() >= 1500000000) {
									record.setAmount(record.getAmount() - 1500000000);
								}

								// Skip transaction with null amount
								if (record.getAmount() == null || record.getAmount() <= 1) {
									continue;
								}
							}

							if (record != null) {
								// Unknown currency
								if (record.getCurrency() == null) {
									record.setCurrency(CurrencyEnum.XXX);
								}
								listRecord.add(record);
							}
						} catch (Exception e) {
							LOGGER.error("Error in transaction format: " + e.getMessage(), e);
						}
					} else {
						// No more transaction log or transaction disabled
						break;
					}
				}
			}
		}
		return listRecord;
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy