using Microsoft.AspNetCore.Mvc; using Nop.Core; using Nop.Plugin.Payments.PayPalCommerce.Domain; using Nop.Plugin.Payments.PayPalCommerce.Factories; using Nop.Plugin.Payments.PayPalCommerce.Models.Public; using Nop.Services.Localization; using Nop.Services.Messages; using Nop.Web.Controllers; using Nop.Web.Framework.Mvc.Filters; namespace Nop.Plugin.Payments.PayPalCommerce.Controllers; [AutoValidateAntiforgeryToken] public class PayPalCommercePublicController : BasePublicController { #region Fields private readonly ILocalizationService _localizationService; private readonly INotificationService _notificationService; private readonly IWebHelper _webHelper; private readonly PayPalCommerceModelFactory _modelFactory; #endregion #region Ctor public PayPalCommercePublicController(ILocalizationService localizationService, INotificationService notificationService, IWebHelper webHelper, PayPalCommerceModelFactory modelFactory) { _localizationService = localizationService; _notificationService = notificationService; _webHelper = webHelper; _modelFactory = modelFactory; } #endregion #region Methods #region Checkout public async Task PluginPaymentInfo() { var model = await _modelFactory.PrepareCheckoutPaymentInfoModelAsync(); return View("~/Plugins/Payments.PayPalCommerce/Views/Public/PluginPaymentInfo.cshtml", model); } [CheckLanguageSeoCode(ignore: true)] public async Task ValidateShoppingCart() { var warnings = await _modelFactory.GetShoppingCartWarningsAsync(); if (warnings?.Any() ?? false) return ErrorJson(warnings.ToArray()); return Json(new { success = true }); } [HttpPost] public async Task CreateOrder(int placement, string paymentSource, int cardId, bool saveCard) { var model = await _modelFactory.PrepareOrderModelAsync((ButtonPlacement)placement, null, paymentSource, cardId, saveCard); if (model.LoginIsRequired) return Json(new { redirect = Url.RouteUrl("Login", new { returnUrl = Url.RouteUrl(PayPalCommerceDefaults.Route.ShoppingCart) }) }); if (!model.CheckoutIsEnabled) return Json(new { redirect = Url.RouteUrl(PayPalCommerceDefaults.Route.ShoppingCart) }); if (!string.IsNullOrEmpty(model.Error)) return ErrorJson(model.Error); //customer can complete 3D Secure if prompted if (cardId > 0 && !string.IsNullOrEmpty(model.PayerActionUrl)) return Json(new { redirect = model.PayerActionUrl }); return Json(new { orderId = model.OrderId, status = model.Status }); } [HttpPost] public async Task GetOrderStatus(int placement, string orderId) { var model = await _modelFactory.PrepareOrderModelAsync((ButtonPlacement)placement, orderId, null, null, false); if (!string.IsNullOrEmpty(model.Error)) return ErrorJson(model.Error); return Json(new { status = model.Status, payerAction = model.PayerActionUrl }); } [HttpPost] public async Task UpdateOrderShipping(OrderShippingModel model) { model = await _modelFactory.PrepareOrderShippingModelAsync(model); if (!string.IsNullOrEmpty(model.Error)) return ErrorJson(model.Error); return Json(new { success = true }); } [HttpPost] public async Task ApproveOrder(string orderId, string liabilityShift) { var model = await _modelFactory.PrepareOrderApprovedModelAsync(orderId, liabilityShift); if (model.LoginIsRequired) return Json(new { redirect = Url.RouteUrl("Login", new { returnUrl = Url.RouteUrl(PayPalCommerceDefaults.Route.ShoppingCart) }) }); if (!model.CheckoutIsEnabled) return Json(new { redirect = Url.RouteUrl(PayPalCommerceDefaults.Route.ShoppingCart) }); if (!string.IsNullOrEmpty(model.Error)) return ErrorJson(model.Error); //order is approved but the customer must confirm it before if (!model.PayNow) { return Json(new { redirect = Url.RouteUrl(PayPalCommerceDefaults.Route.ConfirmOrder, new { orderId = model.OrderId, liabilityShift = liabilityShift }) }); } //or pay it right now var completedModel = await _modelFactory.PrepareOrderCompletedModelAsync(orderId, liabilityShift); if (!string.IsNullOrEmpty(completedModel.Error)) return ErrorJson(completedModel.Error); return Json(new { redirect = Url.RouteUrl(PayPalCommerceDefaults.Route.CheckoutCompleted, new { orderId = completedModel.OrderId }) }); } public async Task ConfirmOrder(string orderId, string token, string liabilityShift, bool approve) { if (string.IsNullOrEmpty(liabilityShift)) liabilityShift = _webHelper.QueryString("liability_shift"); var model = await _modelFactory.PrepareOrderConfirmModelAsync(orderId, token, liabilityShift, approve); if (model.LoginIsRequired) return RedirectToRoute("Login", new { returnUrl = Url.RouteUrl(PayPalCommerceDefaults.Route.ShoppingCart) }); if (!model.CheckoutIsEnabled) return RedirectToRoute(PayPalCommerceDefaults.Route.ShoppingCart); if (!string.IsNullOrEmpty(model.Error)) _notificationService.ErrorNotification(model.Error); return View("~/Plugins/Payments.PayPalCommerce/Views/Public/ConfirmOrder.cshtml", model); } [ValidateCaptcha] [HttpPost] public async Task ConfirmOrderPost(string orderId, string orderGuid, string liabilityShift, bool captchaValid) { var model = await _modelFactory.PrepareOrderConfirmModelAsync(orderId, orderGuid, null, false); if (model.LoginIsRequired) return RedirectToRoute("Login", new { returnUrl = Url.RouteUrl(PayPalCommerceDefaults.Route.ShoppingCart) }); if (!model.CheckoutIsEnabled) return RedirectToRoute(PayPalCommerceDefaults.Route.ShoppingCart); if (!string.IsNullOrEmpty(model.Error)) _notificationService.ErrorNotification(model.Error); if (model.DisplayCaptcha && !captchaValid) { model.Warnings.Add(await _localizationService.GetResourceAsync("Common.WrongCaptchaMessage")); return View("~/Plugins/Payments.PayPalCommerce/Views/Public/ConfirmOrder.cshtml", model); } var completedModel = await _modelFactory.PrepareOrderCompletedModelAsync(orderId, liabilityShift); if (!string.IsNullOrEmpty(completedModel.Error)) { _notificationService.ErrorNotification(completedModel.Error); return View("~/Plugins/Payments.PayPalCommerce/Views/Public/ConfirmOrder.cshtml", model); } if (!string.IsNullOrEmpty(completedModel.Warning)) _notificationService.ErrorNotification(completedModel.Warning); return RedirectToRoute(PayPalCommerceDefaults.Route.CheckoutCompleted, new { orderId = completedModel.OrderId }); } [HttpPost] public async Task AppleTransactionInfo(int placement) { var model = await _modelFactory.PrepareApplePayModelAsync((ButtonPlacement)placement); if (model.LoginIsRequired) return Json(new { redirect = Url.RouteUrl("Login", new { returnUrl = Url.RouteUrl(PayPalCommerceDefaults.Route.ShoppingCart) }) }); if (!model.CheckoutIsEnabled) return Json(new { redirect = Url.RouteUrl(PayPalCommerceDefaults.Route.ShoppingCart) }); if (!string.IsNullOrEmpty(model.Error)) return ErrorJson(model.Error); var totalItem = model.Items.FirstOrDefault(item => item.Type == "TOTAL"); var items = model.Items.Where(item => item.Type != "TOTAL"); var transactionInfo = new { currencyCode = model.CurrencyCode, billingContact = model.BillingAddress is null ? null : new { emailAddress = model.BillingAddress.Email, givenName = model.BillingAddress.FirstName, familyName = model.BillingAddress.LastName, addressLines = model.BillingAddress.AddressLines?.Where(line => !string.IsNullOrEmpty(line)).ToArray(), locality = model.BillingAddress.City, subAdministrativeArea = model.BillingAddress.County, administrativeArea = model.BillingAddress.State, countryCode = model.BillingAddress.Country, postalCode = model.BillingAddress.PostalCode }, shippingContact = model.ShippingAddress is null ? null : new { emailAddress = model.ShippingAddress.Email, givenName = model.ShippingAddress.FirstName, familyName = model.ShippingAddress.LastName, addressLines = model.ShippingAddress.AddressLines?.Where(line => !string.IsNullOrEmpty(line)).ToArray(), locality = model.ShippingAddress.City, administrativeArea = model.ShippingAddress.State, countryCode = model.ShippingAddress.Country, postalCode = model.ShippingAddress.PostalCode }, //shippingType = model.ShippingAddress is null ? null : (model.ShippingAddress.PickupInStore ? "storePickup" : "shipping"), //shippingContactEditingMode = model.ShippingAddress is null ? null : "storePickup", shippingMethods = model.ShippingOptions ?.Select(option => new { identifier = option.Id, amount = option.Price, label = option.Label, detail = option.Description }) .ToArray(), total = new { label = totalItem.Label, type = totalItem.Status, amount = totalItem.Price }, lineItems = items .Select(item => new { label = item.Label, type = item.Status, amount = item.Price }) .ToArray() }; return Json(new { transactionInfo = transactionInfo }); } [HttpPost] public async Task UpdateAppleShipping(ApplePayShippingModel model) { model = await _modelFactory.PrepareApplePayShippingModelAsync(model); if (!string.IsNullOrEmpty(model.Error)) return ErrorJson(model.Error); var totalItem = model.Items.FirstOrDefault(item => item.Type == "TOTAL"); var items = model.Items.Where(item => item.Type != "TOTAL"); //update total, items and shipping methods var shippingMethods = model.ShippingOptions ?.Select(option => new { identifier = option.Id, amount = option.Price, label = option.Label, detail = option.Description }) .ToArray(); var total = new { label = totalItem.Label, type = totalItem.Status, amount = totalItem.Price }; var lineItems = items .Select(item => new { label = item.Label, type = item.Status, amount = item.Price }) .ToArray(); return Json(new { shippingMethods = shippingMethods, total = total, lineItems = lineItems }); } [HttpPost] public async Task GoogleTransactionInfo(int placement) { var model = await _modelFactory.PrepareGooglePayModelAsync((ButtonPlacement)placement); if (model.LoginIsRequired) return Json(new { redirect = Url.RouteUrl("Login", new { returnUrl = Url.RouteUrl(PayPalCommerceDefaults.Route.ShoppingCart) }) }); if (!model.CheckoutIsEnabled) return Json(new { redirect = Url.RouteUrl(PayPalCommerceDefaults.Route.ShoppingCart) }); if (!string.IsNullOrEmpty(model.Error)) return ErrorJson(model.Error); var totalItem = model.Items.FirstOrDefault(item => item.Type == "TOTAL"); var items = model.Items.Where(item => item.Type != "TOTAL"); var transactionInfo = new { currencyCode = model.CurrencyCode, countryCode = model.Country, checkoutOption = "DEFAULT", totalPriceStatus = totalItem.Status, totalPrice = totalItem.Price, totalPriceLabel = totalItem.Label, displayItems = items.Select(item => new { type = item.Type, label = item.Label, price = item.Price, status = item.Status }).ToArray() }; var callbacks = new List { "PAYMENT_AUTHORIZATION" }; if (model.ShippingIsRequired) callbacks.AddRange(new List { "SHIPPING_ADDRESS", "SHIPPING_OPTION" }); return Json(new { transactionInfo = transactionInfo, shipping = model.ShippingIsRequired, callbacks = callbacks.ToArray() }); } [HttpPost] public async Task CheckGoogleShipping(int placement, int? productId) { var (shippingIsRequired, error) = await _modelFactory.CheckShippingIsRequiredAsync(productId); if (!string.IsNullOrEmpty(error)) return ErrorJson(error); return Json(new { shippingIsRequired = shippingIsRequired && placement != (int)ButtonPlacement.PaymentMethod }); } [HttpPost] public async Task UpdateGoogleShipping(GooglePayShippingModel model) { model = await _modelFactory.PrepareGooglePayShippingModelAsync(model); if (!string.IsNullOrEmpty(model.Error)) return ErrorJson(model.Error); var totalItem = model.Items.FirstOrDefault(item => item.Type == "TOTAL"); var items = model.Items.Where(item => item.Type != "TOTAL"); var transactionInfo = new { currencyCode = model.CurrencyCode, countryCode = model.Country, checkoutOption = "DEFAULT", totalPriceStatus = totalItem.Status, totalPrice = totalItem.Price, totalPriceLabel = totalItem.Label, displayItems = items.Select(item => new { type = item.Type, label = item.Label, price = item.Price, status = item.Status }).ToArray() }; var options = model.Options?.Select(option => new { id = option.Id, label = option.Label, description = option.Description }).ToArray(); return Json(new { transactionInfo = transactionInfo, options = new { shippingOptions = options, defaultSelectedOptionId = model.OptionId } }); } #endregion #region Payment tokens public async Task PaymentTokens() { var model = await _modelFactory.PreparePaymentTokenListModelAsync(); if (!model.VaultIsEnabled) return RedirectToRoute(PayPalCommerceDefaults.Route.CustomerInfo); if (!string.IsNullOrEmpty(model.Error)) _notificationService.ErrorNotification(model.Error); return View("~/Plugins/Payments.PayPalCommerce/Views/Public/PaymentTokens.cshtml", model); } [HttpPost] public async Task PaymentTokensDelete(int tokenId) { var model = await _modelFactory.PreparePaymentTokenListModelAsync(deleteTokenId: tokenId); if (!model.VaultIsEnabled) return Json(new { redirect = Url.RouteUrl(PayPalCommerceDefaults.Route.CustomerInfo) }); if (!string.IsNullOrEmpty(model.Error)) return ErrorJson(model.Error); return Json(new { redirect = Url.RouteUrl(PayPalCommerceDefaults.Route.PaymentTokens) }); } [HttpPost] public async Task PaymentTokensMarkDefault(int tokenId) { var model = await _modelFactory.PreparePaymentTokenListModelAsync(defaultTokenId: tokenId); if (!model.VaultIsEnabled) return Json(new { redirect = Url.RouteUrl(PayPalCommerceDefaults.Route.CustomerInfo) }); if (!string.IsNullOrEmpty(model.Error)) return ErrorJson(model.Error); return Json(new { redirect = Url.RouteUrl(PayPalCommerceDefaults.Route.PaymentTokens) }); } [HttpPost] public async Task GetSavedCards(int placement) { var model = await _modelFactory.PrepareSavedCardListModelAsync((ButtonPlacement)placement); if (!string.IsNullOrEmpty(model.Error)) return ErrorJson(model.Error); if (model.PaymentTokens?.Any() != true) return Json(new { }); var cards = model.PaymentTokens.Select(token => new { id = token.Id, label = token.Title }).ToArray(); return Json(new { cards = cards, defaultId = model.PaymentTokens.FirstOrDefault().Id }); } #endregion #endregion }