Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
95a06f1
Update All Dependencies
renovate-bot Feb 8, 2026
8581790
Update OC preview and fix build errors.
sarahelsaig Feb 21, 2026
ba5ae83
Update OC preview.
sarahelsaig Feb 22, 2026
554a17a
Update OC preview.
sarahelsaig Mar 7, 2026
b07a61f
Update OC preview.
sarahelsaig Mar 7, 2026
adbdc05
Update Lombiq nugets.
sarahelsaig Mar 7, 2026
155dcd5
Merge remote-tracking branch 'origin/renovate/all-dependencies' into …
sarahelsaig Mar 7, 2026
0efaa31
Update docker files.
sarahelsaig Mar 7, 2026
7444e21
Fix Dockerfile.
sarahelsaig Mar 7, 2026
e936e57
Block false positive.
sarahelsaig Mar 7, 2026
0e9dd19
Merge remote-tracking branch 'origin/main' into issue/NEST-608
sarahelsaig Mar 8, 2026
1007dae
Use MediaTypeNames.
sarahelsaig Mar 11, 2026
e851d5e
Fix package downgrades.
sarahelsaig Mar 12, 2026
dd21e35
Use DbException instead of SqlException.
sarahelsaig Mar 28, 2026
d1dc67f
Simplify.
sarahelsaig Mar 28, 2026
69555e2
unusing
sarahelsaig Mar 28, 2026
28e6685
Merge remote-tracking branch 'origin/main' into issue/OCC-436
sarahelsaig Apr 11, 2026
1874263
Fix OC package consolidation.
sarahelsaig Apr 11, 2026
d491781
Post upgrade fixes.
sarahelsaig Apr 12, 2026
6761f3d
Merge branch 'issue/OCC-436' into issue/NEST-608
sarahelsaig Apr 12, 2026
a8466a3
Fix package versions.
sarahelsaig Apr 14, 2026
6761663
Update OC preview.
sarahelsaig Apr 28, 2026
3b853f4
Minor JS call cleanup.
sarahelsaig Apr 29, 2026
45ec4dc
Refactor stripePaymentForm and get rid of unused parameters.
sarahelsaig Apr 29, 2026
7f3654c
Get rid of NRE warnings.
sarahelsaig Apr 29, 2026
05c300e
Remove unused action.
sarahelsaig Apr 29, 2026
1dec6b2
Refactor payment intent storage.
sarahelsaig Apr 29, 2026
12a1727
Don't create new payment intents when editing the addresses during ch…
sarahelsaig Apr 29, 2026
160be2e
OC NuGet package versions.
sarahelsaig Apr 29, 2026
98b8f7b
Remove unused service.
sarahelsaig Apr 29, 2026
f119a74
Fix new obsoletes.
sarahelsaig Apr 29, 2026
ff154c6
dummy
sarahelsaig Apr 29, 2026
9ba0cda
Updated GHA in the background.
sarahelsaig Apr 29, 2026
22b7145
Fix MD047.
sarahelsaig Apr 29, 2026
884ceff
Updated GHA in the background.
sarahelsaig Apr 29, 2026
bc8b771
Remove trailing spaces.
sarahelsaig Apr 29, 2026
cc4abd3
Fix eslint warnings.
sarahelsaig Apr 29, 2026
0554e05
Fix max-len.
sarahelsaig Apr 29, 2026
c91efb0
Revert "Fix new obsoletes."
sarahelsaig May 2, 2026
1d25e0b
Merge commit '6761663fe3fcb3e6b9cfb7d36e89b40c4092fdac' into issue/OC…
sarahelsaig May 2, 2026
b65fb77
Update NuGets.
sarahelsaig May 2, 2026
b7a3327
Fix build errors and warnings.
sarahelsaig May 2, 2026
9aadd5a
Check retrieved storedPaymentIntent value against total.
sarahelsaig May 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions 4.0.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,7 @@ Please check the GitHub release entry [here](https://github.com/OrchardCMS/Orcha
### SKU Generators

Now it's possible to automatically generate the product SKU. Manual entry remains the default for backwards compatibility, but if the SKU doesn't have to be a specific value you can enable the "Orchard Core Commerce - SKU Generator - GUID" feature. This makes the SKU field read-only even during product creation. Instead, when you publish the product a new GUID string is automatically generated and filled as the SKU. You can also implement your own SKU generator by implementing the `ISkuGenerator` service.

### Stripe

The client script in _stripe-payment-form.js_ has changed. Now `stripePaymentForm` accepts an object instead of a lot of individual positional parameters. Additionally, some parameters that are no longer used have been removed. If you have overridden the `CheckoutStripe` shape on your site, make sure to update it based on the one in OCC.
28 changes: 14 additions & 14 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,28 @@
<!-- The Orchard Core version should always be x.y.0 of the latest minor version for maximum compatibility when
distributed as NuGet packages. On the other hand, the consuming projects (including OrchardCore.Commerce.Web)
should use Orchard Core references for the latest patch version to pull all versions up in the final app. -->
<OrchardCoreVersion>3.0.0-preview-18960</OrchardCoreVersion>
<OrchardCoreVersion>3.0.0-preview-18997</OrchardCoreVersion>
</PropertyGroup>

<ItemGroup>
<PackageVersion Include="GraphQL.MicrosoftDI" Version="8.8.4" />
<PackageVersion Include="GraphQL.SystemTextJson" Version="8.8.4" />
<PackageVersion Include="HtmlSanitizer" Version="9.1.893-beta" />
<PackageVersion Include="Lombiq.Analyzers.OrchardCore" Version="5.2.1-alpha.3.osoe-925" />
<PackageVersion Include="Lombiq.HelpfulLibraries.OrchardCore" Version="12.6.1-alpha.6.osoe-925" />
<PackageVersion Include="Lombiq.HelpfulLibraries.AspNetCore" Version="12.6.1-alpha.6.osoe-925" />
<PackageVersion Include="Lombiq.HelpfulLibraries.Common" Version="12.6.1-alpha.6.osoe-925" />
<PackageVersion Include="Lombiq.HelpfulLibraries.Refit" Version="12.6.1-alpha.6.osoe-925" />
<PackageVersion Include="HtmlSanitizer" Version="9.1.923-beta" />
<PackageVersion Include="Lombiq.Analyzers.OrchardCore" Version="5.2.1-alpha.4.osoe-925" />
<PackageVersion Include="Lombiq.HelpfulLibraries.OrchardCore" Version="12.6.1-alpha.7.osoe-925" />
<PackageVersion Include="Lombiq.HelpfulLibraries.AspNetCore" Version="12.6.1-alpha.7.osoe-925" />
<PackageVersion Include="Lombiq.HelpfulLibraries.Common" Version="12.6.1-alpha.7.osoe-925" />
<PackageVersion Include="Lombiq.HelpfulLibraries.Refit" Version="12.6.1-alpha.7.osoe-925" />
<PackageVersion Include="Lombiq.Tests" Version="5.0.1-alpha.1.osoe-925" />
<PackageVersion Include="Lombiq.Tests.UI" Version="14.2.2-alpha.7.osoe-925" />
<PackageVersion Include="Lombiq.Tests.UI.AppExtensions" Version="14.2.2-alpha.7.osoe-925" />
<PackageVersion Include="Lombiq.Tests.UI.Shortcuts" Version="14.2.2-alpha.7.osoe-925" />
<PackageVersion Include="Microsoft.Identity.Web" Version="4.6.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.3.0" />
<PackageVersion Include="Lombiq.Tests.UI" Version="14.2.2-alpha.14.osoe-925" />
<PackageVersion Include="Lombiq.Tests.UI.AppExtensions" Version="14.2.2-alpha.14.osoe-925" />
<PackageVersion Include="Lombiq.Tests.UI.Shortcuts" Version="14.2.2-alpha.14.osoe-925" />
<PackageVersion Include="Microsoft.Identity.Web" Version="4.9.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.5.1" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.4" />
<PackageVersion Include="OrchardCore.Application.Cms.Targets" Version="$(OrchardCoreVersion)" />
<PackageVersion Include="OrchardCore.ContentFields" Version="$(OrchardCoreVersion)" />
<PackageVersion Include="OrchardCore.Commerce.Translations" Version="1.0.0-preview.4.occ-121" />
<PackageVersion Include="OrchardCore.Commerce.Translations" Version="1.0.0-preview.5.occ-221" />
<PackageVersion Include="OrchardCore.ContentManagement" Version="$(OrchardCoreVersion)" />
<PackageVersion Include="OrchardCore.ContentManagement.Abstractions" Version="$(OrchardCoreVersion)" />
<PackageVersion Include="OrchardCore.ContentTypes" Version="$(OrchardCoreVersion)" />
Expand All @@ -46,7 +46,7 @@
<PackageVersion Include="OrchardCore.Workflows.Abstractions" Version="$(OrchardCoreVersion)" />
<PackageVersion Include="Shouldly" Version="4.3.0" />
<PackageVersion Include="SixLabors.ImageSharp" Version="3.1.12" />
<PackageVersion Include="Stripe.net" Version="50.4.1" />
<PackageVersion Include="Stripe.net" Version="51.1.0" />
<PackageVersion Include="xunit.v3" Version="3.2.2" />
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.5" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ public static Task<ShoppingCartViewModel> CreateShoppingCartViewModelAsync(
string shoppingCartId,
IContent order)
{
var orderPart = order as OrderPart ?? order.As<OrderPart>();
var orderPart = order as OrderPart ?? order.GetOrCreate<OrderPart>();

return service.CreateShoppingCartViewModelAsync(
shoppingCartId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@ public static Task<User> GetCurrentFullUserAsync(this IUserService service, IHtt
: Task.FromResult<User>(null);

public static TPart GetUserSetting<TPart>(this IUserService service, User user, string contentType = null)
where TPart : ContentPart
where TPart : ContentPart, new()
{
contentType ??= typeof(TPart).Name.RegexReplace("Part$", string.Empty);
return service.GetUserSetting(user, contentType)?.As<TPart>();
return service.GetUserSetting(user, contentType)?.GetMaybe<TPart>();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ await this.SafeJsonAsync(async () =>
shoppingCartId,
notifyOnError: false,
throwOnError: true);
return await _exactlyService.CreateTransactionAsync(order.As<OrderPart>());
return await _exactlyService.CreateTransactionAsync(order.GetOrCreate<OrderPart>());
});

public async Task<IActionResult> GetRedirectUrl(string transactionId) =>
Expand All @@ -84,7 +84,7 @@ public async Task<IActionResult> VerifyApi()
order.DisplayText = S["Exactly API test order"];
await _contentManager.CreateAsync(order);

var result = await _exactlyService.CreateTransactionAsync(order.As<OrderPart>(), testAmount);
var result = await _exactlyService.CreateTransactionAsync(order.GetOrCreate<OrderPart>(), testAmount);
var action = await GetActionRedirectRequestedAsync(result.Id);

await _notifier.SuccessAsync(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ public ExactlyPaymentProvider(

public async Task<object> CreatePaymentProviderDataAsync(IPaymentViewModel model, bool isPaymentRequest = false, string shoppingCartId = null)
{
var settings = (await _siteService.GetSiteSettingsAsync())?.As<ExactlySettings>();
return string.IsNullOrEmpty(settings?.ApiKey) || string.IsNullOrEmpty(settings.ProjectId) ? null : new object();
var settings = await _siteService.GetSettingsAsync<ExactlySettings>();
return string.IsNullOrEmpty(settings.ApiKey) || string.IsNullOrEmpty(settings.ProjectId) ? null : new object();
}

public async Task<PaymentOperationStatusViewModel> UpdateAndRedirectToFinishedOrderAsync(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,9 @@ public class ExactlySettingsConfiguration : IConfigureOptions<ExactlySettings>

public ExactlySettingsConfiguration(ISiteService siteService) => _siteService = siteService;

public void Configure(ExactlySettings options)
{
var siteSettings = _siteService
public void Configure(ExactlySettings options) =>
_siteService
.GetSiteSettings()
.As<ExactlySettings>();

siteSettings.CopyTo(options);
}
.GetOrCreate<ExactlySettings>()
.CopyTo(options);
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#nullable enable
using OrchardCore.Commerce.Payment.Stripe.Models;
using System.Threading.Tasks;

namespace OrchardCore.Commerce.Payment.Stripe.Abstractions;
Expand All @@ -8,17 +10,17 @@ namespace OrchardCore.Commerce.Payment.Stripe.Abstractions;
public interface IPaymentIntentPersistence
{
/// <summary>
/// Returns the payment intent Id stored in the current session.
/// Returns the payment intent information stored in the current session.
/// </summary>
Task<string> RetrieveAsync(string shoppingCartId = null);
Task<PaymentIntentPersistenceInfo?> RetrieveAsync(string? shoppingCartId);

/// <summary>
/// Saves a payment intent Id to the session.
/// Saves a payment intent information to the session.
/// </summary>
Task StoreAsync(string paymentIntentId, string shoppingCartId = null);
Task StoreAsync(string? shoppingCartId, PaymentIntentPersistenceInfo info);

/// <summary>
/// Removes the payment intent Id stored in the current session.
/// Removes the payment intent information stored in the current session.
/// </summary>
Task RemoveAsync(string shoppingCartId = null);
Task RemoveAsync(string? shoppingCartId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,14 @@ namespace OrchardCore.Commerce.Payment.Stripe.Controllers;

public class StripeController : PaymentBaseController
{
private readonly IPaymentIntentPersistence _paymentIntentPersistence;
private readonly IStripePaymentService _stripePaymentService;

public StripeController(
IOrchardServices<StripeController> services,
INotifier notifier,
IPaymentIntentPersistence paymentIntentPersistence,
IStripePaymentService stripePaymentService)
: base(notifier, services.Logger.Value)
{
_paymentIntentPersistence = paymentIntentPersistence;
: base(notifier, services.Logger.Value) =>
_stripePaymentService = stripePaymentService;
}

public async Task<IActionResult> UpdatePaymentIntent(string paymentIntent)
{
await _paymentIntentPersistence.StoreAsync(paymentIntent);
return Ok();
}

[AllowAnonymous]
[HttpGet("stripe/middleware")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public async Task<IActionResult> Index([FromHeader(Name = "Stripe-Signature")] s
var json = await streamReader.ReadToEndAsync(HttpContext.RequestAborted);
try
{
var stripeApiSettings = (await _siteService.GetSiteSettingsAsync()).As<StripeApiSettings>();
var stripeApiSettings = (await _siteService.GetSiteSettingsAsync()).GetOrCreate<StripeApiSettings>();
var webhookSigningKey = stripeApiSettings.DecryptWebhookSigningSecret(_dataProtectionProvider, _logger);

var stripeEvent = _stripeHelperService.PrepareStripeEvent(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
using OrchardCore.Commerce.MoneyDataType;

namespace OrchardCore.Commerce.Payment.Stripe.Models;

public record PaymentIntentPersistenceInfo(string PaymentIntentId, Amount Amount);
Original file line number Diff line number Diff line change
@@ -1,52 +1,81 @@
#nullable enable

using Microsoft.AspNetCore.Http;
using OrchardCore.Commerce.Payment.Stripe.Abstractions;
using OrchardCore.Commerce.Payment.Stripe.Models;
using System.Text.Json;
using System.Threading.Tasks;

namespace OrchardCore.Commerce.Payment.Stripe.Services;

public class PaymentIntentPersistence : IPaymentIntentPersistence
{
// Using _ as a separator to avoid separator character conflicts.
private const string PaymentIntentKeyPrefix = "OrchardCore_Commerce_PaymentIntent";
private const string PaymentIntentKeyPrefix = "OrchardCore_Commerce_" + nameof(PaymentIntentPersistenceInfo);

private readonly IHttpContextAccessor _httpContextAccessor;
private ISession Session => _httpContextAccessor.HttpContext?.Session;

private ISession? Session => _httpContextAccessor.HttpContext?.Session;
private HttpRequest? Request => _httpContextAccessor.HttpContext?.Request;

public PaymentIntentPersistence(IHttpContextAccessor httpContextAccessor) => _httpContextAccessor = httpContextAccessor;

public Task<string> RetrieveAsync(string shoppingCartId = null)
public Task<PaymentIntentPersistenceInfo?> RetrieveAsync(string? shoppingCartId)
{
var key = GetCacheId(shoppingCartId);
var serialized = Session.GetString(key);
if (serialized == null && _httpContextAccessor.HttpContext != null)

if (Session?.GetString(key)?.Trim() is { Length: > 0 } serializedFromSession &&
TryParse(serializedFromSession, out var sessionResult))
{
_httpContextAccessor.HttpContext.Request.Cookies.TryGetValue(key, out var serializedCart);
return Task.FromResult(serializedCart);
return Task.FromResult(sessionResult);
}

return Task.FromResult(serialized);
if (Request != null &&
Request.Cookies.TryGetValue(key, out var serializedFromCookie) &&
TryParse(serializedFromCookie, out var cookieResult))
{
return Task.FromResult(cookieResult);
}

return Task.FromResult<PaymentIntentPersistenceInfo?>(null);
}

public Task StoreAsync(string paymentIntentId, string shoppingCartId = null)
public Task StoreAsync(string? shoppingCartId, PaymentIntentPersistenceInfo info)
{
var key = GetCacheId(shoppingCartId);
if (Session.GetString(key) == paymentIntentId) return Task.CompletedTask;
var serialized = JsonSerializer.Serialize(info);

Session.SetString(key, paymentIntentId);
_httpContextAccessor.SetCookieForever(key, paymentIntentId);
Session?.SetString(key, serialized);
_httpContextAccessor.SetCookieForever(key, serialized);

return Task.CompletedTask;
}

public Task RemoveAsync(string shoppingCartId = null)
public Task RemoveAsync(string? shoppingCartId)
{
var key = GetCacheId(shoppingCartId);
Session.Remove(key);
Session?.Remove(key);
_httpContextAccessor.HttpContext?.Response.Cookies.Delete(key);

return Task.CompletedTask;
}

protected string GetCacheId(string shoppingCartId) =>
protected string GetCacheId(string? shoppingCartId) =>
string.IsNullOrEmpty(shoppingCartId) ? PaymentIntentKeyPrefix : $"{PaymentIntentKeyPrefix}_{shoppingCartId}";

private static bool TryParse(string serialized, out PaymentIntentPersistenceInfo? result)
{
result = null;
if (string.IsNullOrWhiteSpace(serialized)) return false;

try
{
result = JsonSerializer.Deserialize<PaymentIntentPersistenceInfo?>(serialized);
return !string.IsNullOrWhiteSpace(result?.PaymentIntentId);
}
catch
{
return false;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ ILogger<RequestOptionsService> logger

_apiKeyAccessor = siteSettings =>
siteSettings
.As<StripeApiSettings>()
.GetOrCreate<StripeApiSettings>()
.SecretKey
.DecryptStripeApiKey(dataProtectionProvider, logger);
}
Expand All @@ -41,9 +41,9 @@ public async Task<RequestOptions> SetIdempotencyKeyAsync()
var requestOptions = await GetOrCreateRequestOptionsAsync();
requestOptions.IdempotencyKey = Guid.NewGuid().ToString();

if (siteSettings.As<StripeApiSettings>().AccountId != null)
if (siteSettings.GetOrCreate<StripeApiSettings>().AccountId != null)
{
requestOptions.StripeAccount = siteSettings.As<StripeApiSettings>().AccountId;
requestOptions.StripeAccount = siteSettings.GetOrCreate<StripeApiSettings>().AccountId;
}

return requestOptions;
Expand All @@ -53,7 +53,7 @@ private async Task<RequestOptions> CreateRequestOptionsAsync()
{
var siteSettings = await _siteService.GetSiteSettingsAsync();
var apiKey = _apiKeyAccessor(siteSettings);
var accountId = siteSettings.As<StripeApiSettings>().AccountId;
var accountId = siteSettings.GetOrCreate<StripeApiSettings>().AccountId;

_requestOptions =
accountId != null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public void Configure(StripeApiSettings options)
{
var settings = _siteService
.GetSiteSettings()
.As<StripeApiSettings>();
.GetOrCreate<StripeApiSettings>();

options.PublishableKey = settings.PublishableKey;

Expand Down
Loading
Loading