feat(reseller): multi-tenant reseller portal — auth, DID onboarding, seat billing, reverse-charge invoicing#274
Merged
Merged
Conversation
…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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.