// Step 2.5
        /// <summary>
        /// Update our Order info with the info sent back to us from PayPal, based on the user's selections on the PayPal site.
        /// </summary>
        /// <param name="order"></param>
        /// <param name="payPalToken"></param>
        public void UpdateOrderWithExpressCheckoutDetails(Order order, string payPalToken)
        {
            Dictionary <string, string> fields = GetExpressCheckoutDetails(order, payPalToken);

            string    ack     = fields.TryGetValueOrEmpty("ACK");
            AckValues ackType = WA.Enum <AckValues> .TryParseOrDefault(ack, AckValues.Failure);

            if (ackType == AckValues.Success || ackType == AckValues.SuccessWithWarning)
            {
                // parse response fields into variables
                string firstName     = fields.TryGetValueOrEmpty("FIRSTNAME");
                string lastName      = fields.TryGetValueOrEmpty("LASTNAME");
                string email         = fields.TryGetValueOrEmpty("EMAIL");
                string payPalPayerId = fields.TryGetValueOrEmpty("PAYERID");

                string shipToName      = fields.TryGetValueOrEmpty("SHIPTONAME");
                string shipAddress1    = fields.TryGetValueOrEmpty("SHIPTOSTREET");
                string shipAddress2    = fields.TryGetValueOrEmpty("SHIPTOSTREET2");
                string shipCity        = fields.TryGetValueOrEmpty("SHIPTOCITY");
                string shipRegion      = fields.TryGetValueOrEmpty("SHIPTOSTATE");
                string shipPostalCode  = fields.TryGetValueOrEmpty("SHIPTOZIP");
                string shipCountryCode = fields.TryGetValueOrEmpty("SHIPTOCOUNTRYCODE");

                //decimal amount = WA.Parser.ToDecimal(fields.TryGetValueOrEmpty("AMT")).Value;
                decimal shippingAmount = WA.Parser.ToDecimal(fields.TryGetValueOrEmpty("SHIPPINGAMT")).GetValueOrDefault(order.ShippingAmount.GetValueOrDefault(0));
                //decimal handlingAmount = WA.Parser.ToDecimal(fields.TryGetValueOrEmpty("HANDLINGAMT")).GetValueOrDefault(0);
                decimal taxAmount = WA.Parser.ToDecimal(fields.TryGetValueOrEmpty("TAXAMT")).GetValueOrDefault(order.TaxAmount.GetValueOrDefault(0));

                // update the order fields with info from PayPal
                order.CustomerFirstName = firstName;
                order.CustomerLastName  = lastName;
                order.CustomerEmail     = email;

                order.ShipRecipientName = shipToName;
                order.ShipAddress1      = shipAddress1;
                order.ShipAddress2      = shipAddress2;
                order.ShipCity          = shipCity;
                order.ShipRegion        = shipRegion;
                order.ShipPostalCode    = shipPostalCode;
                order.ShipCountryCode   = shipCountryCode;

                order.BillAddress1    = shipAddress1;
                order.BillAddress2    = shipAddress2;
                order.BillCity        = shipCity;
                order.BillRegion      = shipRegion;
                order.BillPostalCode  = shipPostalCode;
                order.BillCountryCode = shipCountryCode;

                order.ShippingAmount = shippingAmount;
                order.TaxAmount      = taxAmount;

                order.Save();
            }
        }
        public override PaymentStatusName ProcessDirectPaymentResponse(Order order, HttpWebResponse response)
        {
            string responseString = HttpHelper.WebResponseToString(response);

            // Decode the response fields from PayPal API
            string[] encodedPairs = responseString.Split('&');
            Dictionary <string, string> fields = new Dictionary <string, string>(encodedPairs.Length);

            foreach (string field in encodedPairs)
            {
                string[] pair  = field.Split('=');
                string   name  = HttpUtility.UrlDecode(pair[0]);
                string   value = HttpUtility.UrlDecode(pair[1]);

                fields[name] = value;
            }

            // parse response fields into variables
            string    ack     = fields["ACK"];
            AckValues ackType = WA.Enum <AckValues> .TryParseOrDefault(ack, AckValues.Failure);

            int?    orderId       = WA.Parser.ToInt(fields.TryGetValueOrEmpty("CUSTOM"));
            string  transactionId = fields.TryGetValueOrEmpty("TRANSACTIONID");
            decimal?amount        = WA.Parser.ToDecimal(fields.TryGetValueOrEmpty("AMT"));

            string error1Code     = fields.TryGetValueOrEmpty("L_ERRORCODE0");
            string error1ShortMsg = fields.TryGetValueOrEmpty("L_SHORTMESSAGE0");
            string error1LongMsg  = fields.TryGetValueOrEmpty("L_LONGMESSAGE0");
            string error1Severity = fields.TryGetValueOrEmpty("L_SEVERITYCODE0");

            PaymentTransaction newTransaction = new PaymentTransaction();

            newTransaction.OrderId              = order.Id;
            newTransaction.PaymentProviderId    = this.ProviderId;
            newTransaction.GatewayUrl           = ProviderUrl;
            newTransaction.GatewayTransactionId = transactionId;
            newTransaction.GatewayDebugResponse = HttpUtility.UrlDecode(responseString);
            newTransaction.Amount = amount;

            PaymentStatusName paymentStatus = order.PaymentStatus;

            if (ackType == AckValues.Success || ackType == AckValues.SuccessWithWarning)
            {
                bool paymentAmountMatches = (amount.GetValueOrDefault(-1) == order.Total.Value);
                if (paymentAmountMatches)
                {
                    newTransaction.GatewayResponse = ackType.ToString();
                    paymentStatus = PaymentStatusName.Completed;
                }
                else
                {
                    newTransaction.GatewayResponse = ackType.ToString() + " Amount from Payment Provider does not match the Order amount.";
                    paymentStatus = PaymentStatusName.Pending;
                }

                if (ackType == AckValues.SuccessWithWarning)
                {
                    newTransaction.GatewayError = ackType.ToString() + string.Format(@" ErrorCode: {0}, ShortMsg: {1}, LongMsg: {2}, SeverityCode: {3}", error1Code, error1ShortMsg, error1LongMsg, error1Severity);
                }
            }
            else if (ackType == AckValues.Failure || ackType == AckValues.FailureWithWarning || ackType == AckValues.Warning)
            {
                newTransaction.GatewayError = ackType.ToString() + string.Format(@" ErrorCode: {0}, ShortMsg: {1}, LongMsg: {2}, SeverityCode: {3}", error1Code, error1ShortMsg, error1LongMsg, error1Severity);
                paymentStatus = PaymentStatusName.ProviderError;
            }
            else
            {
                newTransaction.GatewayError = ack + " Invalid / Unknown Response from Payment Provider";
                paymentStatus = PaymentStatusName.ProviderError;
            }

            newTransaction.Save();

            return(paymentStatus);
        }
        // Step 3.
        public PaymentStatusName DoExpressCheckoutPayment(Order order, Dictionary <string, string> payPalVariables)
        {
            string token   = payPalVariables["token"];
            string payerId = payPalVariables["PayerID"];

            Dictionary <string, string> vars = new Dictionary <string, string>();

            vars["METHOD"]  = "DoExpressCheckoutPayment";
            vars["VERSION"] = "60.0";

            vars["USER"]      = ApiUsername;
            vars["PWD"]       = ApiPassword;
            vars["SIGNATURE"] = ApiSignature;

            vars["TOKEN"]   = token;
            vars["PAYERID"] = payerId;

            IEnumerable <OrderItem> orderitems = order.OrderItemCollectionByOrderId;
            int itemNumber = 0;

            foreach (OrderItem orderItem in orderitems)
            {
                string orderItemAttributes = orderItem.GetProductFieldDataPlainTextDisplayString();

                vars["L_NAME" + itemNumber]   = orderItem.Name.Left(127);
                vars["L_DESC" + itemNumber]   = (!string.IsNullOrEmpty(orderItemAttributes) ? " (" + orderItemAttributes + ")" : "").Left(127);
                vars["L_AMT" + itemNumber]    = orderItem.PriceForSingleItem.ToString("N2");
                vars["L_QTY" + itemNumber]    = orderItem.Quantity.Value.ToString("N0");
                vars["L_NUMBER" + itemNumber] = (!string.IsNullOrEmpty(orderItem.Sku) ? " (" + orderItem.Sku + ")" : "");

                itemNumber++;
            }

            vars["PAYMENTACTION"] = "Sale";
            vars["AMT"]           = order.Total.Value.ToString("N2");
            vars["CURRENCYCODE"]  = order.UpToStoreByStoreId.Currency.Code;

            // Send Http POST to PayPal
            HttpWebResponse webResponse    = HttpHelper.HttpPost(ProviderUrl, vars);
            string          responseString = HttpHelper.WebResponseToString(webResponse);

            // Decode the response fields from PayPal API
            Dictionary <string, string> fields = HttpHelper.DecodeVarsFromHttpString(responseString);

            // parse response fields into variables
            string    ack     = fields["ACK"];
            AckValues ackType = WA.Enum <AckValues> .TryParseOrDefault(ack, AckValues.Failure);

            string  correlationId = fields.TryGetValueOrEmpty("CORRELATIONID");
            string  transactionId = fields.TryGetValueOrEmpty("TOKEN");
            decimal?amount        = WA.Parser.ToDecimal(fields.TryGetValueOrEmpty("AMT"));
            decimal?taxAmount     = WA.Parser.ToDecimal(fields.TryGetValueOrEmpty("TAXAMT"));
            // TODO - do we need shipping amount too?


            PaymentTransaction newTransaction = new PaymentTransaction();

            newTransaction.OrderId              = order.Id;
            newTransaction.PaymentProviderId    = this.ProviderId;
            newTransaction.GatewayUrl           = ProviderUrl;
            newTransaction.GatewayTransactionId = transactionId;
            newTransaction.GatewayResponse      = vars["METHOD"] + ": " + ack;
            newTransaction.GatewayDebugResponse = HttpUtility.UrlDecode(responseString);

            PaymentStatusName paymentStatus = order.PaymentStatus;

            if (ackType == AckValues.Success || ackType == AckValues.SuccessWithWarning)
            {
                bool paymentAmountMatches = (amount.GetValueOrDefault(-1) == order.Total.Value);
                if (paymentAmountMatches)
                {
                    paymentStatus         = PaymentStatusName.Completed;
                    newTransaction.Amount = amount;
                }
                else
                {
                    newTransaction.GatewayError = string.Format("Payment amount does not match Order Total. Order total {0:N2}. Payment amount {1:N2}", order.Total, amount);
                    if (amount >= order.Total)
                    {
                        paymentStatus         = PaymentStatusName.Completed;
                        newTransaction.Amount = amount;
                    }
                    else
                    {
                        paymentStatus = PaymentStatusName.Pending;
                    }
                }
            }
            else
            {
                newTransaction.GatewayError = ack + " Invalid / Unknown Response from Payment Provider";
                paymentStatus = PaymentStatusName.ProviderError;
            }
            newTransaction.Save();

            return(paymentStatus);
        }
        // Step 2.
        private Dictionary <string, string> GetExpressCheckoutDetails(Order order, string payPalToken)
        {
            // TODO - implement this so we can get and update the Tax, Shipping, etc. amounts for the order

            Dictionary <string, string> vars = new Dictionary <string, string>();

            vars["METHOD"]  = "GetExpressCheckoutDetails";
            vars["VERSION"] = "60.0";

            vars["USER"]      = ApiUsername;
            vars["PWD"]       = ApiPassword;
            vars["SIGNATURE"] = ApiSignature;

            vars["TOKEN"] = payPalToken;

            IEnumerable <OrderItem> orderitems = order.OrderItemCollectionByOrderId;
            int itemNumber = 0;

            foreach (OrderItem orderItem in orderitems)
            {
                string orderItemAttributes = orderItem.GetProductFieldDataPlainTextDisplayString();

                vars["L_NAME" + itemNumber]   = orderItem.Name.Left(127);
                vars["L_DESC" + itemNumber]   = (!string.IsNullOrEmpty(orderItemAttributes) ? " (" + orderItemAttributes + ")" : "").Left(127);
                vars["L_AMT" + itemNumber]    = orderItem.PriceForSingleItem.ToString("N2");
                vars["L_QTY" + itemNumber]    = orderItem.Quantity.Value.ToString("N0");
                vars["L_NUMBER" + itemNumber] = (!string.IsNullOrEmpty(orderItem.Sku) ? " (" + orderItem.Sku + ")" : "");

                itemNumber++;
            }

            // Send Http POST to PayPal
            HttpWebResponse webResponse    = HttpHelper.HttpPost(ProviderUrl, vars);
            string          responseString = HttpHelper.WebResponseToString(webResponse);

            // Decode the response fields from PayPal API
            Dictionary <string, string> fields = HttpHelper.DecodeVarsFromHttpString(responseString);

            // parse response fields into variables
            string    ack     = fields.TryGetValueOrEmpty("ACK");
            AckValues ackType = WA.Enum <AckValues> .TryParseOrDefault(ack, AckValues.Failure);

            PaymentTransaction newTransaction = new PaymentTransaction();

            newTransaction.OrderId              = order.Id;
            newTransaction.PaymentProviderId    = this.ProviderId;
            newTransaction.GatewayUrl           = ProviderUrl;
            newTransaction.GatewayTransactionId = payPalToken;
            newTransaction.GatewayResponse      = vars["METHOD"] + ": " + ack;
            newTransaction.GatewayDebugResponse = HttpUtility.UrlDecode(responseString);
            //newTransaction.Amount = order.Total;

            if (ackType == AckValues.Success || ackType == AckValues.SuccessWithWarning)
            {
                // nothing
            }
            else
            {
                newTransaction.GatewayError = ack + " Invalid / Unknown Response from Payment Provider";
            }
            newTransaction.Save();


            return(fields);
        }
        // Step 1.
        public string SetExpressCheckoutAndGetToken(Order order, string cancelUrl, string returnUrl)
        {
            Dictionary <string, string> vars = new Dictionary <string, string>();

            vars["METHOD"]  = "SetExpressCheckout";
            vars["VERSION"] = "60.0";

            vars["USER"]      = ApiUsername;
            vars["PWD"]       = ApiPassword;
            vars["SIGNATURE"] = ApiSignature;

            vars["RETURNURL"] = returnUrl;
            vars["CANCELURL"] = cancelUrl;

            //vars["CUSTOM"] = order.Id.HasValue ? order.Id.Value.ToString().Left(256) : "";
            //vars["INVNUM"] = order.Id.HasValue ? order.Id.Value.ToString().Left(127) : "";
            vars["CUSTOM"] = order.OrderNumber.Left(256);
            vars["INVNUM"] = order.OrderNumber.Left(127);

            vars["PAYMENTACTION"] = "Sale";
            vars["AMT"]           = order.Total.Value.ToString("N2");
            vars["CURRENCYCODE"]  = order.UpToStoreByStoreId.Currency.Code;

            if (order.HasNonEmptyShippingAddress())
            {
                // Address Override (for when user choose PayPal on the Payment Screen, instead of the "check out with paypal" button)
                vars["ADDROVERRIDE"] = "1";

                vars["SHIPTONAME"]        = order.ShipRecipientName + (!string.IsNullOrEmpty(order.ShipRecipientBusinessName) ? " " + order.ShipRecipientBusinessName : "");
                vars["SHIPTOSTREET"]      = order.ShipAddress1;
                vars["SHIPTOSTREET2"]     = order.ShipAddress2;
                vars["SHIPTOCITY"]        = order.ShipCity;
                vars["SHIPTOSTATE"]       = order.ShipRegion;
                vars["SHIPTOZIP"]         = order.ShipPostalCode;
                vars["SHIPTOCOUNTRYCODE"] = order.ShipCountryCode;
            }

            IEnumerable <OrderItem> orderitems = order.OrderItemCollectionByOrderId;
            int itemNumber = 0;

            foreach (OrderItem orderItem in orderitems)
            {
                string orderItemAttributes = orderItem.GetProductFieldDataPlainTextDisplayString();

                vars["L_NAME" + itemNumber]   = orderItem.Name.Left(127);
                vars["L_DESC" + itemNumber]   = (!string.IsNullOrEmpty(orderItemAttributes) ? " (" + orderItemAttributes + ")" : "").Left(127);
                vars["L_AMT" + itemNumber]    = orderItem.PriceForSingleItem.ToString("N2");
                vars["L_QTY" + itemNumber]    = orderItem.Quantity.Value.ToString("N0");
                vars["L_NUMBER" + itemNumber] = (!string.IsNullOrEmpty(orderItem.Sku) ? " (" + orderItem.Sku + ")" : "");

                itemNumber++;
            }

            // Send Http POST to PayPal
            HttpWebResponse webResponse    = HttpHelper.HttpPost(ProviderUrl, vars);
            string          responseString = HttpHelper.WebResponseToString(webResponse);

            // Decode the response fields from PayPal API
            Dictionary <string, string> fields = HttpHelper.DecodeVarsFromHttpString(responseString);

            // parse response fields into variables
            string    ack     = fields["ACK"];
            AckValues ackType = WA.Enum <AckValues> .TryParseOrDefault(ack, AckValues.Failure);

            string correlationId = fields.TryGetValueOrEmpty("CORRELATIONID");
            string transactionId = fields.TryGetValueOrEmpty("TOKEN");

            //string error1Code = fields.TryGetValueOrEmpty("L_ERRORCODE0");
            //string error1ShortMsg = fields.TryGetValueOrEmpty("L_SHORTMESSAGE0");
            //string error1LongMsg = fields.TryGetValueOrEmpty("L_LONGMESSAGE0");
            //string error1Severity = fields.TryGetValueOrEmpty("L_SEVERITYCODE0");

            PaymentTransaction newTransaction = new PaymentTransaction();

            newTransaction.OrderId              = order.Id;
            newTransaction.PaymentProviderId    = this.ProviderId;
            newTransaction.GatewayUrl           = ProviderUrl;
            newTransaction.GatewayTransactionId = transactionId;
            newTransaction.GatewayResponse      = vars["METHOD"] + ": " + ack;
            newTransaction.GatewayDebugResponse = HttpUtility.UrlDecode(responseString);
            newTransaction.Amount = order.Total;

            string token = "";

            if (ackType == AckValues.Success || ackType == AckValues.SuccessWithWarning)
            {
                token = transactionId;
            }
            else
            {
                token = "";
                newTransaction.GatewayError = ack + " Invalid / Unknown Response from Payment Provider";
            }
            newTransaction.Save();

            return(token);
        }