SeriesPart 1 of Commerce architecture in practiceView the cluster →
Medusa & ArchitecturePillar

One customer, one limited product, one purchase: enforcing the rule across checkout

For one client, selected products could be purchased only once per person. We designed the checkout so that rule survives multiple carts, hosted payment and a return to the flow.

We Are Souk article cover: One customer, one limited product, one purchase: enforcing the rule across checkout
Souk EngineeringCommerce architectureAug 2026·8 min read
Key takeaways
  • A quantity rule in one cart cannot enforce one purchase per person across several carts.
  • Hosted-payment initiation is a business decision boundary, not merely a button action.
  • Reserve a scarce entitlement before control leaves the platform.
  • On re-entry, replay durable state before considering genuinely new work.
  • The payment provider and Medusa provide generic primitives; the integrator owns the client-specific invariant between them.

The client rule was larger than one cart

Some products on a commerce platform built for a European industrial client could be purchased only once by each customer. That sounds like a catalogue rule: add a flag to the product, reject a quantity above one and the work appears finished. It is not. A customer can open two carts, begin payment in one tab and return to checkout in another. A second click can reach payment while the first commercial operation is still resolving.

The real requirement was not merely ‘keep the quantity at one’. It was: one customer must hold at most one active right to purchase the product across carts, payment attempts and the final order. A cart-level validation provides useful early feedback, but it cannot enforce that wider promise. The rule needed its own durable lifecycle.

Model the promise as a commercial claim

We represented the one-purchase rule explicitly. The claim connects a product with the customer identity, the cart that temporarily holds the right and the order that eventually consumes it. A time-bounded reservation protects the interval in which the customer leaves the storefront for hosted payment. A completed claim records that the entitlement has been used. On a later relevant server operation, the expiry policy can move an abandoned reservation out of the active set so it does not block the customer forever.

The active uniqueness rule lives in the database for both a known customer account and a normalised email identity. Two browser tabs therefore cannot both win merely because they read the same old interface state. This is the architectural shift: the interface explains the rule, while durable commercial state enforces it.

The platforms cannot own the client’s rule

The hosted-payment provider supplied the external payment journey. Medusa supplied the cart, payment and order primitives. Their normal integration already owned payment status, provider events and cart completion. We did not need to repair either product or invent a second payment engine.

Neither technology could decide the client’s policy for selected products. ‘This person may buy this product only once’ is not a universal payment rule or a generic commerce-platform default. It belongs to the business operating the platform. Our work was to connect that policy to hosted checkout without taking over responsibilities already handled by the provider or Medusa.

Payment initiation became the decision boundary

We chose payment initiation as the decisive boundary because it is the last point where the platform can inspect its commercial state before asking an external provider to start a new operation. The route does not automatically create a payment. It first decides what the current checkout already means.

First, it looks for an order already linked to the cart. If one exists, it returns that durable outcome instead of starting again. Second, it checks whether the commerce platform already records authorised or captured payment state. If it does, the flow waits rather than opening another hosted session. Only a genuinely new attempt reserves the one-purchase entitlement. The platform creates the provider session after that reservation succeeds.

The order is deliberate: replay an outcome that already exists; wait when a payment operation is already visible; reserve the client-specific entitlement; create a new provider operation only when the previous decisions permit it. Retry is therefore a server-side policy decision, not a reflex attached to a button.

A concrete two-cart walkthrough

Consider one limited product and one customer identity. Cart A reaches payment initiation first. The server finds no existing order and no payment operation underway. It creates an active claim for the product and customer, attaches that claim to Cart A and only then asks Medusa to create the hosted-payment session.

Before Cart A completes, the same customer opens Cart B. A storefront-only quantity rule would see one unit in each cart and accept both. The durable claim sees that this person already has an active right to the product. The second reservation cannot enter the active set, so Cart B never becomes a second external operation for the same entitlement.

If the customer later returns through Cart A after its order exists, the server returns that order. If the order is absent but authorised or captured payment state is already recorded, it waits. The claim, existing-order replay and payment-state check answer three different business questions; one browser boolean cannot replace them.

Carry the entitlement through the order lifecycle

The reservation cannot become permanent merely because someone opened hosted checkout. It also cannot disappear immediately after redirection, because another cart could then claim the same product while the first payment is still underway. The claim follows the commercial outcome instead.

When an order is placed, the reservation becomes completed and links to that order. When the platform records an outcome that legitimately permits another attempt, the claim can leave the active set. Old reservations are also expired lazily when a later relevant server operation runs the expiry policy. ‘Purchase once’ is therefore not a boolean checked on a product page. It is a transition from available, to reserved, to completed—or back to available when no purchase completes.

Expiry is a recovery transition, not the definition of success. An order event turns the temporary claim into a completed commercial fact. Keeping those states separate also lets the business distinguish an entitlement already consumed, a cart currently holding it and an abandoned attempt eligible for release.

Create correlation before the order exists

The same temporal problem appeared in another client requirement. The payment provider, the eventual Medusa order, the administration interface, transactional emails and the ERP needed a readable commercial reference. But the order did not yet exist when the browser left for hosted checkout.

We created a project reference on the cart before session creation and carried it in provider data and metadata with the cart identity. When the order was placed, the project ensured that the order had its own readable operational reference, which downstream systems could use.

Provider metadata does not replace a database relationship, and the source does not prove that the pre-checkout and final-order reference are always the same value. The defensible contract is correlation: preserve cart context through the external operation, then establish and propagate the final order reference when that order exists.

The entitlement claim and the reference solve different problems but expose the same principle: the order may not exist yet while the business commitment already does.

Keep the outbound boundary narrow

The same initiation route owns two narrower controls. It accepts only the payment provider deliberately exposed by the project; a crafted storefront request cannot select another configured provider. It also checks the checkout destination returned by the session before exposing it to the browser. The host and path must match the expected hosted-checkout destination.

Those controls are not the main innovation. They follow from placing the handoff behind one server-side boundary: the component that decides whether a new commercial operation may begin also controls which provider can receive it and where the customer can be sent.

Design re-entry before designing retry

Checkout interfaces naturally encourage retry: if nothing visible happened, show the button again. But the browser knows only what it has rendered. The server can see that an order already exists, that payment is underway or that the customer’s entitlement is reserved by another cart.

We made re-entry a first-class transition. The flow evaluates durable commercial state and chooses between replay, wait and genuinely new work. The customer still sees a simple checkout, while the platform preserves the promise across refreshes, tabs and an external payment journey.

This does not make the system ‘exactly once’. It gives repeated requests explicit meanings and places the strongest client rule in durable state. The accurate architectural claim is narrower: new work begins only after the server has inspected the business state it owns.

The reusable architecture pattern

Not every checkout needs this custom initiation layer. This design was driven by a client-specific rule that crossed carts and orders. The pattern becomes relevant when checkout consumes a scarce or durable business entitlement: a one-time product, a limited allocation, a benefit or a reservation that must survive hosted payment.

Before redirecting, ask what entitlement checkout is about to consume, who owns it, whether two carts may reserve it simultaneously, which state makes it temporary, which event makes it permanent and which outcomes make it available again. Then decide what a repeated request should replay and when genuinely new work is allowed.

Also decide which commercial identity must exist before the order, which system creates it, how the provider carries it and how it becomes attached to the final record. The provider owns hosted payment, the commerce platform owns its generic model, the merchant owns its commercial rules and the integrator owns the explicit contract between them.

For this client, Medusa provided the commerce model and an external provider handled hosted payment. We added the project-specific contract between customer identity, product entitlement, cart, payment initiation and order. That is the value of composable commerce: not rewriting the provider, but giving a real business rule a durable place in the transaction.

FAQ

Common questions

Why is a quantity limit in the cart not enough?

Because it protects only one cart at one moment. A one-purchase rule must also account for another cart, an active payment attempt and an order that already consumed the entitlement.

Does every Medusa project need this layer?

No. It is useful when a client-specific business entitlement must be reserved before hosted payment and remain coherent across carts and orders. Medusa and the payment provider already handle their normal commerce and payment responsibilities.

What should happen when a customer starts checkout again?

The server should inspect durable state first: return an existing order, wait for an operation already underway or reserve the entitlement and start a genuinely new payment only when policy permits it.

Read next
Keep the useful ideas coming

One practical commerce field note at a time.

Join the WeAreSouk journal for grounded stories about Medusa, Shopify, AI, integrations and the systems behind serious commerce.

Working on a similar problem?Bring us the business constraint. We’ll help map the system behind it.Talk to Souk →
Souk AI · online now

Turn the article into an implementation plan.

Ask how this applies to your store, your stack, or your current bottleneck.

01 Describe your current setup.02 Name the workflow or signal that feels unreliable.03 Get a practical first architecture back.
I can help map this article to your stack. Tell me what you sell, what platform you use, and where the medusa & architecture question hurts.
Continue the cluster

Commerce architecture in practice

Start a conversation

Tell us what commerce needs to do for your business.

No scheduling maze. Send the context, the constraint or the idea. We will read it and come back to you directly.