Content pages could carry script
Rich-text content entered in the console was rendered as raw HTML on the storefront, a stored-XSS path.
Sanitised CMS HTML before rendering, in the same week the storefront was first committed.
Buyers of new apartments from a large Israeli residential developer can now furnish and upgrade their home from vetted suppliers in one Hebrew storefront. They enter a card once; the platform splits the payment between suppliers, keeps every catalog in sync each night, and gives the developer's staff one console to run it all.
The developer wanted to offer its apartment buyers a curated catalog: kitchens, lighting, appliances and fit-out work, sold by independent suppliers at prices that differ before and after the buyer takes occupancy. That raises three problems at once. Each supplier publishes products in its own spreadsheet or web table, so the catalog has to be rebuilt from moving files every night without losing the developer's own edits. Buyers must sign in against the developer's CRM and see only what applies to their apartment. And a payment provider sale belongs to exactly one seller, so a cart with three suppliers cannot simply be one charge.
The diagram is wider than the screen: swipe sideways to see all of it →
The public and admin APIs are separate hosts, so the buyer-facing surface never exposes management endpoints. Both, and the worker, register the same core through one shared registration call, so business rules cannot drift between them. Each external service sits behind an interface, and an empty setting selects a fail-closed stub, which keeps developer machines away from real payments, SMS and mail.
The buyer's card is authorised once for the cart total against the marketplace's own seller. After approval the order is created, and each supplier's share is captured separately against that supplier's seller account. A failed capture leaves the order Pending for reconciliation rather than deleting an order the buyer has already paid for. Trade-off: every supplier needs full KYC with the provider, so a single flag falls back to charging the whole cart and settling suppliers outside the system.
The payment callback is anonymous, so anyone can post it. It is only a prompt: the server re-reads the sale from the provider, matches the reference it issued to the cart, checks the amount against the cart total, and returns the existing order on a repeat delivery. Checkout details are stored on the cart before the redirect, so the callback never has to carry them.
Staff can override five fields of a supplier product (description, both prices, active flag, category). Overrides sit in their own table and are projected into precomputed Effective* columns on every sync and every edit. The storefront reads only those columns, an override takes effect immediately, and it survives the next night's import. Trade-off: every write path must call the projection, which the tests enforce.
A product that disappears from a feed is hidden, not removed, and comes back with its overrides if it reappears. Orders store a full snapshot of items, prices, suppliers, variants and addresses, so order history stays correct whatever happens to the catalog later.
The nightly sync runs as its own process with its own settings, outside IIS, so an app pool that recycles or idles cannot skip a night. Imports of the same supplier are serialised and committed in one transaction, so a manual sync racing the nightly run cannot double-insert or leave half a catalog.
Most of the hard work was at the edges: the payment provider, the mail tenant, and supplier files nobody controls. A selection:
Rich-text content entered in the console was rendered as raw HTML on the storefront, a stored-XSS path.
Sanitised CMS HTML before rendering, in the same week the storefront was first committed.
Unique indexes on names and slugs counted soft-deleted rows, so a deleted category blocked a new one with the same name.
Moved to filtered unique indexes that ignore deleted rows, and fixed the confirmed admin-flow bugs found in an end-to-end review across 7 flows.
The client's mail tenant began rejecting the deployed app as an unauthorised sender. The first diagnosis, a missing TLS client certificate, was wrong.
The real cause was the source IP: once the server's address was allow-listed, mail flowed with no certificate. I documented why the other settings must stay empty, added an SMTP probe that stops before sending, and an admin-only diagnostics endpoint that sends through the real path and reports which provider answered.
The payment provider ties each sale to exactly one seller, so a multi-supplier cart could not be one charge.
Agreed a two-step flow with the provider (authorise, then multi-capture) and ran it end to end in the sandbox: one authorisation, two captures to two suppliers. The run measured what the docs left vague: amounts in agorot, an authorisation that holds for 168 hours, a limit of 10 calls per 10 seconds, and an origin sale that stays “authorized” after partial capture.
The provider retries a callback up to 3 times, so one payment could arrive more than once.
Added a unique index on the provider transaction and made the callback idempotent: a repeat returns the order that already exists.
A feed missing one mandatory column raised one error per row. Thousands of identical lines hid every other fault in the run, and the supplier's file had often changed by the time anyone looked.
The Hebrew failure report now groups errors by cause, with a row count and example product IDs, and each run archives its own copy of the source file and links it from the report.
The client moved buyer sign-in from national ID to phone number. A phone lookup before any code is sent can be used to discover who is a buyer.
Switched identity to phone across backend and storefront, and added a per-number attempt cap and lockout on the lookup step, separate from the cap on code guesses.
In QA, a damaged spreadsheet threw a framework exception whose name clashes with the engine's own feed exception. It escaped the handler, so no report was written, no alert went out, and a stack trace reached the API response.
Caught it explicitly. A corrupt file now produces a failed sync report and an alert, and the existing catalog stays live.
An illustration of the checkout flow with made-up amounts and suppliers. It contains no client data or code. Step through it to see where each check happens.
Still open at hand-over, all on the client side: KYC for each supplier's seller account, the commission rate, and a public callback address registered with the provider.
Free scoping call · 30 minutes · No commitment
Write a few lines: the process or system, what hurts today (time, errors, cost or an AI pilot that stalled) and who uses it. After a short call you get a written plan and a fixed quote within ≈3 days.
Opens a pre-filled email · reply within one business day
The model reads. The code counts.
All projects