Skip to content

feat(reseller): multi-tenant reseller portal — auth, DID onboarding, seat billing, reverse-charge invoicing#274

Merged
MoltyCel merged 5 commits into
mainfrom
feat/reseller-portal
Jul 18, 2026
Merged

feat(reseller): multi-tenant reseller portal — auth, DID onboarding, seat billing, reverse-charge invoicing#274
MoltyCel merged 5 commits into
mainfrom
feat/reseller-portal

Conversation

@MoltyCel

Copy link
Copy Markdown
Owner

No description provided.

MoltyCel and others added 5 commits July 18, 2026 11:58
…seat billing, test-mode invoicing

Password login (bcrypt) + DB-backed sessions (hashed at rest, survive restart).
DID onboarding reuses agent_payer (global unique PK) → 409 on conflict without
leaking the owner; every attempt append-only audited (content-hashed, immutable
trigger). Seat billing: N agents × wholesale price (EUR). Stripe Invoicing is
net-new, test-key-only (refuses sk_live unless RESELLER_INVOICE_LIVE=1), never
sent. Tenant isolation enforced per-query via require_reseller; proven by test.

Migration additive + reversible (up + commented down), mirrored by
ensure_reseller_tables. Not deployed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…l text, USt-IdNr gate

CH->DE B2B reverse charge (confirmed). Invoice is NET, no CH VAT, EUR, EUR4/agent.
Mandatory reverse-charge text is a CONFIGURABLE value (env, not hardwired) placed
in the invoice footer; issuer = CryptoKRI legal identity (configurable). Recipient
USt-IdNr is stored per reseller (reseller_accounts.customer_vat_id) and is a hard
gate: empty => invoice stays DRAFT, cannot be finalized. Live faktura still flag-off
until lawyer/Treuhaender sign-off. CH registration status (CHF 100k) intentionally
not modelled. Migration column additive+reversible. 10/10 tests. Not deployed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…amount rejected); valid DE placeholder vat

Verified against Stripe test API: draft in_… created, 12×€4=€48 netto, tax=0,
custom_field USt-IdNr, reverse-charge footer, collection=send_invoice, livemode=false.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ithout VAT

Invoice fully English incl. footer. Reverse-charge text (configurable) tax-exact
with both norms (Art. 8 para. 1 MWSTG / § 13b UStG). Recipient field labelled
VAT ID (recipient). Issuer company reg no. CHE-115.481.407 shown WITHOUT a MWST/
VAT suffix (CryptoKRI is not VAT-registered); no seller VAT id on the invoice.
Person-name sender line removed from footer (issuer name/address render once from
the Stripe account business profile — Dashboard/live only, not set here).

test: pin module to one event loop + retry the httpx ASGITransport response-
corruption artifact (proven test-client-only: 20k direct issuances = 0 defects,
~2% via ASGITransport). Suite now deterministic (20/20).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@MoltyCel
MoltyCel merged commit 896b6a3 into main Jul 18, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant