diff --git a/src/Modules/OrchardCore.Commerce.Payment.Stripe/Abstractions/IStripePaymentService.cs b/src/Modules/OrchardCore.Commerce.Payment.Stripe/Abstractions/IStripePaymentService.cs
index fbdabd4ce..19af46bbd 100644
--- a/src/Modules/OrchardCore.Commerce.Payment.Stripe/Abstractions/IStripePaymentService.cs
+++ b/src/Modules/OrchardCore.Commerce.Payment.Stripe/Abstractions/IStripePaymentService.cs
@@ -17,6 +17,8 @@ namespace OrchardCore.Commerce.Payment.Stripe.Abstractions;
///
public interface IStripePaymentService
{
+ long GetPaymentAmount(Amount total);
+
///
/// Returns the public key of the Stripe account.
///
diff --git a/src/Modules/OrchardCore.Commerce.Payment.Stripe/EndPoints/Api/StripeEndpoint.cs b/src/Modules/OrchardCore.Commerce.Payment.Stripe/EndPoints/Api/StripeEndpoint.cs
index 51431ae43..5268134f8 100644
--- a/src/Modules/OrchardCore.Commerce.Payment.Stripe/EndPoints/Api/StripeEndpoint.cs
+++ b/src/Modules/OrchardCore.Commerce.Payment.Stripe/EndPoints/Api/StripeEndpoint.cs
@@ -4,19 +4,51 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
+using OrchardCore.Commerce.Endpoints;
using OrchardCore.Commerce.Endpoints.Permissions;
+using OrchardCore.Commerce.MoneyDataType;
+using OrchardCore.Commerce.Payment.Abstractions;
using OrchardCore.Commerce.Payment.Stripe.Abstractions;
+using OrchardCore.Commerce.Payment.Stripe.EndPoints.Models;
+using OrchardCore.Commerce.Payment.Stripe.Models;
+using OrchardCore.Commerce.Payment.Stripe.Services;
using OrchardCore.Modules;
-using Stripe;
+using OrchardCore.Settings;
+using System.Collections.Generic;
+using System.Linq;
using System.Threading.Tasks;
namespace OrchardCore.Commerce.Payment.Stripe.EndPoints.Api;
public static class StripeEndpoint
{
+ // Get total amount
+ public static IEndpointRouteBuilder AddStripeTotalEndpoint(this IEndpointRouteBuilder builder)
+ {
+ builder.MapGet("api/checkout/stripe/total/{shoppingCartId?}", GetStripeTotalAsync)
+ .AllowAnonymous()
+ .DisableAntiforgery();
+
+ return builder;
+ }
+
+ private static async Task GetStripeTotalAsync(
+ [FromRoute] string? shoppingCartId,
+ [FromServices] IShoppingCartService shoppingCartService,
+ [FromServices] IStripePaymentService stripePaymentService
+ )
+ {
+ var shoppingCartViewModel = await shoppingCartService.GetAsync(shoppingCartId);
+ var total = shoppingCartViewModel.Totals.Single();
+ return TypedResults.Ok(new
+ {
+ Amount = stripePaymentService.GetPaymentAmount(total),
+ total.Currency,
+ });
+ }
+
public static IEndpointRouteBuilder AddStripePublicKeyEndpoint(this IEndpointRouteBuilder builder)
{
builder.MapGet("api/checkout/stripe/public-key", GetStripePublicKeyAsync)
- .RequireAuthorization("Api")
.DisableAntiforgery();
return builder;
@@ -41,7 +73,6 @@ HttpContext httpContext
public static IEndpointRouteBuilder AddStripePaymentIntentEndpoint(this IEndpointRouteBuilder builder)
{
builder.MapPost("api/checkout/stripe/payment-intent", CreatePaymentIntentAsync)
- .RequireAuthorization("Api")
.DisableAntiforgery();
return builder;
@@ -49,22 +80,20 @@ public static IEndpointRouteBuilder AddStripePaymentIntentEndpoint(this IEndpoin
[HttpPost]
public static async Task CreatePaymentIntentAsync(
- [FromBody] string amount,
- [FromServices] PaymentIntentService paymentIntentService
+ [FromBody] CreatePaymentIntentViewModel viewModel,
+ [FromServices] ISiteService siteService,
+ [FromServices] IStripePaymentService stripePaymentService,
+ [FromServices] IShoppingCartService shoppingCartService,
+ [FromServices] IEnumerable paymentProviders
)
{
- var paymentIntent = await paymentIntentService.CreateAsync(new PaymentIntentCreateOptions
- {
- Amount = long.Parse(amount),
- Currency = "eur",
- AutomaticPaymentMethods = new PaymentIntentAutomaticPaymentMethodsOptions { Enabled = true, },
- });
+ var shoppingCartViewModel = await shoppingCartService.GetAsync(viewModel.ShoppingCartId);
+ var total = shoppingCartViewModel.Totals.Single();
+ var paymentIntent = await stripePaymentService.CreatePaymentIntentAsync(total);
return TypedResults.Ok(new
{
clientSecret = paymentIntent.ClientSecret,
- // [DEV]: For demo purposes only, you should avoid exposing the PaymentIntent ID in the client-side code.
- dpmCheckerLink = $"https://dashboard.stripe.com/settings/payment_methods/review?transaction_id={paymentIntent.Id}",
});
}
diff --git a/src/Modules/OrchardCore.Commerce.Payment.Stripe/EndPoints/Models/CreatePaymentIntentViewModel.cs b/src/Modules/OrchardCore.Commerce.Payment.Stripe/EndPoints/Models/CreatePaymentIntentViewModel.cs
new file mode 100644
index 000000000..a04b84411
--- /dev/null
+++ b/src/Modules/OrchardCore.Commerce.Payment.Stripe/EndPoints/Models/CreatePaymentIntentViewModel.cs
@@ -0,0 +1,6 @@
+namespace OrchardCore.Commerce.Payment.Stripe.EndPoints.Models;
+
+public class CreatePaymentIntentViewModel
+{
+ public string ShoppingCartId { get; set; }
+}
diff --git a/src/Modules/OrchardCore.Commerce.Payment.Stripe/OrchardCore.Commerce.Payment.Stripe.csproj b/src/Modules/OrchardCore.Commerce.Payment.Stripe/OrchardCore.Commerce.Payment.Stripe.csproj
index 7359308f4..1eaad2231 100644
--- a/src/Modules/OrchardCore.Commerce.Payment.Stripe/OrchardCore.Commerce.Payment.Stripe.csproj
+++ b/src/Modules/OrchardCore.Commerce.Payment.Stripe/OrchardCore.Commerce.Payment.Stripe.csproj
@@ -48,8 +48,5 @@
-
-
-
diff --git a/src/Modules/OrchardCore.Commerce.Payment.Stripe/Services/PaymentIntentPersistence.cs b/src/Modules/OrchardCore.Commerce.Payment.Stripe/Services/PaymentIntentPersistence.cs
index a6c02fc22..52b41281a 100644
--- a/src/Modules/OrchardCore.Commerce.Payment.Stripe/Services/PaymentIntentPersistence.cs
+++ b/src/Modules/OrchardCore.Commerce.Payment.Stripe/Services/PaymentIntentPersistence.cs
@@ -5,16 +5,37 @@ namespace OrchardCore.Commerce.Payment.Stripe.Services;
public class PaymentIntentPersistence : IPaymentIntentPersistence
{
- private const string PaymentIntentKey = "OrchardCore:Commerce:PaymentIntent";
+ // Using _ as a separator to avoid separator character conflicts.
+ private const string PaymentIntentKey = "OrchardCore_Commerce_PaymentIntent";
private readonly IHttpContextAccessor _httpContextAccessor;
private ISession Session => _httpContextAccessor.HttpContext?.Session;
public PaymentIntentPersistence(IHttpContextAccessor httpContextAccessor) => _httpContextAccessor = httpContextAccessor;
- public string Retrieve() => Session.GetString(PaymentIntentKey);
+ public string Retrieve()
+ {
+ var serialized = Session.GetString(PaymentIntentKey);
+ if (serialized == null && _httpContextAccessor.HttpContext != null)
+ {
+ _httpContextAccessor.HttpContext.Request.Cookies.TryGetValue(PaymentIntentKey, out var serializedCart);
+ return serializedCart;
+ }
- public void Store(string paymentIntentId) => Session.SetString(PaymentIntentKey, paymentIntentId);
+ return serialized;
+ }
- public void Remove() => Session.Remove(PaymentIntentKey);
+ public void Store(string paymentIntentId)
+ {
+ if (Session.GetString(PaymentIntentKey) == paymentIntentId) return;
+
+ Session.SetString(PaymentIntentKey, paymentIntentId);
+ _httpContextAccessor.SetCookieForever(PaymentIntentKey, paymentIntentId);
+ }
+
+ public void Remove()
+ {
+ Session.Remove(PaymentIntentKey);
+ _httpContextAccessor.HttpContext?.Response.Cookies.Delete(PaymentIntentKey);
+ }
}
diff --git a/src/Modules/OrchardCore.Commerce.Payment.Stripe/Services/StripePaymentService.cs b/src/Modules/OrchardCore.Commerce.Payment.Stripe/Services/StripePaymentService.cs
index be3ba379f..3eed8e095 100644
--- a/src/Modules/OrchardCore.Commerce.Payment.Stripe/Services/StripePaymentService.cs
+++ b/src/Modules/OrchardCore.Commerce.Payment.Stripe/Services/StripePaymentService.cs
@@ -304,7 +304,7 @@ await _contentManager.GetAsync(orderId) is not { } order)
};
}
- private static long GetPaymentAmount(Amount total)
+ public long GetPaymentAmount(Amount total)
{
if (CurrencyCollectionConstants.ZeroDecimalCurrencies.Contains(total.Currency.CurrencyIsoCode))
{
diff --git a/src/Modules/OrchardCore.Commerce.Payment.Stripe/Startup.cs b/src/Modules/OrchardCore.Commerce.Payment.Stripe/Startup.cs
index 6b78735a0..ee2e9d501 100644
--- a/src/Modules/OrchardCore.Commerce.Payment.Stripe/Startup.cs
+++ b/src/Modules/OrchardCore.Commerce.Payment.Stripe/Startup.cs
@@ -49,5 +49,7 @@ public override void ConfigureServices(IServiceCollection services)
public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) =>
routes.AddStripeMiddlewareEndpoint()
.AddStripePublicKeyEndpoint()
- .AddStripePaymentIntentEndpoint();
+ .AddStripePaymentIntentEndpoint()
+ .AddStripeTotalEndpoint()
+ ;
}
diff --git a/src/Modules/OrchardCore.Commerce/Endpoints/Api/ShoppingCartLineEndpoint.cs b/src/Modules/OrchardCore.Commerce/Endpoints/Api/ShoppingCartLineEndpoint.cs
index 7d16e7fb8..624194d5b 100644
--- a/src/Modules/OrchardCore.Commerce/Endpoints/Api/ShoppingCartLineEndpoint.cs
+++ b/src/Modules/OrchardCore.Commerce/Endpoints/Api/ShoppingCartLineEndpoint.cs
@@ -9,7 +9,6 @@
using OrchardCore.Commerce.Endpoints.Permissions;
using OrchardCore.Commerce.Endpoints.ViewModels;
using OrchardCore.Modules;
-using System;
using System.Threading.Tasks;
namespace OrchardCore.Commerce.Endpoints.Api;
@@ -17,7 +16,7 @@ public static class ShoppingCartLineEndpoint
{
public static IEndpointRouteBuilder AddGetCartEndpoint(this IEndpointRouteBuilder builder)
{
- builder.MapGet("api/shoppingcart/get-cart/{shoppingCartId}", GetCartAsync)
+ builder.MapGet("api/shoppingcart/get-cart/{shoppingCartId?}", GetCartAsync)
.DisableAntiforgery();
return builder;
@@ -25,7 +24,7 @@ public static IEndpointRouteBuilder AddGetCartEndpoint(this IEndpointRouteBuilde
[Authorize(AuthenticationSchemes = "Api")]
private static async Task GetCartAsync(
- [FromRoute] string shoppingCartId,
+ [FromRoute] string? shoppingCartId,
[FromServices] IAuthorizationService authorizationService,
[FromServices] IShoppingCartService shoppingCartService,
HttpContext httpContext)
@@ -65,8 +64,6 @@ private static async Task AddItemAsync(
return httpContext.ChallengeOrForbid("Api");
}
- if (string.IsNullOrEmpty(addItemVM.ShoppingCartId)) { addItemVM.ShoppingCartId = Guid.NewGuid().ToString("n"); }
-
var errored = await shoppingCartService.AddItemAsync(addItemVM.Line, addItemVM.Token, addItemVM.ShoppingCartId);
if (string.IsNullOrEmpty(errored))
{
diff --git a/src/Modules/OrchardCore.Commerce/Services/SessionShoppingCartPersistence.cs b/src/Modules/OrchardCore.Commerce/Services/SessionShoppingCartPersistence.cs
index 4631dd035..01119796d 100644
--- a/src/Modules/OrchardCore.Commerce/Services/SessionShoppingCartPersistence.cs
+++ b/src/Modules/OrchardCore.Commerce/Services/SessionShoppingCartPersistence.cs
@@ -23,8 +23,17 @@ public SessionShoppingCartPersistence(
_shoppingCartSerializer = shoppingCartSerializer;
}
- protected override Task RetrieveInnerAsync(string key) =>
- _shoppingCartSerializer.DeserializeAsync(Session.GetString(key));
+ protected override Task RetrieveInnerAsync(string key)
+ {
+ var serialized = Session.GetString(key);
+ if (serialized == null && _httpContextAccessor.HttpContext != null)
+ {
+ _httpContextAccessor.HttpContext.Request.Cookies.TryGetValue(key, out var serializedCart);
+ return _shoppingCartSerializer.DeserializeAsync(serializedCart);
+ }
+
+ return _shoppingCartSerializer.DeserializeAsync(serialized);
+ }
protected override async Task StoreInnerAsync(string key, ShoppingCart items)
{
@@ -32,6 +41,8 @@ protected override async Task StoreInnerAsync(string key, ShoppingCart ite
if (Session.GetString(key) == cartString) return false;
Session.SetString(key, cartString);
+ _httpContextAccessor.SetCookieForever(key, cartString);
+
return true;
}
}
diff --git a/src/Modules/OrchardCore.Commerce/Services/ShoppingCartPersistenceBase.cs b/src/Modules/OrchardCore.Commerce/Services/ShoppingCartPersistenceBase.cs
index d5584cd38..29062b5c1 100644
--- a/src/Modules/OrchardCore.Commerce/Services/ShoppingCartPersistenceBase.cs
+++ b/src/Modules/OrchardCore.Commerce/Services/ShoppingCartPersistenceBase.cs
@@ -9,7 +9,8 @@ namespace OrchardCore.Commerce.Services;
public abstract class ShoppingCartPersistenceBase : IShoppingCartPersistence
{
- private const string ShoppingCartPrefix = "OrchardCore:Commerce:ShoppingCart";
+ // Using _ as a separator to avoid separator character conflicts.
+ private const string ShoppingCartPrefix = "OrchardCore_Commerce_ShoppingCart";
private readonly Dictionary _scopeCache = [];
@@ -68,5 +69,5 @@ public async Task StoreAsync(ShoppingCart items)
protected abstract Task StoreInnerAsync(string key, ShoppingCart items);
protected string GetCacheId(string shoppingCartId) =>
- string.IsNullOrEmpty(shoppingCartId) ? ShoppingCartPrefix : $"{ShoppingCartPrefix}:{shoppingCartId}";
+ string.IsNullOrEmpty(shoppingCartId) ? ShoppingCartPrefix : $"{ShoppingCartPrefix}_{shoppingCartId}";
}