/// <summary>
        /// Captures payment
        /// </summary>
        /// <param name="capturePaymentRequest">Capture payment request</param>
        /// <returns>Capture payment result</returns>
        public CapturePaymentResult Capture(CapturePaymentRequest capturePaymentRequest)
        {
            var result = new CapturePaymentResult();

            try
            {
                var apiContext    = PaypalHelper.GetApiContext(_paypalDirectPaymentSettings);
                var authorization = Authorization.Get(apiContext, capturePaymentRequest.Order.AuthorizationTransactionId);
                var currency      = _currencyService.GetCurrencyById(_currencySettings.PrimaryStoreCurrencyId);
                var capture       = new Capture
                {
                    amount = new Amount
                    {
                        total    = capturePaymentRequest.Order.OrderTotal.ToString("N", new CultureInfo("en-US")),
                        currency = currency != null ? currency.CurrencyCode : null
                    },
                    is_final_capture = true
                };
                capture = authorization.Capture(apiContext, capture);

                result.CaptureTransactionId     = capture.id;
                result.CaptureTransactionResult = capture.state;
                result.NewPaymentStatus         = GetPaymentStatus(capture.state);
            }
            catch (PayPal.PayPalException exc)
            {
                if (exc is PayPal.ConnectionException)
                {
                    var error = JsonFormatter.ConvertFromJson <Error>((exc as PayPal.ConnectionException).Response);
                    if (error != null)
                    {
                        result.AddError(string.Format("PayPal error: {0} ({1})", error.message, error.name));
                        if (error.details != null)
                        {
                            error.details.ForEach(x => result.AddError(string.Format("{0} {1}", x.field, x.issue)));
                        }
                    }
                }

                //if there are not the specific errors add exception message
                if (result.Success)
                {
                    result.AddError(exc.InnerException != null ? exc.InnerException.Message : exc.Message);
                }
            }

            return(result);
        }
        /// <summary>
        /// Uninstall the plugin
        /// </summary>
        public override void Uninstall()
        {
            //delete webhook
            var settings = _settingService.LoadSetting <PayPalDirectPaymentSettings>();

            if (!string.IsNullOrEmpty(settings.WebhookId))
            {
                try
                {
                    var apiContext = PaypalHelper.GetApiContext(settings);
                    Webhook.Delete(apiContext, settings.WebhookId);
                }
                catch (PayPal.PayPalException) { }
            }

            //settings
            _settingService.DeleteSetting <PayPalDirectPaymentSettings>();

            //locales
            this.DeletePluginLocaleResource("Plugins.Payments.PayPalDirect.Fields.AdditionalFee");
            this.DeletePluginLocaleResource("Plugins.Payments.PayPalDirect.Fields.AdditionalFee.Hint");
            this.DeletePluginLocaleResource("Plugins.Payments.PayPalDirect.Fields.AdditionalFeePercentage");
            this.DeletePluginLocaleResource("Plugins.Payments.PayPalDirect.Fields.AdditionalFeePercentage.Hint");
            this.DeletePluginLocaleResource("Plugins.Payments.PayPalDirect.Fields.ClientId");
            this.DeletePluginLocaleResource("Plugins.Payments.PayPalDirect.Fields.ClientId.Hint");
            this.DeletePluginLocaleResource("Plugins.Payments.PayPalDirect.Fields.ClientSecret");
            this.DeletePluginLocaleResource("Plugins.Payments.PayPalDirect.Fields.ClientSecret.Hint");
            this.DeletePluginLocaleResource("Plugins.Payments.PayPalDirect.Fields.TransactMode");
            this.DeletePluginLocaleResource("Plugins.Payments.PayPalDirect.Fields.TransactMode.Hint");
            this.DeletePluginLocaleResource("Plugins.Payments.PayPalDirect.Fields.UseSandbox");
            this.DeletePluginLocaleResource("Plugins.Payments.PayPalDirect.Fields.UseSandbox.Hint");
            this.DeletePluginLocaleResource("Plugins.Payments.PayPalDirect.Fields.WebhookId");
            this.DeletePluginLocaleResource("Plugins.Payments.PayPalDirect.Fields.WebhookId.Hint");
            this.DeletePluginLocaleResource("Plugins.Payments.PayPalDirect.PaymentMethodDescription");
            this.DeletePluginLocaleResource("Plugins.Payments.PayPalDirect.WebhookCreate");
            this.DeletePluginLocaleResource("Plugins.Payments.PayPalDirect.WebhookError");

            base.Uninstall();
        }
        /// <summary>
        /// Cancels a recurring payment
        /// </summary>
        /// <param name="cancelPaymentRequest">Request</param>
        /// <returns>Result</returns>
        public CancelRecurringPaymentResult CancelRecurringPayment(CancelRecurringPaymentRequest cancelPaymentRequest)
        {
            var result = new CancelRecurringPaymentResult();

            try
            {
                var apiContext   = PaypalHelper.GetApiContext(_paypalDirectPaymentSettings);
                var subscription = Agreement.Get(apiContext, cancelPaymentRequest.Order.SubscriptionTransactionId);
                var reason       = new AgreementStateDescriptor
                {
                    note = string.Format("Cancel subscription {0}", cancelPaymentRequest.Order.OrderGuid)
                };
                subscription.Cancel(apiContext, reason);
            }
            catch (PayPal.PayPalException exc)
            {
                if (exc is PayPal.ConnectionException)
                {
                    var error = JsonFormatter.ConvertFromJson <Error>((exc as PayPal.ConnectionException).Response);
                    if (error != null)
                    {
                        result.AddError(string.Format("PayPal error: {0} ({1})", error.message, error.name));
                        if (error.details != null)
                        {
                            error.details.ForEach(x => result.AddError(string.Format("{0} {1}", x.field, x.issue)));
                        }
                    }
                }

                //if there are not the specific errors add exception message
                if (result.Success)
                {
                    result.AddError(exc.InnerException != null ? exc.InnerException.Message : exc.Message);
                }
            }

            return(result);
        }
        /// <summary>
        /// Voids a payment
        /// </summary>
        /// <param name="voidPaymentRequest">Request</param>
        /// <returns>Result</returns>
        public VoidPaymentResult Void(VoidPaymentRequest voidPaymentRequest)
        {
            var result = new VoidPaymentResult();

            try
            {
                var apiContext    = PaypalHelper.GetApiContext(_paypalDirectPaymentSettings);
                var authorization = Authorization.Get(apiContext, voidPaymentRequest.Order.AuthorizationTransactionId);
                authorization = authorization.Void(apiContext);

                result.NewPaymentStatus = GetPaymentStatus(authorization.state);
            }
            catch (PayPal.PayPalException exc)
            {
                if (exc is PayPal.ConnectionException)
                {
                    var error = JsonFormatter.ConvertFromJson <Error>((exc as PayPal.ConnectionException).Response);
                    if (error != null)
                    {
                        result.AddError(string.Format("PayPal error: {0} ({1})", error.message, error.name));
                        if (error.details != null)
                        {
                            error.details.ForEach(x => result.AddError(string.Format("{0} {1}", x.field, x.issue)));
                        }
                    }
                }

                //if there are not the specific errors add exception message
                if (result.Success)
                {
                    result.AddError(exc.InnerException != null ? exc.InnerException.Message : exc.Message);
                }
            }

            return(result);
        }
        /// <summary>
        /// Process recurring payment
        /// </summary>
        /// <param name="processPaymentRequest">Payment info required for an order processing</param>
        /// <returns>Process payment result</returns>
        public ProcessPaymentResult ProcessRecurringPayment(ProcessPaymentRequest processPaymentRequest)
        {
            var result = new ProcessPaymentResult();

            var customer = _customerService.GetCustomerById(processPaymentRequest.CustomerId);

            if (customer == null)
            {
                throw new Exception("Customer cannot be loaded");
            }

            try
            {
                var apiContext = PaypalHelper.GetApiContext(_paypalDirectPaymentSettings);
                var currency   = _currencyService.GetCurrencyById(_currencySettings.PrimaryStoreCurrencyId);

                //check that webhook exists
                if (string.IsNullOrEmpty(_paypalDirectPaymentSettings.WebhookId))
                {
                    result.AddError("Recurring payments are not available until you create a webhook");
                    return(result);
                }

                Webhook.Get(apiContext, _paypalDirectPaymentSettings.WebhookId);

                //create the plan
                var url         = _webHelper.GetStoreLocation(_storeContext.CurrentStore.SslEnabled);
                var billingPlan = new Plan
                {
                    name                 = processPaymentRequest.OrderGuid.ToString(),
                    description          = string.Format("nopCommerce billing plan for the {0} order", processPaymentRequest.OrderGuid),
                    type                 = "fixed",
                    merchant_preferences = new MerchantPreferences
                    {
                        return_url       = url,
                        cancel_url       = url,
                        auto_bill_amount = "YES",
                        //setting setup fee as the first payment (workaround for the processing first payment immediately)
                        setup_fee = new PayPal.Api.Currency
                        {
                            currency = currency != null ? currency.CurrencyCode : null,
                            value    = processPaymentRequest.OrderTotal.ToString("N", new CultureInfo("en-US"))
                        }
                    },
                    payment_definitions = new List <PaymentDefinition>
                    {
                        new PaymentDefinition
                        {
                            name = "nopCommerce payment for the billing plan",
                            type = "REGULAR",
                            frequency_interval = processPaymentRequest.RecurringCycleLength.ToString(),
                            frequency          = processPaymentRequest.RecurringCyclePeriod.ToString().TrimEnd('s'),
                            cycles             = (processPaymentRequest.RecurringTotalCycles - 1).ToString(),
                            amount             = new PayPal.Api.Currency
                            {
                                currency = currency != null ? currency.CurrencyCode : null,
                                value    = processPaymentRequest.OrderTotal.ToString("N", new CultureInfo("en-US"))
                            }
                        }
                    }
                }.Create(apiContext);

                //activate the plan
                var patchRequest = new PatchRequest()
                {
                    new Patch()
                    {
                        op    = "replace",
                        path  = "/",
                        value = new Plan
                        {
                            state = "ACTIVE"
                        }
                    }
                };
                billingPlan.Update(apiContext, patchRequest);

                //create subscription
                var subscription = new Agreement
                {
                    name = string.Format("nopCommerce subscription for the {0} order", processPaymentRequest.OrderGuid),
                    //we set order guid in the description, then use it in the webhook handler
                    description = processPaymentRequest.OrderGuid.ToString(),
                    //setting start date as the next date of recurring payments as the setup fee was the first payment
                    start_date = GetStartDate(processPaymentRequest.RecurringCyclePeriod, processPaymentRequest.RecurringCycleLength),

                    #region payer

                    payer = new Payer()
                    {
                        payment_method = "credit_card",

                        #region credit card info

                        funding_instruments = new List <FundingInstrument>
                        {
                            new FundingInstrument
                            {
                                credit_card = new CreditCard
                                {
                                    type         = processPaymentRequest.CreditCardType.ToLowerInvariant(),
                                    number       = processPaymentRequest.CreditCardNumber,
                                    cvv2         = processPaymentRequest.CreditCardCvv2,
                                    expire_month = processPaymentRequest.CreditCardExpireMonth,
                                    expire_year  = processPaymentRequest.CreditCardExpireYear
                                }
                            }
                        },

                        #endregion

                        #region payer info

                        payer_info = new PayerInfo
                        {
                            #region billing address

                            billing_address = customer.BillingAddress == null ? null : new Address
                            {
                                country_code = customer.BillingAddress.Country != null ? customer.BillingAddress.Country.TwoLetterIsoCode : null,
                                state        = customer.BillingAddress.StateProvince != null ? customer.BillingAddress.StateProvince.Abbreviation : null,
                                city         = customer.BillingAddress.City,
                                line1        = customer.BillingAddress.Address1,
                                line2        = customer.BillingAddress.Address2,
                                phone        = customer.BillingAddress.PhoneNumber,
                                postal_code  = customer.BillingAddress.ZipPostalCode
                            },

                            #endregion

                            email      = customer.BillingAddress.Email,
                            first_name = customer.BillingAddress.FirstName,
                            last_name  = customer.BillingAddress.LastName
                        }

                        #endregion
                    },

                    #endregion

                    #region shipping address

                    shipping_address = customer.ShippingAddress == null ? null : new ShippingAddress
                    {
                        country_code = customer.ShippingAddress.Country != null ? customer.ShippingAddress.Country.TwoLetterIsoCode : null,
                        state        = customer.ShippingAddress.StateProvince != null ? customer.ShippingAddress.StateProvince.Abbreviation : null,
                        city         = customer.ShippingAddress.City,
                        line1        = customer.ShippingAddress.Address1,
                        line2        = customer.ShippingAddress.Address2,
                        phone        = customer.ShippingAddress.PhoneNumber,
                        postal_code  = customer.ShippingAddress.ZipPostalCode
                    },

                    #endregion

                    plan = new Plan
                    {
                        id = billingPlan.id
                    }
                }.Create(apiContext);

                //if first payment failed, try again
                if (string.IsNullOrEmpty(subscription.agreement_details.last_payment_date))
                {
                    subscription.BillBalance(apiContext, new AgreementStateDescriptor {
                        amount = subscription.agreement_details.outstanding_balance
                    });
                }

                result.SubscriptionTransactionId = subscription.id;
            }
            catch (PayPal.PayPalException exc)
            {
                if (exc is PayPal.ConnectionException)
                {
                    var error = JsonFormatter.ConvertFromJson <Error>((exc as PayPal.ConnectionException).Response);
                    if (error != null)
                    {
                        result.AddError(string.Format("PayPal error: {0} ({1})", error.message, error.name));
                        if (error.details != null)
                        {
                            error.details.ForEach(x => result.AddError(string.Format("{0} {1}", x.field, x.issue)));
                        }
                    }
                }

                //if there are not the specific errors add exception message
                if (result.Success)
                {
                    result.AddError(exc.InnerException != null ? exc.InnerException.Message : exc.Message);
                }
            }

            return(result);
        }
        /// <summary>
        /// Process a payment
        /// </summary>
        /// <param name="processPaymentRequest">Payment info required for an order processing</param>
        /// <returns>Process payment result</returns>
        public ProcessPaymentResult ProcessPayment(ProcessPaymentRequest processPaymentRequest)
        {
            var result = new ProcessPaymentResult();

            var customer = _customerService.GetCustomerById(processPaymentRequest.CustomerId);

            if (customer == null)
            {
                throw new Exception("Customer cannot be loaded");
            }

            try
            {
                var apiContext = PaypalHelper.GetApiContext(_paypalDirectPaymentSettings);
                var currency   = _currencyService.GetCurrencyById(_currencySettings.PrimaryStoreCurrencyId);
                var payment    = new Payment()
                {
                    #region payer

                    payer = new Payer()
                    {
                        payment_method = "credit_card",

                        #region credit card info

                        funding_instruments = new List <FundingInstrument>
                        {
                            new FundingInstrument
                            {
                                credit_card = new CreditCard
                                {
                                    type         = processPaymentRequest.CreditCardType.ToLowerInvariant(),
                                    number       = processPaymentRequest.CreditCardNumber,
                                    cvv2         = processPaymentRequest.CreditCardCvv2,
                                    expire_month = processPaymentRequest.CreditCardExpireMonth,
                                    expire_year  = processPaymentRequest.CreditCardExpireYear
                                }
                            }
                        },

                        #endregion

                        #region payer info

                        payer_info = new PayerInfo
                        {
                            #region billing address

                            billing_address = customer.BillingAddress == null ? null : new Address
                            {
                                country_code = customer.BillingAddress.Country != null ? customer.BillingAddress.Country.TwoLetterIsoCode : null,
                                state        = customer.BillingAddress.StateProvince != null ? customer.BillingAddress.StateProvince.Abbreviation : null,
                                city         = customer.BillingAddress.City,
                                line1        = customer.BillingAddress.Address1,
                                line2        = customer.BillingAddress.Address2,
                                phone        = customer.BillingAddress.PhoneNumber,
                                postal_code  = customer.BillingAddress.ZipPostalCode
                            },

                            #endregion

                            email      = customer.BillingAddress != null ? customer.BillingAddress.Email : null,
                            first_name = customer.BillingAddress != null ? customer.BillingAddress.FirstName : null,
                            last_name  = customer.BillingAddress != null ? customer.BillingAddress.LastName : null
                        }

                        #endregion
                    },

                    #endregion

                    #region transaction

                    transactions = new List <Transaction>()
                    {
                        new Transaction
                        {
                            #region amount

                            amount = new Amount
                            {
                                total    = processPaymentRequest.OrderTotal.ToString("N", new CultureInfo("en-US")),
                                currency = currency != null ? currency.CurrencyCode : null
                            },

                            #endregion

                            #region shipping address

                            item_list = customer.ShippingAddress == null ? null : new ItemList
                            {
                                shipping_address = new ShippingAddress
                                {
                                    country_code   = customer.ShippingAddress.Country != null ? customer.ShippingAddress.Country.TwoLetterIsoCode : null,
                                    state          = customer.ShippingAddress.StateProvince != null ? customer.ShippingAddress.StateProvince.Abbreviation : null,
                                    city           = customer.ShippingAddress.City,
                                    line1          = customer.ShippingAddress.Address1,
                                    line2          = customer.ShippingAddress.Address2,
                                    phone          = customer.ShippingAddress.PhoneNumber,
                                    postal_code    = customer.ShippingAddress.ZipPostalCode,
                                    recipient_name = string.Format("{0} {1}", customer.ShippingAddress.FirstName, customer.ShippingAddress.LastName)
                                }
                            },

                            #endregion

                            invoice_number = processPaymentRequest.OrderGuid != Guid.Empty ? processPaymentRequest.OrderGuid.ToString() : null
                        }
                    },

                    #endregion

                    intent = _paypalDirectPaymentSettings.TransactMode == TransactMode.Authorize ? "authorize" : "sale",
                }.Create(apiContext);

                if (payment.transactions[0].related_resources.Any() && payment.transactions[0].related_resources[0] != null)
                {
                    if (_paypalDirectPaymentSettings.TransactMode == TransactMode.Authorize)
                    {
                        var authorization = payment.transactions[0].related_resources[0].authorization;
                        if (authorization != null)
                        {
                            if (authorization.fmf_details != null)
                            {
                                result.AuthorizationTransactionResult = string.Format("Authorization is {0}. Based on fraud filter: {1}. {2}",
                                                                                      authorization.fmf_details.filter_type, authorization.fmf_details.name, authorization.fmf_details.description);
                                result.NewPaymentStatus = GetPaymentStatus(Authorization.Get(apiContext, authorization.id).state);
                            }
                            else
                            {
                                result.AuthorizationTransactionResult = authorization.state;
                                result.NewPaymentStatus = GetPaymentStatus(authorization.state);
                            }
                            result.AuthorizationTransactionId = authorization.id;
                        }
                    }
                    else
                    {
                        var sale = payment.transactions[0].related_resources[0].sale;
                        if (sale != null)
                        {
                            if (sale.fmf_details != null)
                            {
                                result.CaptureTransactionResult = string.Format("Sale is {0}. Based on fraud filter: {1}. {2}",
                                                                                sale.fmf_details.filter_type, sale.fmf_details.name, sale.fmf_details.description);
                                result.NewPaymentStatus = GetPaymentStatus(Sale.Get(apiContext, sale.id).state);
                            }
                            else
                            {
                                result.CaptureTransactionResult = sale.state;
                                result.NewPaymentStatus         = GetPaymentStatus(sale.state);
                            }
                            result.CaptureTransactionId = sale.id;
                            result.AvsResult            = sale.processor_response != null ? sale.processor_response.avs_code : string.Empty;
                        }
                    }
                }
                else
                {
                    result.AddError("PayPal error");
                }
            }
            catch (PayPal.PayPalException exc)
            {
                if (exc is PayPal.ConnectionException)
                {
                    var error = JsonFormatter.ConvertFromJson <Error>((exc as PayPal.ConnectionException).Response);
                    if (error != null)
                    {
                        result.AddError(string.Format("PayPal error: {0} ({1})", error.message, error.name));
                        if (error.details != null)
                        {
                            error.details.ForEach(x => result.AddError(string.Format("{0} {1}", x.field, x.issue)));
                        }
                    }
                }

                //if there are not the specific errors add exception message
                if (result.Success)
                {
                    result.AddError(exc.InnerException != null ? exc.InnerException.Message : exc.Message);
                }
            }

            return(result);
        }