Skip to content

Configured Commerce Upgrade Watch: January 2026

sts 5.2.2601.315+sts Covers January 2026

Written by Lance Farquhar January 26, 2026

Release 5.2.2601.315+sts, published January 22, 2026. Each month we read what Optimizely says shipped, then go verify it against the actual source — frontend and backend — so you get more than a changelog recap.

The 30-Second Version

  • Headline feature: PayPal and Google Pay now work directly inside one-page checkout — no more routing either provider through a separate flow.
  • Two breaking changes worth ten minutes: a new required method on IHtmlContentProvider, and a NaturalKey change on UserPaymentProfile for the Payment Service.
  • Worth knowing: three integration-job parameter columns lost their length caps, and a new rule type lets promotions and shipping rules match products by category branch instead of just direct category.
  • Our verdict: a tight release. The three headline items all show clean, minimal, well-scoped diffs across both repos — the best-case outcome for this kind of cross-check.

The Headline: PayPal and Google Pay on One-Page Checkout

One-page checkout previously meant picking from whatever payment methods didn't need a redirect. PayPal and Google Pay are redirect-based by nature, so getting them into a single-page flow means folding an external approval step into a save that's supposed to happen in place.

January's release does that with a single new handler chain rather than a parallel checkout path:

PayPal on one-page checkout sequence

The new chain, CheckoutWithPayPal.ts, takes the redirect URL PayPal's SDK hands back and folds it straight into the existing cart-update call:

// Store/Pages/OnePageCheckout/Handlers/CheckoutWithPayPal.ts
export const PopulateApiParameter: HandlerType = props => {
    ...
    cart.paymentOptions!.isPayPal = true;
    cart.paymentOptions!.payPalPaymentUrl = props.parameter.redirectUri;
    cart.status = "PaypalSetup";
    ...
};

Three fields — a flag, a URL, and a status — are all it takes to hand the browser off to PayPal and pick the checkout back up when it returns. GooglePayButtonWrapper.tsx and PayPalButton.tsx (both carried over from standard checkout) picked up new width and button-style props so the two buttons render correctly inside the tighter one-page layout, rather than needing separate components built for the new context.

What Else Shipped

Two breaking changes, both low risk, both worth a direct check:

  • IHtmlContentProvider gained a new method, CleanHtmlContent — walked through below, because the reasoning behind it is worth reading.
  • UserPaymentProfile's unique index now includes AccountIdentifier — also below, with the migration.
  • Optimizely also lists a third breaking change, tightening HTML/URL/email sanitization on the Spire share endpoints (tellafriend and shareEntity). If you have custom code touching either endpoint, this is one to test directly against a running instance rather than take on faith from any changelog.

Search Service Authorization was updated to validate permissions through the Commerce Product Instance Details, which Optimizely describes as simplifying and improving that check.

Integration job parameter columnsIntegrationJobParameter.Value, JobDefinitionParameter.DefaultValue, and JobDefinitionStepParameter.DefaultValue — all moved from a 512/1,024-character cap to nvarchar(max). Covered below with the migration.

A new promotion and shipping-rule criteria type, "Ordered Product From Category Branch," lets a rule match anything under a category — including its subcategories — instead of only products filed directly under it. Covered below.

Integration job failure emails got more useful: WebServiceHandler.cs now builds a direct link to the job in the admin console and appends it to the notification:

// WebServiceHandler.cs
var jobLink = $"{baseUrl}admin/data/integrationjobs/{integrationJob.Id}";
message += $"\n\nYou can view the job details <a href='{jobLink}'>here</a>.";

No more copying a job ID out of an email and typing it into the admin console by hand.

Everything else — eleven bug fixes and eight more enhancements — shipped as hotfixes on December's release and is simply being documented here now. A pattern worth noting: every one of those hotfixed items we went looking for was already present, unchanged, in December's code — exactly what you'd expect if the hotfix dates are accurate, and a decent sanity check on the notes themselves.

What We Actually Found: An Activity Log Nobody Announced

Every post in this series, we go looking for something the release notes don't mention. This month it's a genuinely useful feature that shipped quietly.

The Order Approval Details page used to end with a plain "Continue Shopping" link. January's diff swaps that out for a "Show Activity Log" button that opens a drawer — a brand-new 188-line component with its own message list and a text area for adding notes:

Activity log before and after
// Store/Pages/OrderApprovalDetails/ActivityLog.tsx
const showActivityLogClickHandler = () => {
    setActivityLogDrawerIsOpen(true);
};

Nothing in the Highlights, Enhancements, or Bug Fixes sections names it. The closest match is a listed hotfix about order notes not displaying on this page — but what actually shipped is a full messaging UI, with its own send handler and message list, not a display fix. If your team runs an approval workflow with any back-and-forth between submitters and approvers, this is worth trying out even though nobody told you it existed.

Under the Hood

CleanHtmlContent isn't just a new method — it's a documented reason. The interface picked up exactly what the notes describe:

public interface IHtmlContentProvider : IDependency
{
    string GetHtmlContent(Guid? contentManagerId);
    string GetHtmlContent(ContentManager contentManager);
    string CleanHtmlContent(string htmlContent);
}

and the implementation adds an ActuallyCleanHtmlContent helper built on HtmlAgilityPack, with a comment explaining why it exists: parsing HTML for client-side rendering breaks on certain tags, and while the WYSIWYG editor blocks those tags, content can still arrive with them intact through imports or integration jobs. That's the actual mechanism behind the breaking change's stated symptom — a malformed Product Detail page — and it's the kind of fix that comes from tracing a bug back to its real cause instead of patching the symptom.

The AccountIdentifier migration is smaller than the note makes it sound. The column itself already existed on UserPaymentProfile — what's new is a migration that rebuilds the unique index to include it:

-- 2026.01.09.01.AddAccountIdentifierToUserPaymentProfileNaturalKey.sql
CREATE UNIQUE NONCLUSTERED INDEX [IX_UserPaymentProfile_NaturalKey] ON [dbo].[UserPaymentProfile]
(
	[UserProfileId] ASC, [CardType] ASC, [MaskedCardNumber] ASC,
	[ExpirationDate] ASC, [AccountIdentifier] ASC
)

If you've hit the "unhandled error when saving a credit card" bug this fixes, this index is why — the profile's uniqueness check wasn't accounting for a field that mattered.

The parameter-column widening touches both sides of the ORM cleanly. [StringLength(1024)] and [StringLength(512)] attributes came off the three affected model properties, and a matching migration alters all three columns at once:

-- 2025.12.24.01.ExtendParameterColumns.sql
ALTER TABLE [dbo].[IntegrationJobParameter] ALTER COLUMN [Value] NVARCHAR(MAX);
ALTER TABLE [dbo].[JobDefinitionParameter] ALTER COLUMN [DefaultValue] NVARCHAR(MAX);
ALTER TABLE [dbo].[JobDefinitionStepParameter] ALTER COLUMN [DefaultValue] NVARCHAR(MAX);

The category-branch promotion rule is a real 332-line addition, not a config tweak. CriteriaTypeOrderedProductCategoryBranch.cs is a new file under the rules engine, and both the internal name and the display string match the release notes exactly:

[DependencyName("OrderedProductCategoryBranch")]
public sealed class CriteriaTypeOrderedProductCategoryBranch(...) : CriteriaTypeBase
{
    public override string DisplayName => "Ordered Product From Category Branch";

A rule that used to require listing every subcategory by hand now walks the category tree on its own — the kind of feature that's simple to describe in a release note and considerably less simple to implement correctly.

What This Means for You

  • If you've implemented IHtmlContentProvider yourself, add CleanHtmlContent — it's non-extensible, so this is the one breaking change here that can actually stop custom code from compiling.
  • If you've customized the PayPal or Google Pay buttons on standard checkout, double-check your overrides against the new width/style props before rolling this out — they're shared components across both flows now.
  • If you build against the Spire share endpoints, test the sanitization change directly — it changes what your integration can expect to receive back from the endpoint.
  • If you run integration jobs with long parameter values, the 512/1,024-character ceiling is gone.
  • If you use category-based promotions or shipping rules, the new branch-matching criteria type is worth a look before you rebuild that logic another way.
  • If your team uses order approvals, go find the Activity Log button — it's there, it works, and the release notes won't tell you about it.

Upgrading a Configured Commerce storefront and want a second set of eyes on what actually changed under a release before you take it? That's exactly the kind of review Nishtech does for clients every month — reach out and we'll walk through it with you.