From c5d3b3fca67f92a154db1241fb9ed2d5c49dd8c6 Mon Sep 17 00:00:00 2001
From: mahmutKaya <33642821+mahmutkaya@users.noreply.github.com>
Date: Sun, 9 Aug 2026 02:46:03 +0200
Subject: [PATCH] =?UTF-8?q?feat(catalog):=20add=20online-payments=20at=20?=
=?UTF-8?q?=E2=82=AC19,=20not=20yet=20sellable=20(S10,=203/4)=20(#115)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* feat(catalog): add online-payments at €19, not yet sellable
Slice S10 of SOFRA-PAYMENTS-PLAN — the control-plane third of a four-way
lockstep (backend ModuleIds.cs, frontend modules.ts, deploy provision-tenant.sh).
€19/mo, priced on value rather than on UI surface. The catalog's usual rule
(staff surfaces 12, feature modules 9) reads it as a 9, but Sofra takes NO
commission and the restaurant pays Stripe's fees — so this module is the entire
revenue from enabling payments, and it is the one add-on whose value scales with
the tenant's turnover while its price does not. GloriaFood charges $29 for the
same capability. The support burden lands here too: chargebacks, KYC onboarding,
a 2FA step in the runbook.
NOT SELLABLE YET, and that is the load-bearing part of this commit. The
vocabulary and the price list are the SAME array, so adding an id to make
provisioning accept it also puts it on the public signup page — selling a module
with no endpoint, no enforcement and no Stripe onboarding flow. A `sellable`
flag now gates both purchase surfaces (SignupConfigurator and the founder's
ProvisionPicker), with tests pinning that it is known-but-unsellable and that no
bundle smuggles it in. Delete the flag in S9, when the flow works end to end.
Co-Authored-By: Claude Opus 5
* chore(deps): patch the js-yaml and nanoid high CVEs
Lockfile only. These fail OSV-Scanner, Trivy and `npm audit` on EVERY sofra PR,
not just this one — they are pre-existing on develop and were blocking the merge
of a change that does not touch dependencies.
Regenerated with `npx npm@10`, not the local npm 11: npm 11 writes a lockfile
node-22 CI rejects, and CI pins node 22. lockfileVersion stays 3, `npm ci`
resolves clean, 261/261 tests and tsc green.
Co-Authored-By: Claude Opus 5
---------
Co-authored-by: Claude Opus 5
---
components/SignupConfigurator.tsx | 2 +-
components/control/ProvisionPicker.tsx | 2 +-
lib/module-catalog.ts | 21 ++++++++++++++++++
package-lock.json | 30 +++++++++++++-------------
tests/unit/module-catalog.test.ts | 18 ++++++++++++++++
5 files changed, 56 insertions(+), 17 deletions(-)
diff --git a/components/SignupConfigurator.tsx b/components/SignupConfigurator.tsx
index d44751a..1877163 100644
--- a/components/SignupConfigurator.tsx
+++ b/components/SignupConfigurator.tsx
@@ -35,7 +35,7 @@ export default function SignupConfigurator() {
const saving = quote.aLaCarteCents - quote.monthlyCents;
const optionalModules = MODULES.filter(
- (m) => m.id !== "core" && m.id !== "extra-languages",
+ (m) => m.id !== "core" && m.id !== "extra-languages" && m.sellable !== false,
);
return (
diff --git a/components/control/ProvisionPicker.tsx b/components/control/ProvisionPicker.tsx
index ee7fdef..48e8aa5 100644
--- a/components/control/ProvisionPicker.tsx
+++ b/components/control/ProvisionPicker.tsx
@@ -76,7 +76,7 @@ export default function ProvisionPicker({
{labels.modulesCore} · {eur(MODULES.find((m) => m.id === "core")!.priceCents)}