# Neue Windows-App ins Kotsch.Tech-Lizenzsystem einbinden

Schritt-für-Schritt-Leitfaden, um **jede** neue WinUI-3/.NET-App lizenzpflichtig zu machen —
gegen den bereits laufenden Lizenz-Server `https://license.kotsch.tech`. Referenz-Implementierung
ist **Convertly** (alle genannten Dateien existieren dort).

---

## Kernprinzip (wichtig zu verstehen)

- Es gibt **EIN gemeinsames RSA-Schlüsselpaar** für alle Apps: der **private** Schlüssel liegt nur
  am Server, der **öffentliche** wird in **jede** App fest eingebaut (Abschnitt 5).
- Apps werden über die **`appId`** im Token unterschieden — **nicht** über eigene Schlüssel.
  → Eine neue App braucht **keinen** neuen Schlüssel, nur eine neue `appId`.
- Krypto-Vertrag (unveränderbar, der Server hält ihn ein): RSA-2048, **RSASSA-PSS / SHA-256 /
  saltLength 32**, Token = `base64url(JSON).base64url(signatur)`.
- Modell: Einmalkauf, dauerhaft, **3 Geräte/Key**, keine Testphase, Online-Aktivierung + Lease 365 Tage,
  großzügig offline.

Damit gilt: **Nur die `LicenseConfig` ändert sich pro App. Alles andere wird 1:1 übernommen.**

---

## Schritt 1 — Server-Seite: App registrieren (einmalig)

Im Laravel-Admin (oder direkt in der DB) die neue App bekannt machen:

1. Zeile in Tabelle `apps`: `appId` (z. B. `notizmeister`) + `name` (z. B. „Notizmeister").
2. Key-Präfix festlegen (Konvention: 4 Großbuchstaben, z. B. `NOTI`) — `createLicense()` nutzt ihn,
   damit Keys wie `NOTI-XXXXX-XXXXX-XXXXX` aussehen.
3. (Optional, Phase 3) Shop-Produkt anlegen, dessen Webhook `createLicense(appId: 'notizmeister', …)`
   aufruft.

Ab jetzt lassen sich im Admin Lizenzen für diese App ausgeben (immer einer Person zugeordnet).

---

## Schritt 2 — App-Seite: Lizenz-SDK übernehmen

### 2.1 Dateien aus Convertly kopieren
- `Windows/Licensing/Licensing.cs` → enthält `LicenseConfig`, `DeviceId`, `LicenseToken`,
  `LicenseStore`, `LicenseService`, `TokenPayload`. **App-unabhängig** außer `LicenseConfig`.
- `Windows/Views/ActivationPage.xaml` + `.xaml.cs` → der Willkommen-/Aktivierungs-Screen.

> Sauberer für viele Apps: das SDK einmalig als eigene Klassenbibliothek `Kotsch.Licensing`
> auslagern und überall referenzieren. Für den Anfang reicht Kopieren + Namespace anpassen.

### 2.2 NuGet-Paket
```
dotnet add package System.Security.Cryptography.ProtectedData
```
(HttpClient, System.Text.Json, RSA sind in .NET eingebaut.)

### 2.3 `LicenseConfig` anpassen — das EINZIGE, was pro App anders ist
```csharp
public static class LicenseConfig
{
    public const string AppId    = "notizmeister";                 // eindeutig, == apps-Tabelle
    public const string AppName  = "Notizmeister";
    public const string ServerUrl = "https://license.kotsch.tech"; // IMMER gleich
    public const string ProductUrl = "https://kotsch.tech/notizmeister";
    public const string PublicKeyB64 = "<gemeinsamer Public-Key aus Abschnitt 5>"; // IMMER gleich
    public const int OfflineGraceDays = 14;
}
```

### 2.4 Aktivierungs-Gate einbauen (in `MainWindow`)
Vor dem Hauptfenster prüfen — exakt wie in Convertly `MainWindow.xaml.cs`:
```csharp
if (Licensing.LicenseService.IsLicensed())
{
    RootFrame.Navigate(typeof(Views.ShellPage));      // deine Haupt-Shell
    Licensing.LicenseService.RefreshInBackground();   // stille Lease-Erneuerung + Sperr-Check
}
else
{
    RootFrame.Navigate(typeof(Views.ActivationPage));
}
// + public void GoToShell()      { RootFrame.Navigate(typeof(Views.ShellPage)); RootFrame.BackStack.Clear(); }
// + public void GoToActivation() { RootFrame.Navigate(typeof(Views.ActivationPage)); RootFrame.BackStack.Clear(); }
```
`ActivationPage` ruft nach Erfolg `((MainWindow)App.Window).GoToShell()` auf.

### 2.5 Optional (empfohlen): Personalisierung
- **Begrüßung** mit Name: `LicenseService.DisplayName()` → z. B. in den Header („Hallo, {Name}!").
- **Einstellungen → „Lizenz"**-Karte mit „Gerät abmelden" → `await LicenseService.DeactivateAsync();`
  dann `GoToActivation()`. (Siehe Convertly `SettingsPage`.)

### 2.6 Styling-Hinweis
`ActivationPage` nutzt die Brushes `PageBackgroundBrush`, `GlassCard`, `AppAccentBrush`,
`AppAccentSoftBrush`. Entweder die Theme-Dateien aus Convertly mitnehmen
(`Themes/Colors.xaml`, `Themes/Styles.xaml`, Accent-Brushes in `App.xaml`) **oder** diese
Verweise in `ActivationPage.xaml` durch System-Brushes ersetzen (z. B.
`{ThemeResource AccentFillColorDefaultBrush}`, `{ThemeResource LayerFillColorDefaultBrush}`).

---

## Schritt 3 — Bauen + als Kotsch.Tech signieren

`Package.appxmanifest`:
```xml
<Identity Name="KotschTech.Notizmeister" Publisher="CN=Kotsch.Tech" Version="1.0.0.0" />
<Properties><PublisherDisplayName>Kotsch.Tech</PublisherDisplayName> … </Properties>
```
Build + Paket:
```powershell
dotnet build Notizmeister.csproj -c Release -p:Platform=x64
winapp cert generate --manifest . --if-exists skip          # oder vorhandenes devcert.pfx nutzen
winapp package bin\x64\Release\<tfm>\win-x64 --cert ./devcert.pfx --self-contained
```
> Für die „nicht unsicher"-Anzeige bei fremden Nutzern später ein **CA-/EV-Zertifikat** kaufen und
> dieselbe MSIX **neu signieren** — die App selbst ändert sich dadurch nicht.

---

## Schritt 4 — Test-Checkliste (muss grün sein)
1. App-Zeile in `apps` vorhanden (`appId` korrekt).
2. App starten ohne Lizenz → **Willkommen-/Aktivierungs-Screen**.
3. Im Admin Key für diese `appId` erstellen → eingeben → **App startet, Begrüßung mit Name**.
4. 3 Geräte ok, das **4. → „device_limit"**.
5. „Gerät abmelden" → zurück zum Welcome, Slot frei.
6. Neustart → bleibt aktiviert (Token via DPAPI, offline-tauglich).

---

## Schritt 5 — Der gemeinsame Public-Key (in JEDE App einbauen)

`LicenseConfig.PublicKeyB64` =
```
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtsK+pZ71sJAmY0cu6zmNe8oFPRNxyC9Nfq4gWuBZY9p4D2Ly6fkesjzTuNOpicPHUXFzn5CxSd7uBfxGmcZnij7km5S3OroWhabc3Q8numb9TLvysW2dt24FUj5fD2HMQgAohMDCxMjbkJCKwORfdQuXbCkvnImPsjluikss3cpgfpoPNrSn0GbzzMmjAxZLhVSZeX1a5/wl7ELs3YH/Dk+L88ATm83CxMI+RRVvnJLtPD/eeiCCX69E4YM0YOlwOWeSVHqy7sCKSIbhmUuoW/VqM8O4JQz2KwjbZdbEQZIPmZbAd2S0YIKvPQY+OGmnv1/Wal01Qh8opYAYvveXKwIDAQAB
```
(SPKI DER, base64 — passt zum privaten Schlüssel auf `license.kotsch.tech`. Bei einer
Schlüssel-Rotation: neuen Public-Key hier eintragen und alle Apps neu bauen.)

---

## Anhang — Was die App-Seite intern tut (zur Orientierung)
- **Geräte-Fingerprint** = SHA-256 aus Windows `MachineGuid` (+ AppId) → identifiziert das Gerät.
- **Aktivierung**: `POST /api/activate {key, appId, fingerprint, deviceName}` → signiertes Token.
- **Speicherung**: Token via **DPAPI** (an Benutzer/Gerät gebunden) in `ApplicationData.LocalFolder`.
- **Start**: Signatur (Public-Key) + `app` + `dev` + `exp` prüfen → gültig: rein; abgelaufen: stille
  `/api/refresh`; offline: Karenz (14 Tage), dann sperren.
- **Sperren am Server** wirkt beim nächsten Online-Kontakt (Token wird nicht erneuert).
