/// <summary>
        /// Processes the Authorize and AuthorizeAndCapture transactions
        /// </summary>
        /// <param name="invoice">The <see cref="IInvoice"/> to be paid</param>
        /// <param name="payment">The <see cref="Core.Models.IPayment"/> record</param>
        /// <param name="args"></param>
        /// <returns>The <see cref="Core.Gateways.Payment.IPaymentResult"/></returns>
        public IPaymentResult InitializePayment(IInvoice invoice, IPayment payment, CreditCard creditCard)
        {
            try
            {
                #region New

                //Credit Card Information
                var models   = Map(invoice, payment, creditCard);
                var response = CardConnectService.AuthorizeTransaction(models);

                #endregion New

                if (response != null)
                {
                    //Get the CardConnect Response
                    switch (response.Respstat)
                    {
                    case "A":
                    {
                        payment.Collected  = false;
                        payment.Authorized = true;
                        payment.SaveCarConnectTransactionRecord(response);
                        GatewayProviderService service = new GatewayProviderService();
                        service.ApplyPaymentToInvoice(
                            payment.Key,
                            invoice.Key, Core.AppliedPaymentType.Debit,
                            "CardConnect: " + response.Resptext + " Authorized Amount " + string.Format("{0:C}", invoice.Total) + " for Capture... RetRef: " + response.Retref,
                            0
                            );

                        //If the payment was acepted, redirect the user to a thank you landing page
                        return(new PaymentResult(Attempt <IPayment> .Succeed(payment), invoice, true));
                    }

                    case "B":
                    case "C":
                    default:
                    {
                        payment.Collected  = false;
                        payment.Authorized = false;
                        //If the payment was'nt acepted, redirect the user to a Cancel Url
                        return(new PaymentResult(Attempt <IPayment> .Fail(payment, new Exception("CardConnect: " + response.Resptext)), invoice, true));
                    }
                    }
                }
                else
                {
                    payment.Collected  = false;
                    payment.Authorized = false;
                    return(new PaymentResult(Attempt <IPayment> .Fail(payment, new Exception("CardConnect: Null Response")), invoice, true));
                }
            }
            catch (Exception ex)
            {
                payment.Collected  = false;
                payment.Authorized = false;
                return(new PaymentResult(Attempt <IPayment> .Fail(payment, ex), invoice, true));
            }
        }
        public IPaymentResult CapturePayment(IInvoice invoice, IPayment payment, decimal amount, bool isPartialPayment)
        {
            try
            {
                payment.ExtendedData.SetValue(Constants.ExtendedDataKeys.PaymentCaptured, "true");
                payment.Collected = true;
            }
            catch (Exception ex)
            {
                return(new PaymentResult(Attempt <IPayment> .Fail(payment, ex), invoice, false));
            }

            return(new PaymentResult(Attempt <IPayment> .Succeed(payment), invoice, true));
        }
        /// <summary>
        /// Create a dictionary with credentials for SagePay service.
        /// </summary>
        /// <param name="settings"></param>
        /// <returns></returns>
        //private static Dictionary<string, string> CreateSagePayApiConfig(SagePayProcessorSettings settings)
        //{
        //    return new Dictionary<string, string>
        //            {
        //                {"mode", GetModeString(settings.LiveMode)},
        //                {"account.vendorName", settings.VendorName},
        //                {"account.encyptionPassword", settings.EncyptionPassword},
        //                {"account.apiVersion", settings.ApiVersion}
        //            };
        //}


        public IPaymentResult AuthorizePayment(IInvoice invoice, IPayment payment)
        {
            try
            {
                payment.ExtendedData.SetValue(Constants.ExtendedDataKeys.PaymentAuthorized, "true");
                payment.Authorized = true;
            }
            catch (Exception ex)
            {
                return(new PaymentResult(Attempt <IPayment> .Fail(payment, ex), invoice, false));
            }

            return(new PaymentResult(Attempt <IPayment> .Succeed(payment), invoice, true));
        }
        public IPaymentResult CapturePayment(IInvoice invoice, IPayment payment, decimal amount, bool isPartialPayment)
        {
            try
            {
                payment.ExtendedData.SetValue(Constants.ExtendedDataKeys.PaymentCaptured, "true");
                payment.Collected = true;
            }
            catch (Exception ex)
            {
                return new PaymentResult(Attempt<IPayment>.Fail(payment, ex), invoice, false);
            }

            return new PaymentResult(Attempt<IPayment>.Succeed(payment), invoice, true);
        }
		/// <summary>
		/// Create a dictionary with credentials for SagePay service.
		/// </summary>
		/// <param name="settings"></param>
		/// <returns></returns>
        //private static Dictionary<string, string> CreateSagePayApiConfig(SagePayProcessorSettings settings)
        //{
        //    return new Dictionary<string, string>
        //            {
        //                {"mode", GetModeString(settings.LiveMode)},
        //                {"account.vendorName", settings.VendorName},
        //                {"account.encyptionPassword", settings.EncyptionPassword},
        //                {"account.apiVersion", settings.ApiVersion}
        //            };
        //}
     

        public IPaymentResult AuthorizePayment(IInvoice invoice, IPayment payment)
        {         
            try
            {
                payment.ExtendedData.SetValue(Constants.ExtendedDataKeys.PaymentAuthorized, "true");
                payment.Authorized = true;
            }
            catch (Exception ex)
            {
                return new PaymentResult(Attempt<IPayment>.Fail(payment, ex), invoice, false);
            }

            return new PaymentResult(Attempt<IPayment>.Succeed(payment), invoice, true);
        }
 private CardConnectAuthorizationRequestModel Map(IInvoice invoice, IPayment payment, CreditCard creditCard)
 {
     return(new CardConnectAuthorizationRequestModel
     {
         Merchid = MerchId,
         Accttype = creditCard.CreditCardType,
         Account = creditCard.CardNumber,
         Name = creditCard.CardholderName,
         Expiry = creditCard.ExpireMonth + creditCard.ExpireYear,
         Cvv2 = creditCard.CardCode,
         Amount = payment.Amount,
         Currency = invoice.CurrencyCode,
         Orderid = invoice.InvoiceNumber,
         Address = invoice.BillToAddress1,
         City = invoice.BillToLocality,
         Region = invoice.BillToRegion,
         Country = invoice.BillToCountryCode,
         Postal = invoice.BillToPostalCode,
         Tokenize = "Y",
         Capture = "Y"
     });
 }
Пример #7
0
        /// <summary>
        /// Create a dictionary with credentials for SagePay service.
        /// </summary>
        /// <param name="settings"></param>
        /// <returns></returns>
        //private static Dictionary<string, string> CreateSagePayApiConfig(SagePayProcessorSettings settings)
        //{
        //    return new Dictionary<string, string>
        //            {
        //                {"mode", GetModeString(settings.LiveMode)},
        //                {"account.vendorName", settings.VendorName},
        //                {"account.encyptionPassword", settings.EncyptionPassword},
        //                {"account.apiVersion", settings.ApiVersion}
        //            };
        //}


        /// <summary>
        /// Processes the Authorize and AuthorizeAndCapture transactions
        /// </summary>
        /// <param name="invoice">The <see cref="IInvoice"/> to be paid</param>
        /// <param name="payment">The <see cref="Core.Models.IPayment"/> record</param>
        /// <param name="args"></param>
        /// <returns>The <see cref="Core.Gateways.Payment.IPaymentResult"/></returns>
        public IPaymentResult InitializePayment(IInvoice invoice, IPayment payment, ProcessorArgumentCollection args)
        {
            try
            {
                // Gather SagePay settings info and formulate it into a Dictionary for posting to the gateway
                var sagePayFormIntegration = new SagePayFormIntegration(Settings);
                var request = sagePayFormIntegration.FormPaymentRequest();
                SetSagePayApiData(request, invoice, payment);

                // Do basic validation as per the SagePay kit
                var errors = sagePayFormIntegration.Validation(request);
                if (errors.Count > 0)
                {
                    return(new PaymentResult(Attempt <IPayment> .Fail(payment, base.CreateErrorResult(errors)), invoice, true));
                }

                // Use the SagePay methods to encrypt the form so it can be posted
                sagePayFormIntegration.ProcessRequest(request);


                // Prepare a HTTP post
                var content =
                    new FormUrlEncodedContent(
                        new Dictionary <string, string>
                {
                    { "VPSProtocol", Settings.ApiVersion },
                    { "TxType", Constants.TransactionType },
                    { "Vendor", Settings.VendorName },
                    //{"Profile", "LOW"},  // Provider setting?
                    { "Crypt", request.Crypt }
                });
                try
                {
                    // Post the form to SagePay VSP
                    var formPaymentUrl = string.Format("https://{0}.sagepay.com/gateway/service/vspform-register.vsp", Settings.Environment);
                    // TEST form post
                    //var formPaymentUrl = string.Format("https://{0}.sagepay.com/showpost/showpost.asp", GetModeString(Settings.LiveMode));

                    var result = new HttpClient().PostAsync(formPaymentUrl, content).Result;

                    // Store transaction details in ExtendedData
                    payment.ExtendedData.SetValue(Constants.ExtendedDataKeys.VendorTransactionCode, request.VendorTxCode);

                    // Flag an error in the backoffice if the result is not successful
                    if (!result.IsSuccessStatusCode)
                    {
                        return(new PaymentResult(Attempt <IPayment> .Fail(payment, CreateErrorResult(result.RequestMessage.Content)), invoice, true));
                    }

                    // Process the response from SagePay - this contains the redirect URL for the customer to complete their payment.
                    var redirectUrl = result.RequestMessage.RequestUri.ToString();
                    payment.ExtendedData.SetValue(Constants.ExtendedDataKeys.SagePayPaymentUrl, redirectUrl);
                }
                catch (Exception ex)
                {
                    LogHelper.Error <SagePayFormPaymentProcessor>("SagePay form post failed. Crypt value: " + request.Crypt, ex);
                }

                // Store our site return URL and cancel URL in extendedData so it can be used in the callback
                var returnUrl = GetWebsiteUrl() + Settings.ReturnUrl;
                payment.ExtendedData.SetValue(Constants.ExtendedDataKeys.ReturnUrl, returnUrl);

                var cancelUrl = GetWebsiteUrl() + Settings.CancelUrl;
                payment.ExtendedData.SetValue(Constants.ExtendedDataKeys.CancelUrl, cancelUrl);

                return(new PaymentResult(Attempt <IPayment> .Succeed(payment), invoice, true));
            }
            catch (Exception ex)
            {
                return(new PaymentResult(Attempt <IPayment> .Fail(payment, ex), invoice, true));
            }
        }
Пример #8
0
        //TODO: refactor away to a Service that wraps the SagePay kit horribleness
        private void SetSagePayApiData(IFormPayment request, IInvoice invoice, IPayment payment)
        {
            // Get Merchello data
            var billingAddress  = invoice.GetBillingAddress();
            var shippingAddress = billingAddress;

            // Shipment - only use a shipping address if there is shipment info in the invoice
            var shipmentLineItem = invoice.ShippingLineItems().FirstOrDefault();

            if (shipmentLineItem != null)
            {
                var shipment = shipmentLineItem.ExtendedData.GetShipment <InvoiceLineItem>();
                shippingAddress = shipment.GetDestinationAddress();
            }

            // SagePay details
            request.VpsProtocol     = Settings.ProtocolVersion;
            request.TransactionType = Settings.TransactionType;
            request.Vendor          = Settings.VendorName;
            request.VendorTxCode    = SagePayFormIntegration.GetNewVendorTxCode();
            request.Amount          = payment.Amount;
            request.Currency        = invoice.CurrencyCode();
            request.Description     = "Goods from " + Settings.VendorName;

            // TODO:  Is there a basket summary I can access?  Or convert the Basket to a sagepay format

            // Set ReturnUrl and CancelUrl of SagePay request to SagePayApiController.
            Func <string, string> adjustUrl = (url) =>
            {
                if (!url.StartsWith("http"))
                {
                    url = GetWebsiteUrl() + (url[0] == '/' ? "" : "/") + url;
                }
                url = url.Replace("{invoiceKey}", invoice.Key.ToString(), StringComparison.InvariantCultureIgnoreCase);
                url = url.Replace("{paymentKey}", payment.Key.ToString(), StringComparison.InvariantCultureIgnoreCase);
                return(url);
            };

            request.SuccessUrl = adjustUrl("/umbraco/MerchelloSagePay/SagePayApi/SuccessPayment?InvoiceKey={invoiceKey}&PaymentKey={paymentKey}");
            request.FailureUrl = adjustUrl("/umbraco/MerchelloSagePay/SagePayApi/AbortPayment?InvoiceKey={invoiceKey}&PaymentKey={paymentKey}");

            // Billing details
            request.BillingSurname    = billingAddress.TrySplitLastName();
            request.BillingFirstnames = billingAddress.TrySplitFirstName();
            request.BillingAddress1   = billingAddress.Address1;
            request.BillingAddress2   = billingAddress.Address2;
            request.BillingPostCode   = billingAddress.PostalCode;
            request.BillingCity       = billingAddress.Locality;
            request.BillingCountry    = invoice.BillToCountryCode;
            request.CustomerEmail     = billingAddress.Email;

            // Shipping details
            request.DeliverySurname    = shippingAddress.TrySplitLastName();
            request.DeliveryFirstnames = shippingAddress.TrySplitFirstName();
            request.DeliveryAddress1   = shippingAddress.Address1;
            request.DeliveryCity       = shippingAddress.Locality;
            request.DeliveryCountry    = shippingAddress.CountryCode;
            request.DeliveryPostCode   = shippingAddress.PostalCode;

            //Optional
            //request.CustomerName = cart.Billing.FirstNames + " " + cart.Billing.Surname;

            //request.VendorEmail = Settings.VendorEmail;
            //request.SendEmail = Settings.SendEmail;

            //request.EmailMessage = Settings.EmailMessage;
            //request.BillingAddress2 = billingAddress.Address2;
            //request.BillingPostCode = billingAddress.PostalCode;
            //request.BillingState = billingAddress.Region;
            //request.BillingPhone = billingAddress.Phone;
            //request.DeliveryAddress2 = shippingAddress.Address2;
            //request.DeliveryPostCode = shippingAddress.PostalCode;
            //request.DeliveryState = shippingAddress.Region;
            //request.DeliveryPhone = shippingAddress.Phone;
        }
Пример #9
0
        /// <summary>
        /// Processes the Authorize and AuthorizeAndCapture transactions
        /// </summary>
        /// <param name="invoice">The <see cref="IInvoice"/> to be paid</param>
        /// <param name="payment">The <see cref="Core.Models.IPayment"/> record</param>
        /// <param name="args"></param>
        /// <returns>The <see cref="Core.Gateways.Payment.IPaymentResult"/></returns>
        public IPaymentResult InitializePayment(IInvoice invoice, IPayment payment, ProcessorArgumentCollection args)
        {
            try
            {
                var sagePayDirectIntegration = new SagePayAPIIntegration(Settings);
                var request = sagePayDirectIntegration.DirectPaymentRequest();

                var creditCard = args.AsCreditCard();

                SetSagePayApiData(request, invoice, payment, creditCard);

                // Incredibly frustratingly, the sagepay integration kit provided by sagepay does not support paypal integration with sagepay direct so if this is a paypal transaction, we have to build the post manually.

                if (request.CardType == CardType.PAYPAL)
                {
                    using (var client = new HttpClient())
                    {
                        var values = new Dictionary <string, string> {
                        };

                        // Build the post and fix the values that the integration kit breaks...
                        foreach (var property in request.GetType().GetAllProperties())
                        {
                            if (property.CanRead && property.GetValue(request) != null)
                            {
                                if (property.Name == "VpsProtocol")
                                {
                                    values.Add(property.Name, "3.00");
                                }
                                else if (property.Name == "TransactionType")
                                {
                                    values.Add("TxType", "PAYMENT");
                                }
                                else
                                {
                                    values.Add(property.Name, property.GetValue(request).ToString());
                                }
                            }
                        }

                        var content = new FormUrlEncodedContent(values);

                        var response = client.PostAsync(string.Format("https://{0}.sagepay.com/gateway/service/vspdirect-register.vsp", Settings.Environment), content).Result;

                        var responseString = response.Content.ReadAsStringAsync().Result.Replace("\r\n", "&");

                        NameValueCollection sagePayResponseValues = HttpUtility.ParseQueryString(responseString);
                        if (sagePayResponseValues["Status"] == "PPREDIRECT")
                        {
                            var redirectUrl = sagePayResponseValues["PayPalRedirectURL"] + "&token=" + sagePayResponseValues["token"];
                            payment.ExtendedData.SetValue(Constants.ExtendedDataKeys.SagePayPaymentUrl, redirectUrl);

                            var returnUrl = GetWebsiteUrl() + Settings.ReturnUrl;
                            payment.ExtendedData.SetValue(Constants.ExtendedDataKeys.ReturnUrl, returnUrl);

                            var cancelUrl = GetWebsiteUrl() + Settings.CancelUrl;
                            payment.ExtendedData.SetValue(Constants.ExtendedDataKeys.CancelUrl, cancelUrl);

                            payment.ExtendedData.SetValue(Constants.ExtendedDataKeys.SagePayTransactionCode, sagePayResponseValues["VPSTxId"]);

                            return(new PaymentResult(Attempt <IPayment> .Succeed(payment), invoice, true));
                        }
                        else
                        {
                            return(new PaymentResult(Attempt <IPayment> .Fail(payment, new Exception(sagePayResponseValues["StatusDetail"])), invoice, true));
                        }
                    }
                }

                IDirectPaymentResult result = sagePayDirectIntegration.ProcessDirectPaymentRequest(request, string.Format("https://{0}.sagepay.com/gateway/service/vspdirect-register.vsp", Settings.Environment));



                if (result.Status == ResponseStatus.OK)
                {
                    payment.Collected  = true;
                    payment.Authorized = true;
                    GatewayProviderService service = new GatewayProviderService();
                    service.ApplyPaymentToInvoice(payment.Key, invoice.Key, Core.AppliedPaymentType.Debit, "SagePay: capture authorized", invoice.Total);
                    return(new PaymentResult(Attempt <IPayment> .Succeed(payment), invoice, true));
                }
                else if (result.Status == ResponseStatus.THREEDAUTH)
                {
                    // For 3D Secure we have to show a client side form which posts to the ACS url.
                    Func <string, string> adjustUrl = (url) =>
                    {
                        if (!url.StartsWith("http"))
                        {
                            url = GetWebsiteUrl() + (url[0] == '/' ? "" : "/") + url;
                        }
                        url = url.Replace("{invoiceKey}", invoice.Key.ToString(), StringComparison.InvariantCultureIgnoreCase);
                        url = url.Replace("{paymentKey}", payment.Key.ToString(), StringComparison.InvariantCultureIgnoreCase);
                        return(url);
                    };

                    var redirectUrl = adjustUrl("/App_Plugins/Merchello.SagePay/3dsecureRedirect.aspx?");
                    redirectUrl += "acsurl=" + Base64Encode(result.AcsUrl);
                    redirectUrl += "&PaReq=" + Base64Encode(result.PaReq);
                    redirectUrl += "&MD=" + Base64Encode(result.Md);
                    redirectUrl += "&TermUrl=" + Base64Encode(adjustUrl("/umbraco/MerchelloSagePay/SagePayApi/ThreeDSecureCallback?InvoiceKey={invoiceKey}&PaymentKey={paymentKey}"));
                    payment.ExtendedData.SetValue(Constants.ExtendedDataKeys.ThreeDSecureUrl, redirectUrl);

                    var returnUrl = GetWebsiteUrl() + Settings.ReturnUrl;
                    payment.ExtendedData.SetValue(Constants.ExtendedDataKeys.ReturnUrl, returnUrl);

                    var cancelUrl = GetWebsiteUrl() + Settings.CancelUrl;
                    payment.ExtendedData.SetValue(Constants.ExtendedDataKeys.CancelUrl, cancelUrl);

                    return(new PaymentResult(Attempt <IPayment> .Succeed(payment), invoice, true));
                }
                //else if (result.Status == ResponseStatus.PPREDIRECT)
                //{
                //    var redirectUrl = result.PayPalRedirectUrl + "&token=" + result.Token;
                //    payment.ExtendedData.SetValue(Constants.ExtendedDataKeys.SagePayPaymentUrl, redirectUrl);

                //    var returnUrl = GetWebsiteUrl() + Settings.ReturnUrl;
                //    payment.ExtendedData.SetValue(Constants.ExtendedDataKeys.ReturnUrl, returnUrl);

                //    var cancelUrl = GetWebsiteUrl() + Settings.CancelUrl;
                //    payment.ExtendedData.SetValue(Constants.ExtendedDataKeys.CancelUrl, cancelUrl);

                //    return new PaymentResult(Attempt<IPayment>.Succeed(payment), invoice, true);
                //}
                else
                {
                    return(new PaymentResult(Attempt <IPayment> .Fail(payment, new Exception(result.StatusDetail)), invoice, true));
                }
            }
            catch (Exception ex)
            {
                return(new PaymentResult(Attempt <IPayment> .Fail(payment, ex), invoice, true));
            }
        }
Пример #10
0
        //TODO: refactor away to a Service that wraps the SagePay kit horribleness
        private void SetSagePayApiData(IDirectPayment request, IInvoice invoice, IPayment payment, CreditCard creditCard)
        {
            // Get Merchello data
            //TODO - what if there is no shipping info?  e.g. Classes only - Get from billing?
            var shipmentLineItem = invoice.ShippingLineItems().FirstOrDefault();
            var shipment         = shipmentLineItem.ExtendedData.GetShipment <InvoiceLineItem>();
            var shippingAddress  = shipment.GetDestinationAddress();
            var billingAddress   = invoice.GetBillingAddress();


            // Merchello info for callback
            //request.InvoiceKey = invoice.Key;
            //request.PayerId = invoice.Pa
            //request.PaymentKey = payment.Key

            // SagePay details
            request.VpsProtocol     = Settings.ProtocolVersion;
            request.TransactionType = Settings.TransactionType;
            request.Vendor          = Settings.VendorName;
            request.VendorTxCode    = SagePayAPIIntegration.GetNewVendorTxCode();
            request.Amount          = payment.Amount;
            request.Currency        = invoice.CurrencyCode();
            request.Description     = "Goods from " + Settings.VendorName;

            // TODO:  Is there a basket summary I can access?  Or convert the Basket to a sagepay format

            // Billing details
            request.BillingSurname    = billingAddress.TrySplitLastName();
            request.BillingFirstnames = billingAddress.TrySplitFirstName();
            request.BillingAddress1   = billingAddress.Address1;
            request.BillingAddress2   = billingAddress.Address2;
            request.BillingPostCode   = billingAddress.PostalCode;
            request.BillingCity       = billingAddress.Locality;
            request.BillingCountry    = invoice.BillToCountryCode;

            // Shipping details
            request.DeliverySurname    = shippingAddress.TrySplitLastName();
            request.DeliveryFirstnames = shippingAddress.TrySplitFirstName();
            request.DeliveryAddress1   = shippingAddress.Address1;
            request.DeliveryCity       = shippingAddress.Locality;
            request.DeliveryCountry    = shippingAddress.CountryCode;
            request.DeliveryPostCode   = shippingAddress.PostalCode;

            request.CardType   = (CardType)Enum.Parse(typeof(CardType), creditCard.CreditCardType);
            request.CardHolder = creditCard.CardholderName;
            request.CardNumber = creditCard.CardNumber;
            request.ExpiryDate = creditCard.ExpireMonth + creditCard.ExpireYear;
            request.Cv2        = creditCard.CardCode;

            request.Apply3dSecure = 0;

            if (request.CardType == CardType.PAYPAL)
            {
                Func <string, string> adjustUrl = (url) =>
                {
                    if (!url.StartsWith("http"))
                    {
                        url = GetWebsiteUrl() + (url[0] == '/' ? "" : "/") + url;
                    }
                    url = url.Replace("{invoiceKey}", invoice.Key.ToString(), StringComparison.InvariantCultureIgnoreCase);
                    url = url.Replace("{paymentKey}", payment.Key.ToString(), StringComparison.InvariantCultureIgnoreCase);
                    return(url);
                };

                request.PayPalCallbackUrl = adjustUrl("/umbraco/MerchelloSagePay/SagePayApi/PaypalCallback?InvoiceKey={invoiceKey}&PaymentKey={paymentKey}");
            }

            //Optional
            //request.CustomerName = cart.Billing.FirstNames + " " + cart.Billing.Surname;
            //request.CustomerEmail = customer.Email;
            //request.VendorEmail = Settings.VendorEmail;
            //request.SendEmail = Settings.SendEmail;

            //request.EmailMessage = Settings.EmailMessage;
            //request.BillingAddress2 = billingAddress.Address2;
            //request.BillingPostCode = billingAddress.PostalCode;
            //request.BillingState = billingAddress.Region;
            //request.BillingPhone = billingAddress.Phone;
            //request.DeliveryAddress2 = shippingAddress.Address2;
            //request.DeliveryPostCode = shippingAddress.PostalCode;
            //request.DeliveryState = shippingAddress.Region;
            //request.DeliveryPhone = shippingAddress.Phone;
        }
		/// <summary>
		/// Create a dictionary with credentials for SagePay service.
		/// </summary>
		/// <param name="settings"></param>
		/// <returns></returns>
        //private static Dictionary<string, string> CreateSagePayApiConfig(SagePayProcessorSettings settings)
        //{
        //    return new Dictionary<string, string>
        //            {
        //                {"mode", GetModeString(settings.LiveMode)},
        //                {"account.vendorName", settings.VendorName},
        //                {"account.encyptionPassword", settings.EncyptionPassword},
        //                {"account.apiVersion", settings.ApiVersion}
        //            };
        //}


        /// <summary>
        /// Processes the Authorize and AuthorizeAndCapture transactions
        /// </summary>
        /// <param name="invoice">The <see cref="IInvoice"/> to be paid</param>
        /// <param name="payment">The <see cref="Core.Models.IPayment"/> record</param>
        /// <param name="args"></param>
        /// <returns>The <see cref="Core.Gateways.Payment.IPaymentResult"/></returns>
        public IPaymentResult InitializePayment(IInvoice invoice, IPayment payment, ProcessorArgumentCollection args)
        {
            try
            {            
                // Gather SagePay settings info and formulate it into a Dictionary for posting to the gateway
                var sagePayFormIntegration = new SagePayFormIntegration(Settings);
                var request = sagePayFormIntegration.FormPaymentRequest();
                SetSagePayApiData(request, invoice, payment);

                // Do basic validation as per the SagePay kit
                var errors = sagePayFormIntegration.Validation(request);
                if (errors.Count > 0)
                {
                    return new PaymentResult(Attempt<IPayment>.Fail(payment, base.CreateErrorResult(errors)), invoice, true);
                }

                // Use the SagePay methods to encrypt the form so it can be posted
                sagePayFormIntegration.ProcessRequest(request);

                
                // Prepare a HTTP post
                var content =
                        new FormUrlEncodedContent(
                            new Dictionary<string, string>
						{
							{"VPSProtocol", Settings.ApiVersion},
                            {"TxType", Constants.TransactionType},
                            {"Vendor", Settings.VendorName},
                            //{"Profile", "LOW"},  // Provider setting?
                            {"Crypt", request.Crypt}            
						});
                try
                {
                    // Post the form to SagePay VSP
                    var formPaymentUrl = string.Format("https://{0}.sagepay.com/gateway/service/vspform-register.vsp", Settings.Environment);
                    // TEST form post
                    //var formPaymentUrl = string.Format("https://{0}.sagepay.com/showpost/showpost.asp", GetModeString(Settings.LiveMode));

                    var result = new HttpClient().PostAsync(formPaymentUrl, content).Result;

                    // Store transaction details in ExtendedData
                    payment.ExtendedData.SetValue(Constants.ExtendedDataKeys.VendorTransactionCode, request.VendorTxCode);

                    // Flag an error in the backoffice if the result is not successful
                    if (!result.IsSuccessStatusCode)
                    {
                        return new PaymentResult(Attempt<IPayment>.Fail(payment, CreateErrorResult(result.RequestMessage.Content)), invoice, true);
                    }

                    // Process the response from SagePay - this contains the redirect URL for the customer to complete their payment.
                    var redirectUrl = result.RequestMessage.RequestUri.ToString();
                    payment.ExtendedData.SetValue(Constants.ExtendedDataKeys.SagePayPaymentUrl, redirectUrl);
                }
                catch (Exception ex)
                {
                    LogHelper.Error<SagePayFormPaymentProcessor>("SagePay form post failed. Crypt value: " + request.Crypt, ex);
                }

                // Store our site return URL and cancel URL in extendedData so it can be used in the callback
                var returnUrl = GetWebsiteUrl() + Settings.ReturnUrl;
                payment.ExtendedData.SetValue(Constants.ExtendedDataKeys.ReturnUrl, returnUrl);

                var cancelUrl = GetWebsiteUrl() + Settings.CancelUrl;
                payment.ExtendedData.SetValue(Constants.ExtendedDataKeys.CancelUrl, cancelUrl);

                return new PaymentResult(Attempt<IPayment>.Succeed(payment), invoice, true);

            }
            catch (Exception ex)
            {
                return new PaymentResult(Attempt<IPayment>.Fail(payment, ex), invoice, true);
            }

        }
        //TODO: refactor away to a Service that wraps the SagePay kit horribleness
        private void SetSagePayApiData(IFormPayment request, IInvoice invoice, IPayment payment)
        {
            // Get Merchello data
            var billingAddress = invoice.GetBillingAddress();
            var shippingAddress = billingAddress;

            // Shipment - only use a shipping address if there is shipment info in the invoice
            var shipmentLineItem = invoice.ShippingLineItems().FirstOrDefault();
            if (shipmentLineItem != null)
            {
                var shipment = shipmentLineItem.ExtendedData.GetShipment<InvoiceLineItem>();
                shippingAddress = shipment.GetDestinationAddress();
            }
            
            // SagePay details
            request.VpsProtocol = Settings.ProtocolVersion;
            request.TransactionType = Settings.TransactionType;
            request.Vendor = Settings.VendorName;
            request.VendorTxCode = SagePayFormIntegration.GetNewVendorTxCode();
            request.Amount = payment.Amount;
            request.Currency = invoice.CurrencyCode();
            request.Description = "Goods from " + Settings.VendorName;
            
            // TODO:  Is there a basket summary I can access?  Or convert the Basket to a sagepay format

            // Set ReturnUrl and CancelUrl of SagePay request to SagePayApiController.
            Func<string, string> adjustUrl = (url) =>
            {
                if (!url.StartsWith("http")) url = GetWebsiteUrl() + (url[0] == '/' ? "" : "/") + url;
                url = url.Replace("{invoiceKey}", invoice.Key.ToString(), StringComparison.InvariantCultureIgnoreCase);
                url = url.Replace("{paymentKey}", payment.Key.ToString(), StringComparison.InvariantCultureIgnoreCase);
                return url;
            };

            request.SuccessUrl = adjustUrl("/umbraco/MerchelloSagePay/SagePayApi/SuccessPayment?InvoiceKey={invoiceKey}&PaymentKey={paymentKey}");
            request.FailureUrl = adjustUrl("/umbraco/MerchelloSagePay/SagePayApi/AbortPayment?InvoiceKey={invoiceKey}&PaymentKey={paymentKey}");

            // Billing details
            request.BillingSurname = billingAddress.TrySplitLastName();
            request.BillingFirstnames = billingAddress.TrySplitFirstName();
            request.BillingAddress1 = billingAddress.Address1;
            request.BillingAddress2 = billingAddress.Address2;
            request.BillingPostCode = billingAddress.PostalCode;
            request.BillingCity = billingAddress.Locality;
            request.BillingCountry = invoice.BillToCountryCode;
            request.CustomerEmail = billingAddress.Email;

            // Shipping details
            request.DeliverySurname = shippingAddress.TrySplitLastName();
            request.DeliveryFirstnames = shippingAddress.TrySplitFirstName();
            request.DeliveryAddress1 = shippingAddress.Address1;
            request.DeliveryCity = shippingAddress.Locality;
            request.DeliveryCountry = shippingAddress.CountryCode;
            request.DeliveryPostCode = shippingAddress.PostalCode;

            //Optional
            //request.CustomerName = cart.Billing.FirstNames + " " + cart.Billing.Surname;
            
            //request.VendorEmail = Settings.VendorEmail;
            //request.SendEmail = Settings.SendEmail;

            //request.EmailMessage = Settings.EmailMessage;
            //request.BillingAddress2 = billingAddress.Address2;
            //request.BillingPostCode = billingAddress.PostalCode;
            //request.BillingState = billingAddress.Region;
            //request.BillingPhone = billingAddress.Phone;
            //request.DeliveryAddress2 = shippingAddress.Address2;
            //request.DeliveryPostCode = shippingAddress.PostalCode;
            //request.DeliveryState = shippingAddress.Region;
            //request.DeliveryPhone = shippingAddress.Phone;

        }
        /// <summary>
        /// Processes the Authorize and AuthorizeAndCapture transactions
        /// </summary>
        /// <param name="invoice">The <see cref="IInvoice"/> to be paid</param>
        /// <param name="payment">The <see cref="Core.Models.IPayment"/> record</param>
        /// <param name="args"></param>
        /// <returns>The <see cref="Core.Gateways.Payment.IPaymentResult"/></returns>
        public IPaymentResult InitializePayment(IInvoice invoice, IPayment payment, ProcessorArgumentCollection args)
        {
            try
            {            
                var sagePayDirectIntegration = new SagePayAPIIntegration(Settings);
                var request = sagePayDirectIntegration.DirectPaymentRequest();

                var creditCard = args.AsCreditCard();
                 
                SetSagePayApiData(request, invoice, payment, creditCard);                

                // Incredibly frustratingly, the sagepay integration kit provided by sagepay does not support paypal integration with sagepay direct so if this is a paypal transaction, we have to build the post manually.

                if (request.CardType == CardType.PAYPAL)
                {
                    using (var client = new HttpClient())
                    {
                        var values = new Dictionary<string, string> { };

                        // Build the post and fix the values that the integration kit breaks...
                        foreach (var property in request.GetType().GetAllProperties())
                        {
                            if (property.CanRead && property.GetValue(request) != null)
                            {
                                if (property.Name == "VpsProtocol")
                                {
                                    values.Add(property.Name, "3.00");
                                }
                                else if (property.Name == "TransactionType")
                                {
                                    values.Add("TxType", "PAYMENT");
                                }
                                else
                                {
                                    values.Add(property.Name, property.GetValue(request).ToString());
                                }
                            }
                        }

                        var content = new FormUrlEncodedContent(values);

                        var response = client.PostAsync(string.Format("https://{0}.sagepay.com/gateway/service/vspdirect-register.vsp", Settings.Environment), content).Result;

                        var responseString = response.Content.ReadAsStringAsync().Result.Replace("\r\n", "&");

                        NameValueCollection sagePayResponseValues = HttpUtility.ParseQueryString(responseString);
                        if (sagePayResponseValues["Status"] == "PPREDIRECT")
                        {
                            var redirectUrl = sagePayResponseValues["PayPalRedirectURL"] + "&token=" + sagePayResponseValues["token"];
                            payment.ExtendedData.SetValue(Constants.ExtendedDataKeys.SagePayPaymentUrl, redirectUrl);

                            var returnUrl = GetWebsiteUrl() + Settings.ReturnUrl;
                            payment.ExtendedData.SetValue(Constants.ExtendedDataKeys.ReturnUrl, returnUrl);

                            var cancelUrl = GetWebsiteUrl() + Settings.CancelUrl;
                            payment.ExtendedData.SetValue(Constants.ExtendedDataKeys.CancelUrl, cancelUrl);

                            payment.ExtendedData.SetValue(Constants.ExtendedDataKeys.SagePayTransactionCode, sagePayResponseValues["VPSTxId"]);

                            return new PaymentResult(Attempt<IPayment>.Succeed(payment), invoice, true);

                        }
                        else
                        {
                            return new PaymentResult(Attempt<IPayment>.Fail(payment, new Exception(sagePayResponseValues["StatusDetail"])), invoice, true);
                        }


                    }

                }

                IDirectPaymentResult result = sagePayDirectIntegration.ProcessDirectPaymentRequest(request, string.Format("https://{0}.sagepay.com/gateway/service/vspdirect-register.vsp", Settings.Environment));
                

                
                if (result.Status == ResponseStatus.OK)
                {
                    payment.Collected = true;
                    payment.Authorized = true;
                    GatewayProviderService service = new GatewayProviderService();
                    service.ApplyPaymentToInvoice(payment.Key, invoice.Key, Core.AppliedPaymentType.Debit, "SagePay: capture authorized", invoice.Total);
                    return new PaymentResult(Attempt<IPayment>.Succeed(payment), invoice, true);
                }
                else if (result.Status == ResponseStatus.THREEDAUTH)
                {
                    
                    // For 3D Secure we have to show a client side form which posts to the ACS url. 
                      Func<string, string> adjustUrl = (url) =>
                {
                    if (!url.StartsWith("http")) url = GetWebsiteUrl() + (url[0] == '/' ? "" : "/") + url;
                    url = url.Replace("{invoiceKey}", invoice.Key.ToString(), StringComparison.InvariantCultureIgnoreCase);
                    url = url.Replace("{paymentKey}", payment.Key.ToString(), StringComparison.InvariantCultureIgnoreCase);
                    return url;
                };

                    var redirectUrl = adjustUrl("/App_Plugins/Merchello.SagePay/3dsecureRedirect.aspx?");
                    redirectUrl += "acsurl=" + Base64Encode(result.AcsUrl);
                    redirectUrl += "&PaReq=" + Base64Encode(result.PaReq);
                    redirectUrl += "&MD=" + Base64Encode(result.Md);
                    redirectUrl += "&TermUrl=" + Base64Encode(adjustUrl("/umbraco/MerchelloSagePay/SagePayApi/ThreeDSecureCallback?InvoiceKey={invoiceKey}&PaymentKey={paymentKey}"));
                    payment.ExtendedData.SetValue(Constants.ExtendedDataKeys.ThreeDSecureUrl, redirectUrl);

                    var returnUrl = GetWebsiteUrl() + Settings.ReturnUrl;
                    payment.ExtendedData.SetValue(Constants.ExtendedDataKeys.ReturnUrl, returnUrl);

                    var cancelUrl = GetWebsiteUrl() + Settings.CancelUrl;
                    payment.ExtendedData.SetValue(Constants.ExtendedDataKeys.CancelUrl, cancelUrl);

                    return new PaymentResult(Attempt<IPayment>.Succeed(payment), invoice, true);

                }
                //else if (result.Status == ResponseStatus.PPREDIRECT)
                //{
                //    var redirectUrl = result.PayPalRedirectUrl + "&token=" + result.Token;
                //    payment.ExtendedData.SetValue(Constants.ExtendedDataKeys.SagePayPaymentUrl, redirectUrl);

                //    var returnUrl = GetWebsiteUrl() + Settings.ReturnUrl;
                //    payment.ExtendedData.SetValue(Constants.ExtendedDataKeys.ReturnUrl, returnUrl);

                //    var cancelUrl = GetWebsiteUrl() + Settings.CancelUrl;
                //    payment.ExtendedData.SetValue(Constants.ExtendedDataKeys.CancelUrl, cancelUrl);

                //    return new PaymentResult(Attempt<IPayment>.Succeed(payment), invoice, true);
                //}
                else
                {
                    return new PaymentResult(Attempt<IPayment>.Fail(payment, new Exception(result.StatusDetail)), invoice, true);
                }

            }
            catch (Exception ex)
            {
                return new PaymentResult(Attempt<IPayment>.Fail(payment, ex), invoice, true);
            }

        }
        //TODO: refactor away to a Service that wraps the SagePay kit horribleness
        private void SetSagePayApiData(IDirectPayment request, IInvoice invoice, IPayment payment, CreditCard creditCard)
        {
            // Get Merchello data
            //TODO - what if there is no shipping info?  e.g. Classes only - Get from billing?
            var shipmentLineItem = invoice.ShippingLineItems().FirstOrDefault();
            var shipment = shipmentLineItem.ExtendedData.GetShipment<InvoiceLineItem>();
            var shippingAddress = shipment.GetDestinationAddress();
            var billingAddress = invoice.GetBillingAddress(); 
            

            // Merchello info for callback
            //request.InvoiceKey = invoice.Key;
            //request.PayerId = invoice.Pa
            //request.PaymentKey = payment.Key
            
            // SagePay details
            request.VpsProtocol = Settings.ProtocolVersion;
            request.TransactionType = Settings.TransactionType;
            request.Vendor = Settings.VendorName;
            request.VendorTxCode = SagePayAPIIntegration.GetNewVendorTxCode();
            request.Amount = payment.Amount;
            request.Currency = invoice.CurrencyCode();
            request.Description = "Goods from " + Settings.VendorName;
            
            // TODO:  Is there a basket summary I can access?  Or convert the Basket to a sagepay format

            // Billing details
            request.BillingSurname = billingAddress.TrySplitLastName();
            request.BillingFirstnames = billingAddress.TrySplitFirstName();
            request.BillingAddress1 = billingAddress.Address1;
            request.BillingAddress2 = billingAddress.Address2;
            request.BillingPostCode = billingAddress.PostalCode;
            request.BillingCity = billingAddress.Locality;
            request.BillingCountry = invoice.BillToCountryCode;

            // Shipping details
            request.DeliverySurname = shippingAddress.TrySplitLastName();
            request.DeliveryFirstnames = shippingAddress.TrySplitFirstName();
            request.DeliveryAddress1 = shippingAddress.Address1;
            request.DeliveryCity = shippingAddress.Locality;
            request.DeliveryCountry = shippingAddress.CountryCode;
            request.DeliveryPostCode = shippingAddress.PostalCode;

            request.CardType = (CardType)Enum.Parse(typeof(CardType), creditCard.CreditCardType);
            request.CardHolder = creditCard.CardholderName;
            request.CardNumber = creditCard.CardNumber;
            request.ExpiryDate = creditCard.ExpireMonth + creditCard.ExpireYear;
            request.Cv2 = creditCard.CardCode;

            request.Apply3dSecure = 0;

            if (request.CardType == CardType.PAYPAL)
            {
                Func<string, string> adjustUrl = (url) =>
                {
                    if (!url.StartsWith("http")) url = GetWebsiteUrl() + (url[0] == '/' ? "" : "/") + url;
                    url = url.Replace("{invoiceKey}", invoice.Key.ToString(), StringComparison.InvariantCultureIgnoreCase);
                    url = url.Replace("{paymentKey}", payment.Key.ToString(), StringComparison.InvariantCultureIgnoreCase);
                    return url;
                };

                request.PayPalCallbackUrl = adjustUrl("/umbraco/MerchelloSagePay/SagePayApi/PaypalCallback?InvoiceKey={invoiceKey}&PaymentKey={paymentKey}");
            }

            //Optional
            //request.CustomerName = cart.Billing.FirstNames + " " + cart.Billing.Surname;
            //request.CustomerEmail = customer.Email;
            //request.VendorEmail = Settings.VendorEmail;
            //request.SendEmail = Settings.SendEmail;

            //request.EmailMessage = Settings.EmailMessage;
            //request.BillingAddress2 = billingAddress.Address2;
            //request.BillingPostCode = billingAddress.PostalCode;
            //request.BillingState = billingAddress.Region;
            //request.BillingPhone = billingAddress.Phone;
            //request.DeliveryAddress2 = shippingAddress.Address2;
            //request.DeliveryPostCode = shippingAddress.PostalCode;
            //request.DeliveryState = shippingAddress.Region;
            //request.DeliveryPhone = shippingAddress.Phone;

        }