/// <summary>
        /// Validates credentials have been entered in the back office.
        /// </summary>
        /// <param name="settings">
        /// The settings.
        /// </param>
        /// <param name="isExpress">
        /// The is express.
        /// </param>
        /// <returns>
        /// The <see cref="Attempt"/>.
        /// </returns>
        private static Attempt<bool> ValidatePayPayProviderSettings(PayPalProviderSettings settings, bool isExpress = false)
        {
            var success = true;
            if (isExpress)
            {
                //success =
                //    !(settings.ApiUsername.IsNullOrWhiteSpace() ||
                //    settings.ApiPassword.IsNullOrWhiteSpace() ||
                //    settings.ApiSignature.IsNullOrWhiteSpace() ||
                //    settings.ApplicationId.IsNullOrWhiteSpace());

                success =
                !(settings.ApiUsername.IsNullOrWhiteSpace() ||
                settings.ApiPassword.IsNullOrWhiteSpace() ||
                settings.ApiSignature.IsNullOrWhiteSpace());
            }
            else
            {
                success = !(settings.ClientId.IsNullOrWhiteSpace() || settings.ClientSecret.IsNullOrWhiteSpace());
            }

            var ex = new PayPalApiException("One or more of the required API credentials has not been set in the back office");

            return success ? Attempt<bool>.Succeed(true) : Attempt<bool>.Fail(false, ex);
        }
        /// <summary>
        /// Performs a refund or a partial refund.
        /// </summary>
        /// <param name="invoice">
        /// The invoice.
        /// </param>
        /// <param name="payment">
        /// The payment.
        /// </param>
        /// <param name="amount">
        /// The amount.
        /// </param>
        /// <param name="args">
        /// The processor arguments.
        /// </param>
        /// <returns>
        /// The <see cref="IPaymentResult"/>.
        /// </returns>
        protected override IPaymentResult PerformRefundPayment(IInvoice invoice, IPayment payment, decimal amount, ProcessorArgumentCollection args)
        {
            var record = payment.GetPayPalTransactionRecord();

            if (StringExtensions.IsNullOrWhiteSpace(record.Data.CaptureTransactionId))
            {
                var error = new NullReferenceException("PayPal transaction could not be found and/or deserialized from payment extended data collection");
                return new PaymentResult(Attempt<IPayment>.Fail(payment, error), invoice, false);
            }

            var attempt = _paypalApiService.ExpressCheckout.Refund(invoice, payment, amount);

            // store the transaction
            var refundTransActions = record.RefundTransactions.ToList();
            refundTransActions.Add(attempt);
            record.RefundTransactions = refundTransActions;

            if (!attempt.Success())
            {
                // In the case of a failure, package up the exception so we can bubble it up.
                var ex = new PayPalApiException("PayPal Checkout Express refund response ACK was not Success");
                if (record.SetExpressCheckout.ErrorTypes.Any()) ex.ErrorTypes = record.SetExpressCheckout.ErrorTypes;

                // ensure that transaction is stored in the payment
                payment.SavePayPalTransactionRecord(record);
                GatewayProviderService.Save(payment);

                return new PaymentResult(Attempt<IPayment>.Fail(payment, ex), invoice, false);
            }

            foreach (var applied in payment.AppliedPayments())
            {
                applied.TransactionType = AppliedPaymentType.Refund;
                applied.Amount = 0;
                applied.Description += " - Refunded";
                this.GatewayProviderService.Save(applied);
            }

            payment.Amount = payment.Amount - amount;

            if (payment.Amount != 0)
            {
                this.GatewayProviderService.ApplyPaymentToInvoice(payment.Key, invoice.Key, AppliedPaymentType.Debit, "To show partial payment remaining after refund", payment.Amount);
            }

            this.GatewayProviderService.Save(payment);

            return new PaymentResult(Attempt<IPayment>.Succeed(payment), invoice, false);
        }
        /// <summary>
        /// Performs the AuthorizePayment operation.
        /// </summary>
        /// <param name="invoice">
        /// The invoice.
        /// </param>
        /// <param name="args">
        /// The <see cref="ProcessorArgumentCollection"/>.
        /// </param>
        /// <returns>
        /// The <see cref="IPaymentResult"/>.
        /// </returns>
        /// <remarks>
        /// For the ExpressCheckout there is not technically an "Authorize" but we use this to start the checkout process and to 
        /// mark intent to pay before redirecting the customer to PayPal.  e.g.  This method is called after the customer has
        /// clicked the Pay button, we then save the invoice and "Authorize" a payment setting the invoice status to Unpaid before redirecting.
        /// IN this way, we have both an Invoice and a Payment (denoting the redirect).  When the customer completes the purchase on PayPal sites
        /// the payment will be used to perform a capture and the invoice status will be changed to Paid.  In the event the customer cancels,
        /// the invoice will either be voided or deleted depending on the configured setting.  
        /// Events are included in the controller handling the response to allow developers to override success and cancel redirect URLs.
        /// </remarks>
        protected override IPaymentResult PerformAuthorizePayment(IInvoice invoice, ProcessorArgumentCollection args)
        {
            var payment = GatewayProviderService.CreatePayment(PaymentMethodType.Redirect, invoice.Total, PaymentMethod.Key);
            payment.CustomerKey = invoice.CustomerKey;
            payment.PaymentMethodName = PaymentMethod.Name;
            payment.ReferenceNumber = PaymentMethod.PaymentCode + "-" + invoice.PrefixedInvoiceNumber();
            payment.Collected = false;
            payment.Authorized = false; // this is technically not the authorization.  We'll mark this in a later step.

            // Have to save here to generate the payment key
            GatewayProviderService.Save(payment);

            // Now we want to get things setup for the ExpressCheckout
            var record = this._paypalApiService.ExpressCheckout.SetExpressCheckout(invoice, payment);
            payment.SavePayPalTransactionRecord(record);

            // Have to save here to persist the record so it can be used in later processing.
            GatewayProviderService.Save(payment);

            // In this case, we want to do our own Apply Payment operation as the amount has not been collected -
            // so we create an applied payment with a 0 amount.  Once the payment has been "collected", another Applied Payment record will
            // be created showing the full amount and the invoice status will be set to Paid.
            GatewayProviderService.ApplyPaymentToInvoice(payment.Key, invoice.Key, AppliedPaymentType.Debit, string.Format("To show promise of a {0} payment via PayPal Express Checkout", PaymentMethod.Name), 0);

            // if the ACK was success return a success IPaymentResult
            if (record.Success)
            {
                return new PaymentResult(Attempt<IPayment>.Succeed(payment), invoice, false, record.SetExpressCheckout.RedirectUrl);
            }

            // In the case of a failure, package up the exception so we can bubble it up.
            var ex = new PayPalApiException("PayPal Checkout Express initial response ACK was not Success");
            if (record.SetExpressCheckout.ErrorTypes.Any()) ex.ErrorTypes = record.SetExpressCheckout.ErrorTypes;

            return new PaymentResult(Attempt<IPayment>.Fail(payment, ex), invoice, false);
        }
        /// <summary>
        /// Refunds or partially refunds a payment.
        /// </summary>
        /// <param name="invoice">
        /// The invoice.
        /// </param>
        /// <param name="payment">
        /// The payment.
        /// </param>
        /// <param name="amount">
        /// The amount of the refund.
        /// </param>
        /// <returns>
        /// The <see cref="PayPalExpressTransactionRecord"/>.
        /// </returns>
        public ExpressCheckoutResponse Refund(IInvoice invoice, IPayment payment, decimal amount)
        {
            var record = payment.GetPayPalTransactionRecord();

            // Ensure the currency code
            if (record.Data.CurrencyCode.IsNullOrWhiteSpace())
            {
                var ex = new PayPalApiException("CurrencyCode was not found in payment extended data PayPal transaction data record.  Cannot perform refund.");
                return _responseFactory.Build(ex);
            }

            // Ensure the transaction id
            if (record.Data.CaptureTransactionId.IsNullOrWhiteSpace())
            {
                var ex = new PayPalApiException("CaptureTransactionId was not found in payment extended data PayPal transaction data record.  Cannot perform refund.");
                return _responseFactory.Build(ex);
            }

            // Get the decimal configuration for the current currency
            var currencyCodeType = PayPalApiHelper.GetPayPalCurrencyCode(record.Data.CurrencyCode);
            var basicAmountFactory = new PayPalBasicAmountTypeFactory(currencyCodeType);

            ExpressCheckoutResponse result = null;

            if (amount > payment.Amount) amount = payment.Amount;

            try
            {
                var request = new RefundTransactionRequestType
                    {
                        InvoiceID = invoice.PrefixedInvoiceNumber(),
                        PayerID = record.Data.PayerId,
                        RefundSource = RefundSourceCodeType.DEFAULT,
                        Version = record.DoCapture.Version,
                        TransactionID = record.Data.CaptureTransactionId,
                        Amount = basicAmountFactory.Build(amount)
                    };

                var wrapper = new RefundTransactionReq { RefundTransactionRequest = request };

                var refundTransactionResponse = GetPayPalService().RefundTransaction(wrapper);

                result = _responseFactory.Build(refundTransactionResponse, record.Data.Token);
            }
            catch (Exception ex)
            {
                result = _responseFactory.Build(ex);
            }

            return result;
        }