/// <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);
        }