diff --git a/src/Dynamicweb.Ecommerce.CheckoutHandlers.StripeCheckout.csproj b/src/Dynamicweb.Ecommerce.CheckoutHandlers.StripeCheckout.csproj index 9f97258..15103e4 100644 --- a/src/Dynamicweb.Ecommerce.CheckoutHandlers.StripeCheckout.csproj +++ b/src/Dynamicweb.Ecommerce.CheckoutHandlers.StripeCheckout.csproj @@ -1,6 +1,6 @@  - 10.7.1 + 10.12.1 1.0.0.0 Stripe Checkout 2.0 Stripe Checkout handler @@ -34,9 +34,9 @@ - - - + + + diff --git a/src/Models/Customer/Customer.cs b/src/Models/Customer/Customer.cs index 6bf0322..dd62fb2 100644 --- a/src/Models/Customer/Customer.cs +++ b/src/Models/Customer/Customer.cs @@ -32,4 +32,7 @@ internal sealed class Customer [DataMember(Name = "livemode")] public bool Livemode { get; set; } + + [DataMember(Name = "deleted")] + public bool Deleted { get; set; } } diff --git a/src/Service/StripeService.cs b/src/Service/StripeService.cs index 1114eff..2a86126 100644 --- a/src/Service/StripeService.cs +++ b/src/Service/StripeService.cs @@ -1,9 +1,10 @@ -using Dynamicweb.Core; +using Dynamicweb.Configuration; +using Dynamicweb.Core; using Dynamicweb.Ecommerce.CheckoutHandlers.StripeCheckout.Models; +using Dynamicweb.Ecommerce.CheckoutHandlers.StripeCheckout.Models.Customer; using Dynamicweb.Ecommerce.CheckoutHandlers.StripeCheckout.Models.Error; using Dynamicweb.Ecommerce.CheckoutHandlers.StripeCheckout.Models.PaymentIntent; using Dynamicweb.Ecommerce.CheckoutHandlers.StripeCheckout.Models.Refund; -using Dynamicweb.Ecommerce.CheckoutHandlers.StripeCheckout.Models.Customer; using Dynamicweb.Ecommerce.Orders; using System; using System.Collections.Generic; @@ -513,21 +514,27 @@ public Session GetSession(string sessionId) private void SetShippingParameters(Order order, Dictionary parameters, string shippingParameter) { + if (string.IsNullOrWhiteSpace(GetAddress()) && SystemConfiguration.Instance.GetBoolean("/Globalsettings/Ecom/Cart/CopyCustomerFieldsToDelivery")) + Services.Carts.CopyCustomerFieldsToDelivery(order); + //name is required parameter, so we should fill it by something string recipientName = !string.IsNullOrWhiteSpace(order.DeliveryName) ? order.DeliveryName : order.DeliveryMiddleName; - if (string.IsNullOrWhiteSpace(shippingParameter)) - recipientName = "Recipient name"; + if (string.IsNullOrWhiteSpace(recipientName)) + recipientName = "Recipient"; parameters[$"{shippingParameter}[name]"] = recipientName; parameters[$"{shippingParameter}[phone]"] = order.DeliveryPhone; string addressParameter = $"{shippingParameter}[address]"; + //line 1 is required parameter - parameters[$"{addressParameter}[line1]"] = !string.IsNullOrWhiteSpace(order.DeliveryAddress) ? order.DeliveryAddress : order.DeliveryAddress2; + parameters[$"{addressParameter}[line1]"] = GetAddress(); parameters[$"{addressParameter}[line2]"] = order.DeliveryAddress2; parameters[$"{addressParameter}[state]"] = order.DeliveryRegion; parameters[$"{addressParameter}[city]"] = order.DeliveryCity; parameters[$"{addressParameter}[country]"] = order.DeliveryCountryCode; parameters[$"{addressParameter}[postal_code]"] = order.DeliveryZip; + + string GetAddress() => !string.IsNullOrWhiteSpace(order.DeliveryAddress) ? order.DeliveryAddress : order.DeliveryAddress2; } } diff --git a/src/StripeCheckout.cs b/src/StripeCheckout.cs index ea02bbb..65cfb49 100644 --- a/src/StripeCheckout.cs +++ b/src/StripeCheckout.cs @@ -1,6 +1,7 @@ using Dynamicweb.Core; using Dynamicweb.Ecommerce.Cart; using Dynamicweb.Ecommerce.CheckoutHandlers.StripeCheckout.Models; +using Dynamicweb.Ecommerce.CheckoutHandlers.StripeCheckout.Models.Customer; using Dynamicweb.Ecommerce.CheckoutHandlers.StripeCheckout.Models.PaymentIntent; using Dynamicweb.Ecommerce.CheckoutHandlers.StripeCheckout.Models.PaymentMethod; using Dynamicweb.Ecommerce.CheckoutHandlers.StripeCheckout.Models.Refund; @@ -76,7 +77,7 @@ public string ErrorTemplate [AddInParameter("Capture now"), AddInParameterEditor(typeof(YesNoParameterEditor), "infoText=Auto-captures a payment when it is authorized. Please note that it is illegal in some countries to capture payment before shipping any physical goods.;")] public bool CaptureNow { get; set; } - + // /// Gets or sets post mode indicates how user will be redirected to Stripe service /// @@ -85,7 +86,7 @@ public string PostModeSelection { get => PostMode.ToString(); set - { + { PostMode = value switch { nameof(PostModes.Auto) => PostModes.Auto, @@ -187,17 +188,32 @@ private OutputResult CreateSession(Order order, bool recurring) var cardSettings = new BasePaymentCardSettings(order); string action = recurring ? "CompleteSetup" : "Complete"; - PaymentCardToken savedCard = Services.PaymentCard.GetByUserId(order.CustomerAccessUserId).FirstOrDefault(card => !string.IsNullOrEmpty(card.Token)); - string[] cardToken = savedCard?.Token?.Split('|'); + PaymentCardToken savedCard = Services.PaymentCard.GetByUserId(order.CustomerAccessUserId) + .FirstOrDefault(card => order.PaymentMethodId.Equals(card.PaymentID, StringComparison.OrdinalIgnoreCase) && !string.IsNullOrEmpty(card.Token)); + string[] cardToken = savedCard?.Token?.Split('|'); + string customerId = cardToken?.ElementAtOrDefault(0); var service = new StripeService(GetSecretKey()); - string idempotencyKey = IdempotencyKeyHelper.GetKey(ApiCommand.CreateSession, MerchantName, order.Id); + if (!string.IsNullOrWhiteSpace(customerId)) + { + try + { + if (service.GetCustomer(customerId) is not Customer customer || customer.Deleted) + customerId = null; + } + catch + { + customerId = null; + } + } + + string idempotencyKey = IdempotencyKeyHelper.GetKey(ApiCommand.CreateSession, MerchantName, order.Id); Session session = service.CreateSession(idempotencyKey, order, new() { AutomaticCapture = CaptureNow, SavePaymentMethod = cardSettings.IsSaveNeeded && SaveCards, - CustomerId = cardToken?.ElementAtOrDefault(0), + CustomerId = customerId, Language = Language, Mode = recurring ? SessionMode.Setup : SessionMode.Payment, EmbeddedForm = PostMode is PostModes.InlineTemplate, @@ -467,12 +483,13 @@ public string UseSavedCard(Order order) private OutputResult UseSavedCardInternal(Order order, bool recurringOrderPayment) { PaymentCardToken savedCard = Services.PaymentCard.GetById(order.SavedCardId); - if (savedCard is null || order.CustomerAccessUserId != savedCard.UserID) + if (savedCard is null || order.CustomerAccessUserId != savedCard.UserID || !order.PaymentMethodId.Equals(savedCard.PaymentID, StringComparison.OrdinalIgnoreCase)) throw new PaymentCardTokenException("Token is incorrect."); LogEvent(order, "Using saved card({0}) with id: {1}", savedCard.Identifier, savedCard.ID); var cardTokenKey = CardTokenKey.Parse(savedCard.Token); + string errorMessage = null; try { var service = new StripeService(GetSecretKey()); @@ -497,19 +514,25 @@ private OutputResult UseSavedCardInternal(Order order, bool recurringOrderPaymen CustomerId = cardTokenKey.CustomerId, PaymentMethodId = cardTokenKey.PaymentMethodId }); + if (setupIntent.Status is not PaymentIntentStatus.Succeeded) throw new Exception("Something went wrong during setup intent creation using saved card data. Probably the payment method is outdated or wasn't configured for off payments. Try to create recurring order using new card data."); return new RedirectOutputResult { RedirectUrl = $"{GetBaseUrl(order)}&Action=CompleteSetup&setup_intent={setupIntent.Id}&SaveCard=false" }; } } - catch + catch (Exception ex) { + errorMessage = ex.Message; CheckoutDone(order); } if (!order.Complete) - throw new Exception("Called create charge, but order is not set complete."); + { + if (string.IsNullOrWhiteSpace(errorMessage)) + errorMessage = "Something went wrong during payment operation. Probably the saved card token is outdated. Try to use another saved card or create new."; + throw new Exception(errorMessage); + } return PassToCart(order); }