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

com.global.api.gateways.PorticoConnector Maven / Gradle / Ivy

There is a newer version: 14.2.3
Show newest version
package com.global.api.gateways;

import com.global.api.builders.*;
import com.global.api.entities.*;
import com.global.api.entities.enums.*;
import com.global.api.entities.exceptions.*;
import com.global.api.entities.reporting.AltPaymentData;
import com.global.api.entities.reporting.CheckData;
import com.global.api.entities.reporting.SurchargeLookup;
import com.global.api.network.NetworkMessageHeader;
import com.global.api.paymentMethods.*;
import com.global.api.utils.*;
import lombok.Setter;
import lombok.experimental.Accessors;
import org.w3c.dom.Document;

import javax.xml.parsers.DocumentBuilderFactory;
import java.io.File;
import java.math.BigDecimal;
import java.text.FieldPosition;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.*;

@Accessors(chain = true)
@Setter
public class PorticoConnector extends XmlGateway implements IPaymentGateway, IReportingService {
    private int siteId;
    private int licenseId;
    private int deviceId;
    private String username;
    private String password;
    private String developerId;
    private String versionNumber;
    private String secretApiKey;
    private String sdkNameVersion;
    private String cardType;
    private static final String TRANSACTION_EXCEPTION = "Either ClientTxnId or GatewayTxnId must be provided for this payment type.";
    private static final String CHECK_QUERY = "CheckQuery";
    private static final String CLERK_ID = "ClerkID";
    private static final String CATEGORY_INDICATOR = "CategoryInd";

    @Override
    public boolean supportsHostedPayments() {
        return false;
    }

    @Override
    public boolean supportsOpenBanking() {
        return false;
    }

    public Transaction processAuthorization(AuthorizationBuilder builder) throws ApiException {
        ElementTree et = new ElementTree();
        TransactionType type = builder.getTransactionType();
        TransactionModifier modifier = builder.getTransactionModifier();
        PaymentMethodType paymentType = builder.getPaymentMethod().getPaymentMethodType();

        // build request
        Element transaction = et.element(mapTransactionType(builder));
        Element block1 = et.subElement(transaction, "Block1");
        if (type.equals(TransactionType.Sale) || type.equals(TransactionType.Auth)) {
            if (paymentType != PaymentMethodType.Gift && paymentType != PaymentMethodType.ACH) {
                et.subElement(block1, "AllowDup", builder.isAllowDuplicates() ? "Y" : "N");
                if (modifier.equals(TransactionModifier.None) && paymentType != PaymentMethodType.EBT && paymentType != PaymentMethodType.Recurring)
                    et.subElement(block1, "AllowPartialAuth", builder.isAllowPartialAuth() ? "Y" : "N");
            }
        }
        et.subElement(block1, "Amt", builder.getAmount());
        et.subElement(block1, "GratuityAmtInfo", builder.getGratuity());
        et.subElement(block1, "ConvenienceAmtInfo", builder.getConvenienceAmount());
        et.subElement(block1, "ShippingAmtInfo", builder.getShippingAmount());

        // surcharge
        if (builder.getSurchargeAmount() != null) {
            et.subElement(block1, "SurchargeAmtInfo", builder.getSurchargeAmount().toString());
        }

        // because plano...
        et.subElement(block1, paymentType == PaymentMethodType.Debit ? "CashbackAmtInfo" : "CashBackAmount", builder.getCashBackAmount());

        // offline auth code
        et.subElement(block1, "OfflineAuthCode", builder.getOfflineAuthCode());

        // alias action
        if (type.equals(TransactionType.Alias)) {
            et.subElement(block1, "Action").text(builder.getAliasAction());
            et.subElement(block1, "Alias").text(builder.getAlias());
        }

        // Check Query
        if (paymentType.equals(PaymentMethodType.ACH) && (type.equals(TransactionType.CheckQueryInfo))) {
            if (builder.getClientTxnId() != null) {
                et.subElement(block1, "ClientTxnId", builder.getClientTxnId());
            } else if (builder.getGatewayTxnId() != null) {
                et.subElement(block1, "GatewayTxnId", builder.getGatewayTxnId());
            } else {
                throw new UnsupportedTransactionException(TRANSACTION_EXCEPTION);
            }
        }

        boolean isCheck = (paymentType.equals(PaymentMethodType.ACH) && (!type.equals(TransactionType.CheckQueryInfo)));
        if (
                isCheck
                        || builder.getBillingAddress() != null
                        || !StringUtils.isNullOrEmpty(builder.getCardHolderLanguage())
                        || (builder.getCustomerData() != null && !StringUtils.isNullOrEmpty(builder.getCustomerData().getEmail()))
        ) {
            Element holder = et.subElement(block1, isCheck ? "ConsumerInfo" : "CardHolderData");

            Address address = builder.getBillingAddress();
            if (address != null) {
                et.subElement(holder, isCheck ? "Address1" : "CardHolderAddr", address.getStreetAddress1());
                et.subElement(holder, isCheck ? "City" : "CardHolderCity", address.getCity());
                et.subElement(holder, isCheck ? "State" : "CardHolderState", address.getProvince());
                et.subElement(holder, isCheck ? "Zip" : "CardHolderZip", address.getPostalCode());
            }

            if (builder.getCustomerData() != null && !StringUtils.isNullOrEmpty(builder.getCustomerData().getEmail())) {
                et.subElement(holder, isCheck ? "EmailAddress" : "CardHolderEmail", builder.getCustomerData().getEmail());
            }

            if (isCheck) {
                eCheck check = (eCheck) builder.getPaymentMethod();
                if (!StringUtils.isNullOrEmpty(check.getCheckHolderName())) {
                    String[] names = check.getCheckHolderName().split(" ", 2);
                    et.subElement(holder, "FirstName", names[0]);
                    et.subElement(holder, "LastName", names[1]);
                }
                et.subElement(holder, "CheckName", check.getCheckName());
                et.subElement(holder, "PhoneNumber", check.getPhoneNumber());
                et.subElement(holder, "DLNumber", check.getDriversLicenseNumber());
                et.subElement(holder, "DLState", check.getDriversLicenseState());

                if (!StringUtils.isNullOrEmpty(check.getSsnLast4()) || check.getBirthYear() != 0) {
                    Element identity = et.subElement(holder, "IdentityInfo");
                    et.subElement(identity, "SSNL4", check.getSsnLast4());
                    et.subElement(identity, "DOBYear", check.getBirthYear());
                }
            } else if (builder.getPaymentMethod() instanceof CreditCardData) {
                CreditCardData card = (CreditCardData) builder.getPaymentMethod();
                if (!StringUtils.isNullOrEmpty(card.getCardHolderName())) {
                    String[] names = card.getCardHolderName().split(" ", 2);
                    et.subElement(holder, "CardHolderFirstName", names[0]);
                    if (names.length > 1) {
                        et.subElement(holder, "CardHolderLastName", names[1]);
                    }
                }
            }

            // card holder language
            if (!isCheck && !StringUtils.isNullOrEmpty(builder.getCardHolderLanguage())) {
                et.subElement(holder, "CardHolderLanguage", builder.getCardHolderLanguage());
            }
        }

        // card data
        String tokenValue = getToken(builder.getPaymentMethod());
        boolean hasToken = !StringUtils.isNullOrEmpty(tokenValue);

        // because debit is weird (Ach too)
        Element cardData = null;
        if (paymentType.equals(PaymentMethodType.Debit) || paymentType.equals(PaymentMethodType.ACH))
            cardData = block1;
        else cardData = et.element("CardData");

        if (builder.getPaymentMethod() instanceof ICardData) {
            ICardData card = (ICardData) builder.getPaymentMethod();

            // card on File
            if (builder.getTransactionInitiator() != null || !StringUtils.isNullOrEmpty(builder.getCardBrandTransactionId()) || builder.getCategoryIndicator() != null) {
                Element cardOnFileData = et.subElement(block1, "CardOnFileData");
                if (builder.getTransactionInitiator() == StoredCredentialInitiator.CardHolder) {
                    et.subElement(cardOnFileData, "CardOnFile", EnumUtils.getMapping(Target.Portico, StoredCredentialInitiator.CardHolder));
                } else {
                    et.subElement(cardOnFileData, "CardOnFile", EnumUtils.getMapping(Target.Portico, StoredCredentialInitiator.Merchant));
                }
                et.subElement(cardOnFileData, "CardBrandTxnId", builder.getCardBrandTransactionId());
                et.subElement(cardOnFileData, CATEGORY_INDICATOR, builder.getCategoryIndicator());
            }

            Element manualEntry = et.subElement(cardData, hasToken ? "TokenData" : "ManualEntry");
            et.subElement(manualEntry, hasToken ? "TokenValue" : "CardNbr").text(tokenValue != null ? tokenValue : card.getNumber());
            et.subElement(manualEntry, "ExpMonth", card.getExpMonth() != null ? card.getExpMonth().toString() : null);
            et.subElement(manualEntry, "ExpYear", card.getExpYear() != null ? card.getExpYear().toString() : null);
            et.subElement(manualEntry, "CVV2", card.getCvn());
            et.subElement(manualEntry, "ReaderPresent", card.isReaderPresent() ? "Y" : "N");
            et.subElement(manualEntry, "CardPresent", card.isCardPresent() ? "Y" : "N");
            block1.append(cardData);

            // secure 3d
            if (card instanceof CreditCardData) {
                ThreeDSecure secureEcom = ((CreditCardData) card).getThreeDSecure();
                CreditCardData creditCardData = (CreditCardData) card;
                if (secureEcom != null) {
                    // 3d Secure Element
                    if (!StringUtils.isNullOrEmpty(secureEcom.getEci()) && !isAppleOrGooglePay(secureEcom.getPaymentDataSource())) {
                        Element secure3D = et.subElement(block1, "Secure3D");
                        et.subElement(secure3D, "Version", getSecure3DVersion(secureEcom.getVersion()));
                        et.subElement(secure3D, "AuthenticationValue", secureEcom.getCavv());
                        et.subElement(secure3D, "ECI", secureEcom.getEci());
                        et.subElement(secure3D, "DirectoryServerTxnId", secureEcom.getXid());
                    }

                    // WalletData Element
                    if (isAppleOrGooglePay(secureEcom.getPaymentDataSource())) {
                        Element walletData = et.subElement(block1, "WalletData");
                        et.subElement(walletData, "PaymentSource", secureEcom.getPaymentDataSource());
                        et.subElement(walletData, "Cryptogram", secureEcom.getCavv());
                        et.subElement(walletData, "ECI", secureEcom.getEci());
                    }
                }

                //WalletData Element
                if (
                        (
                                creditCardData.getMobileType() == MobilePaymentMethodType.APPLEPAY
                                        || creditCardData.getMobileType() == MobilePaymentMethodType.GOOGLEPAY
                        )
                                && creditCardData.getPaymentDataSourceType() != null
                                && isAppleOrGooglePay(creditCardData.getPaymentDataSourceType())
                ) {
                    Element walletData = et.subElement(block1, "WalletData");
                    et.subElement(walletData, "PaymentSource", creditCardData.getPaymentDataSourceType());
                    et.subElement(walletData, "Cryptogram", secureEcom != null ? secureEcom.getCavv() : null);
                    et.subElement(walletData, "ECI", creditCardData.getEci());
                    if (creditCardData.getMobileType() != null) {

                        et.subElementCdata(walletData, "DigitalPaymentToken", creditCardData.getToken());
                        if (block1.has("CardData")) {
                            block1.remove("CardData");
                        }
                    }
                }

            }

            // recurring data
            if (builder.getTransactionModifier().equals(TransactionModifier.Recurring)) {
                Element recurring = et.subElement(block1, "RecurringData");
                et.subElement(recurring, "ScheduleID", builder.getScheduleId());
                et.subElement(recurring, "OneTime").text(builder.isOneTimePayment() ? "Y" : "N");
            }
        } else if (builder.getPaymentMethod() instanceof ITrackData) {
            ITrackData track = (ITrackData) builder.getPaymentMethod();

            Element trackData = et.subElement(cardData, hasToken ? "TokenData" : "TrackData");
            if (!hasToken) {
                trackData.text(track.getValue());
                trackData.set("method", track.getEntryMethod());
                if (paymentType == PaymentMethodType.Credit) {
                    // tag data
                    if (!StringUtils.isNullOrEmpty(builder.getTagData())) {
                        Element tagData = et.subElement(block1, "TagData");
                        Element tagValues = et.subElement(tagData, "TagValues", builder.getTagData());
                        tagValues.set("source", "chip");
                    }
                    Element emvData = et.subElement(block1, "EMVData");
                    if (builder.getEmvChipCondition() != null) {
                        String chipCondition = builder.getEmvChipCondition() == EmvChipCondition.ChipFailPreviousSuccess ? "CHIP_FAILED_PREV_SUCCESS" : "CHIP_FAILED_PREV_FAILED";
                        et.subElement(emvData, "EMVChipCondition", chipCondition);
                    }
                    if (builder.getPaymentMethod() instanceof IPinProtected && (((IPinProtected) builder.getPaymentMethod()).getPinBlock()) != null) {
                        et.subElement(emvData, "PINBlock", ((IPinProtected) builder.getPaymentMethod()).getPinBlock());
                    }
                    if (!block1.has("EMVChipCondition") && !block1.has("PINBlock")) {
                        block1.remove("EMVData");
                    }
                }
                if (paymentType == PaymentMethodType.Debit) {
                    String chipCondition = null;
                    if (builder.getEmvChipCondition() != null)
                        chipCondition = builder.getEmvChipCondition() == EmvChipCondition.ChipFailPreviousSuccess ? "CHIP_FAILED_PREV_SUCCESS" : "CHIP_FAILED_PREV_FAILED";

                    et.subElement(block1, "AccountType",
                            EnumUtils.getMapping(Target.Portico, builder.getAccountType()));
                    et.subElement(block1, "EMVChipCondition", chipCondition);
                    et.subElement(block1, "MessageAuthenticationCode", builder.getMessageAuthenticationCode());
                    et.subElement(block1, "PosSequenceNbr", builder.getPosSequenceNumber());
                    et.subElement(block1, "ReversalReasonCode", builder.getReversalReasonCode());
                    if (!StringUtils.isNullOrEmpty(builder.getTagData())) {
                        Element tagData = et.subElement(block1, "TagData");
                        et.subElement(tagData, "TagValues", builder.getTagData()).set("source", "chip");
                    }
                } else block1.append(cardData);
            } else et.subElement(trackData, "TokenValue").text(tokenValue);
        } else if (builder.getPaymentMethod() instanceof GiftCard) {
            GiftCard card = (GiftCard) builder.getPaymentMethod();

            // currency
            et.subElement(block1, "Currency", builder.getCurrency());

            // if it's replace put the new card and change the card data name to be old card data
            if (type.equals(TransactionType.Replace)) {
                GiftCard replacement = builder.getReplacementCard();
                Element newCardData = et.subElement(block1, "NewCardData");
                et.subElement(newCardData, replacement.getValueType(), replacement.getValue());
                et.subElement(newCardData, "PIN", replacement.getPin());

                cardData = et.element("OldCardData");
            }
            et.subElement(cardData, card.getValueType(), card.getValue());
            et.subElement(cardData, "PIN", card.getPin());

            if (builder.getAliasAction() != AliasAction.Create)
                block1.append(cardData);
        } else if ((builder.getPaymentMethod() instanceof eCheck) && (!type.equals(TransactionType.CheckQueryInfo))) {
            eCheck check = (eCheck) builder.getPaymentMethod();

            // check action
            et.subElement(block1, "CheckAction").text("SALE");

            // account info
            if (StringUtils.isNullOrEmpty(check.getToken())) {
                Element accountInfo = et.subElement(block1, "AccountInfo");
                et.subElement(accountInfo, "RoutingNumber", check.getRoutingNumber());
                et.subElement(accountInfo, "AccountNumber", check.getAccountNumber());
                et.subElement(accountInfo, "CheckNumber", check.getCheckNumber());
                et.subElement(accountInfo, "MICRData", check.getMicrNumber());
                et.subElement(accountInfo, "AccountType", EnumUtils.getMapping(Target.Portico,
                        check.getAccountType()));
            } else et.subElement(block1, "TokenValue").text(tokenValue);

            et.subElement(block1, "DataEntryMode", check.getEntryMode().getValue().toUpperCase(Locale.ENGLISH));
            et.subElement(block1, "CheckType", check.getCheckType());
            et.subElement(block1, "SECCode", check.getSecCode());

            // verify info
            Element verify = et.subElement(block1, "VerifyInfo");
            et.subElement(verify, "CheckVerify").text(check.isCheckVerify() ? "Y" : "N");
            et.subElement(verify, "ACHVerify").text(check.isAchVerify() ? "Y" : "N");
        } else if (builder.getPaymentMethod() instanceof TransactionReference) {
            TransactionReference reference = (TransactionReference) builder.getPaymentMethod();
            et.subElement(block1, "GatewayTxnId", reference.getTransactionId());
            et.subElement(block1, "ClientTxnId", reference.getClientTransactionId());
        } else if (builder.getPaymentMethod() instanceof RecurringPaymentMethod) {
            RecurringPaymentMethod method = (RecurringPaymentMethod) builder.getPaymentMethod();

            // card on File
            if (builder.getTransactionInitiator() != null || !StringUtils.isNullOrEmpty(builder.getCardBrandTransactionId())) {
                Element cardOnFileData = et.subElement(block1, "CardOnFileData");
                if (builder.getTransactionInitiator() == StoredCredentialInitiator.CardHolder) {
                    et.subElement(cardOnFileData, "CardOnFile", EnumUtils.getMapping(Target.Portico, StoredCredentialInitiator.CardHolder));
                } else {
                    et.subElement(cardOnFileData, "CardOnFile", EnumUtils.getMapping(Target.Portico, StoredCredentialInitiator.Merchant));
                }
                et.subElement(cardOnFileData, "CardBrandTxnId", builder.getCardBrandTransactionId());
            }
            // check action
            if (method.getPaymentType().equals("ACH")) {
                block1.remove("AllowDup");
                et.subElement(block1, "CheckAction").text("SALE");
            }

            // payment method stuff
            et.subElement(block1, "PaymentMethodKey").text(method.getKey());
            if (method.getPaymentMethod() != null && method.getPaymentMethod() instanceof CreditCardData) {
                CreditCardData card = (CreditCardData) method.getPaymentMethod();
                Element data = et.subElement(block1, "PaymentMethodKeyData");
                et.subElement(data, "ExpMonth", card.getExpMonth());
                et.subElement(data, "ExpYear", card.getExpYear());
                et.subElement(data, "CVV2", card.getCvn());
            }

            // recurring data
            Element recurring = et.subElement(block1, "RecurringData");
            et.subElement(recurring, "ScheduleID", builder.getScheduleId());
            et.subElement(recurring, "OneTime").text(builder.isOneTimePayment() ? "Y" : "N");
        }

        // pin block
        if (builder.getPaymentMethod() instanceof IPinProtected) {
            if (!type.equals(TransactionType.Reversal))
                et.subElement(block1, "PinBlock", ((IPinProtected) builder.getPaymentMethod()).getPinBlock());
        }

        // encryption
        if (builder.getPaymentMethod() instanceof IEncryptable) {
            EncryptionData encryptionData = ((IEncryptable) builder.getPaymentMethod()).getEncryptionData();

            if (encryptionData != null) {
                Element enc = et.subElement(cardData, "EncryptionData");
                et.subElement(enc, "Version").text(encryptionData.getVersion());
                et.subElement(enc, "EncryptedTrackNumber", encryptionData.getTrackNumber());
                et.subElement(enc, "KTB", encryptionData.getKtb());
                et.subElement(enc, "KSN", encryptionData.getKsn());
            }
        }

        // set token flag
        if (builder.getPaymentMethod() instanceof ITokenizable && builder.getPaymentMethod().getPaymentMethodType() != PaymentMethodType.ACH) {
            et.subElement(cardData, "TokenRequest").text(builder.isRequestMultiUseToken() ? "Y" : "N");
        }

        // Token Parameters
        if (builder.requestUniqueToken) {
            Element tokenParametersBlock = et.subElement(cardData, "TokenParameters");
            et.subElement(tokenParametersBlock, "Mapping").text("UNIQUE");
        }

        // balance inquiry type
        if (builder.getPaymentMethod() instanceof IBalanceable)
            et.subElement(block1, "BalanceInquiryType", builder.getBalanceInquiryType());

        // cpc request
        if (builder.isLevel2Request())
            et.subElement(block1, "CPCReq", "Y");

        // details
        if (!StringUtils.isNullOrEmpty(builder.getCustomerId()) || !StringUtils.isNullOrEmpty(builder.getDescription()) || !StringUtils.isNullOrEmpty(builder.getInvoiceNumber())) {
            Element addons = et.subElement(block1, "AdditionalTxnFields");
            et.subElement(addons, "CustomerID", builder.getCustomerId());
            et.subElement(addons, "Description", builder.getDescription());
            et.subElement(addons, "InvoiceNbr", builder.getInvoiceNumber());
        }

        // ecommerce info
        if (builder.getEcommerceInfo() != null) {
            EcommerceInfo ecom = builder.getEcommerceInfo();
            et.subElement(block1, "Ecommerce", ecom.getChannel());
            if (!StringUtils.isNullOrEmpty(builder.getInvoiceNumber()) || ecom.getShipMonth() != null) {
                Element direct = et.subElement(block1, "DirectMktData");
                et.subElement(direct, "DirectMktInvoiceNbr").text(builder.getInvoiceNumber());
                et.subElement(direct, "DirectMktShipDay").text(ecom.getShipDay().toString());
                et.subElement(direct, "DirectMktShipMonth").text(ecom.getShipMonth().toString());
            }
        }

        // dynamic descriptor
        et.subElement(block1, "TxnDescriptor", builder.getDynamicDescriptor());

        // auto substantiation
        if (builder.getAutoSubstantiation() != null) {
            Element autoSub = et.subElement(block1, "AutoSubstantiation");

            int index = 0;
            String[] fieldNames = new String[]{"First", "Second", "Third", "Fourth"};
            for (Map.Entry amount : builder.getAutoSubstantiation().getAmounts().entrySet()) {
                if (amount.getValue() != null && !amount.getValue().equals(new BigDecimal("0"))) {
                    if (index > 3) {
                        throw new BuilderException("You may only specify three different subtotals in a single transaction.");
                    }

                    Element amountNode = et.subElement(autoSub, fieldNames[index++] + "AdditionalAmtInfo");
                    et.subElement(amountNode, "AmtType", amount.getKey());
                    et.subElement(amountNode, "Amt", StringUtils.toNumeric(amount.getValue()));
                }
            }

            et.subElement(autoSub, "MerchantVerificationValue", builder.getAutoSubstantiation().getMerchantVerificationValue());
            et.subElement(autoSub, "RealTimeSubstantiation", builder.getAutoSubstantiation().isRealTimeSubstantiation() ? "Y" : "N");
        }

        // lodging data
//        if(builder.getLodgingData() != null) {
//            LodgingData lodging = builder.getLodgingData();
//
//            Element lodgingElement = et.subElement(block1, "LodgingData");
//            et.subElement(lodgingElement, "PrestigiousPropertyLimit", lodging.getPrestigiousPropertyLimit());
//            et.subElement(lodgingElement, "NoShow", lodging.isNoShow() ? "Y" : "N");
//            et.subElement(lodgingElement, "AdvancedDepositType", lodging.getAdvancedDepositType());
//            et.subElement(lodgingElement, "PreferredCustomer", lodging.isPreferredCustomer() ? "Y" : "N");
//            if(lodging.getFolioNumber() != null || lodging.getStayDuration() != null || lodging.getCheckInDate() != null || lodging.getCheckOutDate() != null || lodging.getRate() != null || lodging.getExtraCharges() != null) {
//                Element lodgingDataEdit = et.subElement(lodgingElement, "LodgingDataEdit");
//                et.subElement(lodgingDataEdit, "FolioNumber", lodging.getFolioNumber());
//                et.subElement(lodgingDataEdit, "Duration", lodging.getStayDuration());
//                if(lodging.getCheckInDate() != null) {
//                    et.subElement(lodgingDataEdit, "CheckInDate", lodging.getCheckInDate().toString("mm/DD/YYYY"));
//                }
//                if(lodging.getCheckOutDate() != null) {
//                    et.subElement(lodgingDataEdit, "CheckOutDate", lodging.getCheckOutDate().toString("mm/DD/YYYY"));
//                }
//                et.subElement(lodgingDataEdit, "Rate", lodging.getRate());
//                if(lodging.getExtraCharges() != null) {
//                    Element extraChargesElement = et.subElement(lodgingDataEdit, "ExtraCharges");
//                    for(ExtraChargeType chargeType: lodging.getExtraCharges().keySet()) {
//                        et.subElement(extraChargesElement, chargeType.toString(), "Y");
//                    }
//                    et.subElement(lodgingDataEdit, "ExtraChargeAmtInfo", lodging.getExtraChargeAmount());
//                }
//            }
//        }

        String response = doTransaction(buildEnvelope(et, transaction, builder.getClientTransactionId(), builder));
        return mapResponse(response, builder.getPaymentMethod());
    }

    public String serializeRequest(AuthorizationBuilder builder) throws ApiException {
        throw new UnsupportedTransactionException("Portico does not support hosted payments.");
    }

    public Transaction manageTransaction(ManagementBuilder builder) throws ApiException {
        ElementTree et = new ElementTree();
        TransactionType type = builder.getTransactionType();
        TransactionModifier modifier = builder.getTransactionModifier();
        // payment method
        IPaymentMethod paymentMethod = builder.getPaymentMethod();
        if (paymentMethod instanceof TransactionReference) {
            TransactionReference reference = (TransactionReference) paymentMethod;
            paymentMethod = reference.getOriginalPaymentMethod();
        }

        // build request
        Element transaction = et.element(mapTransactionType(builder));

        if (!type.equals(TransactionType.BatchClose)) {
            PaymentMethodType paymentType = builder.getPaymentMethod().getPaymentMethodType();

            Element root;
            if (type.equals(TransactionType.Reversal)
                    || type.equals(TransactionType.Refund)
                    || paymentType.equals(PaymentMethodType.Gift)
                    || paymentType.equals(PaymentMethodType.ACH)
                    || type.equals(TransactionType.Increment))
                root = et.subElement(transaction, "Block1");
            else root = transaction;

            // amount
            if (builder.getAmount() != null)
                et.subElement(root, "Amt").text(builder.getAmount().toString());

            // auth amount
            if (builder.getAuthAmount() != null)
                et.subElement(root, "AuthAmt").text(builder.getAuthAmount().toString());

            // gratuity
            if (builder.getGratuity() != null)
                et.subElement(root, "GratuityAmtInfo").text(builder.getGratuity().toString());

            // surcharge
            if (builder.getSurchargeAmount() != null) {
                et.subElement(root, "SurchargeAmtInfo", builder.getSurchargeAmount().toString());
            }

            // Transaction ID
            et.subElement(root, "GatewayTxnId", builder.getTransactionId());

            // reversal & Capture
            if (type.equals(TransactionType.Reversal)  || ((paymentType != null) && paymentType.equals(PaymentMethodType.ACH))) {
                // client transaction id
                et.subElement(root, "ClientTxnId", builder.getClientTransactionId());

                // reversal reason code & PosSequenceNumber
                if (paymentType.equals(PaymentMethodType.Debit)) {
                    if (builder.getEmvChipCondition() != null) {
                        String chipCondition = builder.getEmvChipCondition() == EmvChipCondition.ChipFailPreviousSuccess ? "CHIP_FAILED_PREV_SUCCESS" : "CHIP_FAILED_PREV_FAILED";
                        et.subElement(root, "EMVChipCondition", chipCondition);
                    }
                    et.subElement(root, "ReversalReasonCode", builder.getReversalReasonCode());
                    et.subElement(root, "PosSequenceNbr", builder.getPosSequenceNumber());
                    et.subElement(root, "AccountType",
                            EnumUtils.getMapping(Target.Portico, builder.getAccountType()));

                    // track data
                    if (paymentMethod != null) {
                        DebitTrackData track = (DebitTrackData) paymentMethod;
                        if (type.equals(TransactionType.Reversal))
                            et.subElement(root, "TrackData", track.getValue());
                        et.subElement(root, "PinBlock", track.getPinBlock());

                        EncryptionData encryptionData = track.getEncryptionData();

                        if (encryptionData != null) {
                            Element enc = et.subElement(root, "EncryptionData");
                            et.subElement(enc, "Version").text(encryptionData.getVersion());
                            et.subElement(enc, "EncryptedTrackNumber", encryptionData.getTrackNumber());
                            et.subElement(enc, "KTB", encryptionData.getKtb());
                            et.subElement(enc, "KSN", encryptionData.getKsn());
                        }
                    }
                }
            }

            if (type.equals(TransactionType.Reversal) || type.equals(TransactionType.Capture) || ((paymentType != null) && paymentType.equals(PaymentMethodType.ACH))) {
                // tag data
                if (!StringUtils.isNullOrEmpty(builder.getTagData())) {
                    Element tagData = et.subElement(root, "TagData");
                    et.subElement(tagData, "TagValues", builder.getTagData()).set("source", "chip");
                }
            }

                // Level II Data and Level_III Data
            if (builder.getCommercialData() != null) {
                CommercialData cd = builder.getCommercialData();
                if (modifier.equals(TransactionModifier.LevelII) || modifier.equals(TransactionModifier.Level_III)) {
                    Element cpc = et.subElement(root, "CPCData");
                    et.subElement(cpc, "CardHolderPONbr", cd.getPoNumber());
                    et.subElement(cpc, "TaxType", cd.getTaxType());
                    et.subElement(cpc, "TaxAmt", cd.getTaxAmount());
                }

                if (modifier.equals(TransactionModifier.Level_III) && (paymentType != null) && (paymentType.equals(PaymentMethodType.Credit))) {
                    Element cdc = et.subElement(root, "CorporateData");
                    boolean isVisa = cardType.equals("Visa");
                    Element data = et.subElement(cdc, isVisa ? "Visa" : "MC");

                    buildLineItems(et, data, isVisa, cd.getLineItems());

                    if (isVisa) {
                        et.subElement(data, "SummaryCommodityCode", cd.getSummaryCommodityCode());
                        et.subElement(data, "DiscountAmt", cd.getDiscountAmount());
                        et.subElement(data, "FreightAmt", cd.getFreightAmount());
                        et.subElement(data, "DutyAmt", cd.getDutyAmount());
                        et.subElement(data, "DestinationPostalZipCode", cd.getDestinationPostalCode());
                        et.subElement(data, "ShipFromPostalZipCode", cd.getOriginPostalCode());
                        et.subElement(data, "DestinationCountryCode", cd.getDestinationCountryCode());
                        et.subElement(data, "InvoiceRefNbr", cd.getCustomerReferenceId());
                        et.subElement(data, "VATTaxAmtFreight", cd.getAdditionalTaxDetails() != null && cd.getAdditionalTaxDetails()
                                .getTaxAmount() != null ? cd.getAdditionalTaxDetails().getTaxAmount() : cd.getTaxAmount());
                        if (cd.getOrderDate() != null) {
                            et.subElement(data, "OrderDate", cd.getOrderDate().toString("yyyy-MM-dd'T'HH:mm:ss"));
                        }
                        if (cd.getAdditionalTaxDetails() != null) {
                            et.subElement(data, "VATTaxRateFreight", cd.getAdditionalTaxDetails().getTaxRate());
                        }
                    }
                }
            }

            // lodging data
            if (builder.getLodgingData() != null) {
                LodgingData lodging = builder.getLodgingData();

                //Element lodgingElement = et.subElement(root, "LodgingData");
                if (lodging.getExtraCharges() != null) {
                    Element lodgingDataEdit = et.subElement(root, "LodgingDataEdit");

                    Element extraChargesElement = et.subElement(lodgingDataEdit, "ExtraCharges");
                    for (ExtraChargeType chargeType : lodging.getExtraCharges().keySet()) {
                        et.subElement(extraChargesElement, chargeType.toString(), "Y");
                    }
                    et.subElement(lodgingDataEdit, "ExtraChargeAmtInfo", lodging.getExtraChargeAmount());
                }
            }

            // Token Management
            if (builder.getTransactionType() == TransactionType.TokenUpdate
                    || builder.getTransactionType() == TransactionType.TokenDelete
                    || builder.getTransactionType() == TransactionType.Capture) {
                if (builder.getPaymentMethod() instanceof ITokenizable) {
                    ITokenizable token = (ITokenizable) builder.getPaymentMethod();

                    // Set the token value
                    et.subElement(root, "TokenValue", token.getToken());

                    Element tokenActions = et.subElement(root, "TokenActions");
                    if (builder.getTransactionType() == TransactionType.TokenUpdate) {
                        CreditCardData card = (CreditCardData) builder.getPaymentMethod();

                        Element setElement = et.subElement(tokenActions, "Set");

                        Element expMonth = et.subElement(setElement, "Attribute");
                        et.subElement(expMonth, "Name", "expmonth");
                        et.subElement(expMonth, "Value", card.getExpMonth());

                        Element expYear = et.subElement(setElement, "Attribute");
                        et.subElement(expYear, "Name", "expyear");
                        et.subElement(expYear, "Value", card.getExpYear());
                    } else {
                        et.subElement(tokenActions, "Delete");
                    }
                }
            }

            // details
            if (!StringUtils.isNullOrEmpty(builder.getCustomerId()) || !StringUtils.isNullOrEmpty(builder.getDescription()) || !StringUtils.isNullOrEmpty(builder.getInvoiceNumber())) {
                Element addons = et.subElement(root, "AdditionalTxnFields");
                et.subElement(addons, "CustomerID", builder.getCustomerId());
                et.subElement(addons, "Description", builder.getDescription());
                et.subElement(addons, "InvoiceNbr", builder.getInvoiceNumber());
            }
        }

        String response = doTransaction(buildEnvelope(et, transaction, builder.getClientTransactionId(), builder));
        return mapResponse(response, builder.getPaymentMethod());
    }

    public  TResult processReport(ReportBuilder builder, Class clazz) throws ApiException {
        ElementTree et = new ElementTree();
        ReportType reportType = builder.getReportType();
        Element transaction = et.element(mapReportType(builder.getReportType()));
        if (builder instanceof TransactionReportBuilder) {
            TransactionReportBuilder trb = (TransactionReportBuilder) builder;

            if(reportType.equals(ReportType.FindTransactions) || reportType.equals(ReportType.TransactionDetail)){
                if (trb.getTransactionId() != null) {
                    et.subElement(transaction, "TxnId", trb.getTransactionId());
                }
            }
            if(reportType.equals(ReportType.FindTransactions)){
                Element criteria = et.subElement(transaction, "Criteria");
                et.subElement(criteria, "StartUtcDT", trb.getStartDate() == null ? null : formatDate(trb.getStartDate()));
                et.subElement(criteria, "EndUtcDT", trb.getEndDate() == null ? null : formatDate(trb.getEndDate()));
                et.subElement(criteria, "AuthCode", trb.getSearchBuilder().getAuthCode());
                et.subElement(criteria, "CardHolderLastName", trb.getSearchBuilder().getCardHolderLastName());
                et.subElement(criteria, "CardHolderFirstName", trb.getSearchBuilder().getCardHolderFirstName());
                et.subElement(criteria, "CardNbrFirstSix", trb.getSearchBuilder().getCardNumberFirstSix());
                et.subElement(criteria, "CardNbrLastFour", trb.getSearchBuilder().getCardNumberLastFour());
                et.subElement(criteria, "InvoiceNbr", trb.getSearchBuilder().getInvoiceNumber());
                et.subElement(criteria, "CardHolderPONbr", trb.getSearchBuilder().getCardHolderPoNumber());
                et.subElement(criteria, "CustomerID", trb.getSearchBuilder().getCustomerId());
                et.subElement(criteria, "IssuerResult", trb.getSearchBuilder().getIssuerTransactionId());
                et.subElement(criteria, "SettlementAmt", trb.getSearchBuilder().getSettlementAmount());
                et.subElement(criteria, "IssTxnId", trb.getSearchBuilder().getIssuerTransactionId());
                et.subElement(criteria, "RefNbr", trb.getSearchBuilder().getReferenceNumber());
                et.subElement(criteria, "UserName", trb.getSearchBuilder().getUsername());
                et.subElement(criteria, "ClerkID", trb.getSearchBuilder().getClerkId());
                et.subElement(criteria, "BatchSeqNbr", trb.getSearchBuilder().getBatchSequenceNumber());
                et.subElement(criteria, "BatchId", trb.getSearchBuilder().getBatchId());
                et.subElement(criteria, "SiteTrace", trb.getSearchBuilder().getSiteTrace());
                et.subElement(criteria, "DisplayName", trb.getSearchBuilder().getDisplayName());
                et.subElement(criteria, "ClientTxnId", trb.getSearchBuilder().getClientTransactionId());
                et.subElement(criteria, "UniqueDeviceId", trb.getSearchBuilder().getUniqueDeviceId());
                et.subElement(criteria, "AcctNbrLastFour", trb.getSearchBuilder().getAccountNumberLastFour());
                et.subElement(criteria, "BankRoutingNbr", trb.getSearchBuilder().getBankRoutingNumber());
                et.subElement(criteria, "CheckNbr", trb.getSearchBuilder().getCheckNumber());
                et.subElement(criteria, "CheckFirstName", trb.getSearchBuilder().getCheckFirstName());
                et.subElement(criteria, "CheckLastName", trb.getSearchBuilder().getCheckLastName());
                et.subElement(criteria, "CheckName", trb.getSearchBuilder().getCheckName());
                et.subElement(criteria, "GiftCurrency", trb.getSearchBuilder().getGiftCurrency());
                et.subElement(criteria, "GiftMaskedAlias", trb.getSearchBuilder().getGiftMaskedAlias());
                et.subElement(criteria, "PaymentMethodKey", trb.getSearchBuilder().getPaymentMethodKey());
                et.subElement(criteria, "ScheduleID", trb.getSearchBuilder().getScheduleId());
                et.subElement(criteria, "BuyerEmailAddress", trb.getSearchBuilder().getBuyerEmailAddress());
                et.subElement(criteria, "AltPaymentStatus", trb.getSearchBuilder().getAltPaymentStatus());
                et.subElement(criteria, "SAFIndicator", trb.getSearchBuilder().getSafIndicator());
            }

            if(reportType.equals(ReportType.Activity)){
                if (trb.getStartDate() != null) {
                    et.subElement(transaction, "RptStartUtcDT", formatDate(trb.getStartDate()));
                }
                if (trb.getEndDate() != null) {
                    et.subElement(transaction, "RptEndUtcDT", formatDate(trb.getEndDate()));
                }
            }
            if(reportType.equals(ReportType.Activity)||reportType.equals(ReportType.OpenAuths) ||reportType.equals(ReportType.BatchDetail)){
                if (trb.getDeviceId() != null) {
                    et.subElement(transaction, "DeviceId", trb.getDeviceId());
                }
                if (trb.getTimeZoneConversion() != null) {
                    et.subElement(transaction, "TzConversion", trb.getTimeZoneConversion());
                }
            }

            if(reportType.equals(ReportType.BatchDetail)){
                if (trb.getBatchId() != 0) {
                    et.subElement(transaction, "BatchId", trb.getBatchId());
                }
            }
        }
        String response = doTransaction(buildEnvelope(et, transaction));
        return mapReportResponse(response, builder.getReportType(), clazz);
    }

    @SuppressWarnings("unchecked")
    public  TResult surchargeEligibilityLookup(SurchargeEligibilityBuilder builder, Class clazz)
            throws ApiException {

        ElementTree et = new ElementTree();
        TransactionType type = builder.getTransType();
        Element transaction = et.element(builder.getTransType().name());
        Element block1 = et.subElement(transaction, "Block1");
        Element cardData = et.element("CardData");

        if (builder.getPaymentMethod() instanceof ICardData) {
            ICardData card = (ICardData) builder.getPaymentMethod();
            Element manualEntry = et.subElement(cardData, "ManualEntry");
            et.subElement(manualEntry, "CardNbr", card.getNumber() != null ?
                    card.getNumber() : null);
            et.subElement(manualEntry, "ExpMonth", card.getExpMonth() != null ?
                    card.getExpMonth().toString() : null);
            et.subElement(manualEntry, "ExpYear", card.getExpYear() != null ?
                    card.getExpYear() : null);
            block1.append(cardData);

        } else if (builder.getPaymentMethod() instanceof ITrackData) {
            ITrackData track = (ITrackData) builder.getPaymentMethod();
            Element trackData = et.subElement(cardData, "TrackData");
            trackData.text(track.getValue());
            trackData.set("method", track.getEntryMethod());
            block1.append(cardData);
        }

        if (builder.getPaymentMethod() instanceof IEncryptable) {
            EncryptionData encryptionData = ((IEncryptable) builder.getPaymentMethod()).getEncryptionData();
            if (encryptionData != null) {
                Element enc = et.subElement(cardData, "EncryptionData");
                et.subElement(enc, "Version").text(encryptionData.getVersion());
                et.subElement(enc, "EncryptedTrackNumber", encryptionData.getTrackNumber());
                et.subElement(enc, "KTB", encryptionData.getKtb());
                et.subElement(enc, "KSN", encryptionData.getKsn());
                et.subElement(enc, "DataFormat", "1");
            }
        }

        try {
            String response = doTransaction(buildEnvelope(et, transaction));
            return (TResult) mapSurchargeLookupResponse(response, clazz);
        } catch (GatewayException e) {
            SurchargeLookup lookupResponse = new SurchargeLookup();
            lookupResponse.setIsSurchargeable("U");
            lookupResponse.setGatewayRspMsg(e.getMessage());
            return (TResult) lookupResponse;
        }
    }

    private String buildEnvelope(ElementTree et, Element transaction) throws BuilderException {
        return buildEnvelope(et, transaction, null, null);
    }

    private String buildEnvelope(ElementTree et, Element transaction, String clientTransactionId, TransactionBuilder builder) throws BuilderException {
        et.addNamespace("soap", "http://schemas.xmlsoap.org/soap/envelope/");
        et.addNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");
        et.addNamespace("xsd", "http://www.w3.org/2001/XMLSchema");

        Element envelope = et.element("soap:Envelope");

        Element body = et.subElement(envelope, "soap:Body");
        Element request = et.subElement(body, "PosRequest").set("xmlns", "http://Hps.Exchange.PosGateway");
        Element version1 = et.subElement(request, "Ver1.0");

        // header
        Element header = et.subElement(version1, "Header");
        et.subElement(header, "SecretAPIKey", secretApiKey);
        et.subElement(header, "SiteId", siteId);
        et.subElement(header, "LicenseId", licenseId);
        et.subElement(header, "DeviceId", deviceId);
        et.subElement(header, "UserName", username);
        et.subElement(header, "Password", password);
        et.subElement(header, "VersionNbr", versionNumber);
        et.subElement(header, "DeveloperID", developerId);
        et.subElement(header, "ClientTxnId", clientTransactionId);
        et.subElement(header, "PosReqDT", this.getPosReqDT());
        et.subElement(header, "SDKNameVersion", sdkNameVersion != null ? sdkNameVersion : "java;version=" + getReleaseVersion());
        if (builder != null && builder.getUniqueDeviceId() != null)
            et.subElement(header, "UniqueDeviceId", builder.getUniqueDeviceId());

        if (builder instanceof AuthorizationBuilder) {
            AuthorizationBuilder authBuilder = (AuthorizationBuilder) builder;
            et.subElement(header, CLERK_ID, authBuilder.getClerkId());
        } else {
            if (builder instanceof ManagementBuilder) {
                ManagementBuilder manageBuilder = (ManagementBuilder) builder;
                if(manageBuilder.getClerkId() != null && manageBuilder.getClerkId().length() > 50){
                    throw new BuilderException("length should not be more than 50 digits");
                }
                else {
                    et.subElement(header, CLERK_ID, manageBuilder.getClerkId());
                }
            }
        }

        if (builder != null && builder.getIsSAFIndicator() != null) {
            Element safData = et.subElement(header, "SAFData");
            et.subElement(safData, "SAFIndicator", builder.getIsSAFIndicator() ? "Y" : "N");

            if (builder.getSafOrignDT() != null) {
                et.subElement(safData, "SAFOrigDT", builder.getSafOrignDT());
            } else {
                throw new UnsupportedOperationException("SAFData operation not supported without SAFOrigDT");
            }
        }

        // Transaction
        Element trans = et.subElement(version1, "Transaction");
        trans.append(transaction);
        return et.toString(envelope);
    }

    @SuppressWarnings("unchecked")
    private  TResult mapSurchargeLookupResponse(String rawResponse, Class clazz) throws ApiException {

        try {
            Element response = ElementTree.parse(rawResponse).get("PosResponse");
            Element doc = ElementTree.parse(rawResponse).get("SurchargeEligibilityLookup");
            TResult rvalue = clazz.newInstance();
            if (rvalue instanceof SurchargeLookup) {
                SurchargeLookup lookupResponse = new SurchargeLookup();
                if (doc != null) {
                    lookupResponse.setIsSurchargeable(doc.getString("IsSurchargeable"));
                } else {
                    lookupResponse.setIsSurchargeable("U");
                }

                if (response != null) {
                    lookupResponse.setGatewayRspCode(normalizeResponse(response.getString("GatewayRspCode")));
                    lookupResponse.setGatewayRspMsg(response.getString("GatewayRspMsg"));
                    lookupResponse.setGatewayTxnId(response.getString("GatewayTxnId"));
                }
                rvalue = (TResult) lookupResponse;
            }
            return rvalue;
        } catch (Exception e) {
            throw new ApiException(e.getMessage(), e);
        }
    }

    private Transaction mapResponse(String rawResponse, IPaymentMethod paymentMethod) throws ApiException {
        Transaction result = new Transaction();

        Element root = ElementTree.parse(rawResponse).get("PosResponse");
        ArrayList acceptedCodes = new ArrayList();
        acceptedCodes.add("00");
        acceptedCodes.add("0");
        acceptedCodes.add("85");
        acceptedCodes.add("10");
        acceptedCodes.add("02");
        acceptedCodes.add("2");

        // check gateway responses
        String gatewayRspCode = normalizeResponse(root.getString("GatewayRspCode"));
        String gatewayRspText = root.getString("GatewayRspMsg");
        cardType = root.getString("CardType");

        if(gatewayRspCode.equals("30")){
            String gatewayTxnId = root.getString("GatewayTxnId");
            throw new GatewayTimeoutException(String.format("Unexpected Gateway Response: %s - %s", gatewayRspCode, gatewayRspText),gatewayRspCode, gatewayRspText, gatewayTxnId);
        }
        if (!acceptedCodes.contains(gatewayRspCode)) {
            throw new GatewayException(
                    String.format("Unexpected Gateway Response: %s - %s", gatewayRspCode, gatewayRspText),
                    gatewayRspCode,
                    gatewayRspText
            );
        } else {
            result.setAuthorizedAmount(root.getDecimal("AuthAmt"));
            result.setAvailableBalance(root.getDecimal("AvailableBalance"));
            result.setAvsResponseCode(root.getString("AVSRsltCode"));
            result.setAvsResponseMessage(root.getString("AVSRsltText"));
            result.setBalanceAmount(root.getDecimal("BalanceAmt"));
            result.setCardType(root.getString("CardType"));
            result.setCardLast4(root.getString("TokenPANLast4"));
            result.setCavvResponseCode(root.getString("CAVVResultCode"));
            result.setCommercialIndicator(root.getString("CPCInd"));
            result.setCvnResponseCode(root.getString("CVVRsltCode"));
            result.setCvnResponseMessage(root.getString("CVVRsltText"));
            result.setEmvIssuerResponse(root.getString("EMVIssuerResp"));
            result.setPointsBalanceAmount(root.getDecimal("PointsBalanceAmt"));
            result.setRecurringDataCode(root.getString("RecurringDataCode"));
            result.setReferenceNumber(root.getString("RefNbr"));
            result.setCardBrandTransactionId(root.getString("CardBrandTxnId"));

            String responseCode = normalizeResponse(root.getString("RspCode"));
            String responseText = root.getString("RspText", "RspMessage");
            result.setResponseCode(responseCode != null ? responseCode : gatewayRspCode);
            result.setResponseMessage(responseText != null ? responseText : gatewayRspText);
            result.setTransactionDescriptor(root.getString("TxnDescriptor"));
            result.setResponseDate(root.getDate(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"), "RspDT"));
            result.setHostResponseDate(root.getDate(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"), "HostRspDT"));

            if (paymentMethod != null) {
                TransactionReference reference = new TransactionReference();
                reference.setPaymentMethodType(paymentMethod.getPaymentMethodType());
                reference.setTransactionId(root.getString("GatewayTxnId"));
                reference.setClientTransactionId(root.getString("ClientTxnId"));
                reference.setAuthCode(root.getString("AuthCode"));
                result.setTransactionReference(reference);
            }

            // gift card create data
            if (root.has("CardData")) {
                GiftCard giftCard = new GiftCard();
                giftCard.setNumber(root.getString("CardNbr"));
                giftCard.setAlias(root.getString("Alias"));
                giftCard.setPin(root.getString("PIN"));

                result.setGiftCard(giftCard);
            }

            // token data
            if (root.has("TokenData")) {
                result.setToken(root.getString("TokenValue"));
            }

            // batch information
            if (root.has("BatchId")) {
                BatchSummary summary = new BatchSummary();
                summary.setBatchId(root.getInt("BatchId"));
                summary.setTransactionCount(root.getInt("TxnCnt"));
                summary.setTotalAmount(root.getDecimal("TotalAmt"));
                summary.setSequenceNumber(root.getString("BatchSeqNbr"));
                result.setBatchSummary(summary);
            }

            // debit mac
            if (root.has("DebitMac")) {
                DebitMac debitMac = new DebitMac();
                debitMac.setTransactionCode(root.getString("TransactionCode"));
                debitMac.setTransmissionNumber(root.getString("TransmissionNumber"));
                debitMac.setBankResponseCode(root.getString("BankResponseCode"));
                debitMac.setMacKey(root.getString("MacKey"));
                debitMac.setPinKey(root.getString("PinKey"));
                debitMac.setFieldKey(root.getString("FieldKey"));
                debitMac.setTraceNumber(root.getString("TraceNumber"));
                debitMac.setMessageAuthenticationCode(root.getString("MessageAuthenticationCode"));
                result.setDebitMac(debitMac);

                // add the track data for debit interact
                result.getTransactionReference().setOriginalPaymentMethod(paymentMethod);
            }

            //duplicate transaction
            if (root.has("AdditionalDuplicateData")) {
                AdditionalDuplicateData additionalDuplicateData = new AdditionalDuplicateData();
                additionalDuplicateData.setOriginalGatewayTxnId(root.getString("OriginalGatewayTxnId"));
                additionalDuplicateData.setOriginalRspDT(root.getDate(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"), "OriginalRspDT").toString());
                additionalDuplicateData.setOriginalClientTxnId(root.getString("OriginalClientTxnId"));
                additionalDuplicateData.setOriginalAuthCode(root.getString("OriginalAuthCode"));
                additionalDuplicateData.setOriginalRefNbr(root.getString("OriginalRefNbr"));
                additionalDuplicateData.setOriginalAuthAmt(root.getDecimal("OriginalAuthAmt"));
                additionalDuplicateData.setOriginalCardType(root.getString("OriginalCardType"));
                additionalDuplicateData.setOriginalCardNbrLast4(root.getString("OriginalCardNbrLast4"));

                // add the duplicate data
                result.setAdditionalDuplicateData(additionalDuplicateData);
            }
        }
        return result;
    }

    @SuppressWarnings("unchecked")
    private  TResult mapReportResponse(String rawResponse, ReportType reportType, Class clazz) throws ApiException {
        Element response = ElementTree.parse(rawResponse).get("PosResponse");
        ArrayList acceptedCodes = new ArrayList();
        acceptedCodes.add("00");
        acceptedCodes.add("0");

        // check gateway responses
        String gatewayRspCode = normalizeResponse(response.getString("GatewayRspCode"));
        String gatewayRspText = response.getString("GatewayRspMsg");

        if (!acceptedCodes.contains(gatewayRspCode)) {
            throw new GatewayException(
                    String.format("Unexpected Gateway Response: %s - %s", gatewayRspCode, gatewayRspText),
                    gatewayRspCode,
                    gatewayRspText
            );
        }

        Element doc = ElementTree.parse(rawResponse).get(mapReportType(reportType));

        try {
            TResult rvalue = clazz.newInstance();
            if (reportType.equals(ReportType.FindTransactions) || reportType.equals(ReportType.Activity) || reportType.equals(ReportType.TransactionDetail) || reportType.equals(ReportType.BatchDetail) || reportType.equals(ReportType.OpenAuths)) {
                // Activity
                if (rvalue instanceof ActivityReport) {
                    for (Element detail : doc.getAll("Details")) {
                        ((ArrayList) rvalue).add(hydrateTransactionSummary(detail));
                    }
                } else if(rvalue instanceof TransactionSummaryList){
                    for (Element detail : doc.getAll("Details")) {
                        ((TransactionSummaryList) rvalue).add(hydrateTransactionSummary(detail));
                    }
                } else {
                    rvalue = (TResult) hydrateTransactionSummary(doc);
                }
            }
            return rvalue;
        } catch (Exception e) {
            throw new ApiException(e.getMessage(), e);
        }
    }

    private String normalizeResponse(String input) {
        if (input != null) {
            if (input.equals("0") || input.equals("85"))
                return "00";
        }
        return input;
    }

    private > String mapTransactionType(T builder) throws ApiException {
        TransactionModifier modifier = builder.getTransactionModifier();
        PaymentMethodType paymentMethodType = null;
        if (builder.getPaymentMethod() != null)
            paymentMethodType = builder.getPaymentMethod().getPaymentMethodType();

        switch (builder.getTransactionType()) {
            case BatchClose:
                return "BatchClose";
            case Decline:
                if (modifier.equals(TransactionModifier.ChipDecline))
                    return "ChipCardDecline";
                else if (modifier.equals(TransactionModifier.FraudDecline))
                    return "OverrideFraudDecline";
                throw new UnsupportedTransactionException();
            case Verify:
                if (modifier.equals(TransactionModifier.EncryptedMobile))
                    throw new UnsupportedTransactionException("Transaction not supported for this payment method.");
                return "CreditAccountVerify";
            case Capture:
                if (paymentMethodType != null) {
                    if (paymentMethodType.equals(PaymentMethodType.Credit))
                        return "CreditAddToBatch";
                    else if (paymentMethodType.equals(PaymentMethodType.Debit))
                        return "DebitAddToBatch";
                    throw new UnsupportedTransactionException("Transaction not supported for this payment method.");
                }
            case Auth:
                if (paymentMethodType != null) {
                    if (paymentMethodType.equals(PaymentMethodType.Credit)) {
                        if (modifier.equals(TransactionModifier.Additional))
                            return "CreditAdditionalAuth";
                        else if (modifier.equals(TransactionModifier.Incremental))
                            return "CreditIncrementalAuth";
                        else if (modifier.equals(TransactionModifier.Offline))
                            return "CreditOfflineAuth";
                        else if (modifier.equals(TransactionModifier.Recurring))
                            return "RecurringBillingAuth";
//                        else if (modifier.equals(TransactionModifier.EncryptedMobile))
//                            throw new UnsupportedTransactionException("Transaction not supported for this payment method.");
                        return "CreditAuth";
                    } else if (paymentMethodType.equals(PaymentMethodType.Recurring))
                        return "RecurringBillingAuth";
                    else if (paymentMethodType.equals(PaymentMethodType.Debit))
                        return "DebitAuth";
                    throw new UnsupportedTransactionException("Transaction not supported for this payment method.");
                }
            case Sale:
                if (paymentMethodType != null) {
                    if (paymentMethodType.equals(PaymentMethodType.Credit)) {
                        if (modifier.equals(TransactionModifier.Offline))
                            return "CreditOfflineSale";
                        else if (modifier.equals(TransactionModifier.Recurring))
                            return "RecurringBilling";
                        else return "CreditSale";
                    } else if (paymentMethodType.equals(PaymentMethodType.Recurring)) {
                        if (((RecurringPaymentMethod) builder.getPaymentMethod()).getPaymentType().equals("ACH"))
                            return "CheckSale";
                        return "RecurringBilling";
                    } else if (paymentMethodType.equals(PaymentMethodType.Debit))
                        return "DebitSale";
                    else if (paymentMethodType.equals(PaymentMethodType.Cash))
                        return "CashSale";
                    else if (paymentMethodType.equals(PaymentMethodType.ACH))
                        return "CheckSale";
                    else if (paymentMethodType.equals(PaymentMethodType.EBT)) {
                        if (modifier.equals(TransactionModifier.CashBack))
                            return "EBTCashBackPurchase";
                        else if (modifier.equals(TransactionModifier.Voucher))
                            return "EBTVoucherPurchase";
                        else return "EBTFSPurchase";
                    } else if (paymentMethodType.equals(PaymentMethodType.Gift))
                        return "GiftCardSale";
                    throw new UnsupportedTransactionException();
                }
            case CheckQueryInfo:
                if (paymentMethodType != null) {
                    if (paymentMethodType.equals(PaymentMethodType.ACH))
                        return CHECK_QUERY;
                    throw new UnsupportedTransactionException();
                }
            case Refund:
                if (paymentMethodType != null) {
                    if (paymentMethodType.equals(PaymentMethodType.Credit))
                        return "CreditReturn";
                    else if (paymentMethodType.equals(PaymentMethodType.Debit)) {
                        if (builder.getPaymentMethod() instanceof TransactionReference)
                            throw new UnsupportedTransactionException();
                        return "DebitReturn";
                    } else if (paymentMethodType.equals(PaymentMethodType.Cash))
                        return "CashReturn";
                    else if (paymentMethodType.equals(PaymentMethodType.EBT)) {
                        if (builder.getPaymentMethod() instanceof TransactionReference)
                            throw new UnsupportedTransactionException();
                        return "EBTFSReturn";
                    }
                    throw new UnsupportedTransactionException();
                }
            case Reversal:
                if (paymentMethodType != null) {
                    if (paymentMethodType.equals(PaymentMethodType.Credit))
                        return "CreditReversal";
                    else if (paymentMethodType.equals(PaymentMethodType.Debit)) {
                        return "DebitReversal";
                    } else if (paymentMethodType.equals(PaymentMethodType.Gift))
                        return "GiftCardReversal";
                    else if (paymentMethodType.equals(PaymentMethodType.EBT))
                        return "EBTFSReversal";
                    throw new UnsupportedTransactionException();
                }
            case Edit:
                if (modifier.equals(TransactionModifier.LevelII) || modifier.equals(TransactionModifier.Level_III))
                    return "CreditCPCEdit";
                else return "CreditTxnEdit";
            case Void:
                if (paymentMethodType != null) {
                    if (paymentMethodType.equals(PaymentMethodType.Credit))
                        return "CreditVoid";
                    else if (paymentMethodType.equals(PaymentMethodType.ACH))
                        return "CheckVoid";
                    else if (paymentMethodType.equals(PaymentMethodType.Gift))
                        return "GiftCardVoid";
                    throw new UnsupportedTransactionException();
                }
            case AddValue:
                if (paymentMethodType != null) {
                    if (paymentMethodType.equals(PaymentMethodType.Credit))
                        return "PrePaidAddValue";
                    else if (paymentMethodType.equals(PaymentMethodType.Debit))
                        return "DebitAddValue";
                    else if (paymentMethodType.equals(PaymentMethodType.Gift))
                        return "GiftCardAddValue";
                    throw new UnsupportedTransactionException();
                }
            case Balance:
                if (paymentMethodType != null) {
                    if (paymentMethodType.equals(PaymentMethodType.EBT))
                        return "EBTBalanceInquiry";
                    else if (paymentMethodType.equals(PaymentMethodType.Gift))
                        return "GiftCardBalance";
                    else if (paymentMethodType.equals(PaymentMethodType.Credit))
                        return "PrePaidBalanceInquiry";
                    throw new UnsupportedTransactionException();
                }
            case BenefitWithdrawal:
                return "EBTCashBenefitWithdrawal";
            case Activate:
                return "GiftCardActivate";
            case Alias:
                return "GiftCardAlias";
            case Deactivate:
                return "GiftCardDeactivate";
            case Replace:
                return "GiftCardReplace";
            case Reward:
                return "GiftCardReward";
            case Increment:
                return "CreditIncrementalAuth";
            case Tokenize:
                return "Tokenize";
            case TokenUpdate:
            case TokenDelete:
                return "ManageTokens";
            case SurchargeEligibilityLookup:
                return "SurchargeEligibilityLookup";
            default:
                throw new UnsupportedTransactionException();
        }

    }

    private String mapReportType(ReportType type) throws UnsupportedTransactionException {
        switch (type) {
            case TransactionDetail:
                return "ReportTxnDetail";
            case Activity:
                return "ReportActivity";
            case FindTransactions:
                return "FindTransactions";
            case BatchDetail:
                return "ReportBatchDetail";
            case OpenAuths:
                return "ReportOpenAuths";
            default:
                throw new UnsupportedTransactionException();
        }
    }

    private String getToken(IPaymentMethod paymentMethod) {
        if (paymentMethod instanceof ITokenizable) {
            String tokenValue = ((ITokenizable) paymentMethod).getToken();
            if (tokenValue != null && !tokenValue.equals(""))
                return tokenValue;
            return null;
        }
        return null;
    }

    private String formatDate(Date date) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
        return sdf.format(date);
    }

    private TransactionSummary hydrateTransactionSummary(Element root) {
        TransactionSummary summary = new TransactionSummary();
        summary.setAccountDataSource(root.getString("AcctDataSrc"));
        summary.setAmount(root.getDecimal("Amt"));
        summary.setAuthCode(root.getString("AuthCode"));
        summary.setAuthorizedAmount(root.getDecimal("AuthAmt"));
        summary.setBatchCloseDate(root.getDateTime("BatchCloseDT"));
        summary.setBatchSequenceNumber(root.getString("BatchSeqNbr"));
        summary.setCaptureAmount(root.getDecimal("CaptureAmtInfo"));
        summary.setEmail(root.getString("CardHolderEmail"));
        summary.setCardSwiped(root.getString("CardSwiped"));
        summary.setCardType(root.getString("CardType"));
        summary.setCavvResponseCode(root.getString("CAVVResultCode"));
        summary.setClerkId(root.getString("ClerkID"));
        summary.setClientTransactionId(root.getString("ClientTxnId"));
        summary.setCompanyName(root.getString("Company"));
        summary.setConvenienceAmount(root.getDecimal("ConvenienceAmtInfo"));
        summary.setCustomerFirstName(root.getString("CustomerFirstname"));
        summary.setCustomerId(root.getString("CustomerID"));
        summary.setCustomerLastName(root.getString("CustomerLastname"));
        summary.setClientTransactionId(root.getString("ClientTxnId"));
        summary.setDebtRepaymentIndicator(root.getString("DebtRepaymentIndicator") == "1");
        summary.setDescription(root.getString("Description"));
        summary.setEmvChipCondition(root.getString("EMVChipCondition"));
        summary.setEmvIssuerResponse(root.getString("EMVIssuerResp"));
        summary.setFraudRuleInfo(root.getString("FraudInfoRule"));
        summary.setFullyCaptured(root.getString("FullyCapturedInd") == "1");
        summary.setGatewayResponseCode(normalizeResponse(root.getString("GatewayRspCode")));
        summary.setGatewayResponseMessage(root.getString("GatewayRspMsg"));
        summary.setGiftCurrency(root.getString("GiftCurrency"));
        summary.setGratuityAmount(root.getDecimal("GratuityAmtInfo"));
        summary.setHasEmvTags(root.getString("HasEMVTag") == "1");
        summary.setHasEcomPaymentData(root.getString("HasEComPaymentData") == "1");
        summary.setInvoiceNumber(root.getString("InvoiceNbr"));
        summary.setIssuerResponseCode(root.getString("IssuerRspCode", "RspCode"));
        summary.setIssuerResponseMessage(root.getString("IssuerRspText", "RspText"));
        summary.setIssuerTransactionId(root.getString("IssTxnId"));
        summary.setMaskedAlias(root.getString("GiftMaskedAlias"));
        summary.setMaskedCardNumber(root.getString("MaskedCardNbr"));
        summary.setOneTimePayment(root.getString("OneTime") == "1");
        summary.setOriginalTransactionId(root.getString("OriginalGatewayTxnId"));
        summary.setPaymentMethodKey(root.getString("PaymentMethodKey"));
        summary.setPaymentType(root.getString("PaymentType"));
        summary.setPoNumber(root.getString("CardHolderPONbr"));
        summary.setRecurringDataCode(root.getString("RecurringDataCode"));
        summary.setReferenceNumber(root.getString("RefNbr"));
        summary.setResponseDate(root.getDateTime("RspDT"));
        summary.setScheduleId(root.getString("ScheduleID"));
        summary.setServiceName(root.getString("ServiceName"));
        summary.setSettlementAmount(root.getDecimal("SettlementAmt"));
        summary.setShippingAmount(root.getDecimal("ShippingAmtInfo"));
        summary.setStatus(root.getString("Status", "TxnStatus"));
        summary.setSurchargeAmount(root.getDecimal("SurchargeAmtInfo"));
        summary.setTaxType(root.getString("TaxType"));
        summary.setTokenPanLastFour(root.getString("TokenPANLast4"));
        summary.setTransactionDate(root.getDateTime("TxnUtcDT", "ReqUtcDT"));
        summary.setTransactionDescriptor(root.getString("TxnDescriptor"));
        summary.setTransactionId(root.getString("GatewayTxnId"));
        summary.setTransactionStatus(root.getString("TxnStatus"));
        summary.setUniqueDeviceId(root.getString("UniqueDeviceId"));
        summary.setUsername(root.getString("UserName"));
        summary.setHasLevelIII(root.getString("HasLevelIII"));

        // lodging data
        if (root.has("LodgingData")) {
            LodgingData lodgingData = new LodgingData();

            String advancedDepositType = root.getString("AdvancedDepositType");
            ReverseStringEnumMap map = new ReverseStringEnumMap(AdvancedDepositType.class);
            lodgingData.setAdvancedDepositType(map.get(advancedDepositType));
            lodgingData.setLodgingDataEdit(root.getString("LodgingDataEdit"));
            summary.setLodgingData(lodgingData);
        }

        // check data
        if (root.has("CheckData")) {
            CheckData checkData = new CheckData();
            checkData.setAccountInfo(root.getString("AccountInfo"));
            checkData.setCheckAction(root.getString("CheckAction"));
            checkData.setCheckType(root.getString("CheckType"));
            checkData.setConsumerInfo(root.getString("ConsumerInfo"));
            checkData.setDataEntryMode(root.getString("DataEntryMode"));
            checkData.setSecCode(root.getString("SECCode"));
            summary.setCheckData(checkData);
        }

        // alt payment data
        if (root.has("AltPaymentData")) {
            AltPaymentData altPaymentData = new AltPaymentData();
            altPaymentData.setBuyerEmailAddress(root.getString("BuyerEmailAddress"));
            altPaymentData.setStateDate(root.getDate("StatusDT"));
            altPaymentData.setStatus(root.getString("Status"));
            altPaymentData.setStatusMessage(root.getString("StatusMsg"));
            summary.setAltPaymentData(altPaymentData);
        }

        // card holder data
        if (root.has("CardHolderData")) {
            summary.setCardHolderFirstName(root.getString("CardHolderFirstName"));
            summary.setCardHolderLastName(root.getString("CardHolderLastName"));
            Address address = new Address();
            address.setStreetAddress1(root.getString("CardHolderAddr"));
            address.setCity(root.getString("CardHolderCity"));
            address.setState(root.getString("CardHolderState"));
            address.setPostalCode(root.getString("CardHolderZip"));
            summary.setBillingAddress(address);
        }

        // 3DSecure
        if (root.has("Secure3D")) {
            ThreeDSecure secure3D = new ThreeDSecure();
            secure3D.setAuthenticationValue(root.getString("AuthenticationValue"));
            secure3D.setDirectoryServerTransactionId(root.getString("DirectoryServerTxnId"));
            secure3D.setEci(root.getString("ECI"));
            
            try {
                // account for default value of Version One. No value will be returned from Portico in this case.
                String versionString = root.getString("Version");
                if (versionString == null || versionString.isEmpty()) {
                    // Default to version One.
                    secure3D.setVersion(Secure3dVersion.ONE);
                } else {
                    int versionNumber = Integer.parseInt(root.getString("Version"));
                    secure3D.setVersion(getSecure3dVersion(versionNumber));
                }
            } catch (ApiException e) {
                throw new RuntimeException("No Matching Version Found");
            }

            summary.setThreeDSecure(secure3D);
        }

        return summary;
    }

    // Get the SDK release version
    private String getReleaseVersion() {
        String version = "";
        try {
            Document pomXml = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new File("pom.xml"));
            org.w3c.dom.Element pomRoot = (org.w3c.dom.Element) pomXml.getElementsByTagName("project").item(0);
            version = pomRoot.getElementsByTagName("version").item(0).getTextContent();
        } catch (Exception ex) {
            System.out.println("JAVA SDK version could not be extracted from pom.xml file.");
        }
        return version;
    }

    public NetworkMessageHeader sendKeepAlive() throws ApiException {
        throw new ApiException("Portico does not support KeepAlive.");
    }

    protected String getPosReqDT() {
        // Override format/parse methods to handle differences in `X` and `Z` format identifiers
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ") {
            @Override
            public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition pos) {
                StringBuffer rfcFormat = super.format(date, toAppendTo, pos);
                return rfcFormat.insert(rfcFormat.length() - 2, ":");
            }

            @Override
            public Date parse(String text, ParsePosition pos) {
                if (text.length() > 3) {
                    text = text.substring(0, text.length() - 3) + text.substring(text.length() - 2);
                }
                return super.parse(text, pos);
            }
        };

        return dateFormat.format(new Date()).replace("::", ":");
    }

    private int getSecure3DVersion(Secure3dVersion version) {
        if (version == null) {
            return 1;
        }
        switch (version) {
            case TWO:
                return 2;
            case ONE:
            case ANY:
            default:
                return 1;
        }
    }

    public static Secure3dVersion getSecure3dVersion(int versionNumber) {
        switch (versionNumber) {
            case 0:
                return Secure3dVersion.NONE;
            case 1:
                return Secure3dVersion.ONE;
            case 2:
                return Secure3dVersion.TWO;
            default:
                return Secure3dVersion.ANY;
        }
    }

    private boolean isAppleOrGooglePay(PaymentDataSourceType paymentDataSource) {
        return paymentDataSource == PaymentDataSourceType.APPLEPAY
                || paymentDataSource == PaymentDataSourceType.APPLEPAYAPP
                || paymentDataSource == PaymentDataSourceType.APPLEPAYWEB
                || paymentDataSource == PaymentDataSourceType.GOOGLEPAYAPP
                || paymentDataSource == PaymentDataSourceType.GOOGLEPAYWEB;
    }

    private void buildLineItems(ElementTree et, Element data, boolean isVisa, List items) {
        if (items == null || items.size() == 0) {
            return;
        }

        Element lineItems = et.subElement(data, "LineItems");
        for (CommercialLineItem item : items) {
            Element lineItem = et.subElement(lineItems, "LineItemDetail");
            et.subElement(lineItem, "ItemDescription", item.getDescription());
            et.subElement(lineItem, "ProductCode", item.getProductCode());
            et.subElement(lineItem, "Quantity", item.getQuantity());
            et.subElement(lineItem, "ItemTotalAmt", item.getTotalAmount());
            et.subElement(lineItem, "UnitOfMeasure", item.getUnitOfMeasure());

            if (!isVisa) {
                continue;
            }

            // The schema says this field should exist, but it's not currently allowed.
            et.subElement(lineItem, "ItemCommodityCode ", item.getCommodityCode());
            et.subElement(lineItem, "UnitCost", item.getUnitCost());
            et.subElement(lineItem, "VATTaxAmt", item.getTaxAmount());
            et.subElement(lineItem, "DiscountAmt", item.getDiscountDetails().getDiscountAmount());
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy