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

com.global.api.gateways.GpEcomConnector 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.ApiException;
import com.global.api.entities.exceptions.BuilderException;
import com.global.api.entities.exceptions.GatewayException;
import com.global.api.entities.exceptions.UnsupportedTransactionException;
import com.global.api.entities.reporting.SearchCriteria;
import com.global.api.network.NetworkMessageHeader;
import com.global.api.paymentMethods.*;
import com.global.api.serviceConfigs.HostedPaymentConfig;
import com.global.api.utils.*;
import com.global.api.utils.masking.ElementToMask;
import com.global.api.utils.masking.MaskValueUtil;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import lombok.var;
import org.joda.time.format.DateTimeFormat;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

import static com.global.api.utils.CardUtils.getBaseCardType;
import static com.global.api.utils.StringUtils.extractDigits;

@Accessors(chain = true)
@Setter
public class GpEcomConnector extends XmlGateway implements IPaymentGateway, IRecurringGateway, IReportingService {
    private static final SimpleDateFormat SDF = new SimpleDateFormat("yyyyMMdd");
    private static HashMap mapCardType = new HashMap() {{
        put("DinersClub", "Diners");
    }};
    private String merchantId;
    private String accountId;
    private String rebatePassword;
    private String refundPassword;
    private String sharedSecret;
    private String channel;
    private HostedPaymentConfig hostedPaymentConfig;
    @Getter
    @Setter
    private String paymentValues;
    @Getter
    @Setter
    private ShaHashType shaHashType;

    public Secure3dVersion getVersion() {
        return Secure3dVersion.ONE;
    }

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

    @Override
    public boolean supportsUpdatePaymentDetails() {
        return true;
    }

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

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

    public Transaction processAuthorization(AuthorizationBuilder builder) throws ApiException {
        ElementTree et = new ElementTree();
        String timestamp = GenerationUtils.generateTimestamp(builder.getTimestamp());
        String orderId = GenerationUtils.generateOrderId(builder.getOrderId());

        // amount and currency are required for googlePay
        if (builder.getPaymentMethod() instanceof CreditCardData) {
            CreditCardData card = (CreditCardData) builder.getPaymentMethod();
            if (builder.getTransactionModifier() == TransactionModifier.EncryptedMobile) {
                if (card.getToken() == null) {
                    throw new BuilderException("Token can not be null");
                }
                if (card.getMobileType() == MobilePaymentMethodType.GOOGLEPAY && (builder.getAmount() == null || builder.getCurrency() == null)) {
                    throw new BuilderException("Amount and Currency cannot be null for capture");
                }
            }
        }

        // Build Request
        Element request =
                et.element("request")
                        .set("type", mapAuthRequestType(builder))
                        .set("timestamp", timestamp);
        et.subElement(request, "merchantid").text(merchantId);
        et.subElement(request, "account", accountId);
        if (builder.getAmount() != null) {
            et.subElement(request, "amount").text(StringUtils.toNumeric(builder.getAmount()))
                    .set("currency", builder.getCurrency());
        }

        // region AUTO/MULTI SETTLE
        //
        if (builder.getTransactionType() == TransactionType.Sale || builder.getTransactionType() == TransactionType.Auth) {
            String autoSettle = builder.getTransactionType() == TransactionType.Sale ? "1" : builder.isMultiCapture() ? "MULTI" : "0";
            et.subElement(request, "autosettle").set("flag", autoSettle);
            Integer estimatedNumberTransactions = builder.getEstimatedNumberTransactions();
            if (estimatedNumberTransactions != null) {
                if (builder.getTransactionType() == TransactionType.Auth && estimatedNumberTransactions > 0) {
                    et.subElement(request, "estnumtxn", estimatedNumberTransactions);
                }
            }
        }
        //

        et.subElement(request, "channel", channel);
        et.subElement(request, "orderid", orderId);
        String surcharge = "";
        if (builder.getSurchargeAmount() != null && !builder.getSurchargeAmount().equals("")) {
            surcharge = StringUtils.toNumeric(builder.getSurchargeAmount());
            et.subElement(request, "surchargeamount", surcharge)
                    .set("type", builder.getCreditDebitIndicator().toString().toLowerCase());
        }

        // Hydrate the payment data fields
        //
        if (builder.getPaymentMethod() instanceof CreditCardData) {
            CreditCardData card = (CreditCardData) builder.getPaymentMethod();

            // for google-pay & apple-pay
            if (builder.getTransactionModifier() == TransactionModifier.EncryptedMobile) {
                et.subElement(request, "token", card.getToken());
                et.subElement(request, "mobile", card.getMobileType().getValue());
            } else {
                Element cardElement = et.subElement(request, "card");
                et.subElement(cardElement, "number", card.getNumber());
                et.subElement(cardElement, "expdate", card.getShortExpiry());
                et.subElement(cardElement, "chname").text(card.getCardHolderName());
                et.subElement(cardElement, "type", mapCardType(getBaseCardType(card.getCardType())).toUpperCase());

                addMaskedData(MaskValueUtil.hideValues(
                        new ElementToMask("request.card.number", card.getNumber(), 4, 6),
                        new ElementToMask("request.card.expdate", card.getShortExpiry())
                ));

                if (card.getCvn() != null) {
                    Element cvnElement = et.subElement(cardElement, "cvn");
                    et.subElement(cvnElement, "number", card.getCvn());
                    et.subElement(cvnElement, "presind", card.getCvnPresenceIndicator().getValue());
                    addMaskedData(MaskValueUtil.hideValues(
                            new ElementToMask("request.card.cvn.number", card.getCvn())
                    ));
                }

                // Card block
                if (builder.getCardTypesBlocking() != null) {
                    var cardTypes = builder.getCardTypesBlocking();

                    var cardTypeBlock = et.subElement(request, "blockcard");
                    if (cardTypes.getCommercialCredit() != null) {
                        et.subElement(cardTypeBlock, "commercialcredit", cardTypes.getCommercialCredit().toString().toLowerCase());
                    }
                    if (cardTypes.getCommercialDebit() != null) {
                        et.subElement(cardTypeBlock, "commercialdebit", cardTypes.getCommercialDebit().toString().toLowerCase());
                    }
                    if (cardTypes.getConsumerCredit() != null) {
                        et.subElement(cardTypeBlock, "consumercredit", cardTypes.getConsumerCredit().toString().toLowerCase());
                    }
                    if (cardTypes.getConsumerDebit() != null) {
                        et.subElement(cardTypeBlock, "consumerdebit", cardTypes.getConsumerDebit().toString().toLowerCase());
                    }
                }
            }

            String hash = null;
            if (builder.getTransactionType() == TransactionType.Verify)
                hash = GenerationUtils.generateHash(sharedSecret, timestamp, merchantId, orderId, card.getNumber());
            else {
                if (builder.getTransactionModifier() == TransactionModifier.EncryptedMobile) {
                    switch (card.getMobileType()) {
                        case GOOGLEPAY:
                        case APPLEPAY:
                            hash = GenerationUtils.generateHash(sharedSecret, timestamp, merchantId, orderId, StringUtils.toNumeric(builder.getAmount()), builder.getCurrency(), card.getToken());
                            break;
                    }
                } else {
                    hash = GenerationUtils.generateHash(sharedSecret, timestamp, merchantId, orderId, StringUtils.toNumeric(builder.getAmount()), builder.getCurrency(), card.getNumber());
                }
            }
            et.subElement(request, "sha1hash", hash);
        } else if (builder.getPaymentMethod() instanceof AlternativePaymentMethod) {
            this.buildAlternativePaymentMethod(builder, request, et);
        }
        //

        //
        if (builder.getDescription() != null) {
            Element comments = et.subElement(request, "comments");
            et.subElement(comments, "comment", builder.getDescription()).set("id", "1");
        }
        //

        if (builder.getPaymentMethod() instanceof AlternativePaymentMethod) {
            String hash = GenerationUtils.generateHash(sharedSecret, timestamp, merchantId, orderId, StringUtils.toNumeric(builder.getAmount()), builder.getCurrency(), ((AlternativePaymentMethod) builder.getPaymentMethod()).getAlternativePaymentMethodType().getValue());
            et.subElement(request, "sha1hash").text(hash);
        }

        //
        if (builder.getPaymentMethod() instanceof RecurringPaymentMethod) {
            RecurringPaymentMethod recurring = (RecurringPaymentMethod) builder.getPaymentMethod();
            et.subElement(request, "payerref").text(recurring.getCustomerKey());
            et.subElement(request, "paymentmethod").text(recurring.getKey());

            // CVN
            if (!StringUtils.isNullOrEmpty(builder.getCvn())) {
                Element paymentData = et.subElement(request, "paymentdata");
                Element cvn = et.subElement(paymentData, "cvn");
                et.subElement(cvn, "number").text(builder.getCvn());
            }

            String hash;
            if (builder.getTransactionType() == TransactionType.Verify)
                hash = GenerationUtils.generateHash(sharedSecret, timestamp, merchantId, orderId, recurring.getCustomerKey());
            else
                hash = GenerationUtils.generateHash(sharedSecret, timestamp, merchantId, orderId, StringUtils.toNumeric(builder.getAmount()), builder.getCurrency(), recurring.getCustomerKey());
            et.subElement(request, "sha1hash", hash);
        }
        //
        //
        else {
            // TODO: Token Processing
        }
        //

        //
        if (builder.getCustomData() != null) {
            Element custom = et.subElement(request, "custom");
            ArrayList customValues = builder.getCustomData();
            for (String[] values : customValues) {
                for (int i = 1; i <= values.length; i++) {
                    et.subElement(custom, "field" + StringUtils.padLeft("" + i, 2, '0'), values[i - 1]);
                }
            }
        }
        //

        //
        if (builder.getCustomerData() != null) {
            Customer customerValue = builder.getCustomerData();
            Element customer = et.subElement(request, "customer");
            et.subElement(customer, "customerid", customerValue.getId());
            et.subElement(customer, "firstname", customerValue.getFirstName());
            et.subElement(customer, "lastname", customerValue.getLastName());
            et.subElement(customer, "dateofbirth", customerValue.getDateOfBirth());
            et.subElement(customer, "customerpassword", customerValue.getCustomerPassword());
            et.subElement(customer, "email", customerValue.getEmail());
            et.subElement(customer, "domainname", customerValue.getDomainName());
            et.subElement(customer, "devicefingerprint", customerValue.getDeviceFingerPrint());
            et.subElement(customer, "phonenumber", customerValue.getHomePhone());
        }
        //

        //
        if (builder.getDccRateData() != null) {
            DccRateData dccRateData = builder.getDccRateData();

            Element dccInfo = et.subElement(request, "dccinfo");
            et.subElement(dccInfo, "ccp", dccRateData.getDccProcessor());
            et.subElement(dccInfo, "type", "1");
            et.subElement(dccInfo, "ratetype", dccRateData.getDccRateType());

            // authorization elements
            et.subElement(dccInfo, "rate", dccRateData.getCardHolderRate());
            if (dccRateData.getCardHolderAmount() != null) {
                et.subElement(dccInfo, "amount", dccRateData.getCardHolderAmount())
                        .set("currency", dccRateData.getCardHolderCurrency());
            }
        }
        //

        //
        // fraud filter mode
        if (builder.getFraudFilterMode() != null && builder.getFraudFilterMode() != FraudFilterMode.None) {
            Element fraudfilter = et.subElement(request, "fraudfilter").set("mode", builder.getFraudFilterMode());
            if (builder.getFraudRules() != null) {
                Element rules = et.subElement(fraudfilter, "rules");

                for (FraudRule fraudRule : builder.getFraudRules().getRules()) {
                    Element rule = et.subElement(rules, "rule");
                    rule.set("id", fraudRule.getKey());
                    rule.set("mode", fraudRule.getMode().getValue());
                }
            }
        }

        // recurring fraud filter
        if (builder.getRecurringType() != null || builder.getRecurringSequence() != null) {
            et.subElement(request, "recurring")
                    .set("type", builder.getRecurringType().getValue().toLowerCase())
                    .set("sequence", builder.getRecurringSequence().getValue().toLowerCase());
        }

        // fraud Decision Manager
        if (builder.getDecisionManager() != null) {
            DecisionManager dmValues = builder.getDecisionManager();
            Element fraud = et.subElement(request, "fraud");
            Element dm = et.subElement(fraud, "dm");
            et.subElement(dm, "billtohostname", dmValues.getBillToHostName());
            et.subElement(dm, "billtohttpbrowsercookiesaccepted", !dmValues.isBillToHttpBrowserCookiesAccepted() ? "false" : "true");
            et.subElement(dm, "billtohttpbrowseremail", dmValues.getBillToHttpBrowserEmail());
            et.subElement(dm, "billtohttpbrowsertype", dmValues.getBillToHttpBrowserType());
            et.subElement(dm, "billtoipnetworkaddress", dmValues.getBillToIpNetworkAddress());
            et.subElement(dm, "businessrulesscorethreshold", dmValues.getBusinessRulesCoreThreshold());
            et.subElement(dm, "billtopersonalid", dmValues.getBillToPersonalId());
            et.subElement(dm, "invoiceheadertendertype", dmValues.getInvoiceHeaderTenderType());
            et.subElement(dm, "invoiceheaderisgift", !dmValues.isInvoiceHeaderIsGift() ? "false" : "true");
            et.subElement(dm, "decisionmanagerprofile", dmValues.getDecisionManagerProfile());
            et.subElement(dm, "invoiceheaderreturnsaccepted", !dmValues.isInvoiceHeaderReturnsAccepted() ? "false" : "true");
            et.subElement(dm, "itemhosthedge", dmValues.getItemHostHedge().getValue());
            et.subElement(dm, "itemnonsensicalhedge", dmValues.getItemNonsensicalHedge().getValue());
            et.subElement(dm, "itemobscenitieshedge", dmValues.getItemObscenitiesHedge().getValue());
            et.subElement(dm, "itemphonehedge", dmValues.getItemPhoneHedge().getValue());
            et.subElement(dm, "itemtimehedge", dmValues.getItemTimeHedge().getValue());
            et.subElement(dm, "itemvelocityhedge", dmValues.getItemVelocityHedge().getValue());
        }
        //

        //
        if (builder.getPaymentMethod() instanceof ISecure3d) {
            ThreeDSecure secureEcom = ((ISecure3d) builder.getPaymentMethod()).getThreeDSecure();
            if (secureEcom != null) {
                Element mpi = et.subElement(request, "mpi");
                et.subElement(mpi, "eci", secureEcom.getEci());
                et.subElement(mpi, "cavv", secureEcom.getCavv());
                et.subElement(mpi, "xid", secureEcom.getXid());
                et.subElement(mpi, "ds_trans_id", secureEcom.getDirectoryServerTransactionId());
                et.subElement(mpi, "authentication_value", secureEcom.getAuthenticationValue());
                et.subElement(mpi, "message_version", secureEcom.getMessageVersion());
                if (secureEcom.getExemptStatus() != null) {
                    et.subElement(mpi, "exempt_status", secureEcom.getExemptStatus().getValue());
                }
            }
        }
        //

        //
        if (builder.getMiscProductData() != null) {
            ArrayList productValues = builder.getMiscProductData();
            Element products = et.subElement(request, "products");
            for (Product values : productValues) {
                Element product = et.subElement(products, "product");
                et.subElement(product, "productid", values.getProductId());
                et.subElement(product, "productname", values.getProductName());
                et.subElement(product, "quantity", values.getQuantity().toString());
                et.subElement(product, "unitprice", StringUtils.toNumeric(values.getUnitPrice()));
                et.subElement(product, "gift", values.isGift() ? "true" : "false");
                et.subElement(product, "type", values.getType());
                et.subElement(product, "risk", values.getRisk());
            }
        }
        //

        //
        if (builder.getTransactionType() == TransactionType.Refund) {
            String refundHash = GenerationUtils.generateHash(refundPassword);
            if (refundHash == null)
                refundHash = "";
            et.subElement(request, "refundhash", refundHash);
        }
        //

        //
        if (builder.getStoredCredential() != null) {
            Element storedCredentialElement = et.subElement(request, "storedcredential");
            et.subElement(storedCredentialElement, "type", EnumUtils.getMapping(Target.Realex, builder.getStoredCredential().getType()));
            if (builder.getStoredCredential().getInitiator() == StoredCredentialInitiator.CardHolder) {
                et.subElement(storedCredentialElement, "initiator", EnumUtils.getMapping(Target.Realex, StoredCredentialInitiator.CardHolder));
            }
            if (builder.getStoredCredential().getInitiator() == StoredCredentialInitiator.Merchant) {
                et.subElement(storedCredentialElement, "initiator", EnumUtils.getMapping(Target.Realex, StoredCredentialInitiator.Merchant));
            }
            if (builder.getStoredCredential().getInitiator() == StoredCredentialInitiator.Scheduled) {
                et.subElement(storedCredentialElement, "initiator", EnumUtils.getMapping(Target.Realex, StoredCredentialInitiator.Scheduled));
            }
            et.subElement(storedCredentialElement, "sequence", EnumUtils.getMapping(Target.Realex, builder.getStoredCredential().getSequence()));
            et.subElement(storedCredentialElement, "srd", builder.getStoredCredential().getSchemeId());
        }
        //

        //
        if (builder.getSupplementaryData() != null) {
            Element supplementaryData = et.subElement(request, "supplementarydata");
            HashMap> suppData = builder.getSupplementaryData();

            for (String key : suppData.keySet()) {
                ArrayList dataSets = suppData.get(key);

                for (String[] data : dataSets) {
                    Element item = et.subElement(supplementaryData, "item").set("type", key);
                    for (int i = 1; i <= data.length; i++) {
                        et.subElement(item, "field" + StringUtils.padLeft(i, 2, '0'), data[i - 1]);
                    }
                }
            }
        }
        //

        //
        if (builder.getCustomerId() != null || builder.getProductId() != null || builder.getCustomerIpAddress() != null || builder.getClientTransactionId() != null || builder.getBillingAddress() != null || builder.getShippingAddress() != null) {
            Element tssInfo = et.subElement(request, "tssinfo");
            et.subElement(tssInfo, "custnum", builder.getCustomerId());
            et.subElement(tssInfo, "prodid", builder.getProductId());
            et.subElement(tssInfo, "varref", builder.getClientTransactionId());
            et.subElement(tssInfo, "custipaddress", builder.getCustomerIpAddress());

            if (builder.getBillingAddress() != null)
                tssInfo.append(buildAddress(et, builder.getBillingAddress()));
            if (builder.getShippingAddress() != null)
                tssInfo.append(buildAddress(et, builder.getShippingAddress()));
        }
        //

        // DYNAMIC DESCRIPTOR
        if (builder.getTransactionType() == TransactionType.Auth || builder.getTransactionType() == TransactionType.Capture || builder.getTransactionType() == TransactionType.Refund) {
            if (!StringUtils.isNullOrEmpty(builder.getDynamicDescriptor())) {
                Element narrative = et.subElement(request, "narrative");
                et.subElement(narrative, "chargedescription", builder.getDynamicDescriptor());
            }
        }

        String response = doTransaction(et.toString(request));
        return mapResponse(response, builder);
    }

    public String serializeRequest(AuthorizationBuilder builder) throws ApiException {
        // check for hpp config
        if (hostedPaymentConfig == null)
            throw new ApiException("Hosted configuration missing, Please check you configuration.");

        IRequestEncoder encoder = (hostedPaymentConfig.getVersion() == HppVersion.Version2) ? null : JsonEncoders.base64Encoder();
        JsonDoc request = new JsonDoc(encoder);

        String orderId = GenerationUtils.generateOrderId(builder.getOrderId());
        final String timestamp = GenerationUtils.generateTimestamp(builder.getTimestamp());

        // check for right transaction types
        if (builder.getTransactionType() != TransactionType.Sale && builder.getTransactionType() != TransactionType.Auth && builder.getTransactionType() != TransactionType.Verify)
            throw new UnsupportedTransactionException("Only Charge and Authorize are supported through hpp.");

        if (builder.getPaymentMethod() instanceof BankPayment && builder.getTransactionType() != TransactionType.Sale) {
            throw new UnsupportedTransactionException("Only Charge is supported for Bank Payment through HPP.");
        }

        request.set("MERCHANT_ID", merchantId);
        request.set("ACCOUNT", accountId);
        request.set("HPP_CHANNEL", channel);
        request.set("ORDER_ID", orderId);
        if (builder.getAmount() != null) {
            request.set("AMOUNT", StringUtils.toNumeric(builder.getAmount()));
        }
        request.set("CURRENCY", builder.getCurrency());
        request.set("TIMESTAMP", timestamp);
        request.set("AUTO_SETTLE_FLAG", (builder.getTransactionType() == TransactionType.Sale) ? "1" : builder.isMultiCapture() ? "MULTI" : "0");
        request.set("COMMENT1", builder.getDescription());
        // request.set("COMMENT2", );
        if (hostedPaymentConfig.isRequestTransactionStabilityScore() != null) {
            request.set("RETURN_TSS", hostedPaymentConfig.isRequestTransactionStabilityScore() ? "1" : "0");
        }
        if (hostedPaymentConfig.isDynamicCurrencyConversionEnabled() != null) {
            request.set("DCC_ENABLE", hostedPaymentConfig.isDynamicCurrencyConversionEnabled() ? "1" : "0");
        }
        if (builder.getHostedPaymentData() != null) {
            HostedPaymentData paymentData = builder.getHostedPaymentData();

            BlockCardType[] blockCardTypes = paymentData.getBlockCardTypes();
            if (blockCardTypes != null && blockCardTypes.length > 0) {
                StringBuilder blockCardTypesStringBuilder = new StringBuilder();
                for (BlockCardType blockCardType : blockCardTypes) {
                    blockCardTypesStringBuilder.append(blockCardType.getValue());
                    blockCardTypesStringBuilder.append("|");
                }
                blockCardTypesStringBuilder.deleteCharAt(blockCardTypesStringBuilder.length() - 1);
                request.set("BLOCK_CARD_TYPE", blockCardTypesStringBuilder.toString());
            }

            request.set("CUST_NUM", paymentData.getCustomerNumber());
            if (hostedPaymentConfig.isDisplaySavedCards() != null && paymentData.getCustomerKey() != null) {
                request.set("HPP_SELECT_STORED_CARD", paymentData.getCustomerKey());
            }
            if (paymentData.isOfferToSaveCard() != null) {
                request.set("OFFER_SAVE_CARD", paymentData.isOfferToSaveCard() ? "1" : "0");
            }
            if (paymentData.isCustomerExists() != null) {
                request.set("PAYER_EXIST", paymentData.isCustomerExists() ? "1" : "0");
            }
            if (hostedPaymentConfig.isDisplaySavedCards() == null) {
                request.set("PAYER_REF", paymentData.getCustomerKey());
            }
            request.set("PMT_REF", paymentData.getPaymentKey());
            request.set("PROD_ID", paymentData.getProductId());

            // APMs Fields
            request.set("HPP_CUSTOMER_COUNTRY", paymentData.getCustomerCountry());
            request.set("HPP_CUSTOMER_FIRSTNAME", paymentData.getCustomerFirstName());
            request.set("HPP_CUSTOMER_LASTNAME", paymentData.getCustomerLastName());
            if (!StringUtils.isNullOrEmpty(paymentData.getCustomerFirstName()) && !StringUtils.isNullOrEmpty(paymentData.getCustomerLastName())) {
                request.set("HPP_NAME", paymentData.getCustomerFirstName() + " " + paymentData.getCustomerLastName());
            }
            request.set("MERCHANT_RESPONSE_URL", paymentData.getMerchantResponseUrl());
            request.set("HPP_TX_STATUS_URL", paymentData.getTransactionStatusUrl());

            request.set("PM_METHODS", getPaymentValueList(paymentData));
            // end APMs Fields

            // 3DSv2
            request.set("HPP_CUSTOMER_EMAIL", paymentData.getCustomerEmail());
            request.set("HPP_CUSTOMER_PHONENUMBER_MOBILE", paymentData.getCustomerPhoneMobile());
            request.set("HPP_PHONE", paymentData.getCustomerPhoneMobile());
            request.set("HPP_CHALLENGE_REQUEST_INDICATOR", paymentData.getChallengeRequestIndicator() != null ? paymentData.getChallengeRequestIndicator().getValue() : null);
            request.set("HPP_ENABLE_EXEMPTION_OPTIMIZATION", builder.getHostedPaymentData().getEnableExemptionOptimization());
            if (paymentData.getAddressesMatch() != null) {
                request.set("HPP_ADDRESS_MATCH_INDICATOR", paymentData.getAddressesMatch() ? "TRUE" : "FALSE");
            }
        } else if (builder.getCustomerId() != null) {
            request.set("CUST_NUM", builder.getCustomerId());
        }

        if (builder.getShippingAddress() != null) {
            // FRAUD VALUES
            request.set("SHIPPING_CODE", generateCode(builder.getShippingAddress()));
            request.set("SHIPPING_CO", CountryUtils.getCountryCodeByCountry(builder.getShippingAddress().getCountry()));

            // 3DS2 VALUES
            request.set("HPP_SHIPPING_STREET1", builder.getShippingAddress().getStreetAddress1());
            request.set("HPP_SHIPPING_STREET2", builder.getShippingAddress().getStreetAddress2());
            request.set("HPP_SHIPPING_STREET3", builder.getShippingAddress().getStreetAddress3());
            request.set("HPP_SHIPPING_CITY", builder.getShippingAddress().getCity());
            request.set("HPP_SHIPPING_STATE", builder.getShippingAddress().getState());
            request.set("HPP_SHIPPING_POSTALCODE", builder.getShippingAddress().getPostalCode());
            request.set("HPP_SHIPPING_COUNTRY", CountryUtils.getNumericCodeByCountry(builder.getShippingAddress().getCountry()));
        }
        // HPP_ADDRESS_MATCH_INDICATOR (is shipping same as billing)

        if (builder.getBillingAddress() != null) {
            // FRAUD VALUES
            request.set("BILLING_CODE", generateCode(builder.getBillingAddress()));
            request.set("BILLING_CO", CountryUtils.getCountryCodeByCountry(builder.getBillingAddress().getCountry()));

            // 3DS2 VALUES
            request.set("HPP_BILLING_STREET1", builder.getBillingAddress().getStreetAddress1());
            request.set("HPP_BILLING_STREET2", builder.getBillingAddress().getStreetAddress2());
            request.set("HPP_BILLING_STREET3", builder.getBillingAddress().getStreetAddress3());
            request.set("HPP_BILLING_CITY", builder.getBillingAddress().getCity());
            request.set("HPP_BILLING_STATE", builder.getBillingAddress().getState());
            request.set("HPP_BILLING_POSTALCODE", builder.getBillingAddress().getPostalCode());
            request.set("HPP_BILLING_COUNTRY", CountryUtils.getNumericCodeByCountry(builder.getBillingAddress().getCountry()));
        }

        request.set("VAR_REF", builder.getClientTransactionId());
        request.set("HPP_LANG", hostedPaymentConfig.getLanguage());
        request.set("MERCHANT_RESPONSE_URL", hostedPaymentConfig.getResponseUrl());
        request.set("CARD_PAYMENT_BUTTON", hostedPaymentConfig.getPaymentButtonText());
        if (hostedPaymentConfig.isCardStorageEnabled() != null) {
            request.set("CARD_STORAGE_ENABLE", hostedPaymentConfig.isCardStorageEnabled() ? "1" : "0");
        }
        if (builder.getTransactionType() == TransactionType.Verify) {
            request.set("VALIDATE_CARD_ONLY", builder.getTransactionType() == TransactionType.Verify ? "1" : "0");
        }
        if (hostedPaymentConfig.getFraudFilterMode() != FraudFilterMode.None) {
            request.set("HPP_FRAUDFILTER_MODE", hostedPaymentConfig.getFraudFilterMode().getValue());
            if (hostedPaymentConfig.getFraudFilterRules() != null) {
                for (FraudRule fraudRule : hostedPaymentConfig.getFraudFilterRules().getRules()) {
                    request.set("HPP_FRAUDFILTER_RULE_" + fraudRule.getKey(), fraudRule.getMode().getValue());
                }
            }
        }
        if (builder.getRecurringType() != null || builder.getRecurringSequence() != null) {
            request.set("RECURRING_TYPE", builder.getRecurringType().getValue().toLowerCase());
            request.set("RECURRING_SEQUENCE", builder.getRecurringSequence().getValue().toLowerCase());
        }
        request.set("HPP_VERSION", hostedPaymentConfig.getVersion());
        request.set("HPP_POST_DIMENSIONS", hostedPaymentConfig.getPostDimensions());
        request.set("HPP_POST_RESPONSE", hostedPaymentConfig.getPostResponse());

        // SUPPLEMENTARY DATA
        if (builder.getSupplementaryData() != null) {
            for (Map.Entry> entry : builder.getSupplementaryData().entrySet()) {
                for (String[] arrayValues : entry.getValue()) {
                    if (arrayValues.length == 1) {
                        request.set(entry.getKey(), arrayValues[0]);
                    } else {
                        StringBuilder serializedValues = new StringBuilder("[");
                        for (int i = 0; i < arrayValues.length; i++) {
                            serializedValues.append(arrayValues[i]);
                            if ((i + 1) < arrayValues.length) {
                                serializedValues.append(" ,");
                            }
                        }
                        request.set(entry.getKey(), serializedValues.append("]").toString());
                    }
                }
            }
        }

        List toHash = new ArrayList(Arrays.asList(
                timestamp, merchantId, orderId,
                (builder.getAmount() != null) ? StringUtils.toNumeric(builder.getAmount()) : null,
                builder.getCurrency()));

        if (builder.getHostedPaymentData() != null) {
            if (hostedPaymentConfig.isCardStorageEnabled() != null || builder.getHostedPaymentData().isOfferToSaveCard() != null || hostedPaymentConfig.isDisplaySavedCards() != null) {
                toHash.add(builder.getHostedPaymentData().getCustomerKey() != null ? builder.getHostedPaymentData().getCustomerKey() : null);
                toHash.add(builder.getHostedPaymentData().getPaymentKey() != null ? builder.getHostedPaymentData().getPaymentKey() : null);
            }

            if (builder.getHostedPaymentData().getCaptureAddress() != null) {
                request.set("HPP_CAPTURE_ADDRESS", builder.getHostedPaymentData().getCaptureAddress().booleanValue() ? "TRUE" : "FALSE");
            }

            if (builder.getHostedPaymentData().getReturnAddress() != null) {
                request.set("HPP_DO_NOT_RETURN_ADDRESS", builder.getHostedPaymentData().getReturnAddress().booleanValue() ? "TRUE" : "FALSE");
            }

            if (builder.getHostedPaymentData().getRemoveShipping() != null) {
                request.set("HPP_REMOVE_SHIPPING", builder.getHostedPaymentData().getRemoveShipping().booleanValue() ? "TRUE" : "FALSE");
            }
        }

        if (hostedPaymentConfig.getFraudFilterMode() != null &&
                hostedPaymentConfig.getFraudFilterMode() != FraudFilterMode.None) {
            toHash.add(hostedPaymentConfig.getFraudFilterMode().getValue());
        }

        if (builder.getPaymentMethod() instanceof BankPayment) {
            BankPayment bankPaymentMethod = (BankPayment) builder.getPaymentMethod();

            request.set("HPP_OB_PAYMENT_SCHEME", bankPaymentMethod.getBankPaymentType() != null ? bankPaymentMethod.getBankPaymentType().toString() : OpenBankingProvider.getBankPaymentType(builder.getCurrency()).toString());
            request.set("HPP_OB_REMITTANCE_REF_TYPE", builder.getRemittanceReferenceType().toString());
            request.set("HPP_OB_REMITTANCE_REF_VALUE", builder.getRemittanceReferenceValue());
            request.set("HPP_OB_DST_ACCOUNT_IBAN", bankPaymentMethod.getIban());
            request.set("HPP_OB_DST_ACCOUNT_NAME", bankPaymentMethod.getAccountName());
            request.set("HPP_OB_DST_ACCOUNT_NUMBER", bankPaymentMethod.getAccountNumber());
            request.set("HPP_OB_DST_ACCOUNT_SORT_CODE", bankPaymentMethod.getSortCode());

            addMaskedData(
                    MaskValueUtil.hideValues(
                            new ElementToMask("request.HPP_OB_DST_ACCOUNT_IBAN", bankPaymentMethod.getIban()),
                            new ElementToMask("request.HPP_OB_DST_ACCOUNT_NUMBER", bankPaymentMethod.getAccountNumber(), 0, bankPaymentMethod.getAccountNumber().length() - 4)
                    )
            );

            if (builder.getHostedPaymentData() != null) {
                var hostedPaymentData = builder.getHostedPaymentData();

                request.set("HPP_OB_CUSTOMER_COUNTRIES", hostedPaymentData.getCustomerCountry());
            }


            if (!StringUtils.isNullOrEmpty(bankPaymentMethod.getSortCode())) {
                toHash.add(bankPaymentMethod.getSortCode());
            }
            if (!StringUtils.isNullOrEmpty(bankPaymentMethod.getAccountNumber())) {
                toHash.add(bankPaymentMethod.getAccountNumber());
            }
            if (!StringUtils.isNullOrEmpty(bankPaymentMethod.getIban())) {
                toHash.add(bankPaymentMethod.getIban());
            }
        }

        if (builder.getDynamicDescriptor() != null) {
            request.set("CHARGE_DESCRIPTION", builder.getDynamicDescriptor());
        }

        request.set("SHA1HASH", GenerationUtils.generateHash(sharedSecret, toHash.toArray(new String[toHash.size()])));

        return request.toString();
    }

    private String getPaymentValueList(HostedPaymentData hostedPaymentData) {

        String hosted = getHostedPaymentMethodsList(hostedPaymentData.getHostedPaymentMethods());
        String alternative = getAlternativePaymentTypeList(hostedPaymentData.getPresetPaymentMethods());

        if (StringUtils.isNullOrEmpty(alternative) && StringUtils.isNullOrEmpty(hosted)) {
            return "";
        } else if (StringUtils.isNullOrEmpty(alternative)) {
            return hosted;
        } else if (StringUtils.isNullOrEmpty(hosted)) {
            return alternative;
        } else {
            return hosted + "|" + alternative;
        }
    }

    private String getHostedPaymentMethodsList(HostedPaymentMethods[] hostedPaymentMethods) {
        if (hostedPaymentMethods == null || hostedPaymentMethods.length == 0) {
            return "";
        }

        StringBuffer hostedPaymentMethodsList = new StringBuffer();
        HostedPaymentMethods hostedPaymentMethod;
        for (int index = 0; index < hostedPaymentMethods.length; index++) {
            hostedPaymentMethod = hostedPaymentMethods[index];
            hostedPaymentMethodsList.append(hostedPaymentMethod.getValue());
            hostedPaymentMethodsList.append("|");
        }
        hostedPaymentMethodsList.deleteCharAt(hostedPaymentMethodsList.length() - 1);

        return hostedPaymentMethodsList.toString();
    }

    private String getAlternativePaymentTypeList(AlternativePaymentType[] paymentTypesKey) {
        if (paymentTypesKey == null || paymentTypesKey.length == 0) {
            return "";
        }

        StringBuffer alternativePaymentTypeList = new StringBuffer();
        AlternativePaymentType alternativePaymentType;
        for (int index = 0; index < paymentTypesKey.length; index++) {
            alternativePaymentType = paymentTypesKey[index];
            alternativePaymentTypeList.append(alternativePaymentType.getValue());
            alternativePaymentTypeList.append("|");
        }
        alternativePaymentTypeList.deleteCharAt(alternativePaymentTypeList.length() - 1);

        return alternativePaymentTypeList.toString();
    }

    private String generateCode(Address address) {
        String countryCode = CountryUtils.getCountryCodeByCountry(address.getCountry());
        switch (countryCode) {
            case "GB":
                return extractDigits(address.getPostalCode()) + (address.getStreetAddress1() != null ? "|" + extractDigits(address.getStreetAddress1()) : "");
            case "US":
            case "CA":
                return address.getPostalCode() + (address.getStreetAddress1() != null ? "|" + address.getStreetAddress1() : "");
            default:
                return null;
        }
    }

    public Transaction manageTransaction(ManagementBuilder builder) throws ApiException {
        ElementTree et = new ElementTree();
        String timestamp = GenerationUtils.generateTimestamp();
        String orderId = builder.getOrderId() != null ? builder.getOrderId() : GenerationUtils.generateOrderId();

        Element request = et.element("request")
                .set("timestamp", timestamp)
                .set("type", mapManageRequestType(builder));
        et.subElement(request, "merchantid").text(merchantId);
        et.subElement(request, "account", accountId);
        if (builder.getAmount() != null) {
            Element amtElement = et.subElement(request, "amount", StringUtils.toNumeric(builder.getAmount()));
            if (!builder.isMultiCapture()) {
                amtElement.set("currency", builder.getCurrency());
            }
        }
        if (!(builder.getPaymentMethod() instanceof AlternativePaymentMethod)) {
            et.subElement(request, "channel", channel);
        }
        et.subElement(request, "orderid", orderId);
        et.subElement(request, "pasref", builder.getTransactionId());
        String surcharge = "";
        if (builder.getSurchargeAmount() != null && !builder.getSurchargeAmount().equals("")) {
            et.subElement(request, "surchargeamount", StringUtils.toNumeric(builder.getSurchargeAmount()))
                    .set("type", builder.getCreditDebitIndicator().toString().toLowerCase());
        }
        //
        if (builder.getDccRateData() != null) {
            DccRateData dccRateData = builder.getDccRateData();

            Element dccInfo = et.subElement(request, "dccinfo");
            et.subElement(dccInfo, "ccp", dccRateData.getDccProcessor());
            et.subElement(dccInfo, "type", "1");
            et.subElement(dccInfo, "ratetype", dccRateData.getDccRateType());

            // settlement elements
            et.subElement(dccInfo, "rate", dccRateData.getCardHolderRate());
            if (dccRateData.getCardHolderAmount() != null) {
                et.subElement(dccInfo, "amount", dccRateData.getCardHolderAmount())
                        .set("currency", dccRateData.getCardHolderCurrency());
            }
        }
        //

        // Capture Authcode
        if (builder.getTransactionType() == TransactionType.Capture && builder.isMultiCapture()){
            et.subElement(request, "authcode").text(builder.getAuthorizationCode());
        }

        // payer authentication response
        if (builder.getTransactionType().equals(TransactionType.VerifySignature)) {
            et.subElement(request, "pares", builder.getPayerAuthenticationResponse());
        }

        // reason code
        if (builder.getReasonCode() != null) {
            et.subElement(request, "reasoncode").text(builder.getReasonCode().toString());
        }

        if (builder.getAlternativePaymentType() != null) {
            et.subElement(request, "paymentmethod", builder.getAlternativePaymentType());

            if (builder.getTransactionType() == TransactionType.Confirm) {
                Element paymentMethodDetails = et.subElement(request, "paymentmethoddetails");

                AlternativePaymentResponse apmResponse = ((TransactionReference) builder.getPaymentMethod()).getAlternativePaymentResponse();

                if (builder.getAlternativePaymentType() == AlternativePaymentType.PAYPAL) {
                    et.subElement(paymentMethodDetails, "Token", apmResponse.getSessionToken());
                    et.subElement(paymentMethodDetails, "PayerID", apmResponse.getProviderReference());
                }
            }
        }

        if (builder.getDescription() != null) {
            Element comments = et.subElement(request, "comments");
            et.subElement(comments, "comment", builder.getDescription()).set("id", "1");
        }

        //
        if (builder.getSupplementaryData() != null) {
            Element supplementaryData = et.subElement(request, "supplementarydata");
            HashMap> suppData = builder.getSupplementaryData();

            for (String key : suppData.keySet()) {
                ArrayList dataSets = suppData.get(key);

                for (String[] data : dataSets) {
                    Element item = et.subElement(supplementaryData, "item").set("type", key);
                    for (int i = 1; i <= data.length; i++) {
                        et.subElement(item, "field" + StringUtils.padLeft(i, 2, '0'), data[i - 1]);
                    }
                }
            }
        }
        //

        //
        if (builder.getCustomerId() != null || builder.getClientTransactionId() != null || builder.getProductId() != null) {
            Element tssInfo = et.subElement(request, "tssinfo");
            et.subElement(tssInfo, "custnum", builder.getCustomerId());
            et.subElement(tssInfo, "prodid", builder.getProductId());
            et.subElement(tssInfo, "varref", builder.getClientTransactionId());
        }
        //

        // dynamic descriptor
        if (builder.getTransactionType() == TransactionType.Capture || builder.getTransactionType() == TransactionType.Refund) {
            if (!StringUtils.isNullOrEmpty(builder.getDynamicDescriptor())) {
                Element narrative = et.subElement(request, "narrative");
                et.subElement(narrative, "chargedescription", builder.getDynamicDescriptor());
            }
        }

        et.subElement(request, "sha1hash", GenerationUtils.generateHash(sharedSecret, timestamp, merchantId, orderId, StringUtils.toNumeric(builder.getAmount()), builder.getCurrency(), builder.getAlternativePaymentType() != null ? builder.getAlternativePaymentType().getValue() : null));

        if (builder.getTransactionType() == TransactionType.Refund) {
            if (builder.getAuthorizationCode() != null) {
                et.subElement(request, "authcode").text(builder.getAuthorizationCode());
            }
            et.subElement(request, "refundhash", GenerationUtils.generateHash(builder.getAlternativePaymentType() != null ? refundPassword : rebatePassword));
        }

        String response = doTransaction(et.toString(request));
        return mapResponse(response, builder);
    }

    public  TResult processReport(ReportBuilder builder, Class clazz) throws ApiException {
        ElementTree et = new ElementTree();
        String timestamp = GenerationUtils.generateTimestamp();

        // build request
        Element request = et.element("request")
                .set("type", mapReportType(builder.getReportType()))
                .set("timestamp", timestamp);
        et.subElement(request, "merchantid").text(merchantId);
        et.subElement(request, "account", accountId);

        if (builder instanceof TransactionReportBuilder) {
            TransactionReportBuilder trb = (TransactionReportBuilder) builder;
            et.subElement(request, "orderid", trb.getTransactionId());

            String sha1hash = GenerationUtils.generateHash(sharedSecret, timestamp, merchantId, trb.getTransactionId(), "", "", "");
            et.subElement(request, "sha1hash").text(sha1hash);
        }

        String response = doTransaction(et.toString(request));
        return mapReportResponse(response, builder.getReportType(), clazz);
    }

    @Override
    public  T surchargeEligibilityLookup(SurchargeEligibilityBuilder builder, Class clazz) throws ApiException {
        throw new UnsupportedTransactionException();
    }

    public  TResult processRecurring(RecurringBuilder builder, Class clazz) throws ApiException {
        ElementTree et = new ElementTree();
        String timestamp = GenerationUtils.generateTimestamp();
        String orderId = builder.getOrderId() != null ? builder.getOrderId() : GenerationUtils.generateOrderId();

        // Build Request
        Element request = et.element("request")
                .set("timestamp", timestamp)
                .set("type", mapRecurringRequestType(builder));
        et.subElement(request, "merchantid").text(merchantId);
        et.subElement(request, "channel", channel);
        et.subElement(request, "account", accountId);

        if (builder.getTransactionType() == TransactionType.Create || builder.getTransactionType() == TransactionType.Edit) {
            if (builder.getEntity() instanceof Customer) {
                et.subElement(request, "orderid", orderId);

                Customer customer = (Customer) builder.getEntity();
                request.append(buildCustomer(et, customer));
                et.subElement(request, "sha1hash").text(GenerationUtils.generateHash(sharedSecret, timestamp, merchantId, orderId, null, null, customer.getKey()));
            } else if (builder.getEntity() instanceof RecurringPaymentMethod) {
                et.subElement(request, "orderid", orderId);

                RecurringPaymentMethod payment = (RecurringPaymentMethod) builder.getEntity();
                Element cardElement = et.subElement(request, "card");
                et.subElement(cardElement, "ref").text(payment.getKey());
                et.subElement(cardElement, "payerref").text(payment.getCustomerKey());

                CreditCardData card = (CreditCardData) payment.getPaymentMethod();
                String expiry = card.getShortExpiry();

                if (payment.getPaymentMethod() != null) {
                    et.subElement(cardElement, "number").text(card.getNumber());
                    et.subElement(cardElement, "expdate").text(expiry);
                    et.subElement(cardElement, "chname").text(card.getCardHolderName());
                    et.subElement(cardElement, "type").text(mapCardType(getBaseCardType(card.getCardType())));

                    addMaskedData(MaskValueUtil.hideValues(
                            new ElementToMask("request.card.number", card.getNumber(), 4, 6),
                            new ElementToMask("request.card.expdate", card.getShortExpiry())
                    ));
                }

                if (payment.getStoredCredential() != null) {
                    Element storedCredentialElement = et.subElement(request, "storedcredential");
                    et.subElement(storedCredentialElement, "srd", payment.getStoredCredential().getSchemeId());
                }

                String sha1hash;
                if (builder.getTransactionType() == TransactionType.Create)
                    sha1hash = GenerationUtils.generateHash(sharedSecret, timestamp, merchantId, orderId, null, null, payment.getCustomerKey(), card.getCardHolderName(), card.getNumber());
                else
                    sha1hash = GenerationUtils.generateHash(sharedSecret, timestamp, merchantId, payment.getCustomerKey(), payment.getKey(), expiry, card.getNumber());
                et.subElement(request, "sha1hash").text(sha1hash);
            }
            //Schedule
            else if (builder.getEntity() instanceof Schedule) {
                var schedule = (Schedule) builder.getEntity();
                var amount = StringUtils.toNumeric(schedule.getAmount());
                var frequency = MapScheduleFrequency(schedule.getFrequency());
                var hash = GenerationUtils.generateHash(sharedSecret, timestamp, merchantId, schedule.getId(), amount, schedule.getCurrency(), schedule.getCustomerKey(), frequency);

                et.subElement(request, "scheduleref", schedule.getId());
                et.subElement(request, "alias", schedule.getName());
                et.subElement(request, "orderidstub", schedule.getOrderPrefix());
                et.subElement(request, "transtype", "auth");
                et.subElement(request, "schedule", frequency);

                if (schedule.getStartDate() != null) {
                    et.subElement(request, "startdate", schedule.getStartDate() != null ? SDF.format(schedule.getStartDate()) : null);
                }
                et.subElement(request, "numtimes", schedule.getNumberOfPayments());
                if (schedule.getEndDate() != null) {
                    et.subElement(request, "enddate", schedule.getEndDate() != null ? SDF.format(schedule.getEndDate()) : null);
                }
                et.subElement(request, "payerref", schedule.getCustomerKey());
                et.subElement(request, "paymentmethod", schedule.getPaymentKey());
                if (!StringUtils.isNullOrEmpty(amount)) {
                    et.subElement(request, "amount", amount)
                            .set("currency", schedule.getCurrency());
                }

                et.subElement(request, "prodid", schedule.getProductId() != null ? schedule.getProductId() : "");
                et.subElement(request, "varref", schedule.getPoNumber() != null ? schedule.getPoNumber() : "");
                et.subElement(request, "custno", schedule.getCustomerNumber());
                et.subElement(request, "comment", schedule.getDescription());
                et.subElement(request, "sha1hash").text(hash);
            }
        } else if (builder.getTransactionType() == TransactionType.Delete) {
            if (builder.getEntity() instanceof RecurringPaymentMethod) {
                RecurringPaymentMethod payment = (RecurringPaymentMethod) builder.getEntity();
                Element cardElement = et.subElement(request, "card");
                et.subElement(cardElement, "ref").text(payment.getKey());
                et.subElement(cardElement, "payerref").text(payment.getCustomerKey());

                String sha1hash = GenerationUtils.generateHash(sharedSecret, timestamp, merchantId, payment.getCustomerKey(), payment.getKey() == null ? payment.getId() : payment.getKey());
                et.subElement(request, "sha1hash").text(sha1hash);
            }
            //Schedule
            else if (builder.getEntity() instanceof Schedule) {
                var schedule = (Schedule) builder.getEntity();
                et.subElement(request, "scheduleref", schedule.getKey());
                var hash = GenerationUtils.generateHash(sharedSecret, timestamp, merchantId, schedule.getKey());
                et.subElement(request, "sha1hash").text(hash);
            }
        } else if (builder.getTransactionType() == TransactionType.Fetch) {
            if (builder.getEntity() instanceof Schedule) {
                var scheduleRef = builder.getEntity().getKey();

                et.subElement(request, "scheduleref", scheduleRef);

                String sha1hash = GenerationUtils.generateHash(sharedSecret, timestamp, merchantId, scheduleRef);
                et.subElement(request, "sha1hash").text(sha1hash);
            }
        } else if (builder.getTransactionType() == TransactionType.Search) {
            if (builder.getEntity() instanceof Schedule) {
                String customerKey = "", paymentKey = "";

                if (builder.getSearchCriteria().containsKey(SearchCriteria.CustomerId.toString())) {
                    customerKey = builder.getSearchCriteria().get(SearchCriteria.CustomerId.toString());
                    et.subElement(request, "payerref", customerKey);
                }

                if (builder.getSearchCriteria().containsKey(SearchCriteria.PaymentMethodKey.toString())) {
                    paymentKey = builder.getSearchCriteria().get(SearchCriteria.PaymentMethodKey.toString());
                    et.subElement(request, "paymentmethod", paymentKey);
                }

                var hash = GenerationUtils.generateHash(sharedSecret, timestamp, merchantId, customerKey, paymentKey);

                et.subElement(request, "sha1hash").text(hash);
            }
        }

        String response = doTransaction(et.toString(request));
        return mapRecurringResponse(response, builder);
    }

    private String MapScheduleFrequency(ScheduleFrequency frequency) {
        if (frequency == null) {
            return null;
        }

        switch (frequency) {
            case BiMonthly:
                return "bimonthly";
            case SemiAnnually:
                return "halfyearly";
            case Annually:
                return "yearly";
            default:
                return frequency.getValue();
        }
    }

    private Transaction mapResponse(String rawResponse, TransactionBuilder builder) throws ApiException {
        Element root = ElementTree.parse(rawResponse).get("response");

        List acceptedCodes = new ArrayList<>();
        if (builder instanceof AuthorizationBuilder) {
            acceptedCodes = mapAcceptedCodes(mapAuthRequestType((AuthorizationBuilder) builder));
        } else if (builder instanceof ManagementBuilder) {
            acceptedCodes = mapAcceptedCodes(mapManageRequestType((ManagementBuilder) builder));
        }

        checkResponse(root, acceptedCodes);
        Transaction result = new Transaction();
        result.setResponseCode(root.getString("result"));
        result.setResponseMessage(root.getString("message"));
        result.setCvnResponseCode(root.getString("cvnresult"));
        result.setAvsResponseCode(root.getString("avspostcoderesponse"));
        result.setTimestamp(root.getAttributeString("timestamp"));

        TransactionReference transReference = new TransactionReference();
        transReference.setAuthCode(root.getString("authcode"));
        transReference.setOrderId(root.getString("orderid"));
        transReference.setPaymentMethodType(PaymentMethodType.Credit);
        transReference.setTransactionId(root.getString("pasref"));
        transReference.setAlternativePaymentType(root.getString("paymentmethod"));
        transReference.setBatchNumber(root.getInt("batchid"));
        result.setTransactionReference(transReference);

        // alternativePaymentResponse
        Element paymentMethodDetails = root.get("paymentmethoddetails");

        if (paymentMethodDetails != null) {

            AlternativePaymentResponse alternativePaymentResponse = new AlternativePaymentResponse();

            alternativePaymentResponse.setPaymentMethod(paymentMethodDetails.getString("paymentmethod"));
            alternativePaymentResponse.setBankAccount(paymentMethodDetails.getString("bankaccount"));
            alternativePaymentResponse.setAccountHolderName(paymentMethodDetails.getString("accountholdername"));
            alternativePaymentResponse.setCountry(paymentMethodDetails.getString("country"));
            alternativePaymentResponse.setRedirectUrl(paymentMethodDetails.getString("redirecturl"));
            alternativePaymentResponse.setPaymentPurpose(paymentMethodDetails.getString("paymentpurpose"));
            alternativePaymentResponse.setPaymentMethod(paymentMethodDetails.getString("paymentmethod"));
            alternativePaymentResponse.setProviderName(root.getString("paymentmethod"));

            Element apmResponseDetails = null;
            if (paymentMethodDetails.getElement() != null && paymentMethodDetails.get("SetExpressCheckoutResponse").getElement() != null) {
                apmResponseDetails = paymentMethodDetails.get("SetExpressCheckoutResponse");
            } else if (paymentMethodDetails.getElement() != null && paymentMethodDetails.get("DoExpressCheckoutPaymentResponse").getElement() != null) {
                apmResponseDetails = paymentMethodDetails.get("DoExpressCheckoutPaymentResponse");
            }

            if (apmResponseDetails != null) {
                alternativePaymentResponse.setSessionToken(apmResponseDetails.getString("Token"));
                alternativePaymentResponse.setAck(apmResponseDetails.getString("Ack"));
                alternativePaymentResponse.setTimeCreatedReference(apmResponseDetails.getDateTime("Timestamp"));
                alternativePaymentResponse.setCorrelationReference(apmResponseDetails.getString("CorrelationID"));
                alternativePaymentResponse.setVersionReference(apmResponseDetails.getString("Version"));
                alternativePaymentResponse.setBuildReference(apmResponseDetails.getString("Build"));

                Element paymentInfo = apmResponseDetails.get("PaymentInfo");
                if (paymentInfo != null) {
                    alternativePaymentResponse.setTransactionReference(paymentInfo.getString("TransactionID"));
                    alternativePaymentResponse.setPaymentType(paymentInfo.getString("PaymentType"));
                    alternativePaymentResponse.setPaymentTimeReference(paymentInfo.getDateTime("PaymentDate"));
                    alternativePaymentResponse.setGrossAmount(paymentInfo.getDecimal("GrossAmount"));
                    alternativePaymentResponse.setFeeAmount(paymentInfo.getDecimal("TaxAmount"));
                    alternativePaymentResponse.setPaymentStatus(paymentInfo.getString("PaymentStatus"));
                    alternativePaymentResponse.setPendingReason(paymentInfo.getString("PendingReason"));
                    alternativePaymentResponse.setReasonCode(paymentInfo.getString("ReasonCode"));
                    alternativePaymentResponse.setAuthProtectionEligibility(paymentInfo.getString("ProtectionEligibility"));
                    alternativePaymentResponse.setAuthProtectionEligibilityType(paymentInfo.getString("ProtectionEligibilityType"));
                }
            }

            result.setAlternativePaymentResponse(alternativePaymentResponse);
        }

        // fraud response
        if (root.has("fraudresponse")) {
            Element fraudResponseElement = root.get("fraudresponse");

            FraudResponse fraudResponse =
                    new FraudResponse()
                            .setMode(FraudFilterMode.fromString(fraudResponseElement.getAttributeString("mode")))
                            .setResult(fraudResponseElement.getString("result"));

            if (fraudResponseElement.has("rules")) {
                for (Element rule : fraudResponseElement.get("rules").getAll("rule")) {
                    fraudResponse.getRules().add((
                            new FraudResponse.Rule()
                                    .setName(rule.getAttributeString("name"))
                                    .setId(rule.getAttributeString("id"))
                                    .setAction(rule.getString("action"))));
                }
            }

            result.setFraudResponse(fraudResponse);
        }

        if (builder instanceof ManagementBuilder) {
            ManagementBuilder mb = (ManagementBuilder) builder;
            if (mb.isMultiCapture()) {
                result.setMultiCapturePaymentCount(mb.getMultiCapturePaymentCount());
                result.setMultiCaptureSequence(mb.getMultiCaptureSequence());
            }
        }

        // dccinfo
        if (root.has("dccinfo")) {
            DccRateData dccRateData = new DccRateData();
            if (builder instanceof AuthorizationBuilder && ((AuthorizationBuilder) builder).getDccRateData() != null) {
                dccRateData = ((AuthorizationBuilder) builder).getDccRateData();
            }

            dccRateData.setCardHolderCurrency(root.getString("cardholdercurrency"));
            dccRateData.setCardHolderAmount(root.getDecimal("cardholderamount"));
            dccRateData.setCardHolderRate(root.getString("cardholderrate"));
            dccRateData.setMerchantCurrency(root.getString("merchantcurrency"));
            dccRateData.setMerchantAmount(root.getDecimal("merchantamount"));
            dccRateData.setMarginRatePercentage(root.getString("marginratepercentage"));
            dccRateData.setExchangeRateSourceName(root.getString("exchangeratesourcename"));
            dccRateData.setCommissionPercentage(root.getString("commissionpercentage"));
            dccRateData.setExchangeRateSourceTimestamp(root.getDateTime(DateTimeFormat.forPattern("yyyyMMdd HH:mm"), "exchangeratesourcetimestamp"));
            result.setDccRateData(dccRateData);
        }

        // 3d secure enrolled
        if (root.has("enrolled")) {
            ThreeDSecure secureEcom = new ThreeDSecure();
            secureEcom.setEnrolled(root.getString("enrolled").equals("Y"));
            secureEcom.setPayerAuthenticationRequest(root.getString("pareq"));
            secureEcom.setXid(root.getString("xid"));
            secureEcom.setIssuerAcsUrl(root.getString("url"));
            result.setThreeDsecure(secureEcom);
        }

        // three d secure
        if (root.has("threedsecure")) {
            ThreeDSecure secureEcom = new ThreeDSecure();
            secureEcom.setStatus(root.getString("status"));
            secureEcom.setEci(root.getString("eci"));
            secureEcom.setXid(root.getString("xid"));
            secureEcom.setCavv(root.getString("cavv"));
            secureEcom.setAlgorithm(root.getInt("algorithm"));
            result.setThreeDsecure(secureEcom);
        }

        // stored credential
        result.setSchemeId(root.getString("srd"));

        return result;
    }

    @SuppressWarnings("unchecked")
    private  TResult mapReportResponse(String rawResponse, ReportType reportType, Class clazz) throws ApiException {
        Element response = ElementTree.parse(rawResponse).get("response");
        checkResponse(response);

        try {
            TResult rvalue = clazz.newInstance();
            if (reportType.equals(ReportType.TransactionDetail)) {
                TransactionSummary summary = new TransactionSummary();
                summary.setTransactionId(response.getString("pasref"));
                summary.setClientTransactionId(response.getString("orderid"));
                summary.setOrderId(response.getString("orderid"));
                summary.setAuthCode(response.getString("authcode"));
                summary.setMaskedCardNumber(response.getString("cardnumber"));
                summary.setAvsResponseCode(response.getString("avspostcoderesponse"));
                summary.setCvnResponseCode(response.getString("cvnresult"));
                summary.setGatewayResponseCode(response.getString("result"));
                summary.setGatewayResponseMessage(response.getString("message"));
                summary.setBatchId(response.getString("batchid"));
                summary.setSchemeReferenceData(response.getString("srd"));

                if (response.has("fraudresponse")) {
                    Element fraud = response.get("fraudresponse");
                    summary.setFraudRuleInfo(fraud.getString("result"));
                }

                if (response.has("threedsecure")) {
                    summary.setCavvResponseCode(response.getString("cavv"));
                    summary.setEciIndicator(response.getString("eci"));
                    summary.setXid(response.getString("xid"));
                }

                rvalue = (TResult) summary;
            }
            return rvalue;
        } catch (Exception e) {
            throw new ApiException(e.getMessage(), e);
        }
    }

    @SuppressWarnings("unchecked")
    private  TResult mapRecurringResponse(String rawResponse, RecurringBuilder builder) throws ApiException {
        Element root = ElementTree.parse(rawResponse).get("response");

        // check response
        checkResponse(root);

        switch (builder.getTransactionType()) {
            case Create:
            case Edit:
            case Delete:
            case Fetch:
                if (builder.getEntity() instanceof Schedule) {
                    try {
                        builder.setEntity(mapScheduleResponse(root, (Schedule) builder.getEntity()));
                    } catch (ParseException pe) {
                        throw new ApiException(pe.getMessage(), pe);
                    }
                }
                break;
            case Search:
                if (builder instanceof RecurringBuilder && builder.getEntity() instanceof Schedule) {
                    try {
                        return ((TResult) mapSchedulesSearchResponse(root));
                    } catch (ParseException pe) {
                        throw new ApiException(pe.getMessage(), pe);
                    }
                }
                break;

            default:
                break;
        }

        return (TResult) builder.getEntity();
    }

    private RecurringCollection mapSchedulesSearchResponse(Element root) throws ParseException {
        RecurringCollection schedulesResponse = new RecurringCollection<>();

        if (root.has("schedules")) {
            for (Element schedule : root.get("schedules").getAll("schedule")) {
                if (schedule.has("scheduleref")) {
                    schedulesResponse.add(mapScheduleResponse(schedule, new Schedule()));
                }
            }
        }

        return schedulesResponse;
    }

    private Schedule mapScheduleResponse(Element root, Schedule entity) throws ParseException {
        var schedule = entity;

        schedule.setAmount(root.getString("amount") != null ? StringUtils.toAmount(root.getString("amount")) : null);
        schedule.setCurrency(root.getString("currency") != null ? root.getString("currency") : null);
        schedule.setId(root.getString("scheduleref") != null ? root.getString("scheduleref") : null);
        schedule.setKey(root.getString("scheduleref") != null ? root.getString("scheduleref") : null);
        schedule.setName(root.getString("alias") != null ? root.getString("alias") : null);
        schedule.setCustomerKey(root.getString("payerref") != null ? root.getString("payerref") : null);
        schedule.setNumberOfPayments(root.getString("numtimes") != null ? Integer.valueOf(root.getString("numtimes")) : null);
        schedule.setCustomerNumber(root.getString("custno") != null ? root.getString("custno") : null);
        schedule.setStartDate(!StringUtils.isNullOrEmpty(root.getString("startdate")) ? SDF.parse(root.getString("startdate")) : null);
        schedule.setEndDate(!StringUtils.isNullOrEmpty(root.getString("enddate")) ? SDF.parse(root.getString("enddate")) : null);
        schedule.setDescription(root.getString("comment") != null ? root.getString("comment") : null);
        schedule.setResponseCode(root.getString("result") != null ? root.getString("result") : null);
        schedule.setResponseMessage(root.getString("message") != null ? root.getString("message") : null);

        return schedule;
    }

    private void checkResponse(Element root) throws GatewayException {
        checkResponse(root, null);
    }

    private void checkResponse(Element root, List acceptCodes) throws GatewayException {
        if (acceptCodes == null) {
            acceptCodes = new ArrayList();
            acceptCodes.add("00");
        }

        String responseCode = root.getString("result");
        String responseMessage = root.getString("message");
        if (!acceptCodes.contains(responseCode)) {
            throw new GatewayException(String.format("Unexpected Gateway Response: %s - %s", responseCode, responseMessage), responseCode, responseMessage);
        }
    }

    private String mapAuthRequestType(AuthorizationBuilder builder) throws ApiException {
        TransactionType trans = builder.getTransactionType();
        IPaymentMethod payment = builder.getPaymentMethod();

        switch (trans) {
            case Sale:
            case Auth: {
                if (payment instanceof Credit) {
                    if (builder.getTransactionModifier().equals(TransactionModifier.Offline)) {
                        if (builder.getPaymentMethod() != null) {
                            return "manual";
                        }
                        return "offline";
                    } else if (builder.getTransactionModifier().equals(TransactionModifier.EncryptedMobile)) {
                        return "auth-mobile";
                    }
                    return "auth";
                } else if (payment instanceof AlternativePaymentMethod) {
                    return "payment-set";
                }
                return "receipt-in";
            }
            case Capture: {
                return "settle";
            }
            case Verify: {
                if (payment instanceof RecurringPaymentMethod) {
                    return "receipt-in-otb";
                }
                return "otb";
            }
            case Refund: {
                if (payment instanceof RecurringPaymentMethod) {
                    return "payment-out";
                }
                return "credit";
            }
            case DccRateLookup: {
                if (payment instanceof RecurringPaymentMethod) {
                    return "realvault-dccrate";
                }
                return "dccrate";
            }
            case VerifyEnrolled: {
                if (payment instanceof RecurringPaymentMethod) {
                    return "realvault-3ds-verifyenrolled";
                }
                return "3ds-verifyenrolled";
            }
            case Reversal: {
                throw new UnsupportedTransactionException();
            }
            default: {
                return "unknown";
            }
        }
    }

    private String mapManageRequestType(ManagementBuilder builder) {
        TransactionType trans = builder.getTransactionType();

        switch (trans) {
            case Capture:
                if (builder.isMultiCapture())
                    return "multisettle";
                else
                    return "settle";
            case Hold:
                return "hold";
            case Refund:
                if (builder.getAlternativePaymentType() != null)
                    return "payment-credit";
                return "rebate";
            case Release:
                return "release";
            case Void:
            case Reversal:
                return "void";
            case VerifySignature:
                return "3ds-verifysig";
            case Confirm:
                return "payment-do";
            default:
                return "unknown";
        }
    }

    private String mapReportType(ReportType reportType) throws ApiException {
        switch (reportType) {
            case TransactionDetail:
                return "query";
            default:
                throw new UnsupportedTransactionException("This reporting call is not supported by your currently configured gateway.");
        }
    }

    @SuppressWarnings("unchecked")
    private  String mapRecurringRequestType(RecurringBuilder builder) throws UnsupportedTransactionException {
        TResult entity = (TResult) builder.getEntity();
        switch (builder.getTransactionType()) {
            case Create:
                if (entity instanceof Customer) {
                    return "payer-new";
                } else if (entity instanceof IPaymentMethod) {
                    return "card-new";
                } else if (entity instanceof Schedule) {
                    return "schedule-new";
                }
                throw new UnsupportedTransactionException();
            case Edit:
                if (entity instanceof Customer) {
                    return "payer-edit";
                } else if (entity instanceof IPaymentMethod) {
                    return "card-update-card";
                }
                throw new UnsupportedTransactionException();
            case Delete:
                if (entity instanceof RecurringPaymentMethod) {
                    return "card-cancel-card";
                } else if (entity instanceof Schedule) {
                    return "schedule-delete";
                }
                throw new UnsupportedTransactionException();
            case Fetch:
                if (entity instanceof Schedule) {
                    return "schedule-get";
                }
                throw new UnsupportedTransactionException();
            case Search:
                if (builder instanceof RecurringBuilder) {
                    return "schedule-search";
                }
                throw new UnsupportedTransactionException();
            default:
                throw new UnsupportedTransactionException();
        }
    }

    private String mapCardType(String cardType) {
        for (var map : mapCardType.keySet()) {
            if (cardType.equals(map)) {
                return mapCardType.get(map);
            }
        }
        return cardType;
    }

    private List mapAcceptedCodes(String transactionType) {
        switch (transactionType) {
            case "3ds-verifysig":
            case "3ds-verifyenrolled":
                return Arrays.asList("00", "110");
            case "payment-set":
                return Arrays.asList("01", "00");
            default:
                return Arrays.asList("00");
        }
    }

    public void buildAlternativePaymentMethod(AuthorizationBuilder builder, Element request, ElementTree et) {
        AlternativePaymentMethod apm = (AlternativePaymentMethod) builder.getPaymentMethod();

        et.subElement(request, "paymentmethod", apm.getAlternativePaymentMethodType().getValue());

        Element paymentmethoddetails = et.subElement(request, "paymentmethoddetails");

        List apmUrls = this.mapAPMUrls(apm.getAlternativePaymentMethodType());
        String returnUrl = apmUrls.get(0);
        String statusUpdateUrl = apmUrls.get(1);
        String cancelUrl = apmUrls.get(2);

        et.subElement(paymentmethoddetails, returnUrl, apm.getReturnUrl());
        et.subElement(paymentmethoddetails, statusUpdateUrl, apm.getStatusUpdateUrl());
        et.subElement(paymentmethoddetails, cancelUrl, apm.getCancelUrl());

        et.subElement(paymentmethoddetails, "descriptor", apm.getDescriptor());
        et.subElement(paymentmethoddetails, "country", apm.getCountry());
        et.subElement(paymentmethoddetails, "accountholdername", apm.getAccountHolderName());
    }

    private List mapAPMUrls(AlternativePaymentType paymentMethodType) {
        switch (paymentMethodType) {
            case PAYPAL:
                return Arrays.asList("ReturnURL", "StatusUpdateURL", "CancelURL");
            default:
                return Arrays.asList("returnurl", "statusupdateurl", "cancelurl");
        }
    }

    private Element buildCustomer(ElementTree et, Customer customer) {
        Element payer = et.element("payer")
                .set("ref", GenerationUtils.generateRecurringKey(customer.getKey()))
                .set("type", "Retail");
        et.subElement(payer, "title", customer.getTitle());
        et.subElement(payer, "firstname", customer.getFirstName());
        et.subElement(payer, "surname", customer.getLastName());
        et.subElement(payer, "company", customer.getCompany());

        if (customer.getAddress() != null) {
            Address addy = customer.getAddress();
            Element address = et.subElement(payer, "address");
            et.subElement(address, "line1", addy.getStreetAddress1());
            et.subElement(address, "line2", addy.getStreetAddress2());
            et.subElement(address, "line3", addy.getStreetAddress3());
            et.subElement(address, "city", addy.getCity());
            et.subElement(address, "county", addy.getProvince());
            et.subElement(address, "postcode", addy.getPostalCode());
            Element country = et.subElement(address, "country", customer.getAddress().getCountry());
            if (country != null)
                country.set("code", customer.getAddress().getCountryCode());
        }

        Element phone = et.subElement(payer, "phonenumbers");
        et.subElement(phone, "home", customer.getHomePhone());
        et.subElement(phone, "work", customer.getWorkPhone());
        et.subElement(phone, "fax", customer.getFax());
        et.subElement(phone, "mobile", customer.getMobilePhone());

        et.subElement(payer, "email", customer.getEmail());

        // comments
        return payer;
    }

    private Element buildAddress(ElementTree et, Address address) {
        if (address == null)
            return null;

        String code = address.getPostalCode();
        if (!StringUtils.isNullOrEmpty(code) && !code.contains("|")) {
            if (address.getStreetAddress1() != null) {
                code = String.format("%s|%s", address.getPostalCode(), address.getStreetAddress1());
            } else {
                code = String.format("%s|", address.getPostalCode());
            }
            if (address.isCountry("GB"))
                if (address.getStreetAddress1() != null) {
                    code = String.format("%s|%s", address.getPostalCode().replaceAll("[^0-9]", ""), address.getStreetAddress1().replaceAll("[^0-9]", ""));
                } else {
                    code = String.format("%s|", address.getPostalCode().replaceAll("[^0-9]", ""));
                }
        }

        Element addressNode = et.element("address").set("type", address.getType().equals(AddressType.Billing) ? "billing" : "shipping");
        et.subElement(addressNode, "code").text(code);
        et.subElement(addressNode, "country").text(address.getCountryCode());

        return addressNode;
    }

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




© 2015 - 2024 Weber Informatics LLC | Privacy Policy