/*******************************************************************************
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 *******************************************************************************/
package org.apache.ofbiz.accounting.thirdparty.verisign;

import java.math.BigDecimal;
import java.util.Date;
import java.util.Iterator;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang.StringUtils;
import org.apache.ofbiz.accounting.payment.PaymentGatewayServices;
import org.apache.ofbiz.base.util.Debug;
import org.apache.ofbiz.base.util.StringUtil;
import org.apache.ofbiz.base.util.UtilDateTime;
import org.apache.ofbiz.base.util.UtilMisc;
import org.apache.ofbiz.base.util.UtilProperties;
import org.apache.ofbiz.base.util.UtilValidate;
import org.apache.ofbiz.base.util.string.FlexibleStringExpander;
import org.apache.ofbiz.entity.Delegator;
import org.apache.ofbiz.entity.GenericEntityException;
import org.apache.ofbiz.entity.GenericValue;
import org.apache.ofbiz.entity.util.EntityQuery;
import org.apache.ofbiz.entity.util.EntityUtilProperties;
import org.apache.ofbiz.order.order.OrderReadHelper;
import org.apache.ofbiz.order.shoppingcart.ShoppingCart;
import org.apache.ofbiz.order.shoppingcart.ShoppingCartItem;
import org.apache.ofbiz.product.store.ProductStoreWorker;
import org.apache.ofbiz.service.DispatchContext;
import org.apache.ofbiz.service.GenericServiceException;
import org.apache.ofbiz.service.LocalDispatcher;
import org.apache.ofbiz.service.ServiceUtil;

import paypal.payflow.PayflowAPI;
import paypal.payflow.SDKProperties;
/**
 * PayflowPro - Verisign PayFlow Pro <=> OFBiz Service Module
 */
public class PayflowPro {

    public static final String module = PayflowPro.class.getName();
    public final static String resource = "AccountingUiLabels";

    /**
     * Authorize credit card payment service. Service wrapper around PayFlow Pro API.
     * @param dctx Service Engine DispatchContext.
     * @param context Map context of parameters.
     * @return Response map, including RESPMSG, and RESULT keys.
     */
    public static Map<String, Object> ccProcessor(DispatchContext dctx, Map<String, ? extends Object> context) {
        Delegator delegator = dctx.getDelegator();
        GenericValue paymentPref = (GenericValue) context.get("orderPaymentPreference");
        GenericValue authTrans = (GenericValue) context.get("authTrans");
        String orderId = (String) context.get("orderId");
        String cvv2 = (String) context.get("cardSecurityCode");
        BigDecimal processAmount = (BigDecimal) context.get("processAmount");
        GenericValue party = (GenericValue) context.get("billToParty");
        GenericValue cc = (GenericValue) context.get("creditCard");
        GenericValue payPalPaymentMethod = (GenericValue) context.get("payPalPaymentMethod");
        GenericValue ps = (GenericValue) context.get("billingAddress");
        String paymentGatewayConfigId = (String) context.get("paymentGatewayConfigId");
        String configString = (String) context.get("paymentConfig");

        if (configString == null) {
            configString = "payment.properties";
        }

        boolean isPayPal = false;
        // Are we doing a cc or a paypal payment?
        if ("EXT_PAYPAL".equals(paymentPref.getString("paymentMethodTypeId"))) {
            isPayPal = true;
        }

        Map<String, String> data = new HashMap<String, String>();

        boolean isReAuth = false;
        if (isPayPal) {
            data.put("TRXTYPE", "A");
            data.put("TENDER", "P");
            data.put("ORIGID", payPalPaymentMethod.getString("transactionId"));
        } else {
            if (authTrans == null) {
                authTrans = PaymentGatewayServices.getAuthTransaction(paymentPref);
            }

            // set the orderId as comment1 so we can query in PF Manager
            data.put("COMMENT1", orderId);
            data.put("PONUM", orderId);
            data.put("CUSTCODE", party.getString("partyId"));

            // transaction type
            if (comparePaymentGatewayConfigValue(delegator, paymentGatewayConfigId, "preAuth", configString, "payment.verisign.preAuth",  "Y")) {
                data.put("TRXTYPE", "A");
                // only support re-auth for auth types; sale types don't do it
                if (authTrans != null) {
                    String refNum = authTrans.getString("referenceNum");
                    data.put("ORIGID", refNum);
                    isReAuth = true;
                }
            } else {
                data.put("TRXTYPE", "S");
            }

            // credit card tender
            data.put("TENDER", "C");

            // card security code
            if (UtilValidate.isNotEmpty(cvv2)) {
                data.put("CVV2", cvv2);
            }

            // get the payment information
            data.put("ACCT", cc.getString("cardNumber"));

            // name on card
            String name = cc.getString("firstNameOnCard") + " " + cc.getString("lastNameOnCard");
            data.put("FIRSTNAME", cc.getString("firstNameOnCard"));
            data.put("LASTNAME", cc.getString("lastNameOnCard"));
            data.put("COMMENT2", name);
            if (cc.get("expireDate") != null) {
                String exp = cc.getString("expireDate");
                String expDate = exp.substring(0, 2);

                expDate = expDate + exp.substring(exp.length() - 2);
                data.put("EXPDATE", expDate);
            }

            // gather the address info
            if (ps != null) {
                String street = ps.getString("address1") + ((UtilValidate.isNotEmpty(ps.getString("address2"))) ? " " + ps.getString("address2") : "");
                data.put("STREET"+"["+street.length()+"]", street);
                data.put("ZIP", ps.getString("postalCode"));
            }
        }

        // set the amount
        data.put("AMT", processAmount.toString());

        PayflowAPI pfp = init(delegator, paymentGatewayConfigId, configString, context);

        // get the base params
        StringBuilder params = makeBaseParams(delegator, paymentGatewayConfigId, configString);

        // parse the context parameters
        params.append("&").append(parseContext(data));

        // transmit the request
        //if (Debug.verboseOn()) Debug.logVerbose("Sending to Verisign: " + params.toString(), module);
        String resp;
        if (!comparePaymentGatewayConfigValue(delegator, paymentGatewayConfigId, "enableTransmit", configString, "payment.verisign.enable_transmit",  "false")) {
            resp = pfp.submitTransaction(params.toString(), pfp.generateRequestId());
        } else {
            resp = "RESULT=0&AUTHCODE=T&PNREF=" + (new Date()).getTime() + "&RESPMSG=Testing";
        }

        if (Debug.verboseOn()) {
            Debug.logVerbose("Response from Verisign: " + resp, module);
        }
        if (isPayPal) {
            // Attach the avs info returned in doExpressCheckout and stored in PayPalPaymentMethod
            resp += "&AVSADDR=" + payPalPaymentMethod.getString("avsAddr") + "&AVSZIP=" + payPalPaymentMethod.getString("avsZip");
        }

        // check the response
        Map<String, Object> result = ServiceUtil.returnSuccess();
        parseAuthResponse(delegator, paymentGatewayConfigId, resp, result, configString, isReAuth, isPayPal);
        result.put("processAmount", processAmount);
        return result;
    }

    public static Map<String, Object> ccCapture(DispatchContext dctx, Map<String, ? extends Object> context) {
        Delegator delegator = dctx.getDelegator();
        GenericValue paymentPref = (GenericValue) context.get("orderPaymentPreference");
        GenericValue authTrans = (GenericValue) context.get("authTrans");
        BigDecimal amount = (BigDecimal) context.get("captureAmount");
        String paymentGatewayConfigId = (String) context.get("paymentGatewayConfigId");
        String configString = (String) context.get("paymentConfig");
        Locale locale = (Locale) context.get("locale");
        if (configString == null) {
            configString = "payment.properties";
        }

        boolean isPayPal = false;
        // Are we doing a cc or a paypal payment?
        if ("EXT_PAYPAL".equals(paymentPref.getString("paymentMethodTypeId"))) {
            isPayPal = true;
        }

        if (authTrans == null) {
            authTrans = PaymentGatewayServices.getAuthTransaction(paymentPref);
        }

        if (authTrans == null) {
            return ServiceUtil.returnError(UtilProperties.getMessage(resource, 
                    "AccountingPaymentTransactionAuthorizationNotFoundCannotCapture", locale));
        }

        // auth ref number
        String refNum = authTrans.getString("referenceNum");
        Map<String, String> data = UtilMisc.toMap("ORIGID", refNum);

        // tx type (Delayed Capture)
        data.put("TRXTYPE", "D");

        if (isPayPal) {
            // paypal tender
            data.put("TENDER", "P");
            data.put("CAPTURECOMPLETE", "N");
        } else {
            // credit card tender
            data.put("TENDER", "C");

            // get the orderID
            String orderId = paymentPref.getString("orderId");
            data.put("COMMENT1", orderId);
        }


        // amount to capture
        data.put("AMT", amount.toString());

        PayflowAPI pfp = init(delegator, paymentGatewayConfigId, configString, context);

        // get the base params
        StringBuilder params = makeBaseParams(delegator, paymentGatewayConfigId, configString);

        // parse the context parameters
        params.append("&").append(parseContext(data));

        // transmit the request
        if (Debug.verboseOn()) Debug.logVerbose("Sending to Verisign: " + params.toString(), module);
        String resp;
        if (!comparePaymentGatewayConfigValue(delegator, paymentGatewayConfigId, "enableTransmit", configString, "payment.verisign.enable_transmit",  "false")) {
            resp = pfp.submitTransaction(params.toString(), pfp.generateRequestId());
        } else {
            resp = "RESULT=0&AUTHCODE=T&PNREF=" + (new Date()).getTime() + "&RESPMSG=Testing";
        }

        if (Debug.verboseOn()) Debug.logVerbose("Response from Verisign: " + resp, module);

        // check the response
        Map<String, Object> result = ServiceUtil.returnSuccess();
        parseCaptureResponse(resp, result);
        result.put("captureAmount", amount);
        return result;
    }

    public static Map<String, Object> ccVoid(DispatchContext dctx, Map<String, ? extends Object> context) {
        Delegator delegator = dctx.getDelegator();
        GenericValue paymentPref = (GenericValue) context.get("orderPaymentPreference");
        GenericValue authTrans = (GenericValue) context.get("authTrans");
        BigDecimal amount = (BigDecimal) context.get("releaseAmount");
        String paymentGatewayConfigId = (String) context.get("paymentGatewayConfigId");
        String configString = (String) context.get("paymentConfig");
        Locale locale = (Locale) context.get("locale");
        if (configString == null) {
            configString = "payment.properties";
        }

        if (authTrans == null) {
            authTrans = PaymentGatewayServices.getAuthTransaction(paymentPref);
        }

        if (authTrans == null) {
            return ServiceUtil.returnError(UtilProperties.getMessage(resource, 
                    "AccountingPaymentTransactionAuthorizationNotFoundCannotRelease", locale));
        }

        boolean isPayPal = false;
        // Are we doing a cc or a paypal payment?
        if ("EXT_PAYPAL".equals(paymentPref.getString("paymentMethodTypeId"))) {
            isPayPal = true;
        }

        // auth ref number
        String refNum = authTrans.getString("referenceNum");
        Map<String, String> data = UtilMisc.toMap("ORIGID", refNum);

        // tx type (Void)
        data.put("TRXTYPE", "V");

        // get the orderID
        String orderId = paymentPref.getString("orderId");

        if (isPayPal) {
            data.put("TENDER", "P");

            data.put("NOTE", orderId);
        } else {
            // credit card tender
            data.put("TENDER", "C");

            data.put("COMMENT1", orderId);

            // amount to void
            data.put("AMT", amount.toString());
        }


        PayflowAPI pfp = init(delegator, paymentGatewayConfigId, configString, context);

        // get the base params
        StringBuilder params = makeBaseParams(delegator, paymentGatewayConfigId, configString);

        // parse the context parameters
        params.append("&").append(parseContext(data));

        // transmit the request
        if (Debug.verboseOn()) Debug.logVerbose("Sending to Verisign: " + params.toString(), module);
        String resp;
        if (!comparePaymentGatewayConfigValue(delegator, paymentGatewayConfigId, "enableTransmit", configString, "payment.verisign.enable_transmit",  "false")) {
            resp = pfp.submitTransaction(params.toString(), pfp.generateRequestId());
        } else {
            resp = "RESULT=0&AUTHCODE=T&PNREF=" + (new Date()).getTime() + "&RESPMSG=Testing";
        }

        if (Debug.verboseOn()) Debug.logVerbose("Response from Verisign: " + resp, module);

        // check the response
        Map<String, Object> result = ServiceUtil.returnSuccess();
        parseVoidResponse(resp, result);
        result.put("releaseAmount", amount);
        return result;
    }

    public static Map<String, Object> ccRefund(DispatchContext dctx, Map<String, ? extends Object> context) {
        Delegator delegator = dctx.getDelegator();
        GenericValue paymentPref = (GenericValue) context.get("orderPaymentPreference");
        BigDecimal amount = (BigDecimal) context.get("refundAmount");
        String paymentGatewayConfigId = (String) context.get("paymentGatewayConfigId");
        String configString = (String) context.get("paymentConfig");
        Locale locale = (Locale) context.get("locale");
        if (configString == null) {
            configString = "payment.properties";
        }

        GenericValue captureTrans = PaymentGatewayServices.getCaptureTransaction(paymentPref);

        if (captureTrans == null) {
            return ServiceUtil.returnError(UtilProperties.getMessage(resource, 
                    "AccountingPaymentTransactionAuthorizationNotFoundCannotRefund", locale));
        }

        boolean isPayPal = false;
        // Are we doing a cc or a paypal payment?
        if ("EXT_PAYPAL".equals(paymentPref.getString("paymentMethodTypeId"))) {
            isPayPal = true;
        }

        // auth ref number
        String refNum = captureTrans.getString("referenceNum");
        Map<String, String> data = UtilMisc.toMap("ORIGID", refNum);

        // tx type (Credit)
        data.put("TRXTYPE", "C");

        // get the orderID
        String orderId = paymentPref.getString("orderId");

        if (isPayPal) {
            data.put("TENDER", "P");

            data.put("MEMO", orderId);
            // PayPal won't allow us to refund more than the capture amount
            BigDecimal captureAmount = captureTrans.getBigDecimal("amount");
            amount = amount.min(captureAmount);
        } else {
            // credit card tender
            data.put("TENDER", "C");

            data.put("COMMENT1", orderId);
        }

        // amount to capture
        data.put("AMT", amount.toString());

        PayflowAPI pfp = init(delegator, paymentGatewayConfigId, configString, context);

        // get the base params
        StringBuilder params = makeBaseParams(delegator, paymentGatewayConfigId, configString);

        // parse the context parameters
        params.append("&").append(parseContext(data));

        // transmit the request
        if (Debug.verboseOn()) Debug.logVerbose("Sending to Verisign: " + params.toString(), module);
        String resp;
        if (!comparePaymentGatewayConfigValue(delegator, paymentGatewayConfigId, "enableTransmit", configString, "payment.verisign.enable_transmit",  "false")) {
            resp = pfp.submitTransaction(params.toString(), pfp.generateRequestId());
        } else {
            resp = "RESULT=0&AUTHCODE=T&PNREF=" + (new Date()).getTime() + "&RESPMSG=Testing";
        }

        if (Debug.verboseOn()) Debug.logVerbose("Response from Verisign: " + resp, module);

        // check the response
        Map<String, Object> result = ServiceUtil.returnSuccess();
        parseRefundResponse(resp, result);
        result.put("refundAmount", amount);
        return result;
    }


    public static Map<String, Object> setExpressCheckout(DispatchContext dctx, Map<String, ? extends Object> context) {
        Delegator delegator = dctx.getDelegator();
        ShoppingCart cart = (ShoppingCart) context.get("cart");
        Locale locale = cart.getLocale();
        GenericValue payPalPaymentSetting = ProductStoreWorker.getProductStorePaymentSetting(delegator, cart.getProductStoreId(), "EXT_PAYPAL", null, true);
        String paymentGatewayConfigId = payPalPaymentSetting.getString("paymentGatewayConfigId");
        String configString = "payment.properties";

        if (cart == null || cart.items().size() <= 0) {
            return ServiceUtil.returnError(UtilProperties.getMessage("AccountingErrorUiLabels", 
                    "AccountingPayPalShoppingCartIsEmpty", locale));
        }

        Map<String, String> data = new HashMap<String, String>();

        data.put("TRXTYPE", "O");
        data.put("TENDER", "P");
        data.put("ACTION", "S");
        String token = (String) cart.getAttribute("payPalCheckoutToken");
        if (UtilValidate.isNotEmpty(token)) {
            data.put("TOKEN", token);
        }
        data.put("RETURNURL", getPaymentGatewayConfigValue(delegator, paymentGatewayConfigId, "returnUrl", configString, "payment.verisign.returnUrl"));
        data.put("CANCELURL", getPaymentGatewayConfigValue(delegator, paymentGatewayConfigId, "cancelReturnUrl", configString, "payment.verisign.cancelReturnUrl"));

        try {
            addCartDetails(data, cart);
        } catch (GenericEntityException e) {
            Debug.logError(e, module);
            return ServiceUtil.returnError(UtilProperties.getMessage(resource, 
                    "AccountingPayflowErrorRetreivingCartDetails", locale));
        }

        PayflowAPI pfp = init(delegator, paymentGatewayConfigId, null, context);

        // get the base params
        StringBuilder params = makeBaseParams(delegator, paymentGatewayConfigId, null);

        // parse the context parameters
        params.append("&").append(parseContext(data));

        // transmit the request
        if (Debug.verboseOn()) Debug.logVerbose("Sending to Verisign: " + params.toString(), module);
        String resp;
        if (!comparePaymentGatewayConfigValue(delegator, paymentGatewayConfigId, "enableTransmit", configString, "payment.verisign.enable_transmit",  "false")) {
            resp = pfp.submitTransaction(params.toString(), pfp.generateRequestId());
        } else {
            resp = "RESULT=0&TOKEN=" + (new Date()).getTime() + "&RESPMSG=Testing";
        }

        if (Debug.verboseOn()) Debug.logVerbose("Response from Verisign: " + resp, module);

        Map<String, String> responseMap = parseResponse(resp);
        String result = responseMap.get("RESULT");
        if (!"0".equals(result)) {
            String respMsg = responseMap.get("RESPMSG");
            Debug.logError("A problem occurred while requesting an express checkout token from paypal: Result = " + result + ", Message = " + respMsg, module);
            return ServiceUtil.returnError(UtilProperties.getMessage("AccountingErrorUiLabels", 
                    "AccountingPayPalCommunicationError", locale));
        }
        token = responseMap.get("TOKEN");
        cart.setAttribute("payPalCheckoutToken", token);
        return ServiceUtil.returnSuccess();
    }

    private static void addCartDetails(Map<String, String> parameterMap, ShoppingCart cart) throws GenericEntityException {
        parameterMap.put("CURRENCY", cart.getCurrency());
        int line = 0;
        for (ShoppingCartItem item : cart.items()) {
            //paramMap.put("L_NUMBER" + line, item.getProductId());
            parameterMap.put("L_NAME" + line, item.getName());
            parameterMap.put("L_DESC" + line, item.getDescription());
            parameterMap.put("L_AMT" + line, item.getBasePrice().setScale(2).toPlainString());
            parameterMap.put("L_QTY" + line, item.getQuantity().toBigInteger().toString());
            line++;
            BigDecimal otherAdjustments = item.getOtherAdjustments();
            if (otherAdjustments.compareTo(BigDecimal.ZERO) != 0) {
                parameterMap.put("L_NAME" + line, item.getName() + " Adjustments");
                parameterMap.put("L_DESC" + line, "Adjustments for item: " + item.getName());
                parameterMap.put("L_AMT" + line, otherAdjustments.setScale(2).toPlainString());
                parameterMap.put("L_QTY" + line, "1");
                line++;
            }
        }
        BigDecimal otherAdjustments = cart.getOrderOtherAdjustmentTotal();
        if (otherAdjustments.compareTo(BigDecimal.ZERO) != 0) {
            parameterMap.put("L_NAME" + line, "Order Adjustments");
            parameterMap.put("L_AMT" + line, otherAdjustments.setScale(2).toPlainString());
            parameterMap.put("L_QTY" + line, "1");
            line++;
        }
        parameterMap.put("ITEMAMT", cart.getSubTotal().add(otherAdjustments).setScale(2).toPlainString());
        parameterMap.put("TAXAMT", cart.getTotalSalesTax().setScale(2).toPlainString());
        parameterMap.put("FREIGHTAMT", cart.getTotalShipping().setScale(2).toPlainString());
        parameterMap.put("AMT", cart.getGrandTotal().setScale(2).toPlainString());

        if (!cart.shippingApplies()) {
            parameterMap.put("NOSHIPPING", "1");
        } else {
            GenericValue shippingAddress = cart.getShippingAddress();
            parameterMap.put("ADDROVERRIDE", "1");
            parameterMap.put("SHIPTOSTREET", StringUtils.left(shippingAddress.getString("address1"), 30));
            parameterMap.put("SHIPTOSTREET2", StringUtils.left(shippingAddress.getString("address2"), 30));
            parameterMap.put("SHIPTOCITY", StringUtils.left(shippingAddress.getString("city"), 40));
            if (shippingAddress.getString("stateProvinceGeoId") != null && !"_NA_".equals(shippingAddress.getString("stateProvinceGeoId"))) {
                GenericValue stateProvinceGeo = shippingAddress.getRelatedOne("StateProvinceGeo", false);
                parameterMap.put("SHIPTOSTATE", StringUtils.left(stateProvinceGeo.getString("geoCode"), 40));
            }
            parameterMap.put("SHIPTOZIP", StringUtils.left(shippingAddress.getString("postalCode"), 16));
            GenericValue countryGeo = shippingAddress.getRelatedOne("CountryGeo", false);
            parameterMap.put("SHIPTOCOUNTRY", StringUtils.left(countryGeo.getString("geoCode"), 2));
        }
    }

    public static Map<String, Object> getExpressCheckout(DispatchContext dctx, Map<String, Object> context) {
        LocalDispatcher dispatcher = dctx.getDispatcher();
        Delegator delegator = dctx.getDelegator();
        ShoppingCart cart = (ShoppingCart) context.get("cart");
        Locale locale = cart.getLocale();
        GenericValue payPalPaymentSetting = ProductStoreWorker.getProductStorePaymentSetting(delegator, cart.getProductStoreId(), "EXT_PAYPAL", null, true);
        String paymentGatewayConfigId = payPalPaymentSetting.getString("paymentGatewayConfigId");
        String configString = "payment.properties";

        Map<String, String> data = new HashMap<String, String>();
        data.put("TRXTYPE", "O");
        data.put("TENDER", "P");
        data.put("ACTION", "G");
        String token = (String) cart.getAttribute("payPalCheckoutToken");
        if (UtilValidate.isNotEmpty(token)) {
            data.put("TOKEN", token);
        }

        PayflowAPI pfp = init(delegator, paymentGatewayConfigId, null, context);

        // get the base params
        StringBuilder params = makeBaseParams(delegator, paymentGatewayConfigId, null);

        // parse the context parameters
        params.append("&").append(parseContext(data));

        // transmit the request
        if (Debug.verboseOn()) Debug.logVerbose("Sending to Verisign: " + params.toString(), module);
        String resp;
        if (!comparePaymentGatewayConfigValue(delegator, paymentGatewayConfigId, "enableTransmit", configString, "payment.verisign.enable_transmit",  "false")) {
            resp = pfp.submitTransaction(params.toString(), pfp.generateRequestId());
        } else {
            resp = "RESULT=0&PAYERID=" + (new Date()).getTime() + "&RESPMSG=Testing";
        }

        Map<String, String> responseMap = parseResponse(resp);
        if (!"0".equals(responseMap.get("RESULT"))) {
            Debug.logError("A problem occurred while requesting the checkout details from paypal: Result = " + responseMap.get("RESULT") + ", Message = " + responseMap.get("RESPMSG"), module);
            return ServiceUtil.returnError(UtilProperties.getMessage("AccountingErrorUiLabels", 
                    "AccountingPayPalCommunicationError", locale));
        }

        Map<String, Object> inMap = new HashMap<String, Object>();
        inMap.put("userLogin", cart.getUserLogin());
        inMap.put("partyId", cart.getOrderPartyId());
        inMap.put("contactMechId", cart.getShippingContactMechId());
        inMap.put("fromDate", UtilDateTime.nowTimestamp());
        inMap.put("payerId", responseMap.get("PAYERID"));
        inMap.put("expressCheckoutToken", token);
        inMap.put("payerStatus", responseMap.get("PAYERSTATUS"));
        inMap.put("avsAddr", responseMap.get("AVSADDR"));
        inMap.put("avsZip", responseMap.get("AVSZIP"));
        inMap.put("correlationId", responseMap.get("CORRELATIONID"));
        Map<String, Object> outMap = null;
        try {
            outMap = dispatcher.runSync("createPayPalPaymentMethod", inMap);
        } catch (GenericServiceException e) {
            Debug.logError(e, module);
            return ServiceUtil.returnError(e.getMessage());
        }
        String paymentMethodId = (String) outMap.get("paymentMethodId");

        cart.clearPayments();
        cart.addPaymentAmount(paymentMethodId, cart.getGrandTotal(), true);

        return ServiceUtil.returnSuccess();

    }

    public static Map<String, Object> doExpressCheckout(DispatchContext dctx, Map<String, Object> context) {
        LocalDispatcher dispatcher = dctx.getDispatcher();
        Delegator delegator = dctx.getDelegator();
        GenericValue userLogin = (GenericValue) context.get("userLogin");
        GenericValue paymentPref = (GenericValue) context.get("orderPaymentPreference");
        OrderReadHelper orh = new OrderReadHelper(delegator, paymentPref.getString("orderId"));
        GenericValue payPalPaymentSetting = ProductStoreWorker.getProductStorePaymentSetting(delegator, orh.getProductStoreId(), "EXT_PAYPAL", null, true);
        String paymentGatewayConfigId = payPalPaymentSetting.getString("paymentGatewayConfigId");
        String configString = "payment.properties";
        GenericValue payPalPaymentMethod = null;
        try {
            payPalPaymentMethod = paymentPref.getRelatedOne("PaymentMethod", false);
            payPalPaymentMethod = payPalPaymentMethod.getRelatedOne("PayPalPaymentMethod", false);
        } catch (GenericEntityException e) {
            Debug.logError(e, module);
            return ServiceUtil.returnError(e.getMessage());
        }
        BigDecimal processAmount = paymentPref.getBigDecimal("maxAmount");

        Map<String, String> data = new HashMap<String, String>();
        data.put("TRXTYPE", "O");
        data.put("TENDER", "P");
        data.put("PAYERID", payPalPaymentMethod.getString("payerId"));
        data.put("TOKEN", payPalPaymentMethod.getString("expressCheckoutToken"));
        data.put("ACTION", "D");
        // set the amount
        data.put("AMT", processAmount.setScale(2).toPlainString());

        PayflowAPI pfp = init(delegator, paymentGatewayConfigId, null, context);

        // get the base params
        StringBuilder params = makeBaseParams(delegator, paymentGatewayConfigId, null);

        // parse the context parameters
        params.append("&").append(parseContext(data));

        // transmit the request
        if (Debug.verboseOn()) Debug.logVerbose("Sending to Verisign: " + params.toString(), module);
        String resp;
        if (!comparePaymentGatewayConfigValue(delegator, paymentGatewayConfigId, "enableTransmit", configString, "payment.verisign.enable_transmit",  "false")) {
            resp = pfp.submitTransaction(params.toString(), pfp.generateRequestId());
        } else {
            resp = "RESULT=0&PAYERID=" + (new Date()).getTime() + "&RESPMSG=Testing";
        }

        Map<String, String> responseMap = parseResponse(resp);

        Map<String, Object> inMap = new HashMap<String, Object>();
        inMap.put("userLogin", userLogin);
        inMap.put("paymentMethodId", payPalPaymentMethod.get("paymentMethodId"));
        inMap.put("transactionId", responseMap.get("PNREF"));
        Map<String, Object> outMap = null;
        try {
            outMap = dispatcher.runSync("updatePayPalPaymentMethod", inMap);
        } catch (GenericServiceException e) {
            Debug.logError(e, module);
            return ServiceUtil.returnError(e.getMessage());
        }
        if (ServiceUtil.isError(outMap)) {
            Debug.logError(ServiceUtil.getErrorMessage(outMap), module);
            return outMap;
        }
        return ServiceUtil.returnSuccess();
    }

    private static Map<String, String> parseResponse(String resp) {
        Debug.logInfo("Verisign response string: " + resp, module);
        Map<String, String> parameters = new HashMap<String, String>();
        List<String> params = StringUtil.split(resp, "&");
        for (String str : params) {
            if (str.length() > 0) {
                List<String> kv = StringUtil.split(str, "=");
                String k = kv.get(0);
                String v = kv.get(1);

                if (k != null && v != null)
                    parameters.put(k, v);
            }
        }
        return parameters;
    }
    private static void parseAuthResponse(Delegator delegator, String paymentGatewayConfigId, String resp, Map<String, Object> result, String resource, boolean isReAuth, boolean isPayPal) {
        Map<String, String> parameters = parseResponse(resp);

        // txType
        boolean isSale = !comparePaymentGatewayConfigValue(delegator, paymentGatewayConfigId, "preAuth", resource, "payment.verisign.preAuth", "Y");

        // avs checking - ignore on re-auth
        boolean avsCheckOkay = true;
        String avsCode = null;
        if (!isReAuth) {
            boolean checkAvs = comparePaymentGatewayConfigValue(delegator, paymentGatewayConfigId, "checkAvs", resource, "payment.verisign.checkAvs", "Y");
            if (checkAvs && !isSale) {
                String addAvs = (String) parameters.get("AVSADDR");
                String zipAvs = (String) parameters.get("AVSZIP");
                avsCode = addAvs + zipAvs;
                if (addAvs == null || "N".equals(addAvs) || zipAvs == null || "N".equals(zipAvs)) {
                    avsCheckOkay = false;
                }
            }
        }

        // cvv2 checking - ignore on re-auth or paypal
        boolean cvv2CheckOkay = true;
        String cvvCode = null;
        if (!isReAuth && !isPayPal) {
            boolean checkCvv2 = comparePaymentGatewayConfigValue(delegator, paymentGatewayConfigId, "checkCvv2", resource, "payment.verisign.checkCvv2", "Y");
            if (checkCvv2 && !isSale) {
                cvvCode = (String) parameters.get("CVV2MATCH");
                if (cvvCode == null || "N".equals(cvvCode)) {
                    cvv2CheckOkay = false;
                }
            }
        }

        String respCode = (String) parameters.get("RESULT");
        int codeInt = -999; // custom response code -- not from payflow docs
        try {
            codeInt = Integer.parseInt(respCode);
        } catch (NumberFormatException e) {
            Debug.logError(e, "Unable to parse response code; not a number!", module);
        }


        result.put("authRefNum", parameters.get("PNREF"));

        if (codeInt == 0 && avsCheckOkay && cvv2CheckOkay) {
            result.put("authResult", Boolean.TRUE);
            result.put("authCode", parameters.get("AUTHCODE"));
        } else if (codeInt < 0) {
            // communications error
            Debug.logWarning("In PayflowPro failing authorization; respCode/RESULT=" + respCode + ", avsCheckOkay=" + avsCheckOkay + ", cvv2CheckOkay=" + cvv2CheckOkay + "; AUTHCODE=" + parameters.get("AUTHCODE"), module);
            result.put("authResult", Boolean.FALSE);
            result.put("authRefNum", respCode);
        } else if (codeInt == 0) {
            Debug.logWarning("In PayflowPro approved, but invalid flags; respCode/RESULT=" + respCode + ", avsCheckOkay=" + avsCheckOkay + ", cvv2CheckOkay=" + cvv2CheckOkay + "; AUTHCODE=" + parameters.get("AUTHCODE"), module);
            result.put("authResult", Boolean.TRUE);
            result.put("authCode", parameters.get("AUTHCODE"));
        } else {
            // other error
            Debug.logWarning("In PayflowPro failing authorization; respCode/RESULT=" + respCode + ", avsCheckOkay=" + avsCheckOkay + ", cvv2CheckOkay=" + cvv2CheckOkay + "; AUTHCODE=" + parameters.get("AUTHCODE"), module);
            result.put("authResult", Boolean.FALSE);

            // now check certain special conditions and report back through the generic params
            if ("12".equals(respCode)) {
                result.put("resultDeclined", Boolean.TRUE);
            } else if ("50".equals(respCode)) {
                result.put("resultNsf", Boolean.TRUE);
            } else if ("23".equals(respCode)) {
                result.put("resultBadCardNumber", Boolean.TRUE);
            } else if ("24".equals(respCode)) {
                result.put("resultBadExpire", Boolean.TRUE);
            }
        }
        result.put("cvCode", cvvCode);
        result.put("avsCode", avsCode);
        result.put("authFlag", parameters.get("RESULT"));
        result.put("authMessage", parameters.get("RESPMSG"));
    }

    private static void parseCaptureResponse(String resp, Map<String, Object> result) {
        Map<String, String> parameters = parseResponse(resp);

        String respCode = (String) parameters.get("RESULT");
        int codeInt = -999; // custom response code -- not from payflow docs
        try {
            codeInt = Integer.parseInt(respCode);
        } catch (NumberFormatException e) {
            Debug.logError(e, "Unable to parse response code; not a number!", module);
        }

        if (codeInt == 0) {
            result.put("captureResult", Boolean.TRUE);
            result.put("captureCode", parameters.get("AUTHCODE"));
        } else if (codeInt < 0) {
            // communications error
            Debug.logWarning("In PayflowPro failing capture; respCode/RESULT=" + respCode + "; PNREF=" + parameters.get("PNREF") + "; AUTHCODE=" + parameters.get("AUTHCODE"), module);
            result.put("captureResult", Boolean.FALSE);
            result.put("captureRefNum", respCode);
        } else {
            Debug.logWarning("In PayflowPro failing capture; respCode/RESULT=" + respCode + "; PNREF=" + parameters.get("PNREF") + "; AUTHCODE=" + parameters.get("AUTHCODE"), module);
            result.put("captureResult", Boolean.FALSE);
        }
        result.put("captureRefNum", parameters.get("PNREF"));
        result.put("captureFlag", parameters.get("RESULT"));
        result.put("captureMessage", parameters.get("RESPMSG"));
    }

    private static void parseVoidResponse(String resp, Map<String, Object> result) {
        Map<String, String> parameters = parseResponse(resp);

        String respCode = (String) parameters.get("RESULT");
        int codeInt = -999; // custom response code -- not from payflow docs
        try {
            codeInt = Integer.parseInt(respCode);
        } catch (NumberFormatException e) {
            Debug.logError(e, "Unable to parse response code; not a number!", module);
        }

        if (codeInt == 0) {
            result.put("releaseResult", Boolean.TRUE);
            result.put("releaseCode", parameters.get("AUTHCODE"));
        } else if (codeInt < 0) {
            // communications error
            Debug.logWarning("In PayflowPro failing void; respCode/RESULT=" + respCode + "; PNREF=" + parameters.get("PNREF") + "; AUTHCODE=" + parameters.get("AUTHCODE"), module);
            result.put("releaseResult", Boolean.FALSE);
            result.put("releaseRefNum", respCode);
        } else {
            Debug.logWarning("In PayflowPro failing void; respCode/RESULT=" + respCode + "; PNREF=" + parameters.get("PNREF") + "; AUTHCODE=" + parameters.get("AUTHCODE"), module);
            result.put("releaseResult", Boolean.FALSE);
        }
        result.put("releaseRefNum", parameters.get("PNREF"));
        result.put("releaseFlag", parameters.get("RESULT"));
        result.put("releaseMessage", parameters.get("RESPMSG"));
    }

    private static void parseRefundResponse(String resp, Map<String, Object> result) {
        Map<String, String> parameters = parseResponse(resp);

        String respCode = (String) parameters.get("RESULT");
        int codeInt = -999; // custom response code -- not from payflow docs
        try {
            codeInt = Integer.parseInt(respCode);
        } catch (NumberFormatException e) {
            Debug.logError(e, "Unable to parse response code; not a number!", module);
        }

        if (codeInt == 0) {
            result.put("refundResult", Boolean.TRUE);
            result.put("refundCode", parameters.get("AUTHCODE"));
        } else if (codeInt < 0) {
            // communications error
            Debug.logWarning("In PayflowPro failing refund; respCode/RESULT=" + respCode + "; PNREF=" + parameters.get("PNREF") + "; AUTHCODE=" + parameters.get("AUTHCODE"), module);
            result.put("refundResult", Boolean.FALSE);
            result.put("refundRefNum", respCode);
        } else {
            Debug.logWarning("In PayflowPro failing refund; respCode/RESULT=" + respCode + "; PNREF=" + parameters.get("PNREF") + "; AUTHCODE=" + parameters.get("AUTHCODE"), module);
            result.put("refundResult", Boolean.FALSE);
        }
        result.put("refundRefNum", parameters.get("PNREF"));
        result.put("refundFlag", parameters.get("RESULT"));
        result.put("refundMessage", parameters.get("RESPMSG"));
    }

    private static String parseContext(Map<String, ? extends Object> context) {
        StringBuilder buf = new StringBuilder();
        Set<String> keySet = context.keySet();
        Iterator<String> i = keySet.iterator();

        while (i.hasNext()) {
            String name = (String) i.next();
            Object valueObj = context.get(name);

            if (valueObj == null || (valueObj instanceof String) && ((String) valueObj).length() == 0) {
                // not valid; do nothing
            } else {
                String value = valueObj.toString();

                // Payflow expects an unencoded name/value pair string
                buf.append(name).append("=");
                buf.append(value);
                if (i.hasNext())
                    buf.append("&");
            }
        }
        return buf.toString();
    }

    private static StringBuilder makeBaseParams(Delegator delegator, String paymentGatewayConfigId, String resource) {
        StringBuilder buf = new StringBuilder();

        try {
            buf.append("PARTNER=");
            buf.append(getPaymentGatewayConfigValue(delegator, paymentGatewayConfigId, "partner", resource, "payment.verisign.partner", "VeriSign"));
            buf.append("&");
            buf.append("VENDOR=");
            buf.append(getPaymentGatewayConfigValue(delegator, paymentGatewayConfigId, "vendor", resource, "payment.verisign.vendor", "nobody"));
            buf.append("&");
            buf.append("USER=");
            buf.append(getPaymentGatewayConfigValue(delegator, paymentGatewayConfigId, "userId", resource, "payment.verisign.user", "nobody"));
            buf.append("&");
            buf.append("PWD=");
            buf.append(getPaymentGatewayConfigValue(delegator, paymentGatewayConfigId, "pwd", resource, "payment.verisign.pwd", "password"));
        } catch (Exception e) {
            Debug.logError(e, module);
            return null;
        }
        return buf;
    }

    private static PayflowAPI init(Delegator delegator, String paymentGatewayConfigId, String resource, Map<String, ? extends Object> context) {
        // No more used
        // String certsPath = FlexibleStringExpander.expandString(getPaymentGatewayConfigValue(delegator, paymentGatewayConfigId, "certsPath", resource, "payment.verisign.certsPath", "pfcerts"), context);
        String hostAddress = getPaymentGatewayConfigValue(delegator, paymentGatewayConfigId, "hostAddress", resource, "payment.verisign.hostAddress", "pilot-payflowpro.paypal.com");
        Integer hostPort = Integer.decode(getPaymentGatewayConfigValue(delegator, paymentGatewayConfigId, "hostPort", resource, "payment.verisign.hostPort", "443"));
        Integer timeout = Integer.decode(getPaymentGatewayConfigValue(delegator, paymentGatewayConfigId, "timeout", resource, "payment.verisign.timeout", "80"));
        String proxyAddress = getPaymentGatewayConfigValue(delegator, paymentGatewayConfigId, "proxyAddress", resource, "payment.verisign.proxyAddress", "");
        Integer proxyPort = Integer.decode(getPaymentGatewayConfigValue(delegator, paymentGatewayConfigId, "proxyPort", resource, "payment.verisign.proxyPort", "80"));
        String proxyLogon = getPaymentGatewayConfigValue(delegator, paymentGatewayConfigId, "proxyLogon", resource, "payment.verisign.proxyLogon", "");
        String proxyPassword = getPaymentGatewayConfigValue(delegator, paymentGatewayConfigId, "proxyPassword", resource, "payment.verisign.proxyPassword", "");
        String logFileName = FlexibleStringExpander.expandString(getPaymentGatewayConfigValue(delegator, paymentGatewayConfigId, "logFileName", resource, "payment.verisign.logFileName", ""), context);
        Integer loggingLevel = Integer.decode(getPaymentGatewayConfigValue(delegator, paymentGatewayConfigId, "loggingLevel", resource, "payment.verisign.loggingLevel", "6"));
        Integer maxLogFileSize = Integer.decode(getPaymentGatewayConfigValue(delegator, paymentGatewayConfigId, "maxLogFileSize", resource, "payment.verisign.maxLogFileSize", "1000000"));
        boolean stackTraceOn = "Y".equalsIgnoreCase(getPaymentGatewayConfigValue(delegator, paymentGatewayConfigId, "stackTraceOn", resource, "payment.verisign.stackTraceOn", "N"));

        PayflowAPI pfp = new PayflowAPI(hostAddress, hostPort, timeout, proxyAddress,
                proxyPort, proxyLogon, proxyPassword);
        SDKProperties.setLogFileName(logFileName);
        SDKProperties.setLoggingLevel(loggingLevel);
        SDKProperties.setMaxLogFileSize(maxLogFileSize);
        SDKProperties.setStackTraceOn(stackTraceOn);
        return pfp;
    }

    private static String getPaymentGatewayConfigValue(Delegator delegator, String paymentGatewayConfigId, String paymentGatewayConfigParameterName,
                                                       String resource, String parameterName) {
        String returnValue = "";
        if (UtilValidate.isNotEmpty(paymentGatewayConfigId)) {
            try {
                GenericValue payflowPro = EntityQuery.use(delegator).from("PaymentGatewayPayflowPro").where("paymentGatewayConfigId", paymentGatewayConfigId).queryOne();
                if (UtilValidate.isNotEmpty(payflowPro)) {
                    Object payflowProField = payflowPro.get(paymentGatewayConfigParameterName);
                    if (payflowProField != null) {
                        returnValue = payflowProField.toString().trim();
                    }
                }
            } catch (GenericEntityException e) {
                Debug.logError(e, module);
            }
        } else {
            String value = EntityUtilProperties.getPropertyValue(resource, parameterName, delegator);
            if (value != null) {
                returnValue = value.trim();
            }
        }
        return returnValue;
    }

    private static String getPaymentGatewayConfigValue(Delegator delegator, String paymentGatewayConfigId, String paymentGatewayConfigParameterName,
                                                       String resource, String parameterName, String defaultValue) {
        String returnValue = getPaymentGatewayConfigValue(delegator, paymentGatewayConfigId, paymentGatewayConfigParameterName, resource, parameterName);
        if (UtilValidate.isEmpty(returnValue)) {
            returnValue = defaultValue;
        }
        return returnValue;
    }

    private static boolean comparePaymentGatewayConfigValue(Delegator delegator, String paymentGatewayConfigId, String paymentGatewayConfigParameterName,
                                                        String resource, String parameterName, String compareValue) {
        boolean returnValue = false;

        String value = getPaymentGatewayConfigValue(delegator, paymentGatewayConfigId, paymentGatewayConfigParameterName, resource, parameterName, compareValue);
        if (UtilValidate.isNotEmpty(value)) {
            returnValue = value.trim().equalsIgnoreCase(compareValue);
        }
        return returnValue;
    }

/*
 * RESULT values (and RESPMSG text)
 *
0 Approved
1 User authentication failed. Error is caused by one or more of the following:
Login information is incorrect. Verify that USER, VENDOR, PARTNER, and
PASSWORD have been entered correctly. See Table 3.2 on page 26 for additional
information and field descriptions.
Invalid Processor information entered. Contact merchant bank to verify.
"Allowed IP Address" security feature implemented. The transaction is coming
from an unknown IP address. See VeriSign Manager online help for details on how
to use Manager to update the allowed IP addresses.
You are using a test (not active) account to submit a transaction to the live VeriSign
servers. Change the URL from pilot-payflowpro.paypal.com to payflowpro.paypal.com.
2 Invalid tender type. Your merchant bank account does not support the following
credit card type that was submitted.
3 Invalid transaction type. Transaction type is not appropriate for this transaction. For
example, you cannot credit an authorization-only transaction.
4 Invalid amount format. Use the format: #####.##  Do not include currency
symbols or commas.
5 Invalid merchant information. Processor does not recognize your merchant account
information. Contact your bank account acquirer to resolve this problem.
6 Invalid or unsupported currency code
7 Field format error. Invalid information entered. See RESPMSG.
8 Not a transaction server
9 Too many parameters or invalid stream
10 Too many line items
11 Client time-out waiting for response
12 Declined. Check the credit card number, expiration date, and transaction information to
make sure they were entered correctly. If this does not resolve the problem, have the
customer call their card issuing bank to resolve.
13 Referral. Transaction cannot be approved electronically but can be approved with a
verbal authorization. Contact your merchant bank to obtain an authorization and submit
a manual Voice Authorization transaction.
14 Invalid Client Certification ID. Check the HTTP header. If the tag, X-VPS-VIT-
CLIENT-CERTIFICATION-ID, is missing, RESULT code 14 is returned.
19 Original transaction ID not found. The transaction ID you entered for this
transaction is not valid. See RESPMSG.
20 Cannot find the customer reference number
22 Invalid ABA number

23 Invalid account number. Check credit card number and re-submit.
24 Invalid expiration date. Check and re-submit.
25 Invalid Host Mapping. You are trying to process a tender type such as Discover Card,
but you are not set up with your merchant bank to accept this card type.
26 Invalid vendor account
27 Insufficient partner permissions
28 Insufficient user permissions
29 Invalid XML document. This could be caused by an unrecognized XML tag or a bad
XML format that cannot be parsed by the system.
30 Duplicate transaction
31 Error in adding the recurring profile
32 Error in modifying the recurring profile
33 Error in canceling the recurring profile
34 Error in forcing the recurring profile
35 Error in reactivating the recurring profile
36 OLTP Transaction failed
37 Invalid recurring profile ID
50 Insufficient funds available in account
99 General error. See RESPMSG.
100 Transaction type not supported by host
101 Time-out value too small
102 Processor not available
103 Error reading response from host
104 Timeout waiting for processor response. Try your transaction again.
105 Credit error. Make sure you have not already credited this transaction, or that this
transaction ID is for a creditable transaction. (For example, you cannot credit an
authorization.)
106 Host not available
107 Duplicate suppression time-out

108 Void error. See RESPMSG. Make sure the transaction ID entered has not already been
voided. If not, then look at the Transaction Detail screen for this transaction to see if it
has settled. (The Batch field is set to a number greater than zero if the transaction has
been settled). If the transaction has already settled, your only recourse is a reversal
(credit a payment or submit a payment for a credit).
109 Time-out waiting for host response
111 Capture error. Either an attempt to capture a transaction that is not an authorization
transaction type, or an attempt to capture an authorization transaction that has already
been captured.
112 Failed AVS check. Address and ZIP code do not match. An authorization may still
exist on the cardholder account.
113 Merchant sale total will exceed the sales cap with current transaction. ACH
transactions only.
114 Card Security Code (CSC) Mismatch. An authorization may still exist on the
cardholder account.
115 System busy, try again later
116 VPS Internal error. Failed to lock terminal number
117 Failed merchant rule check. One or more of the following three failures occurred:
An attempt was made to submit a transaction that failed to meet the security settings
specified on the Payflow Pro Java SDK Security Settings page. If the transaction
exceeded the Maximum Amount security setting, then no values are returned for AVS
or CSC.
AVS validation failed. The AVS return value should appear in the RESPMSG.
CSC validation failed. The CSC return value should appear in the RESPMSG.
118 Invalid keywords found in string fields
122 Merchant sale total will exceed the credit cap with current transaction. ACH
transactions only.
125 Fraud Protection Services Filter _ Declined by filters

126 Fraud Protection Services Filter _ Flagged for review by filters
Important Note: Result code 126 indicates that a transaction triggered a fraud filter.
This is not an error, but a notice that the transaction is in a review status. The
transaction has been authorized but requires you to review and to manually accept the
transaction before it will be allowed to settle.
Result code 126 is intended to give you an idea of the kind of transaction that is
considered suspicious to enable you to evaluate whether you can benefit from using the
Fraud Protection Services.
To eliminate result 126, turn the filters off.
For more information, see the User Guide for Payflow Pro With Fraud Protection
Services or User Guide for Payflow Link Guide With Fraud Protection Services.
127 Fraud Protection Services Filter _ Not processed by filters
128 Fraud Protection Services Filter _ Declined by merchant after being flagged for
review by filters
131 Version 1 Payflow Pro SDK client no longer supported. Upgrade to the most recent
version of the Payflow Pro client.
150 Issuing bank timed out
151 Issuing bank unavailable
1000 Generic host error. This is a generic message returned by your credit card processor.
The RESPMSG will contain more information describing the error.
1001 Buyer Authentication Service unavailable
1002 Buyer Authentication Service _ Transaction timeout
1003 Buyer Authentication Service _ Invalid client version
1004 Buyer Authentication Service _ Invalid timeout value
1011 Buyer Authentication Service unavailable
1012 Buyer Authentication Service unavailable
1013 Buyer Authentication Service unavailable
1014 Buyer Authentication Service _ Merchant is not enrolled for Buyer
Authentication Service (3-D Secure).
1016 Buyer Authentication Service _ 3-D Secure error response received. Instead of
receiving a PARes response to a Validate Authentication transaction, an error response
was received.
1017 Buyer Authentication Service _ 3-D Secure error response is invalid. An error
response is received and the response is not well formed for a Validate Authentication
transaction.

1021 Buyer Authentication Service _ Invalid card type
1022 Buyer Authentication Service _ Invalid or missing currency code
1023 Buyer Authentication Service _ merchant status for 3D secure is invalid
1041 Buyer Authentication Service _ Validate Authentication failed: missing or
invalid PARES
1042 Buyer Authentication Service _ Validate Authentication failed: PARES format is
invalid
1043 Buyer Authentication Service _ Validate Authentication failed: Cannot find
successful Verify Enrollment
1044 Buyer Authentication Service _ Validate Authentication failed: Signature
validation failed for PARES
1045 Buyer Authentication Service _ Validate Authentication failed: Mismatched or
invalid amount in PARES
1046 Buyer Authentication Service _ Validate Authentication failed: Mismatched or
invalid acquirer in PARES
1047 Buyer Authentication Service _ Validate Authentication failed: Mismatched or
invalid Merchant ID in PARES
1048 Buyer Authentication Service _ Validate Authentication failed: Mismatched or
invalid card number in PARES
1049 Buyer Authentication Service _ Validate Authentication failed: Mismatched or
invalid currency code in PARES
1050 Buyer Authentication Service _ Validate Authentication failed: Mismatched or
invalid XID in PARES
1051 Buyer Authentication Service _ Validate Authentication failed: Mismatched or
invalid order date in PARES
1052 Buyer Authentication Service _ Validate Authentication failed: This PARES was
already validated for a previous Validate Authentication transaction

 */

/* RESULT for communication errors (less than 0)
 *
-1 Failed to connect to host
-2 Failed to resolve hostname
-5 Failed to initialize SSL context
-6 Parameter list format error: & in name
-7 Parameter list format error: invalid [ ] name length clause
-8 SSL failed to connect to host
-9 SSL read failed
-10 SSL write failed
-11 Proxy authorization failed
-12 Timeout waiting for response
-13 Select failure
-14 Too many connections
-15 Failed to set socket options
-20 Proxy read failed
-21 Proxy write failed
-22 Failed to initialize SSL certificate
-23 Host address not specified
-24 Invalid transaction type
-25 Failed to create a socket
-26 Failed to initialize socket layer
-27 Parameter list format error: invalid [ ] name length clause
-28 Parameter list format error: name
-29 Failed to initialize SSL connection
-30 Invalid timeout value

-31 The certificate chain did not validate, no local certificate found
-32 The certificate chain did not validate, common name did not match URL
- 40 Unexpected Request ID found in request.
- 41 Required Request ID not found in request
- 42 Required Response ID not found in request
- 43 Unexpected Response ID found in request
- 44 Response ID not found in the response received from the server
-99 Out of memory
-100 Parameter list cannot be empty
-103 Context initialization failed
-104 Unexpected transaction state
-105 Invalid name value pair request
-106 Invalid response format
-107 This XMLPay version is not supported
-108 The server certificate chain did not validate
-109 Unable to do logging
-111 The following error occurred while initializing from message file: <Details of
the error message>
-113 Unable to round and truncate the currency value simultaneously
 */
}
