/// <summary>
        /// Applies business rules to <see cref="PaymentConfiguration"/> instances.
        /// </summary>
        /// <param name="paymentConfiguration">A payment configuration instance.</param>
        /// <returns>A task.</returns>
        private void Normalize(PaymentConfiguration paymentConfiguration)
        {
            paymentConfiguration.AssertNotNull(nameof(paymentConfiguration));

            // Dont validate WebExperienceProfileId since it will break upgrade as existing deployments dont have this configuration.
            paymentConfiguration.ClientId.AssertNotEmpty("ClientId");
            paymentConfiguration.ClientSecret.AssertNotEmpty("ClientSecret");
            paymentConfiguration.AccountType.AssertNotEmpty("Mode");

            if (!supportedPaymentModes.Contains(paymentConfiguration.AccountType))
            {
                throw new PartnerDomainException(Resources.InvalidPaymentModeErrorMessage);
            }
        }
        /// <summary>
        /// Updates the payment configuration.
        /// </summary>
        /// <param name="newPaymentConfiguration">The new payment configuration.</param>
        /// <returns>The updated payment configuration.</returns>
        public async Task <PaymentConfiguration> UpdateAsync(PaymentConfiguration newPaymentConfiguration)
        {
            newPaymentConfiguration.AssertNotNull(nameof(newPaymentConfiguration));

            await NormalizeAsync(newPaymentConfiguration).ConfigureAwait(false);

            CloudBlockBlob paymentConfigurationBlob = await GetPaymentConfigurationBlob().ConfigureAwait(false);

            await paymentConfigurationBlob.UploadTextAsync(JsonConvert.SerializeObject(newPaymentConfiguration)).ConfigureAwait(false);

            // invalidate the cache, we do not update it to avoid race condition between web instances
            await ApplicationDomain.CachingService.ClearAsync(PaymentConfigurationCacheKey).ConfigureAwait(false);

            return(newPaymentConfiguration);
        }
        /// <summary>
        /// Updates the payment configuration.
        /// </summary>
        /// <param name="newPaymentConfiguration">The new payment configuration.</param>
        /// <returns>The updated payment configuration.</returns>
        public async Task <PaymentConfiguration> UpdateAsync(PaymentConfiguration newPaymentConfiguration)
        {
            newPaymentConfiguration.AssertNotNull(nameof(newPaymentConfiguration));

            await this.NormalizeAsync(newPaymentConfiguration);

            var paymentConfigurationBlob = await this.GetPaymentConfigurationBlob();

            await paymentConfigurationBlob.UploadTextAsync(JsonConvert.SerializeObject(newPaymentConfiguration));

            // invalidate the cache, we do not update it to avoid race condition between web instances
            await this.ApplicationDomain.CachingService.ClearAsync(PaymentConfigurationRepository.PaymentConfigurationCacheKey);

            return(newPaymentConfiguration);
        }
        /// <summary>
        /// Applies business rules to <see cref="PaymentConfiguration"/> instances.
        /// </summary>
        /// <param name="paymentConfiguration">A payment configuration instance.</param>
        /// <returns>A task.</returns>
        private async Task NormalizeAsync(PaymentConfiguration paymentConfiguration)
        {
            paymentConfiguration.AssertNotNull(nameof(paymentConfiguration));

            paymentConfiguration.ClientId.AssertNotEmpty("ClientId");
            paymentConfiguration.ClientSecret.AssertNotEmpty("ClientSecret");
            paymentConfiguration.AccountType.AssertNotEmpty("Mode");

            if (!this.supportedPaymentModes.Contains(paymentConfiguration.AccountType))
            {
                throw new PartnerDomainException("Payment mode is not supported");
            }

            await Task.FromResult(0);
        }
        /// <summary>
        /// Validates payment configuration.
        /// </summary>
        /// <param name="paymentConfig">The Payment configuration.</param>
        public void ValidateConfiguration(PaymentConfiguration paymentConfig)
        {
            string[] supportedPaymentModes = { "sandbox", "live" };

            paymentConfig.AssertNotNull(nameof(paymentConfig));

            paymentConfig.ClientId.AssertNotEmpty(nameof(paymentConfig.ClientId));
            paymentConfig.ClientSecret.AssertNotEmpty(nameof(paymentConfig.ClientSecret));
            paymentConfig.AccountType.AssertNotEmpty(nameof(paymentConfig.AccountType));

            if (!supportedPaymentModes.Contains(paymentConfig.AccountType))
            {
                throw new PartnerDomainException(Resources.InvalidPaymentModeErrorMessage);
            }

            try
            {
                Dictionary <string, string> configMap = new Dictionary <string, string>
                {
                    { "clientId", paymentConfig.ClientId },
                    { "clientSecret", paymentConfig.ClientSecret },
                    { "mode", paymentConfig.AccountType },
                    { "connectionTimeout", "120000" }
                };

                string     accessToken = new OAuthTokenCredential(configMap).GetAccessToken();
                APIContext apiContext  = new APIContext(accessToken);
            }
            catch (PayPalException paypalException)
            {
                if (paypalException is IdentityException)
                {
                    // thrown when API Context couldn't be setup.
                    IdentityException identityFailure = paypalException as IdentityException;
                    IdentityError     failureDetails  = identityFailure.Details;
                    if (failureDetails != null && failureDetails.error.Equals("invalid_client", StringComparison.InvariantCultureIgnoreCase))
                    {
                        throw new PartnerDomainException(ErrorCode.PaymentGatewayIdentityFailureDuringConfiguration).AddDetail("ErrorMessage", Resources.PaymentGatewayIdentityFailureDuringConfiguration);
                    }
                }

                // if this is not an identity exception rather some other issue.
                throw new PartnerDomainException(ErrorCode.PaymentGatewayFailure).AddDetail("ErrorMessage", paypalException.Message);
            }
        }