# Stripe-Rechnungen CH-Treuhand-konform aufsetzen Schweizer Treuhand-Praxis verlangt fuer jede PowerOn-Rechnung (Top-Up & Subscription) drei Dinge, die Stripe nicht von sich aus liefert: 1. **MWST 8.1 % (CH)** muss separat ausgewiesen werden -- "obendrauf", nicht inkludiert. 2. **Vollstaendige Empfaenger-Adresse** (Firma, Strasse, PLZ/Ort, Land, optional UID-Nr.) muss auf der Rechnung erscheinen. 3. **Zahlungsstatus** ("bereits bezahlt via Kreditkarte") muss vermerkt sein -- die reine Stripe-Quittung reicht nicht aus. Der Code-Pfad in PowerOn wurde so vorbereitet, dass nur noch die Stripe-Konfiguration gemacht werden muss; danach werden alle neuen Top-Up-Rechnungen automatisch konform erstellt. --- ## 1. App-seitig: was ist bereits implementiert (Stand 2026-04-20) | Bereich | Datei | Was passiert | |---------|-------|--------------| | Mandant-Modell | `gateway/modules/datamodels/datamodelUam.py` -- `Mandate.invoice*` (10 strukturierte Felder) | Rechnungsadresse als einzelne Felder: `invoiceCompanyName`, `invoiceContactName`, `invoiceEmail`, `invoiceLine1`, `invoiceLine2`, `invoicePostalCode`, `invoiceCity`, `invoiceState`, `invoiceCountry` (default `CH`), `invoiceVatNumber`. Der FormGenerator rendert die Felder ueber `order: 200..209` automatisch gruppiert am Ende des Edit-Formulars. | | Stripe-Customer | `serviceCenter/services/serviceBilling/stripeCheckout.py` -- `_ensureStripeCustomer` | Bei jedem Checkout wird ein Stripe-Customer angelegt/aktualisiert: `name` = `invoiceCompanyName` (Fallback Mandant-Label), `email` = `invoiceEmail`, `address` = strukturiertes `{ line1, line2, postal_code, city, state, country }`, `shipping` = z. H. + Adresse, beim _Create_ ausserdem `tax_id_data` aus `invoiceVatNumber` (CHE -> `ch_vat`, LI -> `li_uid`, EU -> `eu_vat`). Die `stripeCustomerId` wird im `BillingSettings` gecacht. | | Rechnungserzeugung | `create_checkout_session` setzt `invoice_creation: { enabled: true, invoice_data: {...} }` | Stripe erzeugt automatisch eine **Rechnung** (statt nur einer Quittung) mit Status `paid`, der vollstaendigen Customer-Adresse, unserem Footer-Hinweis "bereits via Kreditkarte bezahlt" und (bei vorhandener UID/z.H.) `invoice_data.custom_fields` mit `UID-Nr. Empfaenger` / `z. H.`. | | MWST | `create_checkout_session` -- `automatic_tax` ODER `tax_rates` | Wenn `STRIPE_AUTOMATIC_TAX_ENABLED=true`, wird Stripe Tax verwendet. Andernfalls wird der Tax-Rate aus `STRIPE_TAX_RATE_ID_CH_VAT` an jede Line-Item gehaengt. | Das alles passiert pro Top-Up automatisch -- der App-Code ist fertig. **Was zu tun bleibt, ist die Stripe-Dashboard-Konfiguration und das Hinterlegen der Rechnungsadresse pro Mandant.** --- ## 2. Stripe-Dashboard: einmalige Einrichtung ### 2a. MWST 8.1 % aktivieren (Empfehlung: Stripe Tax) **Variante A -- Stripe Tax (empfohlen, automatisch):** 1. Stripe Dashboard -> **Tax** -> Origin address: PowerOn-Hauptsitz (Schweiz) eintragen. 2. Tax registrations -> **Switzerland** hinzufuegen, Steuernummer (UID) hinterlegen. 3. **Default tax behavior** auf `exclusive` (= "MWST kommt obendrauf"). 4. **Default tax category** fuer unser Produkt: `Software as a Service (SaaS)` -- damit greift automatisch CH-MWST 8.1 % (Standard-Rate). 5. APP_CONFIG / `.env` setzen: `STRIPE_AUTOMATIC_TAX_ENABLED=true`. **Variante B -- Manueller Tax-Rate (Fallback ohne Stripe Tax):** 1. Stripe Dashboard -> **Products** -> **Tax rates** -> **Add tax rate**. - Display name: `MWST CH` - Region: `Switzerland (CH)` - Tax rate: `8.1` % - Inclusive: **NEIN** (= "MWST kommt obendrauf") - Description: `Schweizer Mehrwertsteuer 8.1%` 2. Tax-Rate-ID kopieren (Format `txr_...`). 3. APP_CONFIG / `.env` setzen: - `STRIPE_AUTOMATIC_TAX_ENABLED=false` - `STRIPE_TAX_RATE_ID_CH_VAT=txr_xxxxxxxxxxxxxx` ### 2b. Rechnungs-Template 1. Stripe Dashboard -> **Settings** -> **Invoice template**. 2. **Public business name**: `PowerOn` (oder offizieller Firmenname inkl. AG/GmbH). 3. **Business address**: vollstaendige Schweizer Geschaeftsadresse. 4. **Tax IDs to display**: PowerOn-UID-Nr (z. B. `CHE-123.456.789 MWST`) anhaken -- erscheint dann automatisch im Header jeder Rechnung. 5. **Footer / Memo**: ggf. zusaetzlicher Hinweis "Bezahlt via Kreditkarte am Rechnungsdatum" (wir setzen ihn auch im Code als invoice-spezifischen Footer, das hier ist nur Fallback). 6. **Default payment terms**: `Due upon receipt` (sollte fuer Top-Ups eh irrelevant sein, da Status sofort `paid`). ### 2c. Webhook fuer Invoice-Status (optional) Wenn Sie wollen, dass die App-DB protokolliert, dass die Rechnung erfolgreich ausgestellt wurde, koennen Sie das Event `invoice.finalized` und `invoice.paid` zusaetzlich abonnieren -- die Webhook-Route ist bereits vorbereitet (`/api/billing/webhook/stripe`). Aktuell genuegt fuer die reine CH-Konformitaet aber `checkout.session.completed`. --- ## 3. Pro Mandant: Rechnungsadresse erfassen Damit die Stripe-Rechnung die korrekte Empfaengeranschrift traegt, muessen die strukturierten Adressfelder am Mandanten befuellt sein. Seit 2026-04-20 (rev 2) sind das **einzelne Felder** -- der frueher verwendete mehrzeilige Freitext wurde wieder gesplittet, damit Stripe sie 1:1 in `customer.address` ablegen kann (Stripe verlangt strukturierte Adressen, kein Freitext). | Feld am Mandanten | Pflicht | Stripe-Mapping | Beispiel | |-------------------|---------|----------------|----------| | `invoiceCompanyName` | empfohlen | `customer.name` (Fallback: Mandant-Label) | `Muster Treuhand AG` | | `invoiceContactName` | optional | `customer.shipping.name` (`" ()"`) + `invoice_data.custom_fields[z. H.]` + `metadata.contactName` | `Buchhaltung` | | `invoiceEmail` | empfohlen | `customer.email` (Stripe verschickt darauf die Rechnung) | `rechnungen@muster-treuhand.ch` | | `invoiceLine1` | **ja** | `customer.address.line1` | `Bahnhofstrasse 1` | | `invoiceLine2` | optional | `customer.address.line2` | `c/o Buchhaltung` | | `invoicePostalCode` | empfohlen | `customer.address.postal_code` | `8000` | | `invoiceCity` | **ja** | `customer.address.city` | `Zuerich` | | `invoiceState` | optional | `customer.address.state` | `ZH` | | `invoiceCountry` | ja (default `CH`) | `customer.address.country` (ISO-3166 Alpha-2) | `CH` | | `invoiceVatNumber` | bei B2B empfohlen | `customer.tax_id_data` (CHE -> `ch_vat`, LI -> `li_uid`, andere -> `eu_vat`) + `invoice_data.custom_fields[UID-Nr. Empfaenger]` + `metadata.vatNumber` | `CHE-123.456.789 MWST` | > **Pflichtminimum fuer eine "echte" Stripe-Customer-Adresse:** > `invoiceLine1` **und** `invoiceCity`. Fehlt eines davon, faellt > `_buildStripeAddress` auf `None` zurueck und Stripe erfragt die Adresse > beim Checkout selbst (`billing_address_collection: required`); die > Rechnung enthaelt dann die vom Endkunden eingegebene Adresse statt der > hinterlegten. > **`tax_id_data` ist nur beim Customer-_Create_ wirksam.** Aenderst du > `invoiceVatNumber` an einem Mandanten, dessen Stripe-Customer bereits > existiert, musst du die UID einmalig in Stripe haendisch setzen > (Customers -> Tax IDs) -- die App ruft `tax_ids.create` aktuell nicht > auf einem bestehenden Customer auf, weil das `customer.tax_ids` zur > Vermeidung von Duplikaten erfordern wuerde. > **Hinweis Bestandsdaten (vor 2026-04-20):** Die alte JSONB-Spalte > `invoiceAddress` (Freitext oder strukturiertes Dict) wird vom > Schema-Reconciler **nicht** automatisch in die neuen Felder > umgeschrieben. Sie bleibt in der DB liegen, wird aber nicht mehr > gelesen oder geschrieben. Bei Bedarf manuell ein einmaliges > SQL-Update fahren oder die Adresse pro Mandant neu im Form erfassen > (Empfehlung fuer Dev-Umgebungen). --- ## 4. Test-Checkliste vor Go-Live 1. Stripe-Account in **Test-Modus** schalten. 2. Mandant `Demo Treuhand` anlegen und alle `invoice*`-Felder befuellen (mindestens `invoiceLine1`, `invoiceCity`, `invoiceCountry`). 3. Top-Up 25 CHF ausfuehren (Test-Karte 4242 4242 4242 4242). 4. Stripe Dashboard -> Customers -> `Demo Treuhand`: - `name` = `invoiceCompanyName` - `email` = `invoiceEmail` - `address` = strukturiert mit line1/postal_code/city/country - `tax_ids` enthaelt die UID-Nr (Typ `ch_vat`) - `metadata.mandateId` / `metadata.mandateLabel` gesetzt 5. Stripe Dashboard -> Invoices -> letzte Rechnung oeffnen: - Status `Paid` - Empfaenger-Block oben links zeigt strukturierte Adresse + UID - `Custom fields` zeigen `UID-Nr. Empfaenger` und ggf. `z. H.` - Zeile `MWST 8.1%` separat ausgewiesen, Total = Netto + MWST - Footer `Diese Rechnung wurde bereits via Kreditkarte bezahlt` - Header zeigt PowerOn-UID 6. PDF herunterladen und mit Treuhand abgleichen. Wenn alle 6 Punkte stimmen: Live-Modus aktivieren und Roll-out.