Abonnementbestellung und Zahlungs-Webhook – Funktionsweise

Prüfungen und Rechtevergabe des Bestellendpunkts sowie Signaturprüfung, Idempotenz und Zustandswechsel der Benachrichtigung des Zahlungsdienstleisters.

Bestellung (api/subscription/order.order)

POST definition_id, plan_id, months. Der Code prüft kein Funktionsrecht.

  1. API-Schlüssel → der Schlüsselinhaber ist der Besteller (401 bei Fehlen).
  2. getDefinition(): nur status=1 und enabled=1 (sonst 404); definitionHasPlan (400); planOnStore (403).
  3. Gültigkeit: Beginn = Registrierung des Benutzers (registeredAt), Ende = Beginn + months (max. 120; 0 = offen).
  4. Ziele: bei eigenem Mandanten (dlbtclient->homeClient) alle Mitglieder (usersOf), sonst nur der Besteller.
  5. Je Mitglied dlbtprovision->materialise(uid, definition_id, plan_id, from, to, user_id, 'order', false) — ergänzt, ersetzt nicht.
  6. Antwort: company_client_id, users, result (created, updated, deleted), valid_from, valid_to, definition.

Voraussetzung: dlbtprovision registriert (sonst 409). Gelesen: subscription_definition, subscription_definition_plan, subscription_plan, subscription_plan_to_store; geschrieben: user_feature (source='order'). Keine Zahlungsprüfung.

Webhook (api/subscription/webhook)

Öffentlicher, durch Signaturen geschützter Endpunkt.

  • SimplePay (wenn dlbtpayment->provider() = simplepay): Prüfung des Headers Signature über den rohen Body (401); JSON (400); event_ref Pflicht (400); claimEvent() Einfügen mit eindeutigem Schlüssel, ein wiederholtes Ereignis erhält nur eine Quittung; applySimplepay(): Transaktion suchen, provider_ref anhängen, payment.succeeded → Token speichern und settlePaid(), payment.failed → settleFailed(); zuletzt markEventProcessed() und eine signierte Quittung.
  • Allgemeiner Weg: HMAC im Header X-Dlbt-Signature mit dem in den Moduleinstellungen gespeicherten Webhook-Geheimnis (fehlendes Geheimnis ebenfalls 401); id, type, provider, data; Idempotenz (duplicate:true); payment.succeeded → settlePaid, payment.failed → settleFailed, subscription.canceled → Zustand canceled. Bei Verarbeitungsfehler 500 („The event was stored but could not be processed.“).
  • webhook.back: nur Information (pending:true), keine Zustandsänderung — die Rückkehr im Browser ist kein Beweis.

Die eigenen Tabellen der Bibliothek dlbtpayment (Ereignis, Transaktion, Abonnement) lassen sich aus dem Controller nicht benennen. Belastungsanforderungen erzeugt die Hintergrundaufgabe des Abonnement-Lebenszyklus.