Example #1
0
        public PayPalResponse Capture(PayPalApiSettingsBase settings, PayPalSessionData session, CapturePaymentRequest request)
        {
            var data = new Dictionary <string, object>();
            //var isAuthorize = request.Order.AuthorizationTransactionCode.IsCaseInsensitiveEqual("authorize");

            var path = "/v1/payments/authorization/{0}/capture".FormatInvariant(request.Order.AuthorizationTransactionId);

            var store = _services.StoreService.GetStoreById(request.Order.StoreId);

            var amount = new Dictionary <string, object>();

            amount.Add("total", request.Order.OrderTotal.FormatInvariant());
            amount.Add("currency", store.PrimaryStoreCurrency.CurrencyCode);

            data.Add("amount", amount);

            var result = CallApi("POST", path, session.AccessToken, settings, JsonConvert.SerializeObject(data));

            if (result.Success && result.Json != null)
            {
                result.Id = (string)result.Json.id;
            }

            return(result);
        }
Example #2
0
        private void DeleteWebhook(PayPalApiSettingsBase settings, string providerSystemName)
        {
            try
            {
                if (settings?.WebhookId.HasValue() ?? false)
                {
                    var session = new PayPalSessionData {
                        ProviderSystemName = providerSystemName
                    };
                    var result = _payPalService.Value.EnsureAccessToken(session, settings);

                    if (result.Success)
                    {
                        result = _payPalService.Value.DeleteWebhook(settings, session);
                    }

                    if (!result.Success)
                    {
                        Logger.Log(LogLevel.Error, null, result.ErrorMessage, null);
                    }
                }
            }
            catch (Exception ex)
            {
                Logger.Log(LogLevel.Error, ex, null, null);
            }
        }
Example #3
0
        public PayPalResponse Refund(PayPalApiSettingsBase settings, PayPalSessionData session, RefundPaymentRequest request)
        {
            var data   = new Dictionary <string, object>();
            var store  = _services.StoreService.GetStoreById(request.Order.StoreId);
            var isSale = request.Order.AuthorizationTransactionResult.Contains("(sale)");

            var path = "/v1/payments/{0}/{1}/refund".FormatInvariant(isSale ? "sale" : "capture", request.Order.CaptureTransactionId);

            var amount = new Dictionary <string, object>();

            amount.Add("total", request.AmountToRefund.FormatInvariant());
            amount.Add("currency", store.PrimaryStoreCurrency.CurrencyCode);

            data.Add("amount", amount);

            var result = CallApi("POST", path, session.AccessToken, settings, data.Any() ? JsonConvert.SerializeObject(data) : null);

            if (result.Success && result.Json != null)
            {
                result.Id = (string)result.Json.id;
            }

            //Logger.InsertLog(LogLevel.Information, "PayPal Refund", JsonConvert.SerializeObject(data, Formatting.Indented) + "\r\n\r\n" + (result.Json != null ? result.Json.ToString() : ""));

            return(result);
        }
Example #4
0
        public PayPalResponse EnsureAccessToken(PayPalSessionData session, PayPalApiSettingsBase settings)
        {
            if (session.AccessToken.IsEmpty() || DateTime.UtcNow >= session.TokenExpiration)
            {
                var result = CallApi("POST", "/v1/oauth2/token", null, settings, "grant_type=client_credentials");

                if (result.Success)
                {
                    session.AccessToken = (string)result.Json.access_token;

                    var expireSeconds = ((string)result.Json.expires_in).ToInt(5 * 60);

                    session.TokenExpiration = DateTime.UtcNow.AddSeconds(expireSeconds);
                }
                else
                {
                    return(result);
                }
            }

            return(new PayPalResponse
            {
                Success = session.AccessToken.HasValue()
            });
        }
Example #5
0
        public PayPalResponse DeleteCheckoutExperience(PayPalApiSettingsBase settings, PayPalSessionData session)
        {
            var result = CallApi("DELETE", "/v1/payment-experience/web-profiles/" + settings.ExperienceProfileId, session.AccessToken, settings, null);

            if (result.Success && result.Json != null)
            {
                result.Id = (string)result.Json.id;
            }

            return(result);
        }
Example #6
0
        public PayPalResponse GetPayment(PayPalApiSettingsBase settings, PayPalSessionData session)
        {
            var result = CallApi("GET", "/v1/payments/payment/" + session.PaymentId, session.AccessToken, settings, null);

            if (result.Success && result.Json != null)
            {
                result.Id = (string)result.Json.id;
            }

            return(result);
        }
Example #7
0
        public PayPalResponse DeleteWebhook(PayPalApiSettingsBase settings, PayPalSessionData session)
        {
            var result = CallApi("DELETE", "/v1/notifications/webhooks/" + settings.WebhookId, session.AccessToken, settings, null);

            if (result.Success && result.Json != null)
            {
                result.Id = (string)result.Json.id;
            }

            return(result);
        }
        protected CustomSecurityHeaderType GetApiCredentials(PayPalApiSettingsBase settings)
        {
            var customSecurityHeaderType = new CustomSecurityHeaderType();

            customSecurityHeaderType.Credentials           = new UserIdPasswordType();
            customSecurityHeaderType.Credentials.Username  = settings.ApiAccountName;
            customSecurityHeaderType.Credentials.Password  = settings.ApiAccountPassword;
            customSecurityHeaderType.Credentials.Signature = settings.Signature;
            customSecurityHeaderType.Credentials.Subject   = "";

            return(customSecurityHeaderType);
        }
Example #9
0
        public PayPalResponse Void(PayPalApiSettingsBase settings, PayPalSessionData session, VoidPaymentRequest request)
        {
            var path = "/v1/payments/authorization/{0}/void".FormatInvariant(request.Order.AuthorizationTransactionId);

            var result = CallApi("POST", path, session.AccessToken, settings, null);

            if (result.Success && result.Json != null)
            {
                result.Id = (string)result.Json.id;
            }

            return(result);
        }
Example #10
0
        public PayPalResponse CreateWebhook(PayPalApiSettingsBase settings, PayPalSessionData session, string url)
        {
            var data   = new Dictionary <string, object>();
            var events = new List <Dictionary <string, object> >();

            events.Add(new Dictionary <string, object> {
                { "name", "PAYMENT.AUTHORIZATION.VOIDED" }
            });
            events.Add(new Dictionary <string, object> {
                { "name", "PAYMENT.CAPTURE.COMPLETED" }
            });
            events.Add(new Dictionary <string, object> {
                { "name", "PAYMENT.CAPTURE.DENIED" }
            });
            events.Add(new Dictionary <string, object> {
                { "name", "PAYMENT.CAPTURE.PENDING" }
            });
            events.Add(new Dictionary <string, object> {
                { "name", "PAYMENT.CAPTURE.REFUNDED" }
            });
            events.Add(new Dictionary <string, object> {
                { "name", "PAYMENT.CAPTURE.REVERSED" }
            });
            events.Add(new Dictionary <string, object> {
                { "name", "PAYMENT.SALE.COMPLETED" }
            });
            events.Add(new Dictionary <string, object> {
                { "name", "PAYMENT.SALE.DENIED" }
            });
            events.Add(new Dictionary <string, object> {
                { "name", "PAYMENT.SALE.PENDING" }
            });
            events.Add(new Dictionary <string, object> {
                { "name", "PAYMENT.SALE.REFUNDED" }
            });
            events.Add(new Dictionary <string, object> {
                { "name", "PAYMENT.SALE.REVERSED" }
            });

            data.Add("url", url);
            data.Add("event_types", events);

            var result = CallApi("POST", "/v1/notifications/webhooks", session.AccessToken, settings, JsonConvert.SerializeObject(data));

            if (result.Success && result.Json != null)
            {
                result.Id = (string)result.Json.id;
            }

            return(result);
        }
Example #11
0
        public PayPalResponse ExecutePayment(PayPalApiSettingsBase settings, PayPalSessionData session)
        {
            var data = new Dictionary <string, object>();

            data.Add("payer_id", session.PayerId);

            var result = CallApi("POST", "/v1/payments/payment/{0}/execute".FormatInvariant(session.PaymentId), session.AccessToken, settings, JsonConvert.SerializeObject(data));

            if (result.Success && result.Json != null)
            {
                result.Id = (string)result.Json.id;

                //Logger.InsertLog(LogLevel.Information, "PayPal PLUS", JsonConvert.SerializeObject(data, Formatting.Indented) + "\r\n\r\n" + result.Json.ToString());
            }

            return(result);
        }
Example #12
0
        public PayPalResponse UpsertCheckoutExperience(PayPalApiSettingsBase settings, PayPalSessionData session, Store store)
        {
            PayPalResponse result;
            var            name = store.Name;
            var            logo = _pictureService.Value.GetPictureById(store.LogoPictureId);
            var            path = "/v1/payment-experience/web-profiles";

            var data         = new Dictionary <string, object>();
            var presentation = new Dictionary <string, object>();
            var inpuFields   = new Dictionary <string, object>();

            // find existing profile id, only one profile per profile name possible
            if (settings.ExperienceProfileId.IsEmpty())
            {
                result = CallApi("GET", path, session.AccessToken, settings, null);
                if (result.Success && result.Json != null)
                {
                    foreach (var profile in result.Json)
                    {
                        var profileName = (string)profile.name;
                        if (profileName.IsCaseInsensitiveEqual(name))
                        {
                            settings.ExperienceProfileId = (string)profile.id;
                            break;
                        }
                    }
                }
            }

            presentation.Add("brand_name", name);
            presentation.Add("locale_code", _services.WorkContext.WorkingLanguage.UniqueSeoCode.EmptyNull().ToUpper());

            if (logo != null)
            {
                presentation.Add("logo_image", _pictureService.Value.GetPictureUrl(logo, showDefaultPicture: false, storeLocation: store.Url));
            }

            inpuFields.Add("allow_note", false);
            inpuFields.Add("no_shipping", 0);
            inpuFields.Add("address_override", 1);

            data.Add("name", name);
            data.Add("presentation", presentation);
            data.Add("input_fields", inpuFields);

            if (settings.ExperienceProfileId.HasValue())
            {
                path = string.Concat(path, "/", HttpUtility.UrlPathEncode(settings.ExperienceProfileId));
            }

            result = CallApi(settings.ExperienceProfileId.HasValue() ? "PUT" : "POST", path, session.AccessToken, settings, JsonConvert.SerializeObject(data));

            if (result.Success)
            {
                if (result.Json != null)
                {
                    result.Id = (string)result.Json.id;
                }
                else
                {
                    result.Id = settings.ExperienceProfileId;
                }
            }

            return(result);
        }
Example #13
0
        public PayPalResponse CreatePayment(
            PayPalApiSettingsBase settings,
            PayPalSessionData session,
            List <OrganizedShoppingCartItem> cart,
            string providerSystemName,
            string returnUrl,
            string cancelUrl)
        {
            var store        = _services.StoreContext.CurrentStore;
            var customer     = _services.WorkContext.CurrentCustomer;
            var language     = _services.WorkContext.WorkingLanguage;
            var currencyCode = store.PrimaryStoreCurrency.CurrencyCode;

            var dateOfBirth = customer.GetAttribute <DateTime?>(SystemCustomerAttributeNames.DateOfBirth);

            Discount orderAppliedDiscount;
            List <AppliedGiftCard> appliedGiftCards;
            int     redeemedRewardPoints = 0;
            decimal redeemedRewardPointsAmount;
            decimal orderDiscountInclTax;
            decimal totalOrderItems = decimal.Zero;

            var includingTax = (_services.WorkContext.GetTaxDisplayTypeFor(customer, store.Id) == TaxDisplayType.IncludingTax);

            var shipping = (_orderTotalCalculationService.GetShoppingCartShippingTotal(cart) ?? decimal.Zero);

            var paymentFee = _paymentService.GetAdditionalHandlingFee(cart, providerSystemName);

            var total = (_orderTotalCalculationService.GetShoppingCartTotal(cart, out orderDiscountInclTax, out orderAppliedDiscount, out appliedGiftCards,
                                                                            out redeemedRewardPoints, out redeemedRewardPointsAmount) ?? decimal.Zero);

            var data          = new Dictionary <string, object>();
            var redirectUrls  = new Dictionary <string, object>();
            var payer         = new Dictionary <string, object>();
            var payerInfo     = new Dictionary <string, object>();
            var transaction   = new Dictionary <string, object>();
            var amount        = new Dictionary <string, object>();
            var amountDetails = new Dictionary <string, object>();
            var items         = new List <Dictionary <string, object> >();
            var itemList      = new Dictionary <string, object>();

            // "PayPal PLUS only supports transaction type “Sale” (instant settlement)"
            if (providerSystemName == PayPalPlusProvider.SystemName)
            {
                data.Add("intent", "sale");
            }
            else
            {
                data.Add("intent", settings.TransactMode == TransactMode.AuthorizeAndCapture ? "sale" : "authorize");
            }

            if (settings.ExperienceProfileId.HasValue())
            {
                data.Add("experience_profile_id", settings.ExperienceProfileId);
            }

            // redirect urls
            if (returnUrl.HasValue())
            {
                redirectUrls.Add("return_url", returnUrl);
            }

            if (cancelUrl.HasValue())
            {
                redirectUrls.Add("cancel_url", cancelUrl);
            }

            if (redirectUrls.Any())
            {
                data.Add("redirect_urls", redirectUrls);
            }

            // payer, payer_info
            if (dateOfBirth.HasValue)
            {
                payerInfo.Add("birth_date", dateOfBirth.Value.ToString("yyyy-MM-dd"));
            }
            if (customer.BillingAddress != null)
            {
                payerInfo.Add("billing_address", CreateAddress(customer.BillingAddress, false));
            }

            payer.Add("payment_method", "paypal");
            payer.Add("payer_info", payerInfo);
            data.Add("payer", payer);

            // line items
            foreach (var item in cart)
            {
                decimal unitPriceTaxRate = decimal.Zero;
                decimal unitPrice        = _priceCalculationService.GetUnitPrice(item, true);
                decimal productPrice     = _taxService.GetProductPrice(item.Item.Product, unitPrice, includingTax, customer, out unitPriceTaxRate);

                var line = new Dictionary <string, object>();
                line.Add("quantity", item.Item.Quantity);
                line.Add("name", item.Item.Product.GetLocalized(x => x.Name, language.Id, true, false).Truncate(127));
                line.Add("price", productPrice.FormatInvariant());
                line.Add("currency", currencyCode);
                line.Add("sku", item.Item.Product.Sku.Truncate(50));
                items.Add(line);

                totalOrderItems += (productPrice * item.Item.Quantity);
            }

            var itemsPlusMisc = (totalOrderItems + shipping + paymentFee);

            if (total != itemsPlusMisc)
            {
                var line = new Dictionary <string, object>();
                line.Add("quantity", "1");
                line.Add("name", T("Plugins.SmartStore.PayPal.Other").Text.Truncate(127));
                line.Add("price", (total - itemsPlusMisc).FormatInvariant());
                line.Add("currency", currencyCode);
                items.Add(line);

                totalOrderItems += (total - itemsPlusMisc);
            }

            itemList.Add("items", items);
            if (customer.ShippingAddress != null)
            {
                itemList.Add("shipping_address", CreateAddress(customer.ShippingAddress, true));
            }

            // transactions
            amountDetails.Add("shipping", shipping.FormatInvariant());
            amountDetails.Add("subtotal", totalOrderItems.FormatInvariant());
            if (!includingTax)
            {
                // "To avoid rounding errors we recommend not submitting tax amounts on line item basis.
                // Calculated tax amounts for the entire shopping basket may be submitted in the amount objects.
                // In this case the item amounts will be treated as amounts excluding tax.
                // In a B2C scenario, where taxes are included, no taxes should be submitted to PayPal."

                SortedDictionary <decimal, decimal> taxRates = null;
                var taxTotal = _orderTotalCalculationService.GetTaxTotal(cart, out taxRates);

                amountDetails.Add("tax", taxTotal.FormatInvariant());
            }
            if (paymentFee != decimal.Zero)
            {
                amountDetails.Add("handling_fee", paymentFee.FormatInvariant());
            }

            amount.Add("total", total.FormatInvariant());
            amount.Add("currency", currencyCode);
            amount.Add("details", amountDetails);

            transaction.Add("amount", amount);
            transaction.Add("item_list", itemList);
            transaction.Add("invoice_number", session.OrderGuid.ToString());

            data.Add("transactions", new List <Dictionary <string, object> > {
                transaction
            });

            var result = CallApi("POST", "/v1/payments/payment", session.AccessToken, settings, JsonConvert.SerializeObject(data));

            if (result.Success && result.Json != null)
            {
                result.Id = (string)result.Json.id;
            }

            //Logger.InsertLog(LogLevel.Information, "PayPal PLUS", JsonConvert.SerializeObject(data, Formatting.Indented) + "\r\n\r\n" + (result.Json != null ? result.Json.ToString() : ""));

            return(result);
        }
Example #14
0
        public PayPalResponse CallApi(string method, string path, string accessToken, PayPalApiSettingsBase settings, string data)
        {
            var             isJson      = (data.HasValue() && data.StartsWith("{"));
            var             encoding    = (isJson ? Encoding.UTF8 : Encoding.ASCII);
            var             result      = new PayPalResponse();
            HttpWebResponse webResponse = null;

            var url = GetApiUrl(settings.UseSandbox) + path.EnsureStartsWith("/");

            if (method.IsCaseInsensitiveEqual("GET") && data.HasValue())
            {
                url = url.EnsureEndsWith("?") + data;
            }

            if (settings.SecurityProtocol.HasValue)
            {
                ServicePointManager.SecurityProtocol = settings.SecurityProtocol.Value;
            }

            var request = (HttpWebRequest)WebRequest.Create(url);

            request.Method      = method;
            request.Accept      = "application/json";
            request.ContentType = (isJson ? "application/json" : "application/x-www-form-urlencoded");

            try
            {
                if (HttpContext.Current != null && HttpContext.Current.Request != null)
                {
                    request.UserAgent = HttpContext.Current.Request.UserAgent;
                }
                else
                {
                    request.UserAgent = Plugin.SystemName;
                }
            }
            catch { }

            if (path.EmptyNull().EndsWith("/token"))
            {
                // see https://github.com/paypal/sdk-core-dotnet/blob/master/Source/SDK/OAuthTokenCredential.cs
                byte[] credentials = Encoding.UTF8.GetBytes("{0}:{1}".FormatInvariant(settings.ClientId, settings.Secret));

                request.Headers.Add("Authorization", "Basic " + Convert.ToBase64String(credentials));
            }
            else
            {
                request.Headers["Authorization"] = "Bearer " + accessToken.EmptyNull();
            }


            if (data.HasValue() && (method.IsCaseInsensitiveEqual("POST") || method.IsCaseInsensitiveEqual("PUT") || method.IsCaseInsensitiveEqual("PATCH")))
            {
                byte[] bytes = encoding.GetBytes(data);

                request.ContentLength = bytes.Length;

                using (var stream = request.GetRequestStream())
                {
                    stream.Write(bytes, 0, bytes.Length);
                }
            }
            else
            {
                request.ContentLength = 0;
            }

            try
            {
                webResponse    = request.GetResponse() as HttpWebResponse;
                result.Success = ((int)webResponse.StatusCode < 400);
            }
            catch (WebException wexc)
            {
                result.Success      = false;
                result.ErrorMessage = wexc.ToString();
                webResponse         = wexc.Response as HttpWebResponse;
            }
            catch (Exception exception)
            {
                result.Success      = false;
                result.ErrorMessage = exception.ToString();
                LogError(exception);
            }

            try
            {
                if (webResponse != null)
                {
                    using (var reader = new StreamReader(webResponse.GetResponseStream(), Encoding.UTF8))
                    {
                        var rawResponse = reader.ReadToEnd();
                        if (rawResponse.HasValue())
                        {
                            if (webResponse.ContentType.IsCaseInsensitiveEqual("application/json"))
                            {
                                if (rawResponse.StartsWith("["))
                                {
                                    result.Json = JArray.Parse(rawResponse);
                                }
                                else
                                {
                                    result.Json = JObject.Parse(rawResponse);
                                }

                                if (result.Json != null)
                                {
                                    if (!result.Success)
                                    {
                                        var name    = (string)result.Json.name;
                                        var message = (string)result.Json.message;

                                        if (name.IsEmpty())
                                        {
                                            name = (string)result.Json.error;
                                        }

                                        if (message.IsEmpty())
                                        {
                                            message = (string)result.Json.error_description;
                                        }

                                        result.ErrorMessage = "{0} ({1}).".FormatInvariant(message.NaIfEmpty(), name.NaIfEmpty());
                                    }
                                }
                            }
                            else if (!result.Success)
                            {
                                result.ErrorMessage = rawResponse;
                            }
                        }
                    }

                    if (!result.Success)
                    {
                        if (result.ErrorMessage.IsEmpty())
                        {
                            result.ErrorMessage = webResponse.StatusDescription;
                        }

                        LogError(null, result.ErrorMessage, result.Json == null ? null : result.Json.ToString(), false);
                    }
                }
            }
            catch (Exception exception)
            {
                LogError(exception);
            }
            finally
            {
                if (webResponse != null)
                {
                    webResponse.Close();
                    webResponse.Dispose();
                }
            }

            return(result);
        }
Example #15
0
        /// <remarks>return 503 (HttpStatusCode.ServiceUnavailable) to ask paypal to resend it at later time again</remarks>
        public HttpStatusCode ProcessWebhook(
            PayPalApiSettingsBase settings,
            NameValueCollection headers,
            string rawJson,
            string providerSystemName)
        {
            if (rawJson.IsEmpty())
            {
                return(HttpStatusCode.OK);
            }

            dynamic json      = JObject.Parse(rawJson);
            var     eventType = (string)json.event_type;

            //foreach (var key in headers.AllKeys)"{0}: {1}".FormatInvariant(key, headers[key]).Dump();
            //string data = JsonConvert.SerializeObject(json, Formatting.Indented);data.Dump();


            // validating against PayPal SDK failing using sandbox, so better we do not use it:
            //var apiContext = new global::PayPal.Api.APIContext
            //{
            //	AccessToken = "I do not have one here",
            //	Config = new Dictionary<string, string>
            //		{
            //			{ "mode", settings.UseSandbox ? "sandbox" : "live" },
            //			{ "clientId", settings.ClientId },
            //			{ "clientSecret", settings.Secret },
            //			{ "webhook.id", setting.WebhookId },
            //		}
            //};
            //var result = global::PayPal.Api.WebhookEvent.ValidateReceivedEvent(apiContext, headers, rawJson, webhookId);
            //}

            var paymentId = (string)json.resource.parent_payment;

            if (paymentId.IsEmpty())
            {
                LogError(null, T("Plugins.SmartStore.PayPal.FoundOrderForPayment", 0, "".NaIfEmpty()), JsonConvert.SerializeObject(json, Formatting.Indented), isWarning: true);
                return(HttpStatusCode.OK);
            }

            var orders = _orderRepository.Value.Table
                         .Where(x => x.PaymentMethodSystemName == providerSystemName && x.AuthorizationTransactionCode == paymentId)
                         .ToList();

            if (orders.Count != 1)
            {
                LogError(null, T("Plugins.SmartStore.PayPal.FoundOrderForPayment", orders.Count, paymentId), JsonConvert.SerializeObject(json, Formatting.Indented), isWarning: true);
                return(HttpStatusCode.OK);
            }

            var order = orders.First();
            var store = _services.StoreService.GetStoreById(order.StoreId);

            var total           = decimal.Zero;
            var currency        = (string)json.resource.amount.currency;
            var primaryCurrency = store.PrimaryStoreCurrency.CurrencyCode;

            if (!primaryCurrency.IsCaseInsensitiveEqual(currency))
            {
                LogError(null, T("Plugins.SmartStore.PayPal.CurrencyNotEqual", currency.NaIfEmpty(), primaryCurrency), JsonConvert.SerializeObject(json, Formatting.Indented), isWarning: true);
                return(HttpStatusCode.OK);
            }

            eventType = eventType.Substring(eventType.LastIndexOf('.') + 1);

            var newPaymentStatus = GetPaymentStatus(eventType, "authorization", order.PaymentStatus);

            var isValidTotal = decimal.TryParse((string)json.resource.amount.total, NumberStyles.Currency, CultureInfo.InvariantCulture, out total);

            if (newPaymentStatus == PaymentStatus.Refunded && (Math.Abs(order.OrderTotal) - Math.Abs(total)) > decimal.Zero)
            {
                newPaymentStatus = PaymentStatus.PartiallyRefunded;
            }

            switch (newPaymentStatus)
            {
            case PaymentStatus.Pending:
                break;

            case PaymentStatus.Authorized:
                if (_orderProcessingService.CanMarkOrderAsAuthorized(order))
                {
                    _orderProcessingService.MarkAsAuthorized(order);
                }
                break;

            case PaymentStatus.Paid:
                if (_orderProcessingService.CanMarkOrderAsPaid(order))
                {
                    _orderProcessingService.MarkOrderAsPaid(order);
                }
                break;

            case PaymentStatus.Refunded:
                if (_orderProcessingService.CanRefundOffline(order))
                {
                    _orderProcessingService.RefundOffline(order);
                }
                break;

            case PaymentStatus.PartiallyRefunded:
                if (_orderProcessingService.CanPartiallyRefundOffline(order, Math.Abs(total)))
                {
                    _orderProcessingService.PartiallyRefundOffline(order, Math.Abs(total));
                }
                break;

            case PaymentStatus.Voided:
                if (_orderProcessingService.CanVoidOffline(order))
                {
                    _orderProcessingService.VoidOffline(order);
                }
                break;
            }

            order.HasNewPaymentNotification = true;

            AddOrderNote(settings, order, (string)ToInfoString(json));

            return(HttpStatusCode.OK);
        }
        protected bool SaveConfigurationModel(
            ApiConfigurationModel model,
            FormCollection form,
            Action <TSetting> map = null)
        {
            var storeDependingSettingHelper = new StoreDependingSettingHelper(ViewData);
            var storeScope = this.GetActiveStoreScopeConfiguration(Services.StoreService, Services.WorkContext);
            var settings   = Services.Settings.LoadSetting <TSetting>(storeScope);

            var oldClientId  = settings.ClientId;
            var oldSecret    = settings.Secret;
            var oldProfileId = settings.ExperienceProfileId;

            var validator = new PayPalApiConfigValidator(T, x =>
            {
                return(storeScope == 0 || storeDependingSettingHelper.IsOverrideChecked(settings, x, form));
            });

            validator.Validate(model, ModelState);

            // Additional validation.
            if (ModelState.IsValid)
            {
                // PayPal review: check if credentials are valid.
                var credentialChanged = model.ClientId.HasValue() && model.Secret.HasValue() &&
                                        (!model.ClientId.IsCaseInsensitiveEqual(settings.ClientId) || !model.Secret.IsCaseInsensitiveEqual(settings.Secret));

                if (credentialChanged)
                {
                    var session = new PayPalSessionData {
                        ProviderSystemName = ProviderSystemName
                    };
                    var tmpSettings = new PayPalApiSettingsBase
                    {
                        UseSandbox = model.UseSandbox,
                        ClientId   = model.ClientId,
                        Secret     = model.Secret
                    };

                    var result = PayPalService.EnsureAccessToken(session, tmpSettings);
                    if (!result.Success)
                    {
                        ModelState.AddModelError("", T("Plugins.SmartStore.PayPal.InvalidCredentials"));
                        ModelState.AddModelError("", result.ErrorMessage);
                    }
                }
            }

            if (!ModelState.IsValid)
            {
                return(false);
            }

            ModelState.Clear();
            model.TransactMode = TransactMode.AuthorizeAndCapture;

            MiniMapper.Map(model, settings);
            settings.ApiAccountName      = model.ApiAccountName.TrimSafe();
            settings.ApiAccountPassword  = model.ApiAccountPassword.TrimSafe();
            settings.ClientId            = model.ClientId.TrimSafe();
            settings.ExperienceProfileId = model.ExperienceProfileId.TrimSafe();
            settings.Secret    = model.Secret.TrimSafe();
            settings.Signature = model.Signature.TrimSafe();
            settings.WebhookId = model.WebhookId.TrimSafe();

            // Additional mapping.
            map?.Invoke(settings);

            // Credentials changed: reset profile and webhook id to avoid errors.
            if (!oldClientId.IsCaseInsensitiveEqual(settings.ClientId) || !oldSecret.IsCaseInsensitiveEqual(settings.Secret))
            {
                if (oldProfileId.IsCaseInsensitiveEqual(settings.ExperienceProfileId))
                {
                    settings.ExperienceProfileId = null;
                }

                settings.WebhookId = null;
            }

            using (Services.Settings.BeginScope())
            {
                storeDependingSettingHelper.UpdateSettings(settings, form, storeScope, Services.Settings);
            }

            using (Services.Settings.BeginScope())
            {
                // Multistore context not possible, see IPN handling.
                Services.Settings.SaveSetting(settings, x => x.UseSandbox, 0, false);
            }

            NotifySuccess(T("Admin.Common.DataSuccessfullySaved"));
            return(true);
        }