com.global.api.gateways.VapsConnector Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of globalpayments-sdk Show documentation
Show all versions of globalpayments-sdk Show documentation
API for processing payments through Global Payments
package com.global.api.gateways;
import com.global.api.builders.*;
import com.global.api.entities.Address;
import com.global.api.entities.BatchSummary;
import com.global.api.entities.EncryptionData;
import com.global.api.entities.Transaction;
import com.global.api.entities.enums.*;
import com.global.api.entities.exceptions.*;
import com.global.api.entities.payroll.PayrollEncoder;
import com.global.api.network.*;
import com.global.api.network.abstractions.IBatchProvider;
import com.global.api.network.abstractions.IStanProvider;
import com.global.api.network.entities.*;
import com.global.api.serviceConfigs.AcceptorConfig;
import com.global.api.serviceConfigs.GatewayConnectorConfig;
import com.global.api.network.elements.*;
import com.global.api.network.enums.*;
import com.global.api.paymentMethods.*;
import com.global.api.terminals.DeviceMessage;
import com.global.api.terminals.TerminalUtilities;
import com.global.api.terminals.abstractions.IDeviceMessage;
import com.global.api.utils.*;
import org.apache.commons.codec.binary.Base64;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.LinkedList;
public class VapsConnector extends GatewayConnectorConfig {
private AcceptorConfig acceptorConfig;
private IBatchProvider batchProvider;
private CharacterSet characterSet = CharacterSet.ASCII;
private String companyId;
private ConnectionType connectionType;
private String merchantType;
private MessageType messageType;
private String nodeIdentification;
private ProtocolType protocolType;
private IRequestEncoder requestEncoder;
private IStanProvider stanProvider;
private String terminalId;
private String uniqueDeviceId;
private LinkedList resentTransactions;
private Transaction resentBatch;
private NetworkProcessingFlag processingFlag;
private boolean lrcFailure;
BatchSummary summary = new BatchSummary();
public void setAcceptorConfig(AcceptorConfig acceptorConfig) {
this.acceptorConfig = acceptorConfig;
}
public void setBatchProvider(IBatchProvider batchProvider) {
this.batchProvider = batchProvider;
if(this.batchProvider != null && this.batchProvider.getRequestEncoder() != null) {
requestEncoder = batchProvider.getRequestEncoder();
}
}
public void setCompanyId(String companyId) {
this.companyId = companyId;
}
public void setMerchantType(String merchantType) {
this.merchantType = merchantType;
}
public void setMessageType(MessageType messageType) {
this.messageType = messageType;
}
public void setNodeIdentification(String nodeIdentification) {
this.nodeIdentification = nodeIdentification;
}
public void setConnectionType(ConnectionType connectionType) {
this.connectionType = connectionType;
}
public void setProtocolType(ProtocolType protocolType) {
this.protocolType = protocolType;
}
public void setRequestEncoder(IRequestEncoder requestEncoder) {
this.requestEncoder = requestEncoder;
}
public void setStanProvider(IStanProvider provider) {
this.stanProvider = provider;
}
public void setTerminalId(String terminalId) {
this.terminalId = terminalId;
}
public void setUniqueDeviceId(String uniqueDeviceId) {
this.uniqueDeviceId = uniqueDeviceId;
}
public NetworkProcessingFlag getProcessingFlag() {
return processingFlag;
}
public void setProcessingFlag(NetworkProcessingFlag processingFlag) {
this.processingFlag = processingFlag;
}
@Override
public boolean supportsHostedPayments() {
return false;
}
@Override
public boolean supportsOpenBanking() {
return false;
}
public Transaction processAuthorization(AuthorizationBuilder builder) throws ApiException {
validate(builder);
// TODO: These should come from the builder somehow
byte[] orgCorr1 = new byte[2];
byte[] orgCorr2 = new byte[8];
IPaymentMethod paymentMethod = null;
PaymentMethodType paymentMethodType = null;
NetworkMessage request = new NetworkMessage();
paymentMethod = builder.getPaymentMethod();
if (paymentMethod != null)
paymentMethodType = builder.getPaymentMethod().getPaymentMethodType();
TransactionType transactionType = builder.getTransactionType();
boolean isPosSiteConfiguration = transactionType.equals(TransactionType.PosSiteConfiguration);
boolean isVisaFleet2 = acceptorConfig.getSupportVisaFleet2dot0() != null && acceptorConfig.getVisaFleet2()!=null && acceptorConfig.getVisaFleet2();
Iso4217_CurrencyCode currencyCode = Iso4217_CurrencyCode.USD;
EmvData tagData = EmvUtils.parseTagData(builder.getTagData(), isEnableLogging());
if(!StringUtils.isNullOrEmpty(builder.getCurrency())) {
currencyCode = builder.getCurrency().equalsIgnoreCase("USD") ? Iso4217_CurrencyCode.USD : Iso4217_CurrencyCode.CAD;
}
// MTI
String mti = mapMTI(builder);
if(isVisaFleet2 && mti.equals("1200")){
throw new UnsupportedTransactionException("Visa Fleet 2.0 does not support sale transaction");
}
request.setMessageTypeIndicator(mti);
// pos data code
DE22_PosDataCode dataCode = new DE22_PosDataCode();
// handle the payment methods
if(paymentMethod instanceof ICardData) {
ICardData card = (ICardData)builder.getPaymentMethod();
String token = card.getTokenizationData();
if (token == null) {
// DE 2: Primary Account Number (PAN) - LLVAR // 1100, 1200, 1220, 1300, 1310, 1320, 1420
request.set(DataElementId.DE_002, card.getNumber());
// DE 14: Date, Expiration - n4 (YYMM) // 1100, 1200, 1220, 1420
request.set(DataElementId.DE_014, formatExpiry(card.getShortExpiry()));
}
// set data codes
dataCode.setCardDataInputMode(card.isReaderPresent() ? DE22_CardDataInputMode.KeyEntry : DE22_CardDataInputMode.Manual);
dataCode.setCardHolderPresence(card.isCardPresent() ? DE22_CardHolderPresence.CardHolder_Present : DE22_CardHolderPresence.CardHolder_NotPresent);
dataCode.setCardPresence(card.isCardPresent() ? DE22_CardPresence.CardPresent : DE22_CardPresence.CardNotPresent);
if(!StringUtils.isNullOrEmpty(card.getCvn())) {
dataCode.setCardHolderAuthenticationMethod(DE22_CardHolderAuthenticationMethod.OnCard_SecurityCode);
}
}
else if(paymentMethod instanceof ITrackData) {
ITrackData card = (ITrackData)builder.getPaymentMethod();
String token = card.getTokenizationData();
// put the track data
if(transactionType.equals(TransactionType.Refund) && (!paymentMethodType.equals(PaymentMethodType.Debit) && !paymentMethodType.equals(PaymentMethodType.EBT))) {
if(paymentMethod instanceof IEncryptable && ((IEncryptable)paymentMethod).getEncryptionData() != null &&
!acceptorConfig.getSupportedEncryptionType().equals(EncryptionType.TDES)) {
request.set(DataElementId.DE_002, ((IEncryptable)card).getEncryptedPan());
}
else {
request.set(DataElementId.DE_002, card.getPan());
}
request.set(DataElementId.DE_014, card.getExpiry());
}
else if(card.getTrackNumber().equals(TrackNumber.TrackTwo) && token == null) {
// DE 35: Track 2 Data - LLVAR ns.. 37
request.set(DataElementId.DE_035, card.getTrackData());
}
else if(card.getTrackNumber().equals(TrackNumber.TrackOne) && token == null) {
// DE 45: Track 1 Data - LLVAR ans.. 76
request.set(DataElementId.DE_045, card.getTrackData());
}
else {
if(card instanceof IEncryptable && ((IEncryptable) card).getEncryptionData() != null) {
EncryptionData encryptionData = ((IEncryptable) card).getEncryptionData();
if(encryptionData.getTrackNumber().equals("1") && token == null) {
// DE 45: Track 1 Data - LLVAR ans.. 76
request.set(DataElementId.DE_045, card.getValue());
}
else if (encryptionData.getTrackNumber().equals("2") && token == null) {
// DE 35: Track 2 Data - LLVAR ns.. 37
request.set(DataElementId.DE_035, card.getValue());
}
}
}
// set data codes
if(paymentMethodType.equals(PaymentMethodType.Credit) || paymentMethodType.equals(PaymentMethodType.Debit)) {
dataCode.setCardHolderPresence(DE22_CardHolderPresence.CardHolder_Present);
dataCode.setCardPresence(DE22_CardPresence.CardPresent);
if(tagData != null) {
if(tagData.isContactlessMsd()){
dataCode.setCardDataInputMode(DE22_CardDataInputMode.ContactlessMsd);
}
else {
if (card.getEntryMethod().equals(EntryMethod.Proximity)) {
dataCode.setCardDataInputMode(DE22_CardDataInputMode.ContactlessEmv);
} else dataCode.setCardDataInputMode(DE22_CardDataInputMode.ContactEmv);
}
}
else {
if(card.getEntryMethod().equals(EntryMethod.Proximity)) {
dataCode.setCardDataInputMode(DE22_CardDataInputMode.ContactlessMsd);
}
else {
if(builder.getEmvChipCondition() != null) {
dataCode.setCardDataInputMode(DE22_CardDataInputMode.MagStripe_Fallback);
}
else dataCode.setCardDataInputMode(DE22_CardDataInputMode.UnalteredTrackData);
}
}
}
}
else if(paymentMethod instanceof GiftCard) {
GiftCard giftCard = (GiftCard)paymentMethod;
// put the track data
if (giftCard.getValueType()!=null && giftCard.getValueType().equals("TrackData")) {
if (giftCard.getTrackNumber().equals(TrackNumber.TrackTwo)) {
// DE 35: Track 2 Data - LLVAR ns.. 37
request.set(DataElementId.DE_035, giftCard.getTrackData());
} else if (giftCard.getTrackNumber().equals(TrackNumber.TrackOne)) {
// DE 45: Track 1 Data - LLVAR ans.. 76
request.set(DataElementId.DE_045, giftCard.getTrackData());
}
} else {
request.set(DataElementId.DE_002, giftCard.getNumber());
//request.set(DataElementId.DE_014, giftCard.getExpiry());
}
// set data codes
if(!StringUtils.isNullOrEmpty(giftCard.getPin())) {
dataCode.setCardHolderAuthenticationMethod(DE22_CardHolderAuthenticationMethod.PIN);
}
else {
dataCode.setCardHolderAuthenticationMethod(DE22_CardHolderAuthenticationMethod.NotAuthenticated);
}
dataCode.setCardDataInputMode(DE22_CardDataInputMode.MagStripe);
dataCode.setCardHolderPresence(DE22_CardHolderPresence.CardHolder_Present);
dataCode.setCardPresence(DE22_CardPresence.CardPresent);
}
if(paymentMethod instanceof IPinProtected) {
// address validation for acceptorConfig
if(acceptorConfig.getAddress() == null && (paymentMethodType.equals(PaymentMethodType.Debit) || paymentMethodType.equals(PaymentMethodType.EBT))) {
throw new BuilderException("The address in the acceptor config cannot be null for PIN based transactions.");
}
String pinBlock = ((IPinProtected)paymentMethod).getPinBlock();
if(!StringUtils.isNullOrEmpty(pinBlock)) {
// DE 52: Personal Identification Number (PIN) Data - b8
request.set(DataElementId.DE_052, StringUtils.bytesFromHex(pinBlock.substring(0, 16)));
// DE 53: Security Related Control Information - LLVAR an..48
request.set(DataElementId.DE_053, pinBlock.substring(16));
// set the data code
dataCode.setCardHolderAuthenticationMethod(DE22_CardHolderAuthenticationMethod.PIN);
}
else if(tagData != null && tagData.isOfflinePin()) {
// set the data code
dataCode.setCardHolderAuthenticationMethod(DE22_CardHolderAuthenticationMethod.PIN);
}
}
// DE 1: Secondary Bitmap - b8 // M (AUTO GENERATED IN NetworkMessage)
if (!isPosSiteConfiguration) {
// DE 3: Processing Code - n6 (n2: TRANSACTION TYPE, n2: ACCOUNT TYPE 1, n2: ACCOUNT TYPE 2) // M 1100, 1200, 1220, 1420
if(!transactionType.equals(TransactionType.FileAction)) {
DE3_ProcessingCode processingCode = mapProcessingCode(builder);
request.set(DataElementId.DE_003, processingCode);
}
String functionCode=mapFunctionCode(builder);
// DE 4: Amount, Transaction - n12 // C 1100, 1200, 1220, 1420
if(functionCode.equals("100")) {
request.set(DataElementId.DE_004, StringUtils.toDecimal(builder.getAmount(), 12));
}else{
request.set(DataElementId.DE_004, StringUtils.toNumeric(builder.getAmount(), 12));
}
// DE 7: Date and Time, Transmission - n10 (MMDDhhmmss) // C
request.set(DataElementId.DE_007, DateTime.now(DateTimeZone.UTC).toString("MMddhhmmss"));
}
// DE 11: System Trace Audit Number (STAN) - n6 // M
int stan = builder.getSystemTraceAuditNumber();
if(stan == 0 && stanProvider != null) {
stan = stanProvider.generateStan();
}
request.set(DataElementId.DE_011, StringUtils.padLeft(stan, 6, '0'));
// DE 12: Date and Time, Transaction - n12 (YYMMDDhhmmss)
String timestamp = builder.getTimestamp();
if(StringUtils.isNullOrEmpty(timestamp)) {
timestamp = DateTime.now().toString("yyMMddhhmmss");
}
request.set(DataElementId.DE_012, timestamp);
// DE 15: Date, Settlement - n6 (YYMMDD) // C
// DE 17: Date, Capture - n4 (MMDD) // C
// DE 18: Merchant Type - n4 // C 1100, 1200, 1220, 1300, 1320, 1420 (Same as MCC Code - Add to config since will be same for all transactions)
request.set(DataElementId.DE_018, merchantType);
// DE 19: Country Code, Acquiring Institution - n3 (ISO 3166) // C Config value perhaps? Same for each message
//request.set(DataElementId.DE_019, "840");
/* DE 22: Point of Service Data Code - an12 // M 1100, 1200, 1220, 1420 // C 1300, 1320 // O 1500, 1520
22.1 CARD DATA INPUT CAPABILITY an1 The devices/methods available for card/check data input.
22.2 CARDHOLDER AUTHENTICATION CAPABILITY an1 The methods available for authenticating the cardholder.
22.3 CARD CAPTURE CAPABILITY an1 Indicates whether the POS application can retain the card if required to do so.
22.4 OPERATING ENVIRONMENT an1 Indicates whether the POS application is attended by a clerk and the location of the POS application.
22.5 CARDHOLDER PRESENT an1 Indicates whether or not the cardholder is present and if not present then why.
22.6 CARD PRESENT an1 Indicates whether the card is present.
22.7 CARD DATA INPUT MODE an1 The method used for inputting the card data.
22.8 CARDHOLDER AUTHENTICATION METHOD an1 The method used for verifying the cardholder identity.
22.9 CARDHOLDER AUTHENTICATION ENTITY an1 The entity used for verifying the cardholder identity.
22.10 CARD DATA OUTPUT CAPABILITY an1 The methods available for updating the card data.
22.11 TERMINAL OUTPUT CAPABILITY an1 The print/display capabilities of the POS.
22.12 PIN CAPTURE CAPABILITY an1 Indicates whether the PIN data can be captured and if so the maximum PIN data length that can be captured.
*/
if (!isPosSiteConfiguration) {
dataCode.setCardDataInputCapability(acceptorConfig.getCardDataInputCapability());
dataCode.setCardHolderAuthenticationCapability(acceptorConfig.getCardHolderAuthenticationCapability());
dataCode.setCardCaptureCapability(acceptorConfig.isCardCaptureCapability());
dataCode.setOperatingEnvironment(acceptorConfig.getOperatingEnvironment());
dataCode.setCardHolderAuthenticationEntity(acceptorConfig.getCardHolderAuthenticationEntity());
dataCode.setCardDataOutputCapability(acceptorConfig.getCardDataOutputCapability());
dataCode.setTerminalOutputCapability(acceptorConfig.getTerminalOutputCapability());
dataCode.setPinCaptureCapability(acceptorConfig.getPinCaptureCapability());
request.set(DataElementId.DE_022, dataCode);
}
// DE 23: Card Sequence Number - n3 // C 1100, 1120, 1200, 1220, 1420 (Applies to EMV cards if the sequence number is returned from the terminal)
// DE 24: Function Code - n3 // M
request.set(DataElementId.DE_024, mapFunctionCode(builder));
// DE 25: Message Reason Code - n4 // C 1100, 1120, 1200, 1220, 1300, 1320, 1420, 16XX, 18XX
// DE 28: Date, Reconciliation - n6 (YYMMDD)
if(transactionType.equals(TransactionType.BatchClose)){
String date = DateTime.now().toString("yyMMdd");
request.set(DataElementId.DE_028,date);
}
/* DE 30: Amounts, Original - n24
30.1 ORIGINAL AMOUNT, TRANSACTION n12 A copy of amount, transaction (DE 4) from the original transaction.
30.2 ORIGINAL AMOUNT, RECONCILIATION n12 A copy of amount, reconciliation (DE 5) from the original transaction. Since DE 5 is not used, this element will contain all zeros.
*/
// DE 32: Acquiring Institution Identification Code - LLVAR n.. 11
request.set(DataElementId.DE_032, acceptorConfig.getAcquiringInstitutionIdentificationCode());
// DE 34: Primary Account Number, Extended - LLVAR ns.. 28
// DE 37: Retrieval Reference Number - anp12
request.set(DataElementId.DE_037, builder.getClientTransactionId());
// DE 38: Approval Code - anp6
request.set(DataElementId.DE_038, builder.getOfflineAuthCode());
// DE 39: Action Code - n3
// DE 41: Card Acceptor Terminal Identification Code - ans8
String companyIdValue = builder.getCustomerId();
if(StringUtils.isNullOrEmpty(companyIdValue)) {
companyIdValue = companyId;
}
request.set(DataElementId.DE_041, StringUtils.padRight(companyIdValue, 8, ' '));
// DE 42: Card Acceptor Identification Code - ans15
request.set(DataElementId.DE_042, StringUtils.padRight(terminalId, 15, ' '));
/* DE 43: Card Acceptor Name/Location - LLVAR ans.. 99
43.1 NAME-STREET-CITY ans..83 Name\street\city\
43.2 POSTAL-CODE ans10
43.3 REGION ans3 Two letter state/province code for the United States and Canada. Refer to the Heartland Integrator’s Guide.
43.4 COUNTRY-CODE a3 See A.30.1 ISO 3166-1: Country Codes, p. 809.
*/
if(acceptorConfig.getAddress() != null && !(transactionType.equals(TransactionType.PosSiteConfiguration))) {
DE43_CardAcceptorData cardAcceptorData = new DE43_CardAcceptorData();
cardAcceptorData.setAddress(acceptorConfig.getAddress());
request.set(DataElementId.DE_043, cardAcceptorData);
}
/* DE 44: Additional Response Data - LLVAR ans.. 99
44.1 ACTION REASON CODE n4 Contains the reason code for the action. A value of zeros indicates there is no action reason code.
44.2 TEXT MESSAGE ans..95 Contains the text describing the action.
*/
// DE 45: Track 1 Data - LLVAR ans.. 76
/* DE 46: Amounts, Fees - LLLVAR ans..204
46.1 FEE TYPE CODE n2
46.2 CURRENCY CODE, FEE n3
46.3 AMOUNT, FEE x+n8
46.4 CONVERSION RATE, FEE n8
46.5 AMOUNT, RECONCILIATION FEE x+n8
46.6 CURRENCY CODE, RECONCILIATION FEE n3
*/
if(!isPosSiteConfiguration) {
if (paymentMethodType.equals(paymentMethodType.Debit) || paymentMethodType.equals(PaymentMethodType.EBT) || paymentMethodType.equals(paymentMethodType.Credit) ) {
if (builder.getFeeAmount() != null) {
DE46_FeeAmounts feeAmounts = new DE46_FeeAmounts();
feeAmounts.setFeeTypeCode(builder.getFeeType());
feeAmounts.setCurrencyCode(currencyCode);
feeAmounts.setAmount(builder.getFeeAmount());
feeAmounts.setReconciliationCurrencyCode(currencyCode);
request.set(DataElementId.DE_046, feeAmounts);
}
}
}
/* DE 48: Message Control - LLLVAR ans..999
48-0 BIT MAP b8 C Specifies which data elements are present.
48-1 COMMUNICATION DIAGNOSTICS n4 C Data on communication connection.
48-2 HARDWARE & SOFTWARE CONFIGURATION ans20 C Version information from POS application.
48-3 LANGUAGE CODE a2 F Language used for display or print.
48-4 BATCH NUMBER n10 C Current batch.
48-5 SHIFT NUMBER n3 C Identifies shift for reconciliation and tracking.
48-6 CLERK ID LVAR an..9 C Identification of clerk operating the terminal.
48-7 MULTIPLE TRANSACTION CONTROL n9 F Parameters to control multiple related messages.
48-8 CUSTOMER DATA LLLVAR ns..250 C Data entered by customer or clerk.
48-9 TRACK 2 FOR SECOND CARD LLVAR ns..37 C Used to specify the second card in a transaction by the Track 2 format.
48-10 TRACK 1 FOR SECOND CARD LLVAR ans..76 C Used to specify the second card in a transaction by the Track 1 format.
48-11 CARD TYPE anp4 C Card type.
48-12 ADMINISTRATIVELY DIRECTED TASK b1 C Notice to or direction for action to be taken by POS application.
48-13 RFID DATA LLVAR ans..99 C Data received from RFID transponder.
48-14 PIN ENCRYPTION METHODOLOGY ans2 C Used to identify the type of encryption methodology.
48-15, 48-32 RESERVED FOR ANSI USE LLVAR ans..99 These are reserved for future use.
48-33 POS CONFIGURATION LLVAR ans..99 C Values that indicate to the Heartland system capabilities and configuration of the POS application.
48-34 MESSAGE CONFIGURATION LLVAR ans..99 C Information regarding the POS originating message and the host generated response message.
48-35 NAME 1 LLVAR ans..99 D
48-36 NAME 2 LLVAR ans..99 D
48-37 SECONDARY ACCOUNT NUMBER LLVAR ans..28 C Second Account Number for manually entered transactions requiring 2 account numbers.
48-38 RESERVED FOR HEARTLAND USE LLVAR ans..99 F
48-39 PRIOR MESSAGE INFORMATION LLVAR ans..99 C Information regarding the status of the prior message sent by the POS.
48-40, 48-49 ADDRESS 1 THROUGH ADDRESS 10 LLVAR ans..99 D One or more types of addresses.
48-50, 48-64 RESERVED FOR HEARTLAND USE LLVAR ans..99 F
*/
// DE48-5
// messageControl.setShiftNumber(builder.getShiftNumber());
if (!isPosSiteConfiguration) {
DE48_MessageControl messageControl = mapMessageControl(builder);
request.set(DataElementId.DE_048, messageControl);
}
// DE 49: Currency Code, Transaction - n3
if(!currencyCode.equals(Iso4217_CurrencyCode.USD) && builder.getAmount()!=null) {
request.set(DataElementId.DE_049, currencyCode.getValue());
}else if (paymentMethod instanceof GiftCard){
if(currencyCode!=null && builder.getAmount()!=null) {
request.set(DataElementId.DE_049, currencyCode.getValue());
}
}
// DE 50: Currency Code, Reconciliation - n3
/* DE 54: Amounts, Additional - LLVAR ans..120
54.1 ACCOUNT TYPE, ADDITIONAL AMOUNTS n2 Positions 3 and 4 or positions 5 and 6 of the processing code data element.
54.2 AMOUNT TYPE, ADDITIONAL AMOUNTS n2 Identifies the purpose of the transaction amounts.
54.3 CURRENCY CODE, ADDITIONAL AMOUNTS n3 Use DE 49 codes.
54.4 AMOUNT, ADDITIONAL AMOUNTS x + n12 See Use of the Terms Credit and Debit under Table 1-2 Transaction Processing, p. 61.
*/
if(builder.getCashBackAmount() != null || transactionType.equals(TransactionType.BenefitWithdrawal)) {
DE54_AmountsAdditional amountsAdditional = new DE54_AmountsAdditional();
if(paymentMethod instanceof GiftCard) {
amountsAdditional.put(DE54_AmountTypeCode.AmountCash, DE3_AccountType.CashCardAccount, currencyCode, builder.getCashBackAmount());
amountsAdditional.put(DE54_AmountTypeCode.AmountGoodsAndServices, DE3_AccountType.CashCardAccount, currencyCode, builder.getAmount().subtract(builder.getCashBackAmount()));
}
else if(paymentMethod instanceof EBT) {
if(transactionType.equals(TransactionType.BenefitWithdrawal)) {
amountsAdditional.put(DE54_AmountTypeCode.AmountCash, DE3_AccountType.CashBenefitAccount, currencyCode, builder.getAmount());
}
else {
amountsAdditional.put(DE54_AmountTypeCode.AmountCash, DE3_AccountType.CashBenefitAccount, currencyCode, builder.getCashBackAmount());
amountsAdditional.put(DE54_AmountTypeCode.AmountGoodsAndServices, DE3_AccountType.CashBenefitAccount, currencyCode, builder.getAmount().subtract(builder.getCashBackAmount()));
}
}
else if(paymentMethod instanceof Debit) {
amountsAdditional.put(DE54_AmountTypeCode.AmountCash, DE3_AccountType.PinDebitAccount, currencyCode, builder.getCashBackAmount());
amountsAdditional.put(DE54_AmountTypeCode.AmountGoodsAndServices, DE3_AccountType.PinDebitAccount, currencyCode, builder.getAmount().subtract(builder.getCashBackAmount()));
}
if(amountsAdditional.size() > 0) {
request.set(DataElementId.DE_054, amountsAdditional);
}
}
else if (paymentMethod instanceof Credit) {
Credit card = (Credit) paymentMethod;
DE54_AmountsAdditional amountsAdditional = new DE54_AmountsAdditional();
if (builder.getProductData() != null) {
DE63_ProductData productData = builder.getProductData().toDataElement();
if (card.getCardType().equals("VisaFleet") && (isVisaFleet2)) {
if (acceptorConfig.getSupportVisaFleet2dot0().equals(PurchaseType.Fuel) || acceptorConfig.getSupportVisaFleet2dot0().equals(PurchaseType.FuelAndNonFuel)) {
amountsAdditional.put(DE54_AmountTypeCode.NETFUELPRICE, DE3_AccountType.Unspecified, currencyCode, productData.getFuelAmount());
} else if (acceptorConfig.getSupportVisaFleet2dot0().equals(PurchaseType.NonFuel)) {
amountsAdditional.put(DE54_AmountTypeCode.NETFUELPRICE, DE3_AccountType.Unspecified, currencyCode,new BigDecimal(0));
}
if (acceptorConfig.getSupportVisaFleet2dot0().equals(PurchaseType.NonFuel) || acceptorConfig.getSupportVisaFleet2dot0().equals(PurchaseType.FuelAndNonFuel)) {
amountsAdditional.put(DE54_AmountTypeCode.GROSSNONFUELPRICE, DE3_AccountType.Unspecified, currencyCode, productData.getNonFuelWithTax());
amountsAdditional.put(DE54_AmountTypeCode.NETNONFUELPRICE, DE3_AccountType.Unspecified, currencyCode, productData.getNonFuelAmount());
} else if (acceptorConfig.getSupportVisaFleet2dot0().equals(PurchaseType.Fuel)) {
amountsAdditional.put(DE54_AmountTypeCode.GROSSNONFUELPRICE, DE3_AccountType.Unspecified, currencyCode,new BigDecimal(0));
amountsAdditional.put(DE54_AmountTypeCode.NETNONFUELPRICE, DE3_AccountType.Unspecified, currencyCode,new BigDecimal(0));
}
if (acceptorConfig.getSupportVisaFleet2dot0().equals(PurchaseType.Fuel) || acceptorConfig.getSupportVisaFleet2dot0().equals(PurchaseType.FuelAndNonFuel)) {
amountsAdditional.put(DE54_AmountTypeCode.TAXRATEFORFUEL, DE3_AccountType.Unspecified, currencyCode, new BigDecimal(1));
} else if (acceptorConfig.getSupportVisaFleet2dot0().equals(PurchaseType.NonFuel)) {
amountsAdditional.put(DE54_AmountTypeCode.TAXRATEFORFUEL, DE3_AccountType.Unspecified, currencyCode,new BigDecimal(0));
}
request.set(DataElementId.DE_054, amountsAdditional);
}
}
}
// DE 55: Integrated Circuit Card (ICC) Data - LLLVAR b..512
if(tagData != null) {
// I have a feeling this will need to come back... leaving it for now
if(!StringUtils.isNullOrEmpty(tagData.getCardSequenceNumber())) {
String cardSequenceNumber = StringUtils.padLeft(tagData.getCardSequenceNumber(), 3, '0');
request.set(DataElementId.DE_023, cardSequenceNumber);
}
request.set(DataElementId.DE_055, tagData.getSendBuffer());
}
/* DE 56: Original Data Elements - LLVAR n..35
56.1 Original message type identifier n4
56.2 Original system trace audit number n6
56.3 Original date and time, local transaction n12
56.4 Original acquiring institution identification code LLVAR n..11
*/
// DE 58: Authorizing Agent Institution Identification Code - LLVAR n..11
// DE 59: Transport Data - LLLVAR ans..999
request.set(DataElementId.DE_059, builder.getTransportData());
// DE 62: Card Issuer Data - LLLVAR ans..999
if (!isPosSiteConfiguration) {
DE62_CardIssuerData cardIssuerData = mapCardIssuerData(builder);
request.set(DataElementId.DE_062, cardIssuerData);
}
// DE 63: Product Data - LLLVAR ans…999
if(builder.getProductData() != null) {
DE63_ProductData productData = builder.getProductData().toDataElement();
request.set(DataElementId.DE_063, productData);
}
if (builder.getPosSiteConfigurationData() != null) {
DE72_DataRecord dataRecord = new DE72_DataRecord(builder.getPosSiteConfigurationData());
request.set(DataElementId.DE_072, dataRecord.toByteArray());
}
// DE 72: Data Record - LLLVAR ans..999
// DE 73: Date, Action - n6 (YYMMDD)
// DE 96: Key Management Data - LLLVAR b..999
// DE 97: Amount, Net Reconciliation - x + n16
// DE 102: Account Identification 1 - LLVAR ans..28
// DE 103: Check MICR Data (Account Identification 2) - LLVAR ans..28
// DE 115: eWIC Overflow Data - LLLVAR ansb..999
// DE 116: eWIC Overflow Data - LLLVAR ansb..999
// DE 117: eWIC Data - LLLVAR ansb..999
// DE 123: Reconciliation Totals - LLLVAR ans..999
// DE 124: Sundry Data - LLLVAR ans..999
// DE 125: Extended Response Data 1 - LLLVAR ans..999
// DE 126: Extended Response Data 2 - LLLVAR ans..999
// DE 127: Forwarding Data - LLLVAR ans..999
DE127_ForwardingData forwardingData = new DE127_ForwardingData();
String encryptedPan = null;
if(paymentMethod instanceof IEncryptable) {
EncryptionData encryptionData = ((IEncryptable)paymentMethod).getEncryptionData();
if((paymentMethod instanceof CreditTrackData) && transactionType.equals(TransactionType.Refund)) {
encryptedPan = ((IEncryptable) paymentMethod).getEncryptedPan();
}
if(encryptionData != null) {
EncryptionType encryptionType=acceptorConfig.getSupportedEncryptionType();
if(encryptedPan != null && encryptionType.equals(EncryptionType.TDES)){
encryptionData.setKtb(encryptedPan);
}
EncryptedFieldMatrix encryptedField=getEncryptionField(paymentMethod,encryptionType, transactionType);
if(encryptionType.equals(EncryptionType.TDES)){
forwardingData.setServiceType(acceptorConfig.getServiceType());
forwardingData.setOperationType(acceptorConfig.getOperationType());
}
forwardingData.setEncryptedField(encryptedField);
// check for encrypted cid
if(paymentMethod instanceof ICardData) {
String encryptedCvn = ((ICardData) paymentMethod).getCvn();
if(!StringUtils.isNullOrEmpty(encryptedCvn)) {
forwardingData.addEncryptionData(encryptionType, encryptionData,encryptedCvn);
} else{
forwardingData.addEncryptionData(encryptionType, encryptionData);
}
}else{
forwardingData.addEncryptionData(encryptionType, encryptionData);
}
request.set(DataElementId.DE_127, forwardingData);
}
}
if (paymentMethod instanceof ICardData) {
ICardData cardData = (ICardData) paymentMethod;
if (cardData != null) {
String tokenizationData = cardData.getTokenizationData();
if (tokenizationData != null) {
setTokenizationData(forwardingData, cardData,null, null, tokenizationData);
request.set(DataElementId.DE_127, forwardingData);
}
}
}
if (paymentMethod instanceof ITrackData) {
ITrackData trackData = (ITrackData) paymentMethod;
if (trackData != null) {
String tokenizationData = trackData.getTokenizationData();
if (tokenizationData != null) {
setTokenizationData(forwardingData,null, trackData, null, tokenizationData);
request.set(DataElementId.DE_127, forwardingData);
}
}
}
return sendRequest(request, builder, orgCorr1, orgCorr2);
}
public Transaction manageTransaction(ManagementBuilder builder) throws ApiException {
validate(builder);
// TODO: These should come from the builder somehow
byte[] orgCorr1 = new byte[2];
byte[] orgCorr2 = new byte[8];
NetworkMessage request = new NetworkMessage();
IPaymentMethod paymentMethod = builder.getPaymentMethod();
TransactionType transactionType = builder.getTransactionType();
boolean isVisaFleet2 = acceptorConfig.getSupportVisaFleet2dot0() != null && acceptorConfig.getVisaFleet2()!=null && acceptorConfig.getVisaFleet2();
Iso4217_CurrencyCode currencyCode = Iso4217_CurrencyCode.USD;
EmvData tagData = EmvUtils.parseTagData(builder.getTagData(), isEnableLogging());
if(!StringUtils.isNullOrEmpty(builder.getCurrency())) {
currencyCode = builder.getCurrency().equalsIgnoreCase("USD") ? Iso4217_CurrencyCode.USD : Iso4217_CurrencyCode.CAD;
}
BigDecimal transactionAmount = builder.getAmount();
if(transactionAmount == null && paymentMethod instanceof TransactionReference) {
TransactionReference transactionReference = (TransactionReference)paymentMethod;
transactionAmount = transactionReference.getOriginalApprovedAmount();
}
// MTI
String mti = mapMTI(builder);
request.setMessageTypeIndicator(mti);
// pos data code
DE22_PosDataCode dataCode = new DE22_PosDataCode();
// DE 1: Secondary Bitmap - b8 // M
request.set(DataElementId.DE_001, new byte[8]); // TODO: This should be better
// DE 2: Primary Account Number (PAN) - LLVAR // 1100, 1200, 1220, 1300, 1310, 1320, 1420
if(paymentMethod instanceof TransactionReference) {
TransactionReference transactionReference = (TransactionReference)paymentMethod;
// Original Card Data
if(transactionReference.getOriginalPaymentMethod() != null) {
IPaymentMethod originalPaymentMethod = transactionReference.getOriginalPaymentMethod();
PaymentMethodType paymentMethodType = originalPaymentMethod.getPaymentMethodType();
if(originalPaymentMethod instanceof ICardData) {
ICardData cardData = (ICardData) originalPaymentMethod;
String token = cardData.getTokenizationData();
if( token == null) {
// DE 2: PAN & DE 14 Expiry
request.set(DataElementId.DE_002, cardData.getNumber());
request.set(DataElementId.DE_014, formatExpiry(cardData.getShortExpiry()));
}
// Data codes
dataCode.setCardDataInputMode(cardData.isReaderPresent() ? DE22_CardDataInputMode.KeyEntry : DE22_CardDataInputMode.Manual);
dataCode.setCardHolderPresence(cardData.isCardPresent() ? DE22_CardHolderPresence.CardHolder_Present : DE22_CardHolderPresence.CardHolder_NotPresent);
dataCode.setCardPresence(cardData.isCardPresent() ? DE22_CardPresence.CardPresent : DE22_CardPresence.CardNotPresent);
}
else if (originalPaymentMethod instanceof ITrackData) {
ITrackData track = (ITrackData)originalPaymentMethod;
String token = track.getTokenizationData();
if (token == null) {
if (track instanceof IEncryptable && ((IEncryptable) track).getEncryptionData() != null) {
//EncryptionData encryptionData = ((IEncryptable) track).getEncryptionData();
// if(encryptionData.getTrackNumber().equals("1")) {
// request.set(DataElementId.DE_045, track.getValue());
// }
// else if (encryptionData.getTrackNumber().equals("2")) {
// request.set(DataElementId.DE_035, track.getValue());
// }
// DE 2: PAN & DE 14 Expiry
if (!acceptorConfig.getSupportedEncryptionType().equals(EncryptionType.TDES)) {
request.set(DataElementId.DE_002, ((IEncryptable) track).getEncryptedPan());
}
request.set(DataElementId.DE_014, track.getExpiry());
} else {
// DE 2: PAN & DE 14 Expiry
request.set(DataElementId.DE_002, track.getPan());
request.set(DataElementId.DE_014, track.getExpiry());
}
}
// set data codes
if(paymentMethodType.equals(PaymentMethodType.Credit) || paymentMethodType.equals(PaymentMethodType.Debit)) {
dataCode.setCardHolderPresence(DE22_CardHolderPresence.CardHolder_Present);
dataCode.setCardPresence(DE22_CardPresence.CardPresent);
if(tagData != null) {
if(tagData.isContactlessMsd()){
dataCode.setCardDataInputMode(DE22_CardDataInputMode.ContactlessMsd);
}
else {
if (track.getEntryMethod().equals(EntryMethod.Proximity)) {
dataCode.setCardDataInputMode(DE22_CardDataInputMode.ContactlessEmv);
} else dataCode.setCardDataInputMode(DE22_CardDataInputMode.ContactEmv);
}
}
else {
if(track.getEntryMethod().equals(EntryMethod.Proximity)) {
dataCode.setCardDataInputMode(DE22_CardDataInputMode.ContactlessMsd);
}
else {
if(builder.getEmvChipCondition() != null) {
dataCode.setCardDataInputMode(DE22_CardDataInputMode.MagStripe_Fallback);
}
else dataCode.setCardDataInputMode(DE22_CardDataInputMode.UnalteredTrackData);
}
}
}
}
else if (originalPaymentMethod instanceof GiftCard) {
GiftCard gift = (GiftCard)originalPaymentMethod;
// DE 35 / DE 45
if(gift.getValueType()!=null && gift.getValueType().equals("TrackData")) {
if(gift.getTrackNumber().equals(TrackNumber.TrackTwo)) {
request.set(DataElementId.DE_035, gift.getTrackData());
}
else {
request.set(DataElementId.DE_045, gift.getTrackData());
}
}
else {
// DE 2: PAN & DE 14 Expiry
request.set(DataElementId.DE_002, gift.getNumber());
if (acceptorConfig.getSupportedEncryptionType().equals(EncryptionType.TDES)) {
request.set(DataElementId.DE_014, gift.getExpiry());
}
}
// set data codes
if(!StringUtils.isNullOrEmpty(gift.getPin())) {
dataCode.setCardHolderAuthenticationMethod(DE22_CardHolderAuthenticationMethod.PIN);
}
else {
dataCode.setCardHolderAuthenticationMethod(DE22_CardHolderAuthenticationMethod.NotAuthenticated);
}
dataCode.setCardDataInputMode(DE22_CardDataInputMode.MagStripe);
dataCode.setCardHolderPresence(DE22_CardHolderPresence.CardHolder_Present);
dataCode.setCardPresence(DE22_CardPresence.CardPresent);
}
}
}
boolean isNeitherBatchCloseNorTimeRequest = !transactionType.equals(TransactionType.BatchClose) && !transactionType.equals(TransactionType.TimeRequest);
// DE 3: Processing Code - n6 (n2: TRANSACTION TYPE, n2: ACCOUNT TYPE 1, n2: ACCOUNT TYPE 2) // M 1100, 1200, 1220, 1420
if(isNeitherBatchCloseNorTimeRequest) {
DE3_ProcessingCode processingCode = mapProcessingCode(builder);
request.set(DataElementId.DE_003, processingCode);
// DE 4: Amount, Transaction - n12 // C 1100, 1200, 1220, 1420
request.set(DataElementId.DE_004, StringUtils.toNumeric(transactionAmount, 12));
// DE 7: Date and Time, Transmission - n10 (MMDDhhmmss) // C
request.set(DataElementId.DE_007, DateTime.now(DateTimeZone.UTC).toString("MMddhhmmss"));
}
// DE 11: System Trace Audit Number (STAN) - n6 // M
int stan = builder.getSystemTraceAuditNumber();
if(stan == 0 && stanProvider != null) {
stan = stanProvider.generateStan();
}
request.set(DataElementId.DE_011, StringUtils.padLeft(stan, 6, '0'));
// DE 12: Date and Time, Transaction - n12 (YYMMDDhhmmss)
String timestamp = builder.getTimestamp();
if(StringUtils.isNullOrEmpty(timestamp)) {
timestamp = DateTime.now(DateTimeZone.UTC).toString("yyMMddhhmmss");
}
request.set(DataElementId.DE_012, timestamp);
// DE 15: Date, Settlement - n6 (YYMMDD) // C
// DE 17: Date, Capture - n4 (MMDD) // C
if(transactionType.equals(TransactionType.Capture) || transactionType.equals(TransactionType.PreAuthCompletion)) {
String captureTimestamp = timestamp.substring(2, 6);
if(paymentMethod instanceof TransactionReference) {
TransactionReference reference = (TransactionReference) paymentMethod;
if(reference.getOriginalPaymentMethod() instanceof EBT) {
request.set(DataElementId.DE_017, captureTimestamp);
}
} else if(paymentMethod instanceof EBT) {
request.set(DataElementId.DE_017, captureTimestamp);
}
}
// DE 18: Merchant Type - n4 // C 1100, 1200, 1220, 1300, 1320, 1420 (Same as MCC Code - Add to config since will be same for all transactions)
request.set(DataElementId.DE_018, merchantType);
// DE 19: Country Code, Acquiring Institution - n3 (ISO 3166) // C Config value perhaps? Same for each message
//request.set(DataElementId.DE_019, "840");
if(isNeitherBatchCloseNorTimeRequest) {
/* DE 22: Point of Service Data Code - an12 // M 1100, 1200, 1220, 1420 // C 1300, 1320 // O 1500, 1520
22.1 CARD DATA INPUT CAPABILITY an1 The devices/methods available for card/check data input.
22.2 CARDHOLDER AUTHENTICATION CAPABILITY an1 The methods available for authenticating the cardholder.
22.3 CARD CAPTURE CAPABILITY an1 Indicates whether the POS application can retain the card if required to do so.
22.4 OPERATING ENVIRONMENT an1 Indicates whether the POS application is attended by a clerk and the location of the POS application.
22.5 CARDHOLDER PRESENT an1 Indicates whether or not the cardholder is present and if not present then why.
22.6 CARD PRESENT an1 Indicates whether the card is present.
22.7 CARD DATA INPUT MODE an1 The method used for inputting the card data.
22.8 CARDHOLDER AUTHENTICATION METHOD an1 The method used for verifying the cardholder identity.
22.9 CARDHOLDER AUTHENTICATION ENTITY an1 The entity used for verifying the cardholder identity.
22.10 CARD DATA OUTPUT CAPABILITY an1 The methods available for updating the card data.
22.11 TERMINAL OUTPUT CAPABILITY an1 The print/display capabilities of the POS.
22.12 PIN CAPTURE CAPABILITY an1 Indicates whether the PIN data can be captured and if so the maximum PIN data length that can be captured.
*/
dataCode.setCardDataInputCapability(acceptorConfig.getCardDataInputCapability());
dataCode.setCardHolderAuthenticationCapability(acceptorConfig.getCardHolderAuthenticationCapability());
dataCode.setCardCaptureCapability(acceptorConfig.isCardCaptureCapability());
dataCode.setOperatingEnvironment(acceptorConfig.getOperatingEnvironment());
dataCode.setCardHolderAuthenticationEntity(acceptorConfig.getCardHolderAuthenticationEntity());
dataCode.setCardDataOutputCapability(acceptorConfig.getCardDataOutputCapability());
dataCode.setTerminalOutputCapability(acceptorConfig.getTerminalOutputCapability());
dataCode.setPinCaptureCapability(acceptorConfig.getPinCaptureCapability());
if(paymentMethod instanceof TransactionReference) {
TransactionReference reference = (TransactionReference)paymentMethod;
String originalPosDataCode = reference.getPosDataCode();
if(!StringUtils.isNullOrEmpty(originalPosDataCode)) {
dataCode.fromByteArray(originalPosDataCode.getBytes());
}
}
request.set(DataElementId.DE_022, dataCode);
}
// DE 23: Card Sequence Number - n3 // C 1100, 1120, 1200, 1220, 1420 (Applies to EMV cards if the sequence number is returned from the terminal)
// DE 24: Function Code - n3 // M
request.set(DataElementId.DE_024, mapFunctionCode(builder));
// DE 25: Message Reason Code - n4 // C 1100, 1120, 1200, 1220, 1300, 1320, 1420, 16XX, 18XX
DE25_MessageReasonCode reasonCode = mapMessageReasonCode(builder);
request.set(DataElementId.DE_025, reasonCode);
// DE 28: Date, Reconciliation - n6 (YYMMDD)
if(transactionType.equals(TransactionType.BatchClose)){
String date = DateTime.now().toString("yyMMdd");
request.set(DataElementId.DE_028, date);
}
/* DE 30: Amounts, Original - n24
30.1 ORIGINAL AMOUNT, TRANSACTION n12 A copy of amount, transaction (DE 4) from the original transaction.
30.2 ORIGINAL AMOUNT, RECONCILIATION n12 A copy of amount, reconciliation (DE 5) from the original transaction. Since DE 5 is not used, this element will contain all zeros.
*/
if(paymentMethod instanceof TransactionReference) {
TransactionReference reference = (TransactionReference)paymentMethod;
if(reference.getOriginalAmount() != null) {
BigDecimal amount = builder.getAmount();
if(amount != null) {
DE30_OriginalAmounts originalAmounts = new DE30_OriginalAmounts();
if(!AmountUtils.areEqual(amount, reference.getOriginalAmount())) {
originalAmounts.setOriginalTransactionAmount(reference.getOriginalAmount());
request.set(DataElementId.DE_030, originalAmounts);
}
else if(reference.getOriginalPaymentMethod() instanceof Debit
&& (transactionType.equals(TransactionType.PreAuthCompletion) || transactionType.equals(TransactionType.Capture))) {
originalAmounts.setOriginalTransactionAmount(reference.getOriginalAmount());
request.set(DataElementId.DE_030, originalAmounts);
// TODO: Exclude Data-Collects for Sale/Refund
}
else if(reference.getOriginalPaymentMethod() instanceof Credit) {
Credit credit = (Credit)reference.getOriginalPaymentMethod();
if(credit.getCardType().equals("WexFleet") && transactionType.equals(TransactionType.Capture)) {
originalAmounts.setOriginalTransactionAmount(reference.getOriginalApprovedAmount());
request.set(DataElementId.DE_030, originalAmounts);
}
}
}
}
}
// DE 32: Acquiring Institution Identification Code - LLVAR n.. 11
request.set(DataElementId.DE_032, acceptorConfig.getAcquiringInstitutionIdentificationCode());
// DE 34: Primary Account Number, Extended - LLVAR ns.. 28
// DE 37: Retrieval Reference Number - anp12
//request.set(DataElementId.DE_037, builder.getClientTransactionId());
// DE 38: Approval Code - anp6
request.set(DataElementId.DE_038, builder.getAuthorizationCode());
// DE 39: Action Code - n3
// DE 41: Card Acceptor Terminal Identification Code - ans8
String companyIdValue = builder.getCompanyId();
if(StringUtils.isNullOrEmpty(companyIdValue)) {
companyIdValue = companyId;
}
request.set(DataElementId.DE_041, StringUtils.padRight(companyIdValue, 8, ' '));
// DE 42: Card Acceptor Identification Code - ans15
request.set(DataElementId.DE_042, StringUtils.padRight(terminalId, 15, ' '));
/* DE 43: Card Acceptor Name/Location - LLVAR ans.. 99
43.1 NAME-STREET-CITY ans..83 Name\street\city\
43.2 POSTAL-CODE ans10
43.3 REGION ans3 Two letter state/province code for the United States and Canada. Refer to the Heartland Integrator’s Guide.
43.4 COUNTRY-CODE a3 See A.30.1 ISO 3166-1: Country Codes, p. 809.
*/
if(acceptorConfig.getAddress() != null) {
DE43_CardAcceptorData cardAcceptorData = new DE43_CardAcceptorData();
cardAcceptorData.setAddress(acceptorConfig.getAddress());
request.set(DataElementId.DE_043, cardAcceptorData);
}
/* DE 44: Additional Response Data - LLVAR ans.. 99
44.1 ACTION REASON CODE n4 Contains the reason code for the action. A value of zeros indicates there is no action reason code.
44.2 TEXT MESSAGE ans..95 Contains the text describing the action.
*/
// DE 45: Track 1 Data - LLVAR ans.. 76
/* DE 46: Amounts, Fees - LLLVAR ans..204
46.1 FEE TYPE CODE n2
46.2 CURRENCY CODE, FEE n3
46.3 AMOUNT, FEE x+n8
46.4 CONVERSION RATE, FEE n8
46.5 AMOUNT, RECONCILIATION FEE x+n8
46.6 CURRENCY CODE, RECONCILIATION FEE n3
*/
if (paymentMethod instanceof TransactionReference) {
TransactionReference reference = (TransactionReference) paymentMethod;
if (reference.getFeeAmount() != null) {
request.set(DataElementId.DE_046, reference.getFeeAmount());
}
}
/* DE 48: Message Control - LLLVAR ans..999
48-0 BIT MAP b8 C Specifies which data elements are present.
48-1 COMMUNICATION DIAGNOSTICS n4 C Data on communication connection.
48-2 HARDWARE & SOFTWARE CONFIGURATION ans20 C Version information from POS application.
48-3 LANGUAGE CODE a2 F Language used for display or print.
48-4 BATCH NUMBER n10 C Current batch.
48-5 SHIFT NUMBER n3 C Identifies shift for reconciliation and tracking.
48-6 CLERK ID LVAR an..9 C Identification of clerk operating the terminal.
48-7 MULTIPLE TRANSACTION CONTROL n9 F Parameters to control multiple related messages.
48-8 CUSTOMER DATA LLLVAR ns..250 C Data entered by customer or clerk.
48-9 TRACK 2 FOR SECOND CARD LLVAR ns..37 C Used to specify the second card in a transaction by the Track 2 format.
48-10 TRACK 1 FOR SECOND CARD LLVAR ans..76 C Used to specify the second card in a transaction by the Track 1 format.
48-11 CARD TYPE anp4 C Card type.
48-12 ADMINISTRATIVELY DIRECTED TASK b1 C Notice to or direction for action to be taken by POS application.
48-13 RFID DATA LLVAR ans..99 C Data received from RFID transponder.
48-14 PIN ENCRYPTION METHODOLOGY ans2 C Used to identify the type of encryption methodology.
48-15, 48-32 RESERVED FOR ANSI USE LLVAR ans..99 These are reserved for future use.
48-33 POS CONFIGURATION LLVAR ans..99 C Values that indicate to the Heartland system capabilities and configuration of the POS application.
48-34 MESSAGE CONFIGURATION LLVAR ans..99 C Information regarding the POS originating message and the host generated response message.
48-35 NAME 1 LLVAR ans..99 D
48-36 NAME 2 LLVAR ans..99 D
48-37 SECONDARY ACCOUNT NUMBER LLVAR ans..28 C Second Account Number for manually entered transactions requiring 2 account numbers.
48-38 RESERVED FOR HEARTLAND USE LLVAR ans..99 F
48-39 PRIOR MESSAGE INFORMATION LLVAR ans..99 C Information regarding the status of the prior message sent by the POS.
48-40, 48-49 ADDRESS 1 THROUGH ADDRESS 10 LLVAR ans..99 D One or more types of addresses.
48-50, 48-64 RESERVED FOR HEARTLAND USE LLVAR ans..99 F
*/
DE48_MessageControl messageControl = mapMessageControl(builder);
request.set(DataElementId.DE_048, messageControl);
// DE 49: Currency Code, Transaction - n3
//String currencyCode=Iso4217_CurrencyCode.valueOf(builder.getCurrency()).getValue();
if(!currencyCode.equals(Iso4217_CurrencyCode.USD) && transactionAmount!=null){
request.set(DataElementId.DE_049,currencyCode.getValue());
}else if (paymentMethod instanceof TransactionReference){
IPaymentMethod orignalPaymentMethod = ((TransactionReference) paymentMethod).getOriginalPaymentMethod();
if(orignalPaymentMethod instanceof GiftCard && transactionAmount!=null) {
request.set(DataElementId.DE_049, currencyCode.getValue());
}
}
// DE 50: Currency Code, Reconciliation - n3
if(transactionType.equals(TransactionType.BatchClose) && !currencyCode.equals(Iso4217_CurrencyCode.USD)) {
if(!StringUtils.isNullOrEmpty(builder.getCurrency())) {
if(builder.getCurrency().equalsIgnoreCase("CAD")) {
request.set(DataElementId.DE_050, Iso4217_CurrencyCode.CAD);
}
}
}
// DE 52: Personal Identification Number (PIN)
if(paymentMethod instanceof TransactionReference) {
IPaymentMethod originalPaymentMethod = ((TransactionReference) paymentMethod).getOriginalPaymentMethod();
if(transactionType.equals(TransactionType.Refund)) {
String pinBlock = null;
if(originalPaymentMethod instanceof EBT){
pinBlock = ((EBT)originalPaymentMethod).getPinBlock();
} else if(originalPaymentMethod instanceof Debit){
pinBlock = ((Debit)originalPaymentMethod).getPinBlock();
}
if(!StringUtils.isNullOrEmpty(pinBlock)) {
// DE 52: Personal Identification Number (PIN) Data - b8
request.set(DataElementId.DE_052, StringUtils.bytesFromHex(pinBlock.substring(0, 16)));
// DE 53: Security Related Control Information - LLVAR an..48
request.set(DataElementId.DE_053, pinBlock.substring(16));
}
}
}
/* DE 54: Amounts, Additional - LLVAR ans..120
54.1 ACCOUNT TYPE, ADDITIONAL AMOUNTS n2 Positions 3 and 4 or positions 5 and 6 of the processing code data element.
54.2 AMOUNT TYPE, ADDITIONAL AMOUNTS n2 Identifies the purpose of the transaction amounts.
54.3 CURRENCY CODE, ADDITIONAL AMOUNTS n3 Use DE 49 codes.
54.4 AMOUNT, ADDITIONAL AMOUNTS x + n12 See Use of the Terms Credit and Debit under Table 1-2 Transaction Processing, p. 61.
*/
if(builder.getCashBackAmount() != null && (isReversal(transactionType) || transactionType.equals(TransactionType.PreAuthCompletion))) {
if(transactionAmount == null) {
transactionAmount = BigDecimal.ZERO;
}
DE54_AmountsAdditional amountsAdditional = new DE54_AmountsAdditional();
if(paymentMethod.getPaymentMethodType().equals(PaymentMethodType.EBT)) {
amountsAdditional.put(DE54_AmountTypeCode.AmountCash, DE3_AccountType.CashBenefitAccount, currencyCode, builder.getCashBackAmount());
amountsAdditional.put(DE54_AmountTypeCode.AmountGoodsAndServices, DE3_AccountType.CashBenefitAccount, currencyCode, transactionAmount.subtract(builder.getCashBackAmount()));
}
else if(paymentMethod.getPaymentMethodType().equals(PaymentMethodType.Debit)) {
amountsAdditional.put(DE54_AmountTypeCode.AmountCash, DE3_AccountType.PinDebitAccount, currencyCode, builder.getCashBackAmount());
amountsAdditional.put(DE54_AmountTypeCode.AmountGoodsAndServices, DE3_AccountType.PinDebitAccount, currencyCode, transactionAmount.subtract(builder.getCashBackAmount()));
}
request.set(DataElementId.DE_054, amountsAdditional);
}
else if (paymentMethod instanceof Credit) {
Credit card = (Credit) paymentMethod;
DE54_AmountsAdditional amountsAdditional = new DE54_AmountsAdditional();
if (builder.getProductData() != null) {
DE63_ProductData productData = builder.getProductData().toDataElement();
if (!StringUtils.isNullOrEmpty(card.getCardType()) && card.getCardType().equals("VisaFleet") && (isVisaFleet2)) {
if (acceptorConfig.getSupportVisaFleet2dot0().equals(PurchaseType.Fuel) || acceptorConfig.getSupportVisaFleet2dot0().equals(PurchaseType.FuelAndNonFuel)) {
amountsAdditional.put(DE54_AmountTypeCode.NETFUELPRICE, DE3_AccountType.Unspecified, currencyCode, productData.getFuelAmount());
} else if (acceptorConfig.getSupportVisaFleet2dot0().equals(PurchaseType.NonFuel)) {
amountsAdditional.put(DE54_AmountTypeCode.NETFUELPRICE, DE3_AccountType.Unspecified, currencyCode, new BigDecimal(0));
}
if (acceptorConfig.getSupportVisaFleet2dot0().equals(PurchaseType.NonFuel) || acceptorConfig.getSupportVisaFleet2dot0().equals(PurchaseType.FuelAndNonFuel)) {
amountsAdditional.put(DE54_AmountTypeCode.GROSSNONFUELPRICE, DE3_AccountType.Unspecified, currencyCode, productData.getNonFuelWithTax());
amountsAdditional.put(DE54_AmountTypeCode.NETNONFUELPRICE, DE3_AccountType.Unspecified, currencyCode, productData.getNonFuelAmount());
} else if (acceptorConfig.getSupportVisaFleet2dot0().equals(PurchaseType.Fuel)) {
amountsAdditional.put(DE54_AmountTypeCode.GROSSNONFUELPRICE, DE3_AccountType.Unspecified, currencyCode,new BigDecimal(0));
amountsAdditional.put(DE54_AmountTypeCode.NETNONFUELPRICE, DE3_AccountType.Unspecified, currencyCode,new BigDecimal(0));
}
if (acceptorConfig.getSupportVisaFleet2dot0().equals(PurchaseType.Fuel) || acceptorConfig.getSupportVisaFleet2dot0().equals(PurchaseType.FuelAndNonFuel)) {
amountsAdditional.put(DE54_AmountTypeCode.TAXRATEFORFUEL, DE3_AccountType.Unspecified, currencyCode, new BigDecimal(1));
} else if (acceptorConfig.getSupportVisaFleet2dot0().equals(PurchaseType.NonFuel)) {
amountsAdditional.put(DE54_AmountTypeCode.TAXRATEFORFUEL, DE3_AccountType.Unspecified, currencyCode, new BigDecimal(0));
}
request.set(DataElementId.DE_054, amountsAdditional);
}
}
}
// DE 55: Integrated Circuit Card (ICC) Data - LLLVAR b..512
if(!StringUtils.isNullOrEmpty(builder.getTagData())) {
if(!StringUtils.isNullOrEmpty(tagData.getCardSequenceNumber())) {
String cardSequenceNumber = StringUtils.padLeft(tagData.getCardSequenceNumber(), 3, '0');
request.set(DataElementId.DE_023, cardSequenceNumber);
}
if(!(transactionType.equals(TransactionType.PreAuthCompletion)||transactionType.equals(TransactionType.Capture))||
(mapCardType(paymentMethod,builder.getTransactionType()).equals(DE48_CardType.WEX)
&& (transactionType.equals(TransactionType.PreAuthCompletion)||transactionType.equals(TransactionType.Capture))))
request.set(DataElementId.DE_055, tagData.getSendBuffer());
}
/* DE 56: Original Data Elements - LLVAR n..35
56.1 Original message type identifier n4
56.2 Original system trace audit number n6
56.3 Original date and time, local transaction n12
56.4 Original acquiring institution identification code LLVAR n..11
*/
if(paymentMethod instanceof TransactionReference) {
TransactionReference reference = (TransactionReference)paymentMethod;
// check that we have enough
if(!StringUtils.isNullOrEmpty(reference.getMessageTypeIndicator())
&& !StringUtils.isNullOrEmpty(reference.getSystemTraceAuditNumber())
&& !StringUtils.isNullOrEmpty(reference.getOriginalTransactionTime())) {
DE56_OriginalDataElements originalDataElements = new DE56_OriginalDataElements();
originalDataElements.setMessageTypeIdentifier(reference.getMessageTypeIndicator());
originalDataElements.setSystemTraceAuditNumber(reference.getSystemTraceAuditNumber());
originalDataElements.setTransactionDateTime(reference.getOriginalTransactionTime());
originalDataElements.setAcquiringInstitutionId(reference.getAcquiringInstitutionId());
request.set(DataElementId.DE_056, originalDataElements);
}
}
// DE 58: Authorizing Agent Institution Identification Code - LLVAR n..11
// DE 59: Transport Data - LLLVAR ans..999
request.set(DataElementId.DE_059, builder.getTransportData());
// DE 62: Card Issuer Data - LLLVAR ans..999
if(isNeitherBatchCloseNorTimeRequest) {
DE62_CardIssuerData cardIssuerData = mapCardIssuerData(builder);
request.set(DataElementId.DE_062, cardIssuerData);
}
else if((builder.getBatchCloseType() != null && builder.getBatchCloseType().equals(BatchCloseType.Forced)) || builder.isForceToHost()) {
DE62_CardIssuerData cardIssuerData = mapCardIssuerData(builder);
request.set(DataElementId.DE_062, cardIssuerData);
}
// DE 63: Product Data - LLLVAR ans…999
if(builder.getProductData() != null) {
DE63_ProductData productData = builder.getProductData().toDataElement();
request.set(DataElementId.DE_063, productData);
}
// DE 72: Data Record - LLLVAR ans..999
// DE 73: Date, Action - n6 (YYMMDD)
// DE 96: Key Management Data - LLLVAR b..999
// DE 97: Amount, Net Reconciliation - x + n16
// DE 102: Account Identification 1 - LLVAR ans..28
// DE 103: Check MICR Data (Account Identification 2) - LLVAR ans..28
// DE 115: eWIC Overflow Data - LLLVAR ansb..999
// DE 116: eWIC Overflow Data - LLLVAR ansb..999
// DE 117: eWIC Data - LLLVAR ansb..999
// DE 123: Reconciliation Totals - LLLVAR ans..999
if (transactionType.equals(TransactionType.BatchClose)) {
DE123_ReconciliationTotals totals = new DE123_ReconciliationTotals();
Integer transactionCount = builder.getTransactionCount();
BigDecimal totalDebits = builder.getTotalDebits();
BigDecimal totalCredits = builder.getTotalCredits();
// transaction count
if (transactionCount == null) {
if (batchProvider != null) {
transactionCount = batchProvider.getTransactionCount();
totalDebits = batchProvider.getTotalDebits();
totalCredits = batchProvider.getTotalCredits();
} else {
transactionCount = 0;
totalDebits = new BigDecimal(0);
totalCredits = new BigDecimal(0);
}
}
// Debits & Credits
totals.setTotalDebits(transactionCount, totalDebits);
totals.setTotalCredits(totalCredits);
request.set(DataElementId.DE_123, totals);
}
// DE 124: Sundry Data - LLLVAR ans..999
// DE 125: Extended Response Data 1 - LLLVAR ans..999
// DE 126: Extended Response Data 2 - LLLVAR ans..999
// DE 127: Forwarding Data - LLLVAR ans..999
DE127_ForwardingData forwardingData = new DE127_ForwardingData();
if (paymentMethod instanceof TransactionReference) {
TransactionReference reference = (TransactionReference) paymentMethod;
String card =null;
if(reference.getOriginalPaymentMethod() instanceof GiftCard) {
card = ((GiftCard) reference.getOriginalPaymentMethod()).getCardType();
}
if(reference.getOriginalPaymentMethod() != null && reference.getOriginalPaymentMethod() instanceof IEncryptable) {
EncryptionData encryptionData = ((IEncryptable) reference.getOriginalPaymentMethod()).getEncryptionData();
String encryptedPan = null;
String encryptedKTB = null;
if (encryptionData != null && encryptionData.getEncryptedKTB() != null) {
encryptedKTB = encryptionData.getEncryptedKTB();
}
boolean nonOriginalTransactions = (!("ValueLink").equals(card)) && (transactionType.equals(TransactionType.Capture) || transactionType.equals(TransactionType.PreAuthCompletion) || transactionType.equals(TransactionType.Reversal) || transactionType.equals(TransactionType.Void)
|| transactionType.equals(TransactionType.Refund));
if (nonOriginalTransactions) {
if ((reference.getOriginalPaymentMethod() instanceof Credit || ((reference.getOriginalPaymentMethod() instanceof Debit
|| reference.getOriginalPaymentMethod() instanceof EBTTrackData) && !(transactionType.equals(TransactionType.Refund))) ||
reference.getOriginalPaymentMethod() instanceof EBTCardData)) {
encryptedPan = ((IEncryptable) reference.getOriginalPaymentMethod()).getEncryptedPan();
}
}
if (encryptionData != null) {
EncryptionType encryptionType=acceptorConfig.getSupportedEncryptionType();
if(encryptedKTB!=null && ((reference.getOriginalPaymentMethod() instanceof Debit || reference.getOriginalPaymentMethod() instanceof EBTTrackData) && transactionType.equals(TransactionType.Refund))){
encryptionData.setKtb(encryptedKTB);
}
else if(encryptedPan != null && encryptionType.equals(EncryptionType.TDES)){
encryptionData.setKtb(encryptedPan);
}
if(encryptionType.equals(EncryptionType.TDES)){
forwardingData.setServiceType(acceptorConfig.getServiceType());
forwardingData.setOperationType(acceptorConfig.getOperationType());
}
EncryptedFieldMatrix encryptedField=getEncryptionField(((TransactionReference) paymentMethod).getOriginalPaymentMethod(),encryptionType, transactionType);
forwardingData.setEncryptedField(encryptedField);
forwardingData.addEncryptionData(encryptionType, encryptionData);
// check for encrypted cid -- THIS MAY NOT BE VALID FOR FOLLOW ON TRANSACTIONS WHERE THE CVN SHOULD NOT BE STORED
// if (reference.getOriginalPaymentMethod() instanceof ICardData) {
// String encryptedCvn = ((ICardData) paymentMethod).getCvn();
// if (!StringUtils.isNullOrEmpty(encryptedCvn)) {
// forwardingData.addEncryptionData(encryptionData.getKtb(), encryptedCvn);
// }
// }
request.set(DataElementId.DE_127, forwardingData);
}
}
if (reference.getOriginalPaymentMethod() instanceof ICardData) {
ICardData cardData = (ICardData) reference.getOriginalPaymentMethod();
if (cardData != null) {
String tokenizationData = cardData.getTokenizationData();
if (tokenizationData != null) {
setTokenizationData(forwardingData, cardData, null, null, tokenizationData);
request.set(DataElementId.DE_127, forwardingData);
}
}
}
if (reference.getOriginalPaymentMethod() instanceof ITrackData) {
ITrackData trackData = (ITrackData) reference.getOriginalPaymentMethod();
if (trackData != null) {
String tokenizationData = trackData.getTokenizationData();
if (tokenizationData != null) {
setTokenizationData(forwardingData, null, trackData, null, tokenizationData);
request.set(DataElementId.DE_127, forwardingData);
}
}
}
}
return sendRequest(request, builder, orgCorr1, orgCorr2);
}
public Transaction resubmitTransaction(ResubmitBuilder builder) throws ApiException {
if(StringUtils.isNullOrEmpty(builder.getTransactionToken())){
throw new BuilderException("The transaction token cannot be null for resubmitted transactions.");
}
String currency = builder.getCurrency();
// get the original request/implied capture
NetworkMessage request = this.decodeRequest(builder.getTransactionToken());
switch(builder.getTransactionType()) {
case BatchClose: {
request.setMessageTypeIndicator("1521");
if(builder.isForceToHost()) {
request.set(DataElementId.DE_025, DE25_MessageReasonCode.Forced_AuthCapture);
}
if(!StringUtils.isNullOrEmpty(currency)) {
if(currency.equalsIgnoreCase("CAD")) {
request.set(DataElementId.DE_050, Iso4217_CurrencyCode.CAD);
}
}
//DE 28
request.set(DataElementId.DE_028,DateTime.now().toString("yyMMdd"));
} break;
case DataCollect:
case Refund:
case Sale: {
request.setMessageTypeIndicator("1221");
if(builder.isForceToHost() && builder.getTransactionType().equals(TransactionType.DataCollect)){
request.set(DataElementId.DE_025,"1381");
}
// STAN
if(builder.getSystemTraceAuditNumber() != 0) {
request.set(DataElementId.DE_011, StringUtils.padLeft(builder.getSystemTraceAuditNumber(), 6, '0'));
}
// Transaction Time
if(!StringUtils.isNullOrEmpty(builder.getTimestamp())) {
request.set(DataElementId.DE_012, builder.getTimestamp());
}
// Function Code
if(builder.getTransactionType().equals(TransactionType.Sale) || builder.getTransactionType().equals(TransactionType.Refund)) {
request.set(DataElementId.DE_024, "200");
}
// Message Reason Code
if(builder.isForceToHost()) {
request.set(DataElementId.DE_025, DE25_MessageReasonCode.Forced_AuthCapture);
}
// Auth Code
if(!StringUtils.isNullOrEmpty(builder.getAuthCode())){
request.set(DataElementId.DE_038, builder.getAuthCode());
}
// Message Control
DE48_MessageControl messageControl = request.getDataElement(DataElementId.DE_048, DE48_MessageControl.class);
if(builder.hasMessageControlData()) {
// Batch Number
if(builder.getBatchNumber() != 0) {
messageControl.setBatchNumber(builder.getBatchNumber());
}
// Sequence Number
if(builder.getSequenceNumber() != 0) {
messageControl.setSequenceNumber(builder.getSequenceNumber());
}
request.set(DataElementId.DE_048, messageControl);
}
// DE 56
if(!(builder.getPaymentMethod() instanceof Debit || builder.getPaymentMethod() instanceof EBT) && builder.getTransactionType().equals(TransactionType.Refund)) {
request.remove(DataElementId.DE_056);
}
// NTS Data
DE62_CardIssuerData issuerData = request.getDataElement(DataElementId.DE_062, DE62_CardIssuerData.class);
if(issuerData == null) {
issuerData = new DE62_CardIssuerData();
}
if(builder.getNtsData() != null) {
issuerData.add(CardIssuerEntryTag.NTS_System, builder.getNtsData().toString());
request.set(DataElementId.DE_062, issuerData);
}
if(!StringUtils.isNullOrEmpty(currency)) {
if(currency.equalsIgnoreCase("CAD")) {
request.set(DataElementId.DE_050, Iso4217_CurrencyCode.CAD);
}
}
// DE 127
if (builder.getPaymentMethod() instanceof Debit || builder.getPaymentMethod() instanceof EBT) {
DE127_ForwardingData forwardingData = new DE127_ForwardingData();
IPaymentMethod paymentMethod = builder.getPaymentMethod();
if (paymentMethod instanceof IEncryptable) {
String encryptedPan = null;
EncryptionData encryptionData = ((IEncryptable) paymentMethod).getEncryptionData();
encryptedPan = ((IEncryptable) paymentMethod).getEncryptedPan();
if (encryptionData != null) {
EncryptionType encryptionType = acceptorConfig.getSupportedEncryptionType();
if (encryptedPan != null && encryptionType.equals(EncryptionType.TDES)) {
encryptionData.setKtb(encryptedPan);
}
if (encryptionType.equals(EncryptionType.TDES)) {
forwardingData.setServiceType(acceptorConfig.getServiceType());
forwardingData.setOperationType(acceptorConfig.getOperationType());
}
forwardingData.setEncryptedField(EncryptedFieldMatrix.Pan);
forwardingData.addEncryptionData(encryptionType, encryptionData);
request.set(DataElementId.DE_127, forwardingData);
}
}
}
}
break;
default:
throw new UnsupportedTransactionException("Only data collect or batch close transactions can be resubmitted");
}
return sendRequest(request, builder, new byte[2], new byte[8]);
}
public T processReport(ReportBuilder builder, Class clazz) throws ApiException {
throw new UnsupportedTransactionException("VAPS does not support reporting.");
}
public String serializeRequest(AuthorizationBuilder builder) throws ApiException {
throw new UnsupportedTransactionException("VAPS does not support hosted payments.");
}
private IDeviceMessage buildMessage(byte[] message, byte[] orgCorr1, byte[] orgCorr2) {
return buildMessage(message, orgCorr1, orgCorr2, false);
}
private IDeviceMessage buildMessage(byte[] message, byte[] orgCorr1, byte[] orgCorr2, Boolean isKeepAlive) {
int messageLength = message.length + 32;
// build the header
NetworkMessageBuilder buffer = new NetworkMessageBuilder()
.append(messageLength, 2) // EH.1: Total Tran Length
.append(isKeepAlive ? NetworkTransactionType.KeepAlive : NetworkTransactionType.Transaction) // EH.2: ID (Transaction or Keep Alive)
.append(0 ,2) // EH.3: Reserved
.append(messageType) // EH.4: Type Message
.append(characterSet) // EH.5: Character Set
.append(0) // EH.6: Response Code
.append(0) // EH.7: Response Code Origin
.append(processingFlag); // EH.8: Processing Flag
// EH.9: Protocol Type
if(protocolType.equals(ProtocolType.Async)) {
if (messageType.equals(MessageType.Heartland_POS_8583) || messageType.equals(MessageType.Heartland_NTS)) {
buffer.append(0x07);
}
else {
buffer.append(protocolType);
}
}
else {
buffer.append(protocolType);
}
// rest of the header
buffer.append(connectionType) // EH.10: Connection Type
.append(nodeIdentification) // EH.11: Node Identification
.append(orgCorr1) // EH.12: Origin Correlation 1 (2 Bytes)
.append(companyId) // EH.13: Company ID
.append(orgCorr2) // EH.14: Origin Correlation 2 (8 bytes)
.append(1); // EH.15: Version (0x01)
// append the 8683 DATA
buffer.append(message);
return new DeviceMessage(buffer.toArray());
}
public NetworkMessageHeader sendKeepAlive() throws ApiException {
IDeviceMessage keepAlive = buildMessage(new byte[0], new byte[2], new byte[8], true);
byte[] responseBuffer = send(keepAlive);
MessageReader mr = new MessageReader(responseBuffer);
// parse the header
NetworkMessageHeader header = NetworkMessageHeader.parse(mr.readBytes(30));
if(!header.getResponseCode().equals(NetworkResponseCode.Success)) {
GatewayException exc = new GatewayException(
String.format("Unexpected response from gateway: %s %s", header.getResponseCode().toString(), header.getResponseCodeOrigin().toString()),
header.getResponseCode().toString(),
header.getResponseCodeOrigin().toString());
throw exc;
} else {
return header;
}
}
private > Transaction sendRequest(NetworkMessage request, T builder, byte[] orgCorr1, byte[] orgCorr2) throws ApiException {
byte[] sendBuffer = request.buildMessage();
if(isEnableLogging()) {
System.out.println("Request Breakdown:\r\n" + request.toString());
}
IDeviceMessage message = buildMessage(sendBuffer, orgCorr1, orgCorr2, false);
TransactionType transactionType = null;
try {
if(builder != null) {
transactionType = builder.getTransactionType();
this.setSimulatedHostErrors(builder.getSimulatedHostErrors());
}
byte[] responseBuffer = send(message);
String functionCode = request.getString(DataElementId.DE_024);
String messageReasonCode = request.getString(DataElementId.DE_025);
String processingCode = request.getString(DataElementId.DE_003);
String stan = request.getString(DataElementId.DE_011);
PriorMessageInformation priorMessageInformation = new PriorMessageInformation();
IPaymentMethod paymentMethod = builder != null ? builder.getPaymentMethod() : null;
if(paymentMethod != null) {
DE48_CardType cardType = mapCardType(paymentMethod, transactionType);
if(cardType != null) {
priorMessageInformation.setCardType(cardType.getValue());
}
}
// priorMessageInformation.setResponseTime(); // TODO: Need to get this from the send message
priorMessageInformation.setFunctionCode(functionCode);
priorMessageInformation.setMessageReasonCode(messageReasonCode);
priorMessageInformation.setMessageTransactionIndicator(request.getMessageTypeIndicator());
priorMessageInformation.setProcessingCode(processingCode);
priorMessageInformation.setSystemTraceAuditNumber(stan);
priorMessageInformation.setProcessingHost(currentHost);
Transaction response = mapResponse(responseBuffer, request, builder);
response.setMessageInformation(priorMessageInformation);
if(batchProvider != null) {
batchProvider.setPriorMessageData(priorMessageInformation);
}
// check to see if we need to send a completion
if(paymentMethod != null && transactionType != null) {
if(stanProvider == null && builder.getFollowOnStan() == null) {
return response;
}
// get the original payment method
IPaymentMethod originalPaymentMethod = paymentMethod;
if(originalPaymentMethod instanceof TransactionReference) {
originalPaymentMethod = ((TransactionReference) paymentMethod).getOriginalPaymentMethod();
}
// check for Debit or EBT
if(originalPaymentMethod instanceof Debit || originalPaymentMethod instanceof EBT) {
ArrayList successCodes = new ArrayList();
successCodes.add("000");
successCodes.add("002");
if((transactionType.equals(TransactionType.Sale) || transactionType.equals(TransactionType.Refund)) && !StringUtils.isNullOrEmpty(response.getTransactionToken())) {
if(!successCodes.contains(response.getResponseCode())) {
return response;
}
Integer followOnStan = builder.getFollowOnStan();
if(followOnStan == null && stanProvider != null) {
followOnStan = stanProvider.generateStan();
}
NetworkMessage impliedCapture = decodeRequest(response.getTransactionToken());
impliedCapture.set(DataElementId.DE_011, StringUtils.padLeft(followOnStan, 6, '0'));
impliedCapture.set(DataElementId.DE_012, DateTime.now().toString("yyMMddhhmmss"));
impliedCapture.set(DataElementId.DE_025, DE25_MessageReasonCode.PinDebit_EBT_Acknowledgement);
if(originalPaymentMethod != null && originalPaymentMethod instanceof IEncryptable) {
DE127_ForwardingData forwardingData = new DE127_ForwardingData();
EncryptionType encryptionType=acceptorConfig.getSupportedEncryptionType();
if(encryptionType.equals(EncryptionType.TDES)) {
EncryptionData encryptionData = ((IEncryptable) originalPaymentMethod).getEncryptionData();
if (encryptionData != null) {
String track = ((IEncryptable) originalPaymentMethod).getEncryptedPan();
if (track != null) {
encryptionData.setKtb(track);
}
if (encryptionType.equals(EncryptionType.TDES)) {
forwardingData.setServiceType(acceptorConfig.getServiceType());
forwardingData.setOperationType(acceptorConfig.getOperationType());
}
EncryptedFieldMatrix encryptedField = getEncryptionField(originalPaymentMethod, encryptionType, TransactionType.Capture);
forwardingData.setEncryptedField(encryptedField);
forwardingData.addEncryptionData(encryptionType, encryptionData);
impliedCapture.set(DataElementId.DE_127, forwardingData);
}
}
}
Transaction dataCollectResponse = sendRequest(impliedCapture, null, orgCorr1, orgCorr2);
response.setPreAuthCompletion(dataCollectResponse);
}
else if(transactionType.equals(TransactionType.Capture) && messageReasonCode != null) {
Integer followOnStan = builder.getFollowOnStan();
if(followOnStan == null && stanProvider != null) {
followOnStan = stanProvider.generateStan();
}
if(messageReasonCode.equals(DE25_MessageReasonCode.AuthCapture.getValue())) {
request.set(DataElementId.DE_011, StringUtils.padLeft(followOnStan, 6, '0'));
request.set(DataElementId.DE_012, DateTime.now().toString("yyMMddhhmmss"));
request.set(DataElementId.DE_025, DE25_MessageReasonCode.PinDebit_EBT_Acknowledgement);
Transaction dataCollectResponse = sendRequest(request, builder, orgCorr1, orgCorr2);
response.setPreAuthCompletion(dataCollectResponse);
}
}
}
else if(originalPaymentMethod instanceof GiftCard) {
/*
Removed As this was deemed not needed by the issuer
Leaving the code for now... in case they change their minds
*/
// if(((GiftCard) originalPaymentMethod).getCardType().equals("ValueLink")) {
// if(transactionType.equals(TransactionType.Capture) && messageReasonCode != null) {
// // check for the right MRC
// if(messageReasonCode.equals(DE25_MessageReasonCode.AuthCapture.getValue())) {
// // make sure it's a capture and not a preAuthCompletion
// request.set(DataElementId.DE_025, DE25_MessageReasonCode.PinDebit_EBT_Acknowledgement);
// Transaction dataCollectResponse = sendRequest(request, builder, orgCorr1, orgCorr2);
// dataCollectResponse.setPreAuthCompletion(response);
// return dataCollectResponse;
// }
// }
// }
}
}
return response;
}
catch(GatewayException exc) {
String transactionToken = checkResponse(null, request, null, builder);
exc.setTransactionToken(transactionToken);
exc.setMessageTypeIndicator(request.getMessageTypeIndicator());
exc.setProcessingCode(request.getString(DataElementId.DE_003));
exc.setTransmissionTime(request.getString(DataElementId.DE_007));
exc.setPosDataCode(request.getString(DataElementId.DE_022));
exc.setHost(currentHost.getValue());
throw exc;
}
}
private > Transaction mapResponse(byte[] buffer, NetworkMessage request, T builder) throws GatewayException {
Transaction result = new Transaction();
MessageReader mr = new MessageReader(buffer);
// parse the header
NetworkMessageHeader header = NetworkMessageHeader.parse(mr.readBytes(30));
if(!header.getResponseCode().equals(NetworkResponseCode.Success)) {
GatewayException exc = new GatewayException(
String.format("Unexpected response from gateway: %s %s", header.getResponseCode().toString(), header.getResponseCodeOrigin().toString()),
header.getResponseCode().toString(),
header.getResponseCodeOrigin().toString());
throw exc;
}
else {
result.setResponseCode("000");
result.setResponseMessage(header.getResponseCodeOrigin().toString());
// parse the message
if(!header.getMessageType().equals(MessageType.NoMessage)) {
String messageTransactionIndicator = mr.readString(4);
NetworkMessage message = NetworkMessage.parse(mr.readBytes(buffer.length), Iso8583MessageType.CompleteMessage);
message.setMessageTypeIndicator(messageTransactionIndicator);
// log out the breakdown
if(isEnableLogging()) {
System.out.println("\r\nResponse Breakdown:\r\n" + message.toString());
}
DE3_ProcessingCode processingCode = message.getDataElement(DataElementId.DE_003, DE3_ProcessingCode.class);
DE44_AdditionalResponseData additionalResponseData = message.getDataElement(DataElementId.DE_044, DE44_AdditionalResponseData.class);
DE48_MessageControl messageControl = message.getDataElement(DataElementId.DE_048, DE48_MessageControl.class);
DE54_AmountsAdditional additionalAmounts = message.getDataElement(DataElementId.DE_054, DE54_AmountsAdditional.class);
DE62_CardIssuerData cardIssuerData = message.getDataElement(DataElementId.DE_062, DE62_CardIssuerData.class);
result.setAuthorizedAmount(message.getAmount(DataElementId.DE_004));
result.setHostResponseDate(message.getDate(DataElementId.DE_012, new SimpleDateFormat("yyMMddhhmmss")));
result.setReferenceNumber(message.getString(DataElementId.DE_037));
String authCode = message.getString(DataElementId.DE_038);
String responseCode = message.getString(DataElementId.DE_039);
String responseText = DE39_ActionCode.getDescription(responseCode);
if(!StringUtils.isNullOrEmpty(responseCode)) {
if(additionalResponseData != null) {
result.setAdditionalResponseCode(additionalResponseData.getActionReasonCode().toString());
responseText += String.format(" - %s: %s", additionalResponseData.getActionReasonCode().toString(), additionalResponseData.getTextMessage());
}
result.setResponseCode(responseCode);
result.setResponseMessage(responseText);
// Issuer Data
if(cardIssuerData != null) {
for (DE62_2_CardIssuerEntry entry : cardIssuerData.getCardIssuerEntries().values()) {
result.setIssuerData(entry.getIssuerTag(), entry.getIssuerEntry());
}
}
if(builder != null) {
String transactionToken = checkResponse(responseCode, request, message, builder);
result.setTransactionToken(transactionToken);
}
}
// EMV response
byte[] emvResponse = message.getByteArray(DataElementId.DE_055);
if(emvResponse != null){
EmvData emvData = EmvUtils.parseTagData(StringUtils.hexFromBytes(emvResponse), isEnableLogging());
result.setEmvIssuerResponse(emvData.getAcceptedTagData());
}
// Transaction Reference
if(builder != null) {
TransactionReference reference = new TransactionReference();
reference.setAuthCode(authCode);
// original data elements
reference.setMessageTypeIndicator(request.getMessageTypeIndicator());
reference.setOriginalApprovedAmount(message.getAmount(DataElementId.DE_004));
reference.setOriginalProcessingCode(request.getString(DataElementId.DE_003));
reference.setSystemTraceAuditNumber(request.getString(DataElementId.DE_011));
reference.setOriginalTransactionTime(request.getString(DataElementId.DE_012));
reference.setPosDataCode(request.getString(DataElementId.DE_022));
reference.setAcquiringInstitutionId(request.getString(DataElementId.DE_032));
reference.setFeeAmount(request.getString(DataElementId.DE_046));
// partial flag
if(!StringUtils.isNullOrEmpty(responseCode)) {
if (responseCode.equals("002")) {
reference.setPartialApproval(true);
} else if (responseCode.equals("000")) {
String requestAmount = request.getString(DataElementId.DE_004);
String responseAmount = message.getString(DataElementId.DE_004);
reference.setPartialApproval(!requestAmount.equals(responseAmount));
}
}
// message control fields
if (messageControl != null) {
reference.setBatchNumber(messageControl.getBatchNumber());
}
// card issuer data
if (cardIssuerData != null) {
reference.setNtsData(cardIssuerData.get("NTS"));
result.setReferenceNumber(cardIssuerData.get("IRR"));
reference.setMastercardBanknetRefNo(cardIssuerData.get(CardIssuerEntryTag.NTS_MastercardBankNet_ReferenceNumber));
reference.setMastercardBanknetSettlementDate(cardIssuerData.get(CardIssuerEntryTag.NTS_MastercardBankNet_SettlementDate));
}
// authorization builder
if (builder instanceof AuthorizationBuilder) {
AuthorizationBuilder authBuilder = (AuthorizationBuilder) builder;
reference.setOriginalAmount(authBuilder.getAmount());
reference.setOriginalEmvChipCondition(authBuilder.getEmvChipCondition());
}
// management builder
else if (builder instanceof ManagementBuilder) {
ManagementBuilder managementBuilder = (ManagementBuilder) builder;
reference.setOriginalAmount(managementBuilder.getAmount());
}
else if(builder instanceof ResubmitBuilder) {
reference.setOriginalAmount(request.getAmount(DataElementId.DE_004));
}
// transaction reference
IPaymentMethod paymentMethod = builder.getPaymentMethod();
if (paymentMethod != null) {
// original payment method
if (paymentMethod instanceof TransactionReference) {
TransactionReference originalReference = (TransactionReference) paymentMethod;
reference.setOriginalPaymentMethod(originalReference.getOriginalPaymentMethod());
// check nts specifics
if (reference.getNtsData() == null) {
reference.setNtsData(originalReference.getNtsData());
}
// get original amounts
if (reference.getOriginalAmount() == null) {
reference.setOriginalAmount(originalReference.getOriginalAmount());
}
} else {
reference.setOriginalPaymentMethod(paymentMethod);
}
}
result.setTransactionReference(reference);
// balance amounts
if(additionalAmounts != null) {
final DE3_AccountType fromAccountType = processingCode.getFromAccount();
final DE3_AccountType toAccountType = processingCode.getToAccount();
// build the list of account types to check
ArrayList accountTypes = new ArrayList() {{
add(fromAccountType);
if(!toAccountType.equals(fromAccountType)) {
add(toAccountType);
}
}};
// account type 60 is generic and the response can contain 60, 65 or 66 we need to check all
if(fromAccountType.equals(DE3_AccountType.CashCardAccount) || toAccountType.equals(DE3_AccountType.CashCardAccount)) {
accountTypes.add(DE3_AccountType.CashCard_CashAccount);
accountTypes.add(DE3_AccountType.CashCard_CreditAccount);
}
result.setBalanceAmount(additionalAmounts.getAmount(accountTypes, DE54_AmountTypeCode.AccountLedgerBalance));
result.setAvailableBalance(additionalAmounts.getAmount(accountTypes, DE54_AmountTypeCode.AccountAvailableBalance));
}
// batch summary
if(builder.getTransactionType().equals(TransactionType.BatchClose)) {
summary.setResponseCode(responseCode);
summary.setResentTransactions(resentTransactions);
summary.setResentBatchClose(resentBatch);
summary.setTransactionToken(result.getTransactionToken());
if(messageControl != null) {
summary.setBatchId(messageControl.getBatchNumber());
summary.setSequenceNumber(messageControl.getSequenceNumber() + "");
}
DE123_ReconciliationTotals reconciliationTotals = message.getDataElement(DataElementId.DE_123, DE123_ReconciliationTotals.class);
if(reconciliationTotals != null) {
int transactionCount = 0;
BigDecimal totalAmount = new BigDecimal(0);
for(DE123_ReconciliationTotal total: reconciliationTotals.getTotals()) {
transactionCount += total.getTransactionCount();
totalAmount = totalAmount.add(total.getTotalAmount());
if(total.getTransactionType().equals(DE123_TransactionType.DebitLessReversals)) {
summary.setDebitCount(total.getTransactionCount());
summary.setDebitAmount(total.getTotalAmount());
}
else if(total.getTransactionType().equals(DE123_TransactionType.CreditLessReversals)) {
summary.setCreditCount(total.getTransactionCount());
summary.setCreditAmount(total.getTotalAmount());
}
}
summary.setTransactionCount(transactionCount);
summary.setTotalAmount(totalAmount);
}
result.setBatchSummary(summary);
}
}
}
}
return result;
}
private > String mapMTI(T builder) {
String mtiValue = "1";
/* MESSAGE CLASS
0 Reserved for ISO use
1 Authorization
2 Financial
3 File action
4 Reversal
5 Reconciliation
6 Administrative
7 Fee collection
8 Network management
9 Reserved for ISO use
*/
switch(builder.getTransactionType()) {
case Auth:
case Balance:
case Verify: {
mtiValue += "1";
} break;
case Activate:
case AddValue:
case BenefitWithdrawal:
case Capture:
case CashOut:
case PreAuthCompletion:
case Refund:
case Sale: {
mtiValue += "2";
} break;
case LoadReversal:
case Reversal:
case Void:{
mtiValue += "4";
} break;
case BatchClose: {
mtiValue += "5";
} break;
case FileAction: {
mtiValue += "3";
} break;
case PosSiteConfiguration:
case TimeRequest: {
mtiValue += "6";
}break;
default:
mtiValue += "0";
}
/* MESSAGE FUNCTION
0 Request
1 Request response
2 Advice
3 Advice response
4 Notification
5–9 Reserved for ISO use
*/
switch (builder.getTransactionType()) {
case BatchClose:
case Capture:
case PreAuthCompletion:
case LoadReversal:
case Reversal:
case PosSiteConfiguration:
case Void: {
mtiValue += "2";
} break;
case Refund: {
IPaymentMethod paymentMethod = builder.getPaymentMethod();
if(paymentMethod instanceof TransactionReference) {
paymentMethod = ((TransactionReference) paymentMethod).getOriginalPaymentMethod();
}
if(paymentMethod instanceof Debit || paymentMethod instanceof EBT) {
mtiValue += "0";
}
else if(paymentMethod instanceof GiftCard) {
String cardType = ((GiftCard) paymentMethod).getCardType();
if(cardType.equals("ValueLink")) {
mtiValue += "0";
}
else mtiValue += "2";
}
else mtiValue += "2";
} break;
case FileAction: {
mtiValue += "0";
}
break;
default:
mtiValue += "0";
}
/* TRANSACTION ORIGINATOR
0 POS application
1 POS application repeat
2 Heartland system
3 Heartland system repeat
4 POS application or Heartland system
5 Reserved for Heartland use
6–9 Reserved for ISO use
*/
switch (builder.getTransactionType()) {
case PosSiteConfiguration:
case TimeRequest:
mtiValue += "4";
break;
default:
mtiValue += "0";
}
return mtiValue;
}
private > DE3_ProcessingCode mapProcessingCode(T builder) throws ApiException {
DE3_ProcessingCode processingCode = new DE3_ProcessingCode();
TransactionType type = builder.getTransactionType();
IPaymentMethod paymentMethod = builder.getPaymentMethod();
if(shouldUseOriginalProcessingCode(paymentMethod, type)) {
// set the transaction type to the original transaction type
if(builder.getPaymentMethod() instanceof TransactionReference) {
TransactionReference reference = (TransactionReference)builder.getPaymentMethod();
return processingCode.fromByteArray(reference.getOriginalProcessingCode().getBytes());
}
throw new BuilderException("The processing code must be specified when performing a reversal.");
}
switch(type) {
case Activate: {
AuthorizationBuilder authBuilder = (AuthorizationBuilder)builder;
if(authBuilder.getAmount() != null) {
processingCode.setTransactionType(DE3_TransactionType.Activate);
}
else {
processingCode.setTransactionType(DE3_TransactionType.Activate_PreValuedCard);
}
} break;
case AddValue: {
if(paymentMethod instanceof Credit) {
Credit card = (Credit)paymentMethod;
if(card.getCardType().equals("VisaReadyLink")) {
processingCode.setTransactionType(DE3_TransactionType.LoadValue);
}
else {
processingCode.setTransactionType(DE3_TransactionType.Deposit);
}
}
else {
processingCode.setTransactionType(DE3_TransactionType.Deposit);
}
} break;
case Auth: {
AuthorizationBuilder authBuilder = (AuthorizationBuilder)builder;
if(authBuilder.getAmount().equals(BigDecimal.ZERO) && authBuilder.getBillingAddress() != null && !authBuilder.isAmountEstimated()){
processingCode.setTransactionType(DE3_TransactionType.AddressOrAccountVerification);
processingCode.setToAccount(DE3_AccountType.Unspecified);
processingCode.setFromAccount(DE3_AccountType.Unspecified);
return processingCode;
}
else {
processingCode.setTransactionType(DE3_TransactionType.GoodsAndService);
}
} break;
case Balance: {
if(builder.getPaymentMethod() instanceof EBT) {
processingCode.setTransactionType(DE3_TransactionType.BalanceInquiry);
}
else if(builder.getPaymentMethod() instanceof GiftCard) {
GiftCard gift = (GiftCard)builder.getPaymentMethod();
if(gift.getCardType().equals("ValueLink")) {
processingCode.setTransactionType(DE3_TransactionType.BalanceInquiry);
}
else processingCode.setTransactionType(DE3_TransactionType.AvailableFundsInquiry);
}
else processingCode.setTransactionType(DE3_TransactionType.AvailableFundsInquiry);
} break;
case BenefitWithdrawal: {
processingCode.setTransactionType(DE3_TransactionType.Cash);
} break;
case CashOut: {
processingCode.setTransactionType(DE3_TransactionType.UnloadValue);
} break;
case Refund: {
processingCode.setTransactionType(DE3_TransactionType.Return);
} break;
case Sale: {
AuthorizationBuilder authBuilder = (AuthorizationBuilder)builder;
if(authBuilder.getCashBackAmount() != null) {
processingCode.setTransactionType(DE3_TransactionType.GoodsAndServiceWithCashDisbursement);
}
else {
processingCode.setTransactionType(DE3_TransactionType.GoodsAndService);
}
} break;
case Verify: {
processingCode.setTransactionType(DE3_TransactionType.BalanceInquiry);
} break;
default: {
processingCode.setTransactionType(DE3_TransactionType.GoodsAndService);
}
}
// check for an original payment method
if(paymentMethod instanceof TransactionReference) {
TransactionReference transactionReference = (TransactionReference)paymentMethod;
if(transactionReference.getOriginalPaymentMethod() != null) {
paymentMethod = transactionReference.getOriginalPaymentMethod();
}
}
// setting the accountType
DE3_AccountType accountType = DE3_AccountType.Unspecified;
if(paymentMethod instanceof Credit) {
Credit card = (Credit)paymentMethod;
if(card.isFleet()) {
accountType = DE3_AccountType.FleetAccount;
}
else if(card.getCardType().equals("VisaReadyLink")) {
accountType = DE3_AccountType.PinDebitAccount;
}
else {
accountType = DE3_AccountType.CreditAccount;
}
}
else if(paymentMethod instanceof Debit) {
accountType = DE3_AccountType.PinDebitAccount;
}
else if(paymentMethod instanceof GiftCard) {
accountType = DE3_AccountType.CashCardAccount;
}
else if(paymentMethod instanceof EBT) {
EBT ebtCard = (EBT)paymentMethod;
if(ebtCard.getEbtCardType().equals(EbtCardType.CashBenefit)) {
accountType = DE3_AccountType.CashBenefitAccount;
}
else {
accountType = DE3_AccountType.FoodStampsAccount;
}
}
switch (type) {
case Activate:
case AddValue:
case Refund: {
processingCode.setToAccount(accountType);
processingCode.setFromAccount(DE3_AccountType.Unspecified);
} break;
default: {
processingCode.setFromAccount(accountType);
processingCode.setToAccount(DE3_AccountType.Unspecified);
}
}
return processingCode;
}
private > String mapFunctionCode(T builder) {
TransactionType type = builder.getTransactionType();
TransactionModifier modifier = builder.getTransactionModifier();
switch(type) {
case Activate:
case AddValue:
case BenefitWithdrawal:
case CashOut:
case Refund:
case Sale: {
return "200";
}
case PreAuthCompletion:
case Capture: {
ManagementBuilder managementBuilder = (ManagementBuilder)builder;
TransactionReference reference = null;
if(managementBuilder.getPaymentMethod() != null && managementBuilder.getPaymentMethod() instanceof TransactionReference) {
reference = (TransactionReference)managementBuilder.getPaymentMethod();
}
// check for ready link and pre-auth completion
if(reference != null) {
IPaymentMethod originalPaymentMethod = reference.getOriginalPaymentMethod();
if (originalPaymentMethod instanceof Credit) {
String cardType = ((Credit) originalPaymentMethod).getCardType();
if (cardType.equals("VisaReadyLink") && type.equals(TransactionType.PreAuthCompletion)) {
return "200";
}
}
}
BigDecimal amount = managementBuilder.getAmount();
if(amount == null && reference != null) {
amount = reference.getOriginalAmount();
}
if(amount != null) {
if(reference != null) {
BigDecimal compareAmount = reference.getOriginalAmount();
if(reference.isUseAuthorizedAmount() && reference.getOriginalApprovedAmount() != null) {
compareAmount = reference.getOriginalApprovedAmount();
}
if(amount.compareTo(compareAmount) == 0) {
return "201";
}
return "202";
}
}
return "201";
}
case Auth: {
if(modifier.equals(TransactionModifier.Offline)) {
return "190";
}
if(builder instanceof AuthorizationBuilder) {
AuthorizationBuilder authBuilder = (AuthorizationBuilder)builder;
if(authBuilder.isAmountEstimated()) {
return "101";
}
else if(authBuilder.getAmount().equals(BigDecimal.ZERO) && authBuilder.getBillingAddress() != null) {
return "181";
}
}
return "100";
}
case Verify:
case Balance: {
return "108";
}
case BatchClose: {
ManagementBuilder managementBuilder = (ManagementBuilder)builder;
if(managementBuilder.getBatchCloseType() == null || managementBuilder.getBatchCloseType().equals(BatchCloseType.Forced)) {
return "572";
}
return "570";
}
case LoadReversal:
case Reversal: {
ManagementBuilder managementBuilder = (ManagementBuilder)builder;
if(managementBuilder.getAmount() != null) {
if(managementBuilder.getPaymentMethod() != null && managementBuilder.getPaymentMethod() instanceof TransactionReference) {
TransactionReference reference = (TransactionReference)managementBuilder.getPaymentMethod();
if(reference.isPartialApproval() && !managementBuilder.isCustomerInitiated()) {
return "401";
}
return "400";
}
}
return "400";
}
case Void: {
return "441";
}
case FileAction: {
return "388";
}
case TimeRequest: {
return "641";
}
case PosSiteConfiguration:{
return "692";
}
default: {
return "000";
}
}
}
private DE25_MessageReasonCode mapMessageReasonCode(ManagementBuilder builder) {
TransactionReference paymentMethod = (TransactionReference)builder.getPaymentMethod();
IPaymentMethod originalPaymentMethod = null;
TransactionType transactionType = builder.getTransactionType();
// get the NTS data
NtsData ntsData = null;
if(paymentMethod != null) {
ntsData = paymentMethod.getNtsData();
originalPaymentMethod = paymentMethod.getOriginalPaymentMethod();
}
// set the fallback and authorizer codes
FallbackCode fallbackCode = null;
AuthorizerCode authorizerCode = null;
if(ntsData != null) {
fallbackCode = ntsData.getFallbackCode();
authorizerCode = ntsData.getAuthorizerCode();
}
DE25_MessageReasonCode reasonCode = null;
if(transactionType.equals(TransactionType.BatchClose)) {
if(builder.isForceToHost()) {
reasonCode = DE25_MessageReasonCode.Forced_AuthCapture;
}
}
else if(transactionType.equals(TransactionType.Capture)) {
if(authorizerCode != null && authorizerCode.equals(AuthorizerCode.Voice_Authorized)) {
reasonCode = DE25_MessageReasonCode.VoiceCapture;
}else if (authorizerCode != null && authorizerCode.equals(AuthorizerCode.Terminal_Authorized)) {
reasonCode = DE25_MessageReasonCode.StandInCapture;
}
else if(fallbackCode != null) {
switch (fallbackCode) {
case CouldNotCommunicateWithHost:
case Received_IssuerTimeout:
case Received_IssuerUnavailable:
case Received_SystemMalfunction:
break;
default: {
if(builder.isForceToHost()) {
reasonCode = DE25_MessageReasonCode.Forced_AuthCapture;
}
else {
reasonCode = DE25_MessageReasonCode.AuthCapture;
}
}
}
}else if (builder.isForceToHost()) {
reasonCode = DE25_MessageReasonCode.Forced_AuthCapture;
}
else {
reasonCode = DE25_MessageReasonCode.AuthCapture;
}
}
if(transactionType.equals(TransactionType.PreAuthCompletion)) {
reasonCode = DE25_MessageReasonCode.PinDebit_EBT_Acknowledgement;
}
else if(isReversal(transactionType) || transactionType.equals(TransactionType.Void)) {
boolean partial = false;
if(paymentMethod != null) {
partial = paymentMethod.isPartialApproval();
}
if(builder.isForceToHost()) {
if(isReversal(transactionType)) {
reasonCode = DE25_MessageReasonCode.Forced_AuthCapture;
}
else {
reasonCode = partial ? DE25_MessageReasonCode.ForceVoid_PartialApproval : DE25_MessageReasonCode.ForceVoid_ApprovedTransaction;
}
}
else if(fallbackCode != null) {
switch (fallbackCode) {
case Received_IssuerTimeout:
case CouldNotCommunicateWithHost:
case Received_IssuerUnavailable: {
reasonCode = DE25_MessageReasonCode.TimeoutWaitingForResponse;
} break;
case Received_SystemMalfunction: {
reasonCode = DE25_MessageReasonCode.SystemTimeout_Malfunction;
} break;
default: {
if(builder.isCustomerInitiated()) {
reasonCode = partial ? DE25_MessageReasonCode.CustomerInitiated_PartialApproval : DE25_MessageReasonCode.CustomerInitiatedReversal;
}
else {
reasonCode = DE25_MessageReasonCode.MerchantInitiatedReversal;
}
}
}
}
else {
if(builder.isCustomerInitiated()) {
reasonCode = partial ? DE25_MessageReasonCode.CustomerInitiated_PartialApproval : DE25_MessageReasonCode.CustomerInitiatedReversal;
}
else {
reasonCode = DE25_MessageReasonCode.MerchantInitiatedReversal;
}
}
}
else if(transactionType.equals(TransactionType.Refund)) {
if(builder.isForceToHost()) {
reasonCode = DE25_MessageReasonCode.Forced_AuthCapture;
}
else if (originalPaymentMethod instanceof Debit || originalPaymentMethod instanceof EBT) {
reasonCode = DE25_MessageReasonCode.PinDebit_EBT_Acknowledgement;
}
}
return reasonCode;
}
private > DE48_MessageControl mapMessageControl(T builder) throws BatchFullException {
DE48_MessageControl messageControl = new DE48_MessageControl();
boolean isTimeRequest=builder.getTransactionType().equals(TransactionType.TimeRequest);
/* DE 48: Message Control - LLLVAR ans..999
48-0 BIT MAP b8 C Specifies which data elements are present.
48-1 COMMUNICATION DIAGNOSTICS n4 C Data on communication connection.
48-2 HARDWARE & SOFTWARE CONFIGURATION ans20 C Version information from POS application.
48-3 LANGUAGE CODE a2 F Language used for display or print.
48-4 BATCH NUMBER n10 C Current batch.
48-5 SHIFT NUMBER n3 C Identifies shift for reconciliation and tracking.
48-6 CLERK ID LVAR an..9 C Identification of clerk operating the terminal.
48-7 MULTIPLE TRANSACTION CONTROL n9 F Parameters to control multiple related messages.
48-8 CUSTOMER DATA LLLVAR ns..250 C Data entered by customer or clerk.
48-9 TRACK 2 FOR SECOND CARD LLVAR ns..37 C Used to specify the second card in a transaction by the Track 2 format.
48-10 TRACK 1 FOR SECOND CARD LLVAR ans..76 C Used to specify the second card in a transaction by the Track 1 format.
48-11 CARD TYPE anp4 C Card type.
48-12 ADMINISTRATIVELY DIRECTED TASK b1 C Notice to or direction for action to be taken by POS application.
48-13 RFID DATA LLVAR ans..99 C Data received from RFID transponder.
48-14 PIN ENCRYPTION METHODOLOGY ans2 C Used to identify the type of encryption methodology.
48-15, 48-32 RESERVED FOR ANSI USE LLVAR ans..99 These are reserved for future use.
48-33 POS CONFIGURATION LLVAR ans..99 C Values that indicate to the Heartland system capabilities and configuration of the POS application.
48-34 MESSAGE CONFIGURATION LLVAR ans..99 C Information regarding the POS originating message and the host generated response message.
48-35 NAME 1 LLVAR ans..99 D
48-36 NAME 2 LLVAR ans..99 D
48-37 SECONDARY ACCOUNT NUMBER LLVAR ans..28 C Second Account Number for manually entered transactions requiring 2 account numbers.
48-38 RESERVED FOR HEARTLAND USE LLVAR ans..99 F
48-39 PRIOR MESSAGE INFORMATION LLVAR ans..99 C Information regarding the status of the prior message sent by the POS.
48-40, 48-49 ADDRESS 1 THROUGH ADDRESS 10 LLVAR ans..99 D One or more types of addresses.
48-50, 48-64 RESERVED FOR HEARTLAND USE LLVAR ans..99 F
*/
// DE48-2 - Hardware/Software Config
DE48_2_HardwareSoftwareConfig hardwareSoftwareConfig = new DE48_2_HardwareSoftwareConfig();
hardwareSoftwareConfig.setHardwareLevel(acceptorConfig.getHardwareLevel());
hardwareSoftwareConfig.setSoftwareLevel(acceptorConfig.getSoftwareLevel());
hardwareSoftwareConfig.setOperatingSystemLevel(acceptorConfig.getOperatingSystemLevel());
messageControl.setHardwareSoftwareConfig(hardwareSoftwareConfig);
// DE48-4 (Sequence Number & Batch Number)
if(!builder.getTransactionType().equals(TransactionType.Auth) && !isTimeRequest) {
int sequenceNumber = 0;
if(!builder.getTransactionType().equals(TransactionType.BatchClose)) {
sequenceNumber = builder.getSequenceNumber();
if (sequenceNumber == 0 && batchProvider != null) {
sequenceNumber = batchProvider.getSequenceNumber();
}
}
messageControl.setSequenceNumber(sequenceNumber);
int batchNumber = builder.getBatchNumber();
if (batchNumber == 0 && batchProvider != null) {
batchNumber = batchProvider.getBatchNumber();
}
messageControl.setBatchNumber(batchNumber);
}
// DE48-5
if(builder instanceof AuthorizationBuilder) {
AuthorizationBuilder authBuilder = (AuthorizationBuilder)builder;
messageControl.setShiftNumber(authBuilder.getShiftNumber());
}
// 48-6 CLERK ID
if(builder instanceof AuthorizationBuilder) {
AuthorizationBuilder authBuilder = (AuthorizationBuilder)builder;
messageControl.setClerkId(authBuilder.getClerkId());
}
// DE48-8 Customer Data
DE48_8_CustomerData customerData = new DE48_8_CustomerData();
if(builder instanceof AuthorizationBuilder) {
AuthorizationBuilder authBuilder = (AuthorizationBuilder)builder;
// postal code
if(authBuilder.getBillingAddress() != null) {
Address address = authBuilder.getBillingAddress();
customerData.set(DE48_CustomerDataType.PostalCode, address.getPostalCode());
}
}
// fleet data
if(builder.getFleetData() != null) {
FleetData fleetData = builder.getFleetData();
if(builder instanceof AuthorizationBuilder) {
AuthorizationBuilder authBuilder = (AuthorizationBuilder) builder;
if (!StringUtils.isNullOrEmpty(authBuilder.getTagData())) {
customerData.setEmvFlag(true);
}
}
DE48_CardType cardType = mapCardType(builder.getPaymentMethod(), builder.getTransactionType());
customerData.set(DE48_CustomerDataType.UnencryptedIdNumber, fleetData.getUserId());
if(cardType != null && cardType.getValue().trim().equals("VF")){
customerData.set(DE48_CustomerDataType.Vehicle_Number_Code3, fleetData.getVehicleNumber() == null ? fleetData.getVehicleNumber() : StringUtils.padLeft(fleetData.getVehicleNumber(),6,'0'));
customerData.set(DE48_CustomerDataType.Id_Number_Code3, fleetData.getIdNumber() == null ? fleetData.getIdNumber() : StringUtils.padLeft(fleetData.getIdNumber(),6,'0'));
customerData.set(DE48_CustomerDataType.DriverId_EmployeeNumber, fleetData.getDriverId() == null ? fleetData.getDriverId() : StringUtils.padLeft(fleetData.getDriverId(),6,'0'));
customerData.set(DE48_CustomerDataType.Odometer_Reading, fleetData.getOdometerReading() == null ? fleetData.getOdometerReading() : StringUtils.padLeft(fleetData.getOdometerReading(),6,'0'));
}else {
customerData.set(DE48_CustomerDataType.Vehicle_Number, fleetData.getVehicleNumber());
customerData.set(DE48_CustomerDataType.DriverId_EmployeeNumber, fleetData.getDriverId());
customerData.set(DE48_CustomerDataType.Odometer_Reading, fleetData.getOdometerReading());
}
customerData.set(DE48_CustomerDataType.VehicleTag, fleetData.getVehicleTag());
customerData.set(DE48_CustomerDataType.DriverLicense_Number, fleetData.getDriversLicenseNumber());
customerData.set(DE48_CustomerDataType.WORKORDER_PONUMBER, fleetData.getWorkOrderPoNumber());
customerData.set(DE48_CustomerDataType.TrailerHours_ReferHours, fleetData.getTrailerReferHours());
customerData.set(DE48_CustomerDataType.EnteredData_Numeric, fleetData.getEnteredData());
customerData.set(DE48_CustomerDataType.ServicePrompt, fleetData.getServicePrompt());
customerData.set(DE48_CustomerDataType.JobNumber, fleetData.getJobNumber());
customerData.set(DE48_CustomerDataType.Department, fleetData.getDepartment());
customerData.set(DE48_CustomerDataType.TripNumber, fleetData.getTripNumber());
customerData.set(DE48_CustomerDataType.UnitNumber, fleetData.getUnitNumber());
customerData.set(DE48_CustomerDataType.MaintenanceNumber,fleetData.getMaintenanceNumber());
customerData.set(DE48_CustomerDataType.TrailerNumber,fleetData.getTrailerNumber());
customerData.set(DE48_CustomerDataType.HubometerNumber,fleetData.getHubometerNumber());
customerData.set(DE48_CustomerDataType.ADDITIONALPROMPTDATA1,fleetData.getAdditionalPromptData1());
customerData.set(DE48_CustomerDataType.ADDITIONALPROMPTDATA2,fleetData.getAdditionalPromptData2());
customerData.set(DE48_CustomerDataType.EMPLOYEENUMBER,fleetData.getEmployeeNumber());
}
// cvn number
if(builder.getPaymentMethod() instanceof ICardData) {
ICardData card = (ICardData)builder.getPaymentMethod();
IEncryptable encryption = null;
if(builder.getPaymentMethod() instanceof IEncryptable) {
encryption = (IEncryptable) builder.getPaymentMethod();
}
if(!StringUtils.isNullOrEmpty(card.getCvn())) {
String cvn = card.getCvn();
if(encryption != null && encryption.getEncryptionData() != null) {
cvn = StringUtils.padLeft(card.getCvn(), card.getCvn().length(), ' ');
}
customerData.set(DE48_CustomerDataType.CardPresentSecurityCode, cvn);
}
}
// gift pin
if(builder.getPaymentMethod() instanceof GiftCard) {
GiftCard giftCard = (GiftCard)builder.getPaymentMethod();
if(!StringUtils.isNullOrEmpty(giftCard.getPin())) {
customerData.set(DE48_CustomerDataType.CardPresentSecurityCode, giftCard.getPin());
}
}
if(customerData.getFieldCount() > 0) {
messageControl.setCustomerData(customerData);
}
// DE48-11
messageControl.setCardType(mapCardType(builder.getPaymentMethod(), builder.getTransactionType()));
// DE48-14
if(builder.getPaymentMethod() instanceof IPinProtected && builder instanceof AuthorizationBuilder) {
IPinProtected pinProtected = (IPinProtected)builder.getPaymentMethod();
if(pinProtected.getPinBlock() != null) {
DE48_14_PinEncryptionMethodology pinEncryptionMethodology = new DE48_14_PinEncryptionMethodology();
pinEncryptionMethodology.setKeyManagementDataCode(DE48_KeyManagementDataCode.DerivedUniqueKeyPerTransaction_DUKPT);
pinEncryptionMethodology.setEncryptionAlgorithmDataCode(DE48_EncryptionAlgorithmDataCode.TripleDES_3Keys);
messageControl.setPinEncryptionMethodology(pinEncryptionMethodology);
}
}
// DE48-33
if(acceptorConfig.hasPosConfiguration_MessageControl() && !isTimeRequest) {
DE48_33_PosConfiguration posConfiguration = new DE48_33_PosConfiguration();
posConfiguration.setTimezone(posConfiguration.getTimezone());
posConfiguration.setSupportsPartialApproval(acceptorConfig.getSupportsPartialApproval());
posConfiguration.setSupportsReturnBalance(acceptorConfig.getSupportsReturnBalance());
posConfiguration.setSupportsCashOver(acceptorConfig.getSupportsCashOver());
posConfiguration.setMobileDevice(acceptorConfig.getMobileDevice());
posConfiguration.setSupportWexAdditionalProducts(acceptorConfig.getSupportWexAdditionalProducts());
posConfiguration.setSupportTerminalPurchaseRestriction(acceptorConfig.getSupportTerminalPurchaseRestriction());
posConfiguration.setSupportVisaFleet2dot0(acceptorConfig.getSupportVisaFleet2dot0());
messageControl.setPosConfiguration(posConfiguration);
}
// DE48-34 // Message Configuration Fields
if(acceptorConfig.hasPosConfiguration_MessageData() && !isTimeRequest) {
DE48_34_MessageConfiguration messageConfigData = new DE48_34_MessageConfiguration();
messageConfigData.setPerformDateCheck(acceptorConfig.getPerformDateCheck());
messageConfigData.setEchoSettlementData(acceptorConfig.getEchoSettlementData());
messageConfigData.setIncludeLoyaltyData(acceptorConfig.getIncludeLoyaltyData());
messageConfigData.setTransactionGroupId(acceptorConfig.getTransactionGroupId());
messageConfigData.setIncrementalSupportIndicator(acceptorConfig.getIncrementalSupportIndicator());
messageControl.setMessageConfiguration(messageConfigData);
}
// DE48-39 // Not a follow up message these should be defaults
PriorMessageInformation priorMessageInformation = new PriorMessageInformation();
if(builder.getPriorMessageInformation() != null) {
priorMessageInformation = builder.getPriorMessageInformation();
}
else if(batchProvider != null && batchProvider.getPriorMessageData() != null) {
priorMessageInformation = batchProvider.getPriorMessageData();
}
DE48_39_PriorMessageInformation pmi = new DE48_39_PriorMessageInformation();
pmi.setResponseTime(priorMessageInformation.getResponseTime());
pmi.setCardType(priorMessageInformation.getCardType());
pmi.setMessageTransactionIndicator(priorMessageInformation.getMessageTransactionIndicator());
pmi.setProcessingCode(priorMessageInformation.getProcessingCode());
pmi.setStan(priorMessageInformation.getSystemTraceAuditNumber());
messageControl.setPriorMessageInformation(pmi);
// DE48-40 Addresses
if(builder instanceof AuthorizationBuilder) {
AuthorizationBuilder authBuilder = (AuthorizationBuilder)builder;
if(authBuilder.getBillingAddress() != null) {
DE48_Address billing = new DE48_Address();
billing.setAddress(authBuilder.getBillingAddress());
billing.setAddressUsage(DE48_AddressUsage.Billing);
if(authBuilder.getAmount().equals(BigDecimal.ZERO) && !authBuilder.isAmountEstimated()) {
billing.setAddressType(DE48_AddressType.AddressVerification);
}
else {
billing.setAddressType(DE48_AddressType.StreetAddress);
}
messageControl.addAddress(billing);
}
if(authBuilder.getShippingAddress() != null) {
DE48_Address shipping = new DE48_Address();
shipping.setAddress(authBuilder.getShippingAddress());
shipping.setAddressUsage(DE48_AddressUsage.Shipping);
shipping.setAddressType(DE48_AddressType.StreetAddress);
messageControl.addAddress(shipping);
}
}
return messageControl;
}
private > DE62_CardIssuerData mapCardIssuerData(T builder) {
// DE 62: Card Issuer Data - LLLVAR ans..999
DE62_CardIssuerData cardIssuerData = new DE62_CardIssuerData();
boolean isVisaFleet2 = acceptorConfig.getSupportVisaFleet2dot0() != null && acceptorConfig.getVisaFleet2()!=null && acceptorConfig.getVisaFleet2();
if(builder.getPaymentMethod() != null) {
IPaymentMethod paymentMethod = builder.getPaymentMethod();
if(paymentMethod instanceof TransactionReference) {
paymentMethod = ((TransactionReference) paymentMethod).getOriginalPaymentMethod();
}
if(paymentMethod instanceof Credit && ((Credit) paymentMethod).getCardType().equals("VisaFleet") && isVisaFleet2) {
cardIssuerData.add(CardIssuerEntryTag.VISAFLEET2DOT0CARDPRESENTBYCARDHOLDER, acceptorConfig.getSupportVisaFleet2dot0().getValue());
}
}
// unique device id
if(!StringUtils.isNullOrEmpty(builder.getUniqueDeviceId())) {
cardIssuerData.add(CardIssuerEntryTag.UniqueDeviceId, builder.getUniqueDeviceId());
}
else if(!StringUtils.isNullOrEmpty(uniqueDeviceId)) {
cardIssuerData.add(CardIssuerEntryTag.UniqueDeviceId, uniqueDeviceId);
}
// pos config
if(acceptorConfig.hasPosConfiguration_IssuerData()) {
cardIssuerData.add(CardIssuerEntryTag.NTS_POS_Capability, acceptorConfig.getPosConfigForIssuerData());
}
// wex support
if(builder.getPaymentMethod() != null) {
IPaymentMethod paymentMethod = builder.getPaymentMethod();
if(paymentMethod instanceof TransactionReference) {
paymentMethod = ((TransactionReference) paymentMethod).getOriginalPaymentMethod();
}
if(paymentMethod instanceof Credit && ((Credit) paymentMethod).getCardType().equals("WexFleet")) {
cardIssuerData.add(CardIssuerEntryTag.Wex_SpecVersionSupport, "0401");
if(builder.getTransactionType().equals(TransactionType.Refund)) {
cardIssuerData.add(CardIssuerEntryTag.IssuerSpecificTransactionMatchData, builder.getTransactionMatchingData().getElementData());
}
// purchase device sequence number
if(builder.getFleetData() != null && builder.getFleetData().getPurchaseDeviceSequenceNumber() != null) {
cardIssuerData.add(CardIssuerEntryTag.Wex_PurchaseDeviceSequenceNumber, builder.getFleetData().getPurchaseDeviceSequenceNumber());
}
else if(paymentMethod instanceof CreditTrackData) {
cardIssuerData.add(CardIssuerEntryTag.Wex_PurchaseDeviceSequenceNumber, ((CreditTrackData) paymentMethod).getPurchaseDeviceSequenceNumber());
}
}
}
//IMC
if(builder.getPaymentMethod() != null) {
IPaymentMethod paymentMethod = builder.getPaymentMethod();
if(paymentMethod instanceof TransactionReference) {
paymentMethod = ((TransactionReference) paymentMethod).getOriginalPaymentMethod();
}
if(paymentMethod instanceof Credit && ((Credit) paymentMethod).getCardType().equals("MC")) {
if(builder.getCitMitIndicator()!=null) {
cardIssuerData.add(CardIssuerEntryTag.MASTERCARD_CIT_MIT_INDICATOR, builder.getCitMitIndicator().getValue());
}
}
}
// NTE
if(builder.isTerminalError()) {
cardIssuerData.add(CardIssuerEntryTag.TerminalError, "Y");
}
// management builder related
if(builder instanceof ManagementBuilder) {
ManagementBuilder mb = (ManagementBuilder)builder;
// IRR Issuer Reference Number
if(!StringUtils.isNullOrEmpty(mb.getReferenceNumber())) {
cardIssuerData.add(CardIssuerEntryTag.RetrievalReferenceNumber, mb.getReferenceNumber());
}
// Forced Batch Close
if(mb.getTransactionType().equals(TransactionType.BatchClose) && (mb.getBatchCloseType().equals(BatchCloseType.Forced) || mb.isForceToHost())) {
cardIssuerData.add(CardIssuerEntryTag.TerminalError, "Y");
}
// Transaction reference
if(mb.getPaymentMethod() instanceof TransactionReference) {
TransactionReference reference = (TransactionReference)mb.getPaymentMethod();
// NTS Specific Data
if(reference.getNtsData() != null) {
if(!mb.getTransactionType().equals(TransactionType.Void) && !isReversal(mb.getTransactionType())) {
cardIssuerData.add(CardIssuerEntryTag.NTS_System, reference.getNtsData().toString());
}
}
// original payment method
if(reference.getOriginalPaymentMethod() != null) {
if(reference.getOriginalPaymentMethod() instanceof CreditCardData) {
cardIssuerData.add(CardIssuerEntryTag.SwipeIndicator,"0");
}
else if (reference.getOriginalPaymentMethod() instanceof ITrackData) {
ITrackData track = (ITrackData) reference.getOriginalPaymentMethod();
if(track.getTrackNumber() != null) {
String nsiValue = track.getTrackNumber().equals(TrackNumber.TrackTwo) ? "2" : "1";
cardIssuerData.add(CardIssuerEntryTag.SwipeIndicator, nsiValue);
}
}
}
// Mastercard Banknet Reference Number
if (reference.getMastercardBanknetRefNo() != null){
cardIssuerData.add(CardIssuerEntryTag.NTS_MastercardBankNet_ReferenceNumber,reference.getMastercardBanknetRefNo());
}
// Mastercard Banknet Settlement Date
if (reference.getMastercardBanknetSettlementDate() != null){
cardIssuerData.add(CardIssuerEntryTag.NTS_MastercardBankNet_SettlementDate,reference.getMastercardBanknetSettlementDate());
}
}
}
else {
AuthorizationBuilder authBuilder = (AuthorizationBuilder)builder;
if(authBuilder.getEmvChipCondition() != null) {
cardIssuerData.add(CardIssuerEntryTag.ChipConditionCode, mapChipCondition(authBuilder.getEmvChipCondition()));
}
// Refund Logic
if(authBuilder.getTransactionType().equals(TransactionType.Refund)) {
IPaymentMethod paymentMethod = authBuilder.getPaymentMethod();
// NTS
if(paymentMethod instanceof Credit) {
cardIssuerData.add(CardIssuerEntryTag.NTS_System, "08 00");
}
// NSI
if(!(paymentMethod instanceof Debit || paymentMethod instanceof EBT)) {
if(paymentMethod instanceof ITrackData) {
ITrackData track = (ITrackData)paymentMethod;
String nsiValue = track.getTrackNumber().equals(TrackNumber.TrackTwo) ? "2" : "1";
cardIssuerData.add(CardIssuerEntryTag.SwipeIndicator, nsiValue);
}
else {
cardIssuerData.add(CardIssuerEntryTag.SwipeIndicator,"0");
}
}
}
}
// catch all
if(builder.getIssuerData() != null) {
LinkedHashMap issuerData = builder.getIssuerData();
for(CardIssuerEntryTag tag: issuerData.keySet()) {
cardIssuerData.add(tag, issuerData.get(tag));
}
}
// put if there are entries
if(cardIssuerData.getNumEntries() > 0) {
return cardIssuerData;
}
return null;
}
private DE48_CardType mapCardType(IPaymentMethod paymentMethod, TransactionType transactionType) {
// check to see if the original payment method is set
if(paymentMethod instanceof TransactionReference) {
TransactionReference transactionReference = (TransactionReference)paymentMethod;
if(transactionReference.getOriginalPaymentMethod() != null) {
paymentMethod = transactionReference.getOriginalPaymentMethod();
}
}
// evaluate and return
if(paymentMethod instanceof DebitTrackData) {
return DE48_CardType.PINDebitCard;
}
else if(paymentMethod instanceof Credit) {
Credit card = (Credit)paymentMethod;
if(card.getCardType().equals("Amex")) {
return DE48_CardType.AmericanExpress;
}
else if(card.getCardType().equals("MC")) {
return DE48_CardType.Mastercard;
}
else if(card.getCardType().equals("MCFleet")) {
return DE48_CardType.MastercardFleet;
}
else if(card.getCardType().equals("WexFleet")) {
return DE48_CardType.WEX;
}
else if(card.getCardType().equals("Visa")) {
return DE48_CardType.Visa;
}
else if(card.getCardType().equals("VisaFleet")) {
return DE48_CardType.VisaFleet;
}
else if(card.getCardType().equals("VisaReadyLink")) {
return DE48_CardType.PINDebitCard;
}
else if(card.getCardType().equals("DinersClub")) {
return DE48_CardType.DinersClub;
}
else if(card.getCardType().equals("Discover")) {
return DE48_CardType.DiscoverCard;
}
else if(card.getCardType().equals("Jcb")) {
return DE48_CardType.JCB;
}
else if(card.getCardType().equals("VoyagerFleet")) {
return DE48_CardType.Voyager;
}
else if(card.getCardType().equals("FuelmanFleet")) {
return DE48_CardType.FleetCorFuelmanPlus;
}
else if(card.getCardType().equals("FleetWide")) {
return DE48_CardType.FleetCorFleetwide;
}else if(card.getCardType().equals("UnionPay")) {
return DE48_CardType.DiscoverCard;
}
}
else if(paymentMethod instanceof GiftCard) {
GiftCard card = (GiftCard)paymentMethod;
if(card.getCardType().equals("ValueLink")) {
return DE48_CardType.ValueLinkStoredValue;
}
else if(card.getCardType().equals("StoredValue")) {
return DE48_CardType.SVSStoredValue;
}
else if(card.getCardType().equals("HeartlandGift")) {
return DE48_CardType.HeartlandGiftCard_Proprietary;
}
}
else if(paymentMethod instanceof EBT) {
EBT ebt = (EBT)paymentMethod;
if(ebt.getEbtCardType().equals(EbtCardType.CashBenefit)) {
return DE48_CardType.EBTCash;
}
else return DE48_CardType.EBTFoodStamps;
}
return null;
}
private String mapChipCondition(EmvChipCondition chipCondition) {
switch(chipCondition){
case ChipFailPreviousSuccess:
return "1";
case ChipFailPreviousFail:
return "2";
default:
return null;
}
}
private String formatExpiry(String shortExpiry) {
if(shortExpiry != null) {
return shortExpiry.substring(2, 4).concat(shortExpiry.substring(0, 2));
}
return shortExpiry;
}
// check result & put to IBatchProvider if data collect
private > String checkResponse(String responseCode, NetworkMessage request, NetworkMessage response, T builder) {
ArrayList successCodes = new ArrayList();
successCodes.add("000");
successCodes.add("002");
successCodes.add("400");
successCodes.add("500");
successCodes.add("501");
successCodes.add("580");
BigDecimal amount = request.getAmount(DataElementId.DE_004);
if(response != null) {
BigDecimal partialAmount = response.getAmount(DataElementId.DE_004);
if (responseCode.equals("002")) {
amount = partialAmount;
request.set(DataElementId.DE_004, StringUtils.toNumeric(amount, 12));
}
}
TransactionType transactionType = null;
IPaymentMethod paymentMethod = null;
PaymentMethodType paymentMethodType = null;
if(builder != null) {
transactionType = builder.getTransactionType();
paymentMethod = builder.getPaymentMethod();
if(paymentMethod != null) {
paymentMethodType = paymentMethod.getPaymentMethodType();
}
}
String encodedRequest = null;
if(builder != null && request.isDataCollect(paymentMethodType)) {
// check if we need to build the implied data-collect
if(transactionType.equals(TransactionType.Sale) || transactionType.equals(TransactionType.Refund)
||transactionType.equals(TransactionType.AddValue)) {
NetworkMessage impliedCapture = buildImpliedCapture(request, response, paymentMethod);
encodedRequest = encodeRequest(impliedCapture);
if(batchProvider != null && successCodes.contains(responseCode)) {
batchProvider.reportDataCollect(transactionType, paymentMethodType, amount, encodedRequest);
}
}
else if(!transactionType.equals(TransactionType.DataCollect)) {
encodedRequest = encodeRequest(request);
if(batchProvider != null && successCodes.contains(responseCode)) {
batchProvider.reportDataCollect(transactionType, paymentMethodType, amount, encodedRequest);
}
}
}
// report successes
if(successCodes.contains(responseCode)) {
// if there's a batch provider handle the batch close stuff
if ((responseCode.equals("500") || responseCode.equals("501")) && batchProvider != null) {
batchProvider.closeBatch(responseCode.equals("500"));
} else if (responseCode.equals("580")) {
if(batchProvider != null) {
try {
LinkedList encodedRequests = batchProvider.getEncodedRequests();
if (encodedRequests != null) {
resentTransactions = new LinkedList();
for (String encRequest : encodedRequests) {
try {
NetworkMessage newRequest = decodeRequest(encRequest);
newRequest.setMessageTypeIndicator("1221");
Transaction resend = sendRequest(newRequest, null, new byte[2], new byte[8]);
resentTransactions.add(resend);
} catch (ApiException exc) {
/* NOM NOM */
// TODO: this should be reported
}
}
// resend the batch close
request.setMessageTypeIndicator("1521");
resentBatch = sendRequest(request, builder, new byte[2], new byte[8]);
}
} catch (ApiException exc) {
/* NOM NOM */
// TODO: this should be reported
}
}
encodedRequest = encodeRequest(request);
}
}
// Tokenize that which has not already.
if(StringUtils.isNullOrEmpty(encodedRequest)) {
// check for pan data and replace it with the truncated track
if(builder != null && builder.getPaymentMethod() instanceof ITrackData) {
ITrackData track = (ITrackData)builder.getPaymentMethod();
if(request.has(DataElementId.DE_035) || request.has(DataElementId.DE_045)) {
if(!StringUtils.isNullOrEmpty(track.getTruncatedTrackData())) {
request.set(track.getTrackNumber().equals(TrackNumber.TrackTwo) ? DataElementId.DE_035 : DataElementId.DE_045, track.getTruncatedTrackData());
}
}
}
encodedRequest = encodeRequest(request);
}
return encodedRequest;
}
private NetworkMessage buildImpliedCapture(NetworkMessage request, NetworkMessage response, IPaymentMethod paymentMethod) {
String authCode = null;
String ntsData = null;
String requestAmount = request.getString(DataElementId.DE_004);
boolean isPartial = false;
if(response != null) {
String responseCode = response.getString(DataElementId.DE_039);
if(responseCode.equals("002")) {
isPartial = true;
}
else if(responseCode.equals("000")) {
String responseAmount = response.getString(DataElementId.DE_004);
if(!requestAmount.equals(responseAmount)) {
isPartial = true;
requestAmount = responseAmount;
}
}
authCode = !StringUtils.isNullOrEmpty(response.getString(DataElementId.DE_038)) ? response.getString(DataElementId.DE_038) : request.getString(DataElementId.DE_038);
DE62_CardIssuerData responseIssuerData = response.getDataElement(DataElementId.DE_062, DE62_CardIssuerData.class);
if(responseIssuerData != null) {
ntsData = responseIssuerData.get(CardIssuerEntryTag.NTS_System);
}
}
return buildImpliedCapture(request, requestAmount, isPartial, authCode, ntsData, paymentMethod);
}
private NetworkMessage buildImpliedCapture(NetworkMessage request, String amount, boolean isPartial, String authCode, String ntsData, IPaymentMethod paymentMethod) {
PaymentMethodType paymentMethodType = null;
if(paymentMethod != null) {
paymentMethodType = paymentMethod.getPaymentMethodType();
}
NetworkMessage impliedCapture = new NetworkMessage(Iso8583MessageType.CompleteMessage);
impliedCapture.setMessageTypeIndicator("1220");
impliedCapture.set(DataElementId.DE_003, request.getString(DataElementId.DE_003));
impliedCapture.set(DataElementId.DE_004, amount);
impliedCapture.set(DataElementId.DE_007, request.getString(DataElementId.DE_007));
impliedCapture.set(DataElementId.DE_011, request.getString(DataElementId.DE_011));
impliedCapture.set(DataElementId.DE_012, request.getString(DataElementId.DE_012));
impliedCapture.set(DataElementId.DE_018, request.getString(DataElementId.DE_018));
impliedCapture.set(DataElementId.DE_022, request.getDataElement(DataElementId.DE_022, DE22_PosDataCode.class));
impliedCapture.set(DataElementId.DE_024, isPartial ? "202" : "201");
impliedCapture.set(DataElementId.DE_030, request.getString(DataElementId.DE_030));
impliedCapture.set(DataElementId.DE_038, authCode);
impliedCapture.set(DataElementId.DE_041, request.getString(DataElementId.DE_041));
impliedCapture.set(DataElementId.DE_042, request.getString(DataElementId.DE_042));
impliedCapture.set(DataElementId.DE_043, request.getString(DataElementId.DE_043));
impliedCapture.set(DataElementId.DE_054, request.getString(DataElementId.DE_054));
impliedCapture.set(DataElementId.DE_063, request.getDataElement(DataElementId.DE_063, DE63_ProductData.class));
impliedCapture.set(DataElementId.DE_127, request.getString(DataElementId.DE_127));
if (paymentMethodType != null) {
if (paymentMethodType.equals(PaymentMethodType.EBT)) {
impliedCapture.set(DataElementId.DE_017, request.getString(DataElementId.DE_012).substring(2, 6));
}
if (acceptorConfig.getSupportedEncryptionType().equals(EncryptionType.TDES) && (paymentMethodType.equals(PaymentMethodType.EBT) ||
paymentMethodType.equals(PaymentMethodType.Debit) || paymentMethodType.equals(PaymentMethodType.Credit))) {
impliedCapture.set(DataElementId.DE_014, request.getString(DataElementId.DE_014));
//DE127 changing field matrix and encrypted data as encrypted pan.
DE127_ForwardingData forwardingData = new DE127_ForwardingData();
if (paymentMethod instanceof IEncryptable) {
String encryptedPan = null;
EncryptionData encryptionData = ((IEncryptable) paymentMethod).getEncryptionData();
// For setting KTB value as encrypted data in manage Transaction
if(!paymentMethodType.equals(PaymentMethodType.Credit)){
encryptionData.setEncryptedKTB(encryptionData.getKtb());
}
encryptedPan = ((IEncryptable) paymentMethod).getEncryptedPan();
if (encryptionData != null) {
EncryptionType encryptionType = acceptorConfig.getSupportedEncryptionType();
if (encryptedPan != null && encryptionType.equals(EncryptionType.TDES)) {
encryptionData.setKtb(encryptedPan);
}
if (encryptionType.equals(EncryptionType.TDES)) {
forwardingData.setServiceType(acceptorConfig.getServiceType());
forwardingData.setOperationType(acceptorConfig.getOperationType());
}
forwardingData.setEncryptedField(EncryptedFieldMatrix.Pan);
forwardingData.addEncryptionData(encryptionType, encryptionData);
impliedCapture.set(DataElementId.DE_127, forwardingData);
}
}
}
}
// DE_048 Message Control
DE48_MessageControl messageControl = request.getDataElement(DataElementId.DE_048, DE48_MessageControl.class);
messageControl.setPinEncryptionMethodology(null);
impliedCapture.set(DataElementId.DE_048, messageControl);
// DE_062 Card Issuer Data
DE62_CardIssuerData requestIssuerData = request.getDataElement(DataElementId.DE_062, DE62_CardIssuerData.class);
if(requestIssuerData == null) {
requestIssuerData = new DE62_CardIssuerData();
}
// NTS
if(ntsData != null) {
requestIssuerData.add(CardIssuerEntryTag.NTS_System, ntsData);
}
// NSI
String nsi = requestIssuerData.get(CardIssuerEntryTag.SwipeIndicator);
// DE_002 / DE_014 - PAN / EXP DATE
if(request.has(DataElementId.DE_002)) {
impliedCapture.set(DataElementId.DE_002, request.getString(DataElementId.DE_002));
impliedCapture.set(DataElementId.DE_014, request.getString(DataElementId.DE_014));
if(StringUtils.isNullOrEmpty(nsi)) {
requestIssuerData.add(CardIssuerEntryTag.SwipeIndicator, "0");
}
}
else {
// get the track object
ITrackData track = null;
if(paymentMethod instanceof ITrackData) {
track = (ITrackData)paymentMethod;
}
else if(request.has(DataElementId.DE_035)) {
track = new CreditTrackData(request.getString(DataElementId.DE_035));
}
else {
track = new CreditTrackData(request.getString(DataElementId.DE_045));
}
// get the encryptable object
IEncryptable encryptable = null;
if(track instanceof IEncryptable) {
encryptable = (IEncryptable)track;
}
if(encryptable != null) {
if(encryptable.getEncryptionData() != null && encryptable.getEncryptedPan() != null && !acceptorConfig.getSupportedEncryptionType().equals(EncryptionType.TDES)) {
impliedCapture.set(DataElementId.DE_002, encryptable.getEncryptedPan());
}
else {
impliedCapture.set(DataElementId.DE_002, track.getPan());
}
}
else {
impliedCapture.set(DataElementId.DE_002, track.getPan());
}
// expiry
impliedCapture.set(DataElementId.DE_014, track.getExpiry());
// NSI swipe indicator
if(StringUtils.isNullOrEmpty(nsi)) {
if(acceptorConfig.getSupportedEncryptionType().equals(EncryptionType.TDES)){
if(track.getTrackNumber() != null) {
String nsiValue = track.getTrackNumber().equals(TrackNumber.TrackTwo) ? "2" : "1";
requestIssuerData.add(CardIssuerEntryTag.SwipeIndicator, nsiValue);
}
}else {
requestIssuerData.add(CardIssuerEntryTag.SwipeIndicator, request.has(DataElementId.DE_035) ? "2" : "1");
}
}
}
impliedCapture.set(DataElementId.DE_062, requestIssuerData); // DE_042 - ISSUER DATA
// DE_025 - MESSAGE REASON CODE
if(paymentMethodType != null && paymentMethodType.equals(PaymentMethodType.Debit)) {
impliedCapture.set(DataElementId.DE_025, "1379");
}
else {
impliedCapture.set(DataElementId.DE_025, "1376");
}
// DE_056 - ORIGINAL TRANSACTION DATA
DE56_OriginalDataElements originalDataElements = new DE56_OriginalDataElements();
originalDataElements.setMessageTypeIdentifier("1200");
originalDataElements.setSystemTraceAuditNumber(request.getString(DataElementId.DE_011));
originalDataElements.setTransactionDateTime(request.getString(DataElementId.DE_012));
impliedCapture.set(DataElementId.DE_056, originalDataElements);
return impliedCapture;
}
private String encodeRequest(NetworkMessage request) {
lrcFailure = false;
int encodeCount = 0;
while(encodeCount++ < 3) {
String encodedRequest = doEncoding(request);
// if(lrcFailure && isEnableLogging()) {
// System.out.println(String.format("[TOKEN TRACE]: framedRequest: %s", encodedRequest));
// }
if(TerminalUtilities.checkLRC(encodedRequest)) {
return encodedRequest;
}
// else if(isEnableLogging()) {
// lrcFailure = true;
// System.out.println(String.format("[TOKEN LRC FAILURE]: attempt: %s %s", encodeCount, encodedRequest));
// }
}
return null;
}
private String doEncoding(NetworkMessage request) {
// base64 encode the message buffer
byte[] encoded = Base64.encodeBase64(request.buildMessage());
String encodedString = new String(encoded);
// if(lrcFailure && isEnableLogging()) {
// System.out.println(String.format("[TOKEN TRACE]: encodedString: %s", encodedString));
// }
// encrypt it
if(requestEncoder == null) {
if(isEnableLogging()) {
System.out.println(String.format("[TOKEN TRACE]: %s %s", companyId, terminalId));
}
requestEncoder = new PayrollEncoder(companyId, terminalId);
}
String token = requestEncoder.encode(encodedString);
// if(lrcFailure && isEnableLogging()) {
// System.out.println(String.format("[TOKEN TRACE]: encryptedToken: %s", token));
// }
// build final token
MessageWriter mw = new MessageWriter();
mw.add(ControlCodes.STX);
mw.addRange(token.getBytes());
mw.add(ControlCodes.ETX);
// generate the CRC
mw.add(TerminalUtilities.calculateLRC(mw.toArray()));
return new String(mw.toArray());
}
private NetworkMessage decodeRequest(String encodedStr) {
if(requestEncoder == null) {
requestEncoder = new PayrollEncoder(companyId, terminalId);
}
byte[] encodedBuffer = encodedStr.getBytes();
MessageReader mr = new MessageReader(encodedBuffer);
String valueToDecrypt = encodedStr;
if(mr.peek() == ControlCodes.STX.getByte()) {
mr.readCode(); // pop the STX off
valueToDecrypt = mr.readToCode(ControlCodes.ETX);
byte lrc = mr.readByte();
if(lrc != TerminalUtilities.calculateLRC(encodedBuffer)) {
// invalid token
}
}
String requestStr = requestEncoder.decode(valueToDecrypt);
byte[] decoded = Base64.decodeBase64(requestStr);
mr = new MessageReader(decoded);
String mti = mr.readString(4);
byte[] buffer = mr.readBytes(decoded.length);
NetworkMessage request = NetworkMessage.parse(buffer, Iso8583MessageType.CompleteMessage);
request.setMessageTypeIndicator(mti);
return request;
}
private boolean isReversal(TransactionType type) {
return type.equals(TransactionType.Reversal) || type.equals(TransactionType.LoadReversal);
}
private boolean shouldUseOriginalProcessingCode(IPaymentMethod paymentMethod, TransactionType type) {
if(paymentMethod instanceof TransactionReference) {
paymentMethod = ((TransactionReference)paymentMethod).getOriginalPaymentMethod();
}
if(isReversal(type) || type.equals(TransactionType.PreAuthCompletion)) {
return true;
}
else if (paymentMethod instanceof GiftCard) {
GiftCard giftCard = (GiftCard)paymentMethod;
return giftCard.getCardType().equals("ValueLink") && type.equals(TransactionType.Void);
}
return false;
}
private > void validate(T builder) throws BuilderException, UnsupportedTransactionException {
IPaymentMethod paymentMethod = builder.getPaymentMethod();
if(paymentMethod instanceof TransactionReference) {
TransactionReference reference = (TransactionReference)paymentMethod;
if(reference.getOriginalPaymentMethod() != null) {
paymentMethod = reference.getOriginalPaymentMethod();
}
}
TransactionType transactionType = builder.getTransactionType();
if(paymentMethod instanceof GiftCard) {
GiftCard giftCard = (GiftCard) paymentMethod;
if (giftCard.getCardType() == null) {
throw new BuilderException("The card type must be specified for Gift transactions.");
}
}
if(paymentMethod instanceof EBT) {
EBT ebtCard = (EBT)paymentMethod;
if(ebtCard.getEbtCardType() == null) {
throw new BuilderException("The card type must be specified for EBT transactions.");
}
// no refunds on cash benefit cards
if(ebtCard.getEbtCardType() == EbtCardType.CashBenefit && transactionType.equals(TransactionType.Refund)) {
throw new UnsupportedTransactionException("Refunds are not allowed for cash benefit cards.");
}
// no authorizations for ebt
if(transactionType.equals(TransactionType.Auth)) {
throw new UnsupportedTransactionException("Authorizations are not allowed for EBT cards.");
}
// no manual balance inquiry
if(transactionType.equals(TransactionType.Balance) && !(paymentMethod instanceof ITrackData)) {
throw new BuilderException("Track data must be used for EBT balance inquiries.");
}
}
// WEX Specific
if(paymentMethod instanceof Credit) {
if(((Credit) paymentMethod).getCardType().equals("WexFleet")) {
if(transactionType.equals(TransactionType.Refund)) {
if (builder.getTransactionMatchingData() == null) {
throw new BuilderException("Transaction mapping data object required for WEX refunds.");
} else {
TransactionMatchingData tmd = builder.getTransactionMatchingData();
if (StringUtils.isNullOrEmpty(tmd.getOriginalBatchNumber()) || StringUtils.isNullOrEmpty(tmd.getOriginalDate())) {
throw new BuilderException("Transaction Matching Data incomplete. Original batch number and date are required for WEX refunds.");
}
}
}
FleetData fleetData = builder.getFleetData();
if(fleetData == null && !(paymentMethod instanceof CreditTrackData)) {
throw new BuilderException("The purchase device sequence number cannot be null for WEX transactions.");
}
else if((fleetData != null && fleetData.getPurchaseDeviceSequenceNumber() == null) && (paymentMethod instanceof CreditTrackData && ((CreditTrackData) paymentMethod).getPurchaseDeviceSequenceNumber() == null)) {
throw new BuilderException("The purchase device sequence number cannot be null for WEX transactions.");
}
}
}
if(builder instanceof ManagementBuilder) {
ManagementBuilder mb = (ManagementBuilder)builder;
TransactionReference reference = (TransactionReference)mb.getPaymentMethod();
if(transactionType.equals(TransactionType.BatchClose)) {
if(batchProvider == null) {
if(mb.getTransactionCount() == null || mb.getTotalCredits() == null || mb.getTotalDebits() == null) {
throw new BuilderException("When an IBatchProvider is not present, you must specify transaction count, total debits and total credits when calling batch close.");
}
if(mb.getBatchNumber() == 0) {
throw new BuilderException("When an IBatchProvider is not present, you must specify a batch and sequence number for a batch close.");
}
}
}
if(transactionType.equals(TransactionType.Refund)) {
if(reference.getOriginalPaymentMethod() instanceof EBT) {
EBT ebtCard = (EBT)reference.getOriginalPaymentMethod();
// no refunds on cash benefit cards
if(ebtCard.getEbtCardType() == EbtCardType.CashBenefit && transactionType.equals(TransactionType.Refund)) {
throw new UnsupportedTransactionException("Refunds are not allowed for cash benefit cards.");
}
}
}
if(isReversal(transactionType)) {
if(StringUtils.isNullOrEmpty(reference.getOriginalProcessingCode())) {
throw new BuilderException("The original processing code should be specified when performing a reversal.");
}
// IRR for fleet reversals
if(paymentMethod instanceof Credit) {
if(((Credit) paymentMethod).isFleet() && StringUtils.isNullOrEmpty(mb.getReferenceNumber())) {
if(reference.getNtsData() != null && reference.getNtsData().getFallbackCode().equals(FallbackCode.None)) {
throw new BuilderException("Reference Number is required for fleet voids/reversals.");
}
}
}
}
if(transactionType.equals(TransactionType.Void)){
// IRR for fleet reversals
if(paymentMethod instanceof Credit) {
if(((Credit) paymentMethod).isFleet() && StringUtils.isNullOrEmpty(((ManagementBuilder) builder).getReferenceNumber())) {
throw new BuilderException("Reference Number is required for fleet voids/reversals.");
}
}
}
}
else {
AuthorizationBuilder authBuilder = (AuthorizationBuilder)builder;
if(paymentMethod !=null && paymentMethod.getPaymentMethodType().equals(PaymentMethodType.Debit)) {
if(acceptorConfig.getAddress() == null) {
throw new BuilderException("Address is required in acceptor config for Debit/EBT Transactions.");
}
}
if(paymentMethod instanceof EBT) {
EBT card = (EBT)paymentMethod;
if(card.getEbtCardType().equals(EbtCardType.FoodStamp) && authBuilder.getCashBackAmount() != null) {
throw new BuilderException("Cash back is not allowed for Food Stamp cards.");
}
}
}
}
private EncryptedFieldMatrix getEncryptionField(IPaymentMethod paymentMethod, EncryptionType encryptionType, TransactionType transactionType){
String card = null;
if(paymentMethod instanceof GiftCard){
card = ((GiftCard) paymentMethod).getCardType();
}
if(encryptionType.equals(EncryptionType.TDES)){
if(paymentMethod instanceof ICardData || (paymentMethod instanceof CreditTrackData && transactionType.equals(TransactionType.Refund))){
return EncryptedFieldMatrix.Pan;
}
else if(paymentMethod instanceof ITrackData && !transactionType.equals(TransactionType.Capture) && !transactionType.equals(TransactionType.PreAuthCompletion) && !transactionType.equals(TransactionType.Void) && !transactionType.equals(TransactionType.Reversal)) {
TrackNumber trackType=((ITrackData)paymentMethod).getTrackNumber();
if (trackType == TrackNumber.TrackOne)
return EncryptedFieldMatrix.Track1;
else if (trackType == TrackNumber.TrackTwo)
return EncryptedFieldMatrix.Track2;
}else if(paymentMethod instanceof GiftCard && ((("ValueLink").equals(card)) || (!transactionType.equals(TransactionType.Capture)
&& !transactionType.equals(TransactionType.PreAuthCompletion)
&& !transactionType.equals(TransactionType.Void) && !transactionType.equals(TransactionType.Reversal)))) {
TrackNumber trackType=((GiftCard)paymentMethod).getTrackNumber();
if (trackType == TrackNumber.TrackOne)
return EncryptedFieldMatrix.Track1;
else if (trackType == TrackNumber.TrackTwo)
return EncryptedFieldMatrix.Track2;
}else{
return EncryptedFieldMatrix.Pan;
}
}else if (encryptionType.equals(EncryptionType.TEP1)||encryptionType.equals(EncryptionType.TEP2)){
if(paymentMethod instanceof ICardData) {
String encryptedCvn = ((ICardData) paymentMethod).getCvn();
if(!StringUtils.isNullOrEmpty(encryptedCvn))
return EncryptedFieldMatrix.CustomerDataCSV;
else
return EncryptedFieldMatrix.CustomerData;
}
}
return EncryptedFieldMatrix.CustomerData;
}
private static void setTokenizationOperationType(DE127_ForwardingData forwardingData, TokenizationOperationType tokenOperationType) {
if (tokenOperationType != null) {
forwardingData.setTokenizationOperationType(tokenOperationType);
switch (tokenOperationType) {
case Tokenize:
case UpdateToken: {
forwardingData.setTokenizedFieldMatrix(TokenizedFieldMatrix.AccountNumber);
}
break;
case DeTokenize: {
forwardingData.setTokenizedFieldMatrix(TokenizedFieldMatrix.TokenizedData);
}
break;
default:
forwardingData.setTokenizedFieldMatrix(TokenizedFieldMatrix.TokenizedData);
}
}
}
private void setTokenizationData(DE127_ForwardingData forwardingData, ICardData cardData, ITrackData trackData,GiftCard giftCard, String tokenizationData) {
//Tokenization Operation type
TokenizationOperationType tokenOperationType = acceptorConfig.getTokenizationOperationType();
//Token data AccountNumber/Token
forwardingData.setTokenOrAcctNum(tokenizationData);
// Card Expiry
if(cardData != null) {
forwardingData.setExpiryDate(formatExpiry(cardData.getShortExpiry()));
}
if(trackData != null){
forwardingData.setExpiryDate(trackData.getExpiry());
if(tokenOperationType.equals(TokenizationOperationType.Tokenize)){
forwardingData.setTokenOrAcctNum(trackData.getPan());
}
}
if(giftCard != null){
forwardingData.setExpiryDate(giftCard.getExpiry());
if(tokenOperationType.equals(TokenizationOperationType.Tokenize)){
forwardingData.setTokenOrAcctNum(giftCard.getPan());
}
}
//Merchant Id
String merchantId = acceptorConfig.getMerchantId();
if(merchantId != null) {
forwardingData.setMerchantId(merchantId);
}
//Service Type #G GP API
ServiceType serviceType = acceptorConfig.getServiceType();
if (serviceType != null) {
forwardingData.setServiceType(serviceType);
}
//tokenization type #Global tokenization-1 #Merchant tokenization-2
TokenizationType tokenizationType = acceptorConfig.getTokenizationType();
if (tokenizationType != null) {
forwardingData.setTokenizationType(tokenizationType);
}
setTokenizationOperationType(forwardingData, tokenOperationType);
forwardingData.addTokenizationData(tokenizationType);
}
}