# Integrationsanleitung für ein benutzerdefiniertes Lizenzsystem

Der aktuelle Quellcode unterstützt zwei Lizenzanbieter: Licentivo und `custom_http`. Der Händler wählt unter **Systemeinstellungen → Lizenzsystem** einen Anbieter aus und aktiviert ihn. Derselbe Anbieter wird für alle Produkte und die Umgebungen sandbox/live verwendet; benutzerdefinierte Verbindungen und Planzuordnungen werden weiterhin je Produkt und Umgebung konfiguriert. Diese Funktion umfasst kein eigenes Lizenzsystem; der Händler verwendet weiterhin seinen eigenen Lizenzdienst und sein eigenes SDK.

Der bestehende Kontoaustausch mit Licentivo, die SDKs für neun Sprachen und die Geräteverwaltung verwenden weiterhin den bisherigen Ablauf. Benutzerdefinierte Produkte synchronisieren den Kaufstatus über das in diesem Dokument beschriebene Protokoll. Kunden lesen die Lizenzbereitstellung im Kundencenter und durchlaufen nicht den Licentivo-SDK-Prozess.

## Schnellstart

1. Implementieren Sie die folgenden vier HTTP-Endpunkte in Ihrem eigenen Dienst und konfigurieren Sie für jede Verbindung einen eigenen HMAC-Signaturschlüssel und eine Kennung.
2. Wählen Sie unter „Lizenzsystem“ den „Benutzerdefinierten Lizenzdienst“, fügen Sie eine Verbindung hinzu und geben Sie Adresse, Umgebung, Verbindungskennung, key ID und einen Schlüssel im Standard-Base64-Format ein. Speichern Sie nach der Prüfung und klicken Sie auf „Benutzerdefinierten Lizenzdienst aktivieren“.
3. Erstellen Sie Produkt- und Verkaufsplanentwürfe. Kaufart und Preis werden in Launch verwaltet; Lizenzregeln verwalten Sie in Ihrem eigenen System.
4. Wählen Sie Produkt und Umgebung sowie die Verbindung aus, geben Sie die Referenzen des externen Produkts und Plans ein und speichern Sie. Der Anbieter wird für die gesamte Website ausgewählt; Licentivo kann nicht gleichzeitig für andere Produkte aktiviert werden.
5. Veröffentlichen Sie auf der Verkaufsplanseite eine Testversion und prüfen Sie Zahlung, Bereitstellung, Verlängerung, Upgrade, Rückerstattung und Wiederherstellung nach Fehlern. Konfigurieren Sie anschließend die Produktionsumgebung separat.

Adresse, Umgebung, Verbindungskennung und Netzwerkzugriffsrichtlinie können nach der Erstellung nicht geändert werden; erstellen Sie bei Bedarf eine neue Verbindung. Der Signaturschlüssel kann rotiert werden; lassen Sie das Feld leer, um den bisherigen Schlüssel beizubehalten. Wenn Bestellungen eines anderen Lizenzsystems vorliegen, kann der Anbieter nicht unmittelbar für die gesamte Website gewechselt werden; zuerst muss die Migration abgeschlossen sein. Nach dem Wechsel sind bisherige Pläne nur noch als Vorschau verfügbar und müssen erneut veröffentlicht werden; gespeicherte Verbindungsdaten und Zuordnungen bleiben erhalten. Bei Produkten mit vorhandenen Bestellungen kann die Verbindung nicht unmittelbar gewechselt werden, und aktive Verkaufszuordnungen können nicht unmittelbar ersetzt werden; verwenden Sie für neue Preise oder Zuordnungen eine neue Planversion.

Rufen Sie bei der Konfiguration über die API zuerst `PUT /api/v1/admin/licensing/provider` mit dem Request-Body `{"provider":"custom_http"}` oder `{"provider":"licentivo"}` auf und speichern Sie anschließend die Produktzuordnung. Für einen nicht aktivierten Anbieter können weder Produktlizenzbindungen noch Licentivo-Planrichtlinienzuordnungen gespeichert werden.

Die Verbindungsprüfung bestätigt Protokoll, Authentifizierung, Umgebung und unterstützte Kaufarten; sie erstellt oder bestätigt keine externen Pläne für den Händler. Vor der produktiven Zahlungsannahme muss die sandbox-Integration mit tatsächlichen externen Plänen abgenommen werden.

## 3. Integrationsablauf für Händler

1. Implementieren Sie die vier in diesem Dokument beschriebenen Schnittstellen vor Ihrem bestehenden Lizenzsystem; die vorhandene Lizenzlogik des Systems bleibt bestehen.
2. Erstellen Sie in Launch eine Verbindung für den „Benutzerdefinierten Lizenzdienst“ und konfigurieren Sie HTTPS-Adresse, Verbindungskennung, Umgebung und Signaturschlüssel.
3. Launch ruft die Schnittstelle zur Fähigkeitsprüfung auf und prüft Protokollversion, Unterstützung für dauerhafte und zeitlich begrenzte Käufe sowie Bereitstellungsarten.
4. Wählen Sie für das Produkt diese Verbindung und ordnen Sie die Verkaufsplanversion dem externen `product_ref` und `plan_ref` zu. Externe Kennungen können eigene Zeichenfolgen des Händlers sein; eine Licentivo UUID ist nicht vorgeschrieben.
5. Prüfen Sie in sandbox Zahlung, Verlängerung, Upgrade, Rückerstattung und Wiederherstellung nach Fehlern und veröffentlichen Sie anschließend die live-Konfiguration separat.
6. Nach der Zahlungsprüfung speichert Launch Bestellung, Kaufabdeckung und ausstehende Sendeaufgabe innerhalb derselben Datenbanktransaktion.
7. Der Hintergrundprozess sendet den vollständigen Kauf-Snapshot an den benutzerdefinierten Lizenzdienst; dieser bestätigt den Empfang erst nach dauerhafter Speicherung des Empfangsdatensatzes.
8. Der Lizenzdienst verarbeitet den Zielzustand. Launch fragt die Quittung des ursprünglichen Ereignisses ab und bestätigt die Synchronisierung erst bei `applied` als abgeschlossen.
9. Launch liest die Bereitstellungsinformationen für angemeldete Kunden mit dem entsprechenden Kauf und zeigt im Kundencenter Lizenzcode, Aktivierungseinstieg oder Klartextanweisungen an. Die Anwendung wird an das eigene Lizenzsystem des Händlers angebunden.
10. Verlängerung, Upgrade, Rückerstattung, Zahlungsrücknahme, Aussetzung und Ablauf erzeugen jeweils eine neue Version des vollständigen Snapshots. Der Lizenzdienst verarbeitet den jeweils neuesten Zielzustand.

Für dasselbe Produkt werden sandbox und live jeweils separat an eine Verbindung gebunden. Nach einem Kauf erfordert der Anbieterwechsel einen gesonderten Migrationsprozess; alte Bestellungen und Aufgaben dürfen nicht aufgrund der Auswahl im Administrationsbereich stillschweigend an den neuen Dienst gesendet werden.

## 4. Vom benutzerdefinierten Lizenzdienst zu implementierende API

Die folgenden Schnittstellen werden alle **vom Lizenzdienst des Händlers entgegengenommen und vom Launch-Server aufgerufen**. `base_url` kann ein festes Pfadpräfix enthalten; die Pfade in der Tabelle werden an dieses Präfix angehängt.

| Methode und Pfad | Zweck |
| --- | --- |
| `GET /launch/v1/capabilities` | Verbindungsprüfung sowie Deklaration von Protokoll und Fähigkeiten |
| `POST /launch/v1/events` | Vollständigen Kaufzielzustand empfangen; Wiederholungen verwenden das ursprüngliche Ereignis |
| `GET /launch/v1/events/{event_id}` | Abfragen, ob dieses Ereignis tatsächlich angewendet wurde, und verlorene Antworten wiederherstellen |
| `GET /launch/v1/entitlements/{entitlement_ref}/delivery` | Bereitstellungsinformationen einer vorhandenen Lizenz lesen, ohne eine zweite Lizenz zu erstellen |

Die erste Version fragt Quittungen aktiv ab und fügt keinen externen Callback-Endpunkt zur Änderung von Bestellungen hinzu. Der benutzerdefinierte Dienst ist nicht berechtigt, Zahlungen zu bestätigen, Preise zu ändern oder Kaufzeiträume zu überschreiben.

### 4.1 Allgemeine Konventionen und Authentifizierung

- JSON verwendet UTF-8; Zeitangaben verwenden UTC RFC 3339; `version` ist eine positive Ganzzahl.
- Eine erfolgreiche Antwort lautet `{"data": {...}, "request_id": "..."}`; eine Fehlerantwort lautet `{"error": {"code": "...", "message": "..."}, "request_id": "..."}`.
- Jede Umgebung und Verbindung verwendet einen eigenen Schlüssel. Der Schlüssel besteht aus mindestens 32 zufälligen Bytes und verweist in Launch auf den vorhandenen verschlüsselten Schlüsselspeicher; er gehört nicht in Frontend-Konfiguration, Beispiele oder Anwendungspakete.
- Jede Anfrage enthält `X-Launch-Key-Id`, `X-Launch-Timestamp`, `X-Launch-Nonce` und `X-Launch-Signature`.
- Der Zeitstempel besteht aus dezimalen Unix-Sekunden. Die nonce ist ein ungepolstert base64url-kodierter Zufallswert mit mindestens 128 Bit; für jeden Netzwerkversuch wird ein neuer Wert erzeugt.

Die Signatureingabe besteht aus den UTF-8-Bytes der folgenden sieben Elemente, verbunden durch jeweils ein einzelnes Zeilenumbruchzeichen; am Ende wird kein Zeilenumbruch hinzugefügt:

```text
v1
<key_id>
<timestamp>
<nonce>
<UPPERCASE HTTP method>
<encoded request path and raw query, including the base_url path prefix>
<lowercase hexadecimal SHA-256 digest of the exact request body>
```

`X-Launch-Signature` besteht aus `v1=` und dem ungepolstert base64url-kodierten Ergebnis von `HMAC-SHA256`. Der Request-Body einer GET-Anfrage ist leer. Der Empfänger prüft vor der Deserialisierung Signatur und Zeitfenster; er verwendet einen Vergleich in konstanter Zeit, erlaubt standardmäßig eine Abweichung von plus oder minus 300 Sekunden und bewahrt nonces zum Schutz vor Replay mindestens 600 Sekunden auf. Zwischen Wiederholungen wird keine nonce wiederverwendet, aber Ereignis-ID und geschäftlicher Request-Body müssen unverändert bleiben.

Die Signatur ersetzt HTTPS nicht. Anfragen enthalten weder Kundenpasswörter noch Zahlungsschlüssel oder Licentivo-Administrationsschlüssel; standardmäßig wird nur eine anonyme Kontoreferenz und keine E-Mail-Adresse gesendet. Der HTTP-Client des Servers benötigt Timeouts, eine Begrenzung der Antwortgröße und muss verhindern, dass Zugangsdaten bei Weiterleitungen mitgesendet werden. Bei der Konfiguration einer benutzerdefinierten Adresse wird die Zieladresse geprüft; der Zugriff auf interne Netzwerke muss eine ausdrücklich vom Administrator konfigurierte Bereitstellungsfunktion sein.

### 4.2 Fähigkeitsprüfung

`GET /launch/v1/capabilities` gibt beispielsweise HTTP 200 zurück:

```json
{
  "data": {
    "protocol": "launch-external-licensing-v1",
    "connection_ref": "merchant-01-sandbox",
    "environment": "sandbox",
    "purchase_types": ["perpetual", "fixed_term"],
    "delivery_types": ["license_key", "activation_url", "instructions"],
    "devices": false,
    "identity_exchange": false
  },
  "request_id": "check-001"
}
```

`protocol`, `connection_ref`, `environment`, `purchase_types` und `delivery_types` sind erforderlich. Die letzten beiden Felder müssen nicht leere Arrays sein; nicht unterstützte Kaufarten dürfen nicht zum Verkauf veröffentlicht werden. In der ersten Version müssen `devices` und `identity_exchange` den Wert `false` haben; Unterstützung darf erst deklariert werden, nachdem der erweiterte Vertrag und seine Implementierung abgeschlossen sind.

### 4.3 Kauf-Snapshot

Anfragebeispiel für `POST /launch/v1/events`:

```json
{
  "schema": "launch-external-licensing-v1",
  "event_id": "7bf84b91-2749-4b01-a6a7-bd527c488ee8",
  "connection_ref": "merchant-01-sandbox",
  "environment": "sandbox",
  "type": "entitlement.snapshot",
  "change_reason": "purchase",
  "account_ref": "customer_opaque_001",
  "entitlement_ref": "purchase_opaque_001",
  "launch_product_id": "c6dd463e-50f7-4b76-8239-0a8ec6fc304b",
  "launch_plan_version_id": "5459a073-9362-4b46-949d-9b0c5d84421a",
  "product_ref": "desktop-editor",
  "plan_ref": "professional-yearly-v1",
  "version": 1,
  "desired_state": "active",
  "valid_from": "2026-10-25T00:00:00Z",
  "valid_until": "2027-10-25T00:00:00Z"
}
```

Alle Felder im obigen Beispiel sind erforderlich. `valid_until` gleich `null` bedeutet einen dauerhaften Kauf; andere Werte müssen nach `valid_from` liegen.

| Feld | Einschränkungen und Bedeutung |
| --- | --- |
| `event_id` | Von Launch erzeugte UUID; bleibt bei Wiederholung desselben Ereignisses unverändert |
| `connection_ref` / `environment` | Muss mit Verbindung und Umgebung des Signaturschlüssels übereinstimmen |
| `account_ref` | Stabile anonyme Kontoreferenz; der Empfänger identifiziert den Kunden anhand von Verbindung, Umgebung und dieser Referenz |
| `entitlement_ref` | Stabile Referenz einer Kaufbeziehung; ein erneuter Kauf nach vollständigem Widerruf verwendet eine neue Referenz |
| `launch_product_id` / `launch_plan_version_id` | UUIDs des Launch-Produkts und der unveränderlichen Planversion |
| `product_ref` / `plan_ref` | Vom Integrationspartner konfigurierte externe Referenzen; 1–128 ASCII-Zeichen, Buchstaben, Ziffern, `_`, `-`, `.` erlaubt; externe Pläne werden nicht automatisch erstellt |
| `version` | Monoton steigende Zielversion innerhalb derselben Verbindung, Umgebung und Kaufreferenz |
| `desired_state` | `active`, `suspended`, `expired`, `revoked` |
| `change_reason` | `purchase`, `renewal`, `upgrade`, `refund`, `reversal`, `suspension`, `resumption`, `expiry`, `reconciliation` |

`account_ref` und `entitlement_ref` verwenden 1–128 Buchstaben, Ziffern, Unterstriche oder Bindestriche. `schema` und `type` sind auf die Werte im Beispiel festgelegt. Der Empfänger wendet das Ergebnis anhand von `desired_state` und der vollständigen absoluten Laufzeit an und fügt nicht selbst anhand des Grundes Tage hinzu oder widerruft die gesamte Lizenz.

Insbesondere bei Erstattungen: Nach Erstattung einer Verlängerungs- oder Upgrade-Bestellung kann ein gültiger Basiskauf bestehen bleiben; der Snapshot kann dann weiterhin `active` sein, aber eine verkürzte Laufzeit oder einen geänderten Plan haben. Erst wenn die Neuberechnung des Ledger keine gültige Kaufabdeckung mehr ergibt, wird der entsprechende Beendigungsstatus gesendet.

### 4.4 Empfang, Quittierung und Wiederherstellung

Der Empfänger darf HTTP 202 erst zurückgeben, nachdem Ereignis-ID, Digest des ursprünglichen Request-Bodys und ausstehende Aufgabe dauerhaft gespeichert wurden; eine bereits synchronisierte doppelte Anfrage kann HTTP 200 zurückgeben. Beide geben die folgende Belegstruktur zurück.

```json
{
  "data": {
    "event_id": "7bf84b91-2749-4b01-a6a7-bd527c488ee8",
    "connection_ref": "merchant-01-sandbox",
    "environment": "sandbox",
    "entitlement_ref": "purchase_opaque_001",
    "version": 1,
    "payload_sha256": "<64-character lowercase hexadecimal SHA-256 digest of the exact event body>",
    "status": "pending",
    "error_code": null
  },
  "request_id": "receipt-001"
}
```

Der Digest-Platzhalter muss durch den tatsächlichen Digest ersetzt werden. Die genannten Belegfelder sind alle erforderlich; `error_code` ist ohne Fehler `null`. Die Abfrage `GET /launch/v1/events/{event_id}` gibt dieselbe Struktur mit HTTP 200 zurück; der Status kann sein:

| `status` | Bedeutung | Verarbeitung durch Launch |
| --- | --- | --- |
| `pending` | Dauerhaft gespeichert, tatsächliche Lizenzänderung noch nicht abgeschlossen | Später abfragen; Synchronisierung darf nicht als abgeschlossen markiert werden |
| `applied` | Lizenzdienst bestätigt, dass der Zielzustand tatsächlich angewendet wurde | Nach Prüfung aller zugehörigen Felder und des Digest Synchronisierung als abgeschlossen bestätigen |
| `rejected` | Anwendung definitiv nicht möglich, beispielsweise weil der externe Plan nicht existiert | Automatische Wiederholungen stoppen und eindeutigen Fehler anzeigen |
| `manual_review` | Ergebnis ungewiss oder Zustandskonflikt; manuelle Prüfung erforderlich | Ursprüngliche Aufgabe und Ereignis behalten und auf auditierbare Bearbeitung warten |

Der Bereitstellungsstatus ist unabhängig vom Synchronisierungsbeleg; `applied` darf nicht unmittelbar als Laufzeitberechtigung der Kundenanwendung gelten.

Zuverlässigkeitsanforderungen:

- Mindestens einmalige Zustellung; der Empfänger muss Geschäfts-Idempotenz sicherstellen. Auf Netzwerkebene kann nicht „nur ein Aufruf“ versprochen werden.
- Dieselbe Ereignis-ID und derselbe ursprüngliche Request-Body geben das bestehende Ergebnis zurück; dieselbe ID mit anderem Body gibt HTTP 409 `IDEMPOTENCY_CONFLICT` zurück.
- Alte Versionen dürfen neue Versionen nicht überschreiben. Unterschiedliche Ereignisse oder Ziele für dieselbe Kaufversion dürfen nicht stillschweigend akzeptiert werden; zur Prüfung HTTP 409 `STATE_VERSION_CONFLICT` zurückgeben.
- Nach Verlust der POST-Antwort oder Prozessneustart fragt Launch zuerst das ursprüngliche Ereignis per GET ab; erst nach ausdrücklichem `EVENT_NOT_FOUND` werden ursprüngliche ID und ursprünglicher Request-Body erneut gesendet.
- Ist das tatsächliche Ausstellungsergebnis der Gegenstelle ungewiss, sollte der Integrationspartner auch anhand seiner eigenen dauerhaft gespeicherten Idempotenzreferenz prüfen; eine Wiederholung darf nicht einfach eine weitere Lizenz erzeugen.
- HTTP 429 folgt `Retry-After`; Timeouts und 5xx werden mit Backoff wiederholt; nach Überschreiten des Limits folgt manuelle Prüfung. Ablehnungen durch Authentifizierung, Konfiguration oder Geschäftsregeln dürfen nicht als erfolgreiche Synchronisierung gelten.

Empfohlene Fehlercodes: `SIGNATURE_INVALID` (401), `SIGNATURE_EXPIRED` (401), `NONCE_REPLAYED` (409), `SCOPE_MISMATCH` (403), `EVENT_NOT_FOUND` (404), `IDEMPOTENCY_CONFLICT` (409), `STATE_VERSION_CONFLICT` (409), `PLAN_UNKNOWN` (422), `PURCHASE_TYPE_UNSUPPORTED` (422), `RATE_LIMITED` (429), `SERVICE_UNAVAILABLE` (503). Fehlermeldungen dürfen keine Zugangsdaten enthalten.

### 4.5 Lizenzbereitstellung

`GET /launch/v1/entitlements/{entitlement_ref}/delivery` darf nur bestehende Bereitstellungsergebnisse dieser Verbindung und Umgebung lesen; die Abfrage darf nicht als neuer Ausstellungsbefehl behandelt werden. HTTP 200-Beispiel:

```json
{
  "data": {
    "connection_ref": "merchant-01-sandbox",
    "environment": "sandbox",
    "entitlement_ref": "purchase_opaque_001",
    "version": 1,
    "status": "ready",
    "delivery": {
      "type": "license_key",
      "value": "EXAMPLE-ONLY-NOT-A-REAL-LICENSE",
      "expires_at": null
    }
  },
  "request_id": "delivery-001"
}
```

`status` ist `pending`, `ready` oder `unavailable`; bei nicht ready ist `delivery` gleich `null`. Bei ready muss `type` eines der deklarierten `license_key`, `activation_url` oder `instructions` sein, `value` umfasst höchstens 4096 UTF-8-Bytes und `expires_at` ist ein UTC-Zeitpunkt oder `null`. Alle zugehörigen Antwortfelder müssen zur aktuellen Launch-Kaufversion passen; Bereitstellung einer alten Version darf nicht als aktuelles Ergebnis gelten.

Lizenzcodes sind sensible Daten; Bereitstellungsantworten verwenden `Cache-Control: no-store`, protokollieren keinen Body und verwenden bei notwendiger Persistenz verschlüsselten Speicher. Aktivierungs-URLs akzeptieren nur HTTPS-Origins, die in der Verbindungskonfiguration erlaubt sind; instructions werden als Klartext angezeigt, HTML oder Skripte nicht ausgeführt. Widerrufene, vollständig erstattete oder nicht mehr gültig kaufberechtigte Konten dürfen Bereitstellungsinformationen nicht weiter über Launch abrufen; die tatsächliche Ungültigmachung bereits bereitgestellter Zugangsdaten führt das externe System aus.

## Launch-Administrations- und Kunden-API

Diese Schnittstellen liegen unter dem vom Händler bereitgestellten Launch-Origin und verwenden nicht den HMAC-Schlüssel des externen Dienstes zur Authentifizierung. Administratoren verwenden die bestehende Website-Sitzung; Schreibanfragen müssen `X-CSRF-Token` enthalten. Kunden dürfen nur ihre eigenen Käufe lesen.

| Methode und Pfad | Zweck |
| --- | --- |
| `GET /api/v1/admin/licensing/connections` | Gibt `connections`, `bindings`, `mappings` und `default_provider` zurück; die Verbindungsliste enthält nur benutzerdefinierte Verbindungen, Licentivo verwendet weiterhin die bestehende Management-API |
| `POST /api/v1/admin/licensing/connections` | Benutzerdefinierte Verbindung prüfen und hinzufügen |
| `PUT /api/v1/admin/licensing/connections/{id}` | Namen, erlaubte Aktivierungs-Origins und Signaturkonfiguration bearbeiten; historische Empfangsadresse bleibt unverändert |
| `POST /api/v1/admin/licensing/connections/{id}/check` | Erneut prüfen; ein nicht nulles `last_check_error` in HTTP 200 bedeutet, dass die Prüfung fehlgeschlagen ist |
| `PUT /api/v1/admin/products/{id}/licensing` | Anbieter, Verbindung und externe Planzuordnung für die gewählte Produktumgebung konfigurieren |
| `GET /api/v1/me/products/{id}/delivery?environment=sandbox` | Aktueller Kunde liest die aktuell gültige Lizenzbereitstellung eines benutzerdefinierten Kaufs; Cookie- oder Desktop-Bearer-Authentifizierung |
| `GET /api/v1/licensing/documentation` | Dieses Dokument, keine Anmeldung erforderlich |
| `GET /api/v1/licensing/protocol` | Vom benutzerdefinierten Dienst zu implementierendes OpenAPI, keine Anmeldung erforderlich |

Request-Body zum Hinzufügen einer benutzerdefinierten Verbindung:

```json
{
  "provider": "custom_http",
  "name": "My licensing service",
  "base_url": "https://licenses.example.com",
  "environment": "sandbox",
  "connection_ref": "merchant-01-sandbox",
  "key_id": "key-1",
  "signing_key": "<standard Base64 encoding of 32 to 128 random bytes>",
  "allow_private_network": false,
  "allow_local_http": false,
  "activation_origins": ["https://activate.example.com"]
}
```

Das Antwortfeld `data` ist das bereinigte Verbindungsobjekt; Signaturschlüssel und secret ID werden nicht zurückgegeben. allow_local_http erlaubt nur localhost-/Loopback-Adressen in sandbox; privates HTTPS muss vom Administrator ausdrücklich erlaubt werden. Ein leerer Schlüssel ist nur beim Bearbeiten einer bestehenden Verbindung erlaubt. Licentivo wird weiterhin über `POST /api/v1/admin/connections/licentivo` eingerichtet.

Request-Body zur Produktkonfiguration:

```json
{
  "provider": "custom_http",
  "environment": "sandbox",
  "connection_id": "<id returned when creating the connection>",
  "mappings": [
    {
      "version_id": "<Launch plan version UUID>",
      "product_ref": "desktop-editor",
      "plan_ref": "professional-yearly-v1"
    }
  ]
}
```

Bei Auswahl von Licentivo `{"provider":"licentivo","environment":"sandbox","mappings":[]}` senden; dessen Produkt-/Richtlinienzuordnung verwendet weiterhin die bestehende API. Der erste Wechsel macht bestehende Veröffentlichungen zur Vorschau; sie müssen für den neuen Anbieter erneut geprüft und veröffentlicht werden. mappings kann nur hinzuzufügende oder zu behaltende Versionen enthalten; Auslassung löscht keine alten Zuordnungen.

Die Kundenbereitstellung ist ein standardmäßiger data envelope mit HTTP 200; kein Kauf ergibt 404, ein Licentivo-Produkt 409 `LICENSING_DELIVERY_UNSUPPORTED`, nicht synchronisiert, ausgesetzt, abgelaufen oder vollständig erstattet ergibt 409 `ENTITLEMENT_NOT_READY`, vorübergehend nicht verfügbarer externer Dienst oder nicht passender Beleg ergibt 503. „Lizenzbereitstellung ansehen“ im Kundencenter prüft Eigentum und Status erneut und schreibt Zugangsdaten nicht in URL oder Protokolle.

Für benutzerdefinierte Produkte geben die Licentivo-Schnittstellen identity, device und integration-check 409 `CUSTOM_LICENSING_SDK_REQUIRED` zurück. Das alte `/credential` gibt weiterhin 410 zurück. Die Anwendung sollte das eigene SDK des Händlers verwenden und den Launch-Status `ready` nicht als Laufzeitautorisierung behandeln.

## Beispiele und Abnahme

Das [Python-Beispiel eines dünnen Adapters](/de/launch-documents/custom-licensing-example/) stellt ein Empfangsgerüst für Signatur, nonce, Ereignisdeduplizierung und dauerhafte Belege bereit; die tatsächliche Lizenzverarbeitung muss an das Händlersystem angebunden werden.

Das [OpenAPI](/de/assets/launch/external-licensing-openapi.json) des benutzerdefinierten Protokolls und das eigene [OpenAPI](/de/assets/launch/openapi.json) von Launch beschreiben die beiden Richtungen separat. Der Empfänger muss weder Licentivo-Produktkatalog, Richtlinienkatalog noch Identitätsvertrauensschnittstelle implementieren.

Lokale Verifikation verwendet isoliertes MySQL, einen echten HTTP-Adapter sowie simulierte Lizenzierungs-/Zahlungsdienste und deckt Antwortverlust, Neustart, Verlängerung, Upgrade, Erstattung, Kontodeaktivierung, nicht passende Belege und Bereitstellungsisolierung ab. Die Produktionsabnahme erfordert zusätzlich den eigenen Lizenzdienst und die Anwendung des Händlers. Nicht deklarierte Geräte-, gemeinsame Kontoanmeldungs- und Offline-Fähigkeiten werden nicht von Launch ergänzt.
