Self-hosted ecommerce platform for developers
Stop rebuilding checkout for every client. Install the engine, hand over the admin. A NestJS REST API on PostgreSQL, Redis and Prisma, an Angular admin dashboard and a server-rendered Analog.js storefront in French, English and Arabic, with cash on delivery, gift cards and an offline license. Launch with the included storefront, or build your own on top of the API: headless commerce is a capability here, not the identity.
The repository opens to the public with the first release. Until then, access is by request.
themerchantengine setup v1.1.0 [1/14] Profile and box role [2/14] License file ! running unlicensed; the admin will show the license as missing [3/14] Store identity [4/14] Domains local profile: http://localhost:53300, http://localhost:53200, http://localhost:53000 [5/14] Brand [6/14] Locales and timezone [7/14] Currency, tax and shipping [8/14] Asset storage local profile: assets are written to ./uploads on this machine [9/14] Transactional mail local profile: console transport, nothing leaves this machine [10/14] Sign-up bot protection (Turnstile) local profile: sign-up runs without a captcha [11/14] Payment methods [12/14] Admin owner account [13/14] Demo catalogue [14/14] Summary and confirm profile: local store: Demo Store (demo), FR domains: http://localhost:53300 | http://localhost:53200 | http://localhost:53000 license: none (unlicensed) brand: #1F4E79 + #F2B441, latin-rounded locales: en, fr (default en), Europe/Paris money: EUR € after, 2 decimals, VAT 20% TTC, shipping 7 storage: local mail: console from Demo Store <noreply@localhost>, redirected to owner@demo.example turnstile: off payments: cod, manual owner: Demo Owner <owner@demo.example> demo catalogue: yes [1/8] Write the environment, the store config and the rendered stack ok deploy/local/docker-compose.yml and deploy/local/.env written [2/8] Build the images from source [3/8] Start Postgres and Redis, create the app role and database [4/8] Push the schema, the search index and the extra indexes [5/8] Seed the store config, roles, templates, tax, shipping and payment methods [6/8] Create the admin owner account [7/8] Start the whole stack [8/8] Smoke: health, the public config and the front doors ok the API health: http://localhost:53000/health ok the public store config: http://localhost:53000/api/v1/store/config ok the admin login page with its security headers: http://localhost:53200/ ok the storefront: http://localhost:53300/ backend server installed successfully! admin: http://localhost:53200/ owner login: owner@demo.example storefront server installed successfully! storefront: http://localhost:53300/
A real run of the installer on a laptop, trimmed to the step lines. The server transcript replaces it after the first fresh-box rehearsal.
What you get
One generic build, configured at install. No client branch, no build per client: identity, brand, locales, money and domains live in the store settings and every app reads them at runtime.
A NestJS REST API on PostgreSQL and Redis: catalog, cart, checkout, orders, inventory, payments, promotions, gift cards, reviews, returns, shipping, notifications, SEO, analytics, RBAC and an audit log, in 34 modules behind one public config endpoint.
$ curl -s http://localhost:53000/api/v1/store/config
{ "data": { "identity": { … }, "brand": { … },
"localization": { "supportedLocales": ["en","fr"],
"defaultLocale": "en", "rtlLocales": [] },
"money": { "currencyCode": "EUR", "displayPrecision": 2 } } }
An Angular dashboard for the store owner: orders with statuses, payment states, filters and CSV export; catalog, customers, promotions, gift cards, reviews and returns; analytics with revenue, conversion, funnel drop-off, product performance and tracking pixels; notifications, roles, and the settings for identity, brand, localization, money, domains and the license. Dark and light: the frame below follows your own colour scheme.
A server-rendered Analog.js storefront: home, catalog, search, cart, checkout and customer accounts, in the locales the store enables, mirrored for Arabic, with sitemaps, structured data and llms.txt served.
A stepper that asks for every store-specific value once, verifies storage, mail and bot protection live before it proceeds, renders the stack, builds the images on the box, seeds the store and the owner, issues TLS and probes the result. Idempotent and resumable: an update is a re-run on a newer tag.
$ ./setup.sh --profile both # or, on a laptop: $ ./setup.sh --local > .\setup.ps1
Same engine, left to right. A store that enables English and French, prices in euros shown tax included, from the store settings alone.
Same engine, right to left. A store that enables Arabic and English with Arabic as default: the layout mirrors, the font kit changes, the dirham sits before the number. No code changed between the two.
How it works
It is not a library you add to a project. It is a stack you install on a box, or on a laptop, and hand to a store owner.
A Linux box with Docker, Compose v2, git and Node 22. DNS for the store, the admin and the API. A bucket, a Resend domain, a Turnstile widget and a license file. The requirements page links to every console.
Clone, then one command per box. The stepper verifies each integration live, so a wrong key stops the install rather than the first order. Nothing answers on a public port before the owner account exists. Every prompt, in order.
The owner signs in, sets the brand, the locales and the money, adds the first product in two locales and places a cash-on-delivery test order. Nightly dumps and certificate renewal are timers, already installed. The first hour.
Two ways to build
Colours, font kit, logo, locales and money come from the store settings; the storefront renders its tokens from them at request time. Hero sections, banners and featured rails are content the owner manages from the admin, no deploy needed. What each settings screen controls.
Every storefront call is a documented REST endpoint with a stable envelope, error codes in every locale, idempotency keys on order creation and a public store config to render from. The shipped storefront is the reference implementation. Start in the docs.
Features
Cart, checkout, order lifecycle, refunds and returns. Idempotent order creation with X-Idempotency-Key, so a double click never creates two orders. How it works. In the docs.
Stock, products and variants with atomic, WHERE-guarded decrements. No overselling when two buyers hit the last unit at once. In the docs.
Every product, category, page and email is a translatable record with one value per enabled locale. A store picks any pair or all three and a default; the storefront mirrors its layout for Arabic. How it works. In the docs.
Any currency with its symbol, position, separators and zero to three decimals, set once at install and formatted the same way in the API, the admin and the storefront. In the docs.
The tenders regional stores run on, live: cash on delivery, manual transfer, and gift cards with race-safe balances. Promotions, coupon campaigns, reviews and wishlists beside them. How it works. In the docs.
Per-locale templates the owner edits in the admin, sent through Resend with a delivery receipt tracked per email. Server-rendered PDF invoices, Arabic included. In the docs.
Server-side rendering, sitemaps, structured data, redirects, IndexNow pings and a Search Console indexing dashboard inside the admin. llms.txt and llms-full.txt rebuilt from the live catalog for AI search. How it works. In the docs.
Role-based access control, an audit log on every admin action, bot protection on public forms, two-factor and magic-link sign-in, soft deletes, and a request id on every log line. In the docs.
A signed file verified at install and at boot. No license server, no telemetry, nothing on the box calls home. Expiry shows a banner and never stops the store. How it works. In the docs.
A semantic token layer with both colour modes, and a brand settings screen that previews the palette and reports the contrast of every pair before the owner saves it. In the docs.
Integrations
Live means shipped in the engine, and for the third-party services (storage, mail, Turnstile, Search Console and Bing) verified with a real call by the installer before the store answers on a port. Payment providers plug into a locked, server-side allow-list, so adding one is a code change, not a config hole.
Card gateways are not in v1. The provider allow-list, the idempotent payment flow and the webhook dedup they plug into exist, and Stripe is the first provider to write. The recipe for adding one lands in the docs with the customize track.
Pricing and license
Two licenses, one rule. A store that takes no money runs under PolyForm Noncommercial: personal, educational and nonprofit stores, free. A store that accepts an order for money needs a yearly commercial license, sized by how many live stores it covers. An agency licenses once and installs per client.
One live store
USD 349 / year
EUR 329
Founder rate USD 199, EUR 189
Up to five live stores
USD 899 / year
EUR 849
Founder rate USD 549, EUR 499
Twenty stores under one license
USD 1,899 / year
EUR 1,790
Founder rate USD 1,199, EUR 1,090
As many stores as you run
from USD 4,900 / year
A conversation, not a checkout. Write to us.
Founder rate from USD 2,990
Founder rates apply to the first twenty licenses and stay locked for the life of the subscription. Renewal is at list price, same tier. Gulf zone (Qatar, Saudi Arabia, the Emirates, Kuwait, Bahrain, Oman), the same figure in QAR, SAR or AED: Single 1,290, Studio 3,290, Agency 6,900, Unlimited 17,900. Maghreb zone (Tunisia, Algeria, Morocco, Egypt), in TND: Single 490, Studio 1,190; Agency and Unlimited at the global price. Every price is per year.
A store is commercial from the moment it accepts an order from a customer for money, by any tender. A store in setup, a staging copy or a demo with test payments only is not a paid store.
Every production boot needs a signed license file, even a noncommercial one. A free thirty-day evaluation license, bound to your domain, is issued on request; paid tiers are issued by hand. Get a license.
A banner thirty days before, a grace banner for thirty days after. The store keeps running and no edit is blocked. A lapsed license keeps the release it has and does not get new ones. Renewal.
Proof
Unit suites, end-to-end suites against a real PostgreSQL and Redis, lint, the three production builds, a secrets scan of the tree and the history, and an install smoke that runs the real installer twice on a fresh box.
Seven minutes for a first local install on a Windows laptop with Docker Desktop, twenty-six seconds for a re-run. The server number is measured on a fresh box before the first release and lands here, not typed.
A guard on every push refuses any client name in the tree, and the fixtures the docs and this page are captured from are fictional stores. What you install is what every client gets.
FAQ
Every answer stands on its own. For anything else, write to bbadii@pm.me; the founder answers every message himself.
setup.ps1 on Docker Desktop, with the demo catalogue and every email kept on the machine. A server install runs setup.sh on a Linux box, because that is where the certificates and the timers live. Install locally.The story
After 10+ ecommerce stores built from scratch, I finally built the one I keep for myself.
Install it today
Clone the engine, run the installer, answer the prompts once. The docs take you from an empty box to a store that takes cash-on-delivery orders.
git clone <the engine repository> themerchantengine cd themerchantengine ./setup.sh --profile both
The repository opens to the public with the first release. Request access until then.