Zum Inhalt springen
getnextpdf.com

Enterprise Edition

Abrechnung — Ausführliche Referenz

Diese Seite ist die ausführliche Referenz für die Abrechnungsfläche von NextPDF Enterprise. Die Fläche hat zwei Schichten. Das Abrechnungsmodell in NextPDF\Enterprise\Billing definiert Tarifstufen, Kontingente, Überschreitungsrichtlinien und deduplizierte Nutzungsalarme. Das Durchsetzungssubstrat in NextPDF\Enterprise\Billing\Substrate platziert dieses Modell auf dem aktiven Anfragepfad, ausfallsicher schließend und nebenläufigkeitssicher. Einstiegspunkte sind PlanRegistry, QuotaManager, OverageCalculator, BillingAlertService und QuotaEnforcementGuard. Für die Anleitung auf Workflow-Ebene siehe die Seite zur Abrechnungsfunktion.

Diese Funktion wird mit NextPDF Enterprise (nextpdf/enterprise) ausgeliefert und aktiviert sich mit einer Lizenzhülle der Enterprise-Stufe. Eine Bereitstellung ohne diese Berechtigung lädt die Klassen der Funktion nicht. Editionen vergleichen und eine Lizenz erwerben.

Abrechnung ist eine grundlegende Enterprise-Funktion ohne separaten Schalter je Feature; sie ist verfügbar, sobald das Enterprise-Paket neben dem Core-Paket installiert ist. NextPDF Core (Apache-2.0) und NextPDF Pro haben kein Tarif-, Kontingent- oder Überschreitungsmodell; für diese Fläche gibt es keine Entsprechung in einer niedrigeren Stufe. Tarifinhalte, Kontingente und Geschäftsbedingungen unterliegen dem Lizenzvertrag, nicht der Laufzeitdurchsetzung; diese Referenz ist keine rechtliche oder vertragliche Stellungnahme.

Alle Symbole leben unter NextPDF\Enterprise\Billing. Mit Substrat gekennzeichnete Zeilen leben unter NextPDF\Enterprise\Billing\Substrate. TenantContext ist der authentifizierte Mandantentyp aus NextPDF\Enterprise\SaaS.

SymbolParameterStandardverhaltenRückgabeWirft oder scheitert mitHinweise
SaaSPlan (enum)String-gestützte Tarifstufen: standard, advanced, high_controlWirft nichtlabel() gibt den Anzeigenamen zurück
PlanDefinition::__constructSaaSPlan $plan, float $includedCuQuota, list<CapabilityCode> $capabilities, non-empty-string $priceTier, bool $intelligencePackIncluded, bool $privacyPackIncludedUnveränderliches Tarif-Wertobjekt; speichert die Eingaben wie übergebenNeue InstanzWirft nichtfinal readonly; hochgezogene öffentliche Eigenschaften
PlanDefinition::includesCapabilityCapabilityCode $capabilityStrikte Identitätsprüfung der ZugehörigkeitboolWirft nicht
PlanRegistry::__constructlist<PlanDefinition> $definitionsIndiziert Definitionen nach Stufe; die letzte Definition je Stufe gewinntNeues RegisterWirft nichtFür Tests und White-Label-Tarifsätze
PlanRegistry::getSaaSPlan $planKanonische TarifsuchePlanDefinitionInvalidArgumentException, wenn der Tarif nicht registriert ist
PlanRegistry::hasSaaSPlan $planRegistrierungsprüfungboolWirft nicht
PlanRegistry::defaultRegistry (static)Produktionsstandards: Standard 1.000 CU; Advanced 5.000 CU plus Intelligence Pack; High Control 20.000 CU plus Intelligence- und Privacy-PackPlanRegistryWirft nichtVerwenden Sie dies, sofern vertragliche Bedingungen keine eigenen Definitionen erfordern
OveragePolicy (enum)hard_stop, soft_stop, budget_alertWirft nichthttpStatusCode() bildet auf 402 / 429 / 200 ab; isBlocking() ist nur bei Hard- und Soft-Stop wahr
QuotaManager::__constructPlanRegistry $planRegistry, OveragePolicy $overagePolicyBindet das Register an eine RichtlinieNeue InstanzWirft nicht
QuotaManager::checkQuotaTenantContext $tenant, SaaSPlan $plan, float $currentCuKehrt bei oder unter dem Kontingent oder unter einer nicht blockierenden Richtlinie stillschweigend zurückvoidQuotaExceededException bei strikter Überschreitung unter einer blockierenden Richtlinie; InvalidArgumentException aus dem Register bei einem nicht registrierten TarifresetsAt = erster Tag des nächsten Monats, Mitternacht UTC
QuotaManager::remainingQuotaSaaSPlan $plan, float $currentCuReine Lesung; blockiert niefloatInvalidArgumentException des RegistersNegativ bei Überschreitung
QuotaManager::usagePercentageSaaSPlan $plan, float $currentCuReine Lesung; blockiert niefloatInvalidArgumentException des Registers0.0, wenn das enthaltene Kontingent nicht positiv ist; über 1.0 bei Überschreitung
OverageCalculator::calculatePlanDefinition $plan, float $currentCuBerechnet eine unveränderliche Überschreitungs-MomentaufnahmeOverageResultWirft nichtfinal readonly, zustandslos
OverageResultincludedCu, usedCu, overageCu, usageRatio, isOverageUnveränderliches BerechnungsergebnisWirft nichtoverageCu = max(0, used - included); isOverage erfordert strikte Überschreitung
BillingAlertType (enum)quota_warning_80, quota_warning_100, budget_exceeded, monthly_cap_reachedWirft nichtthreshold() 0.8 / 1.0 / 1.0 / 1.0; severity() warning / critical / critical / critical
BillingAlertService::__constructAlertStateRepositoryInterface $alertStateBindet den DeduplizierungsspeicherNeue InstanzWirft nicht
BillingAlertService::evaluateTenantContext $tenant, SaaSPlan $plan, PlanDefinition $planDef, float $currentCuFeuert noch nicht ausgelöste Alarme in aufsteigender Schwellenreihenfolge und protokolliert sielist<BillingAlertType>InvalidArgumentException bei Tarif-/DefinitionsdiskrepanzDedup-Schlüssel: Mandant, Typ, UTC-Periode YYYY-MM
BillingAlertService::clearAlertsTenantContext $tenantSetzt den Auslösezustand des Mandanten für die aktuelle UTC-Periode zurückvoidRepository-definierte Fehler werden weitergereichtSchärft Alarme innerhalb derselben Periode neu
AlertStateRepositoryInterfacehasAlertFired(), markAlertFired(), clearForPeriod()Vertrag für die dauerhafte Alarmdeduplizierungs-PersistenzJe MethodeImplementierungsdefiniertDer Operator verantwortet die Haltbarkeit über Replikate hinweg
InMemoryAlertStateRepositoryArray-gestützter AuslösezustandJe SchnittstelleWirft nichtNur für Einzelanfrage-Lebenszyklen und Tests
QuotaExceededExceptionReadonly currentCu, limitCu, resetsAt, tenantId, isSaaSBereitstellungsmodus-bewusste KontingentverweigerungIst der WerfbarehttpStatusCode() 402 SaaS / 403 On-Prem; specCode() SPEC-BILLING-003 / SPEC-LIC-001; toErrorEnvelope() liefert einen strukturierten Fehlerkörper
DeploymentMode (enum)saas, self_hosted_oss, local_developmentWirft nichtSubstrat. enforcesQuota() ist nur für Saas wahr; das Abwählen ist stets explizit
QuotaEnforcementGuard::__constructDeploymentMode, PlanResolverInterface, QuotaManager, UsageCounterStoreInterfaceFügt das aktive Kontingenttor zusammenNeue InstanzWirft nichtSubstrat. final readonly
QuotaEnforcementGuard::enforce?TenantContext $tenant, non-empty-string $featureKey, float $amount = 1.0Ausfallsicher schließendes Kontingenttor mit atomarer ReservierungQuotaDecision (nur erlaubte Ergebnisse)Siehe die Verweigerungstaxonomie untenSubstrat. Nach der Mandantenauthentifizierung, vor dem abrechenbaren Handler einhängen
PlanResolverInterface::resolveTenantContext $tenantLöst einen Mandanten zu seinem Tarif und den Richtlinien je Feature aufResolvedPlanNoPlanForTenantExceptionSubstrat. Ein Standardtarif-Rückfall für unbekannte Mandanten ist ein Defekt
RegistryPlanResolverarray<non-empty-string, ResolvedPlan> $plansByTenantMap-gestützter ResolverResolvedPlanNoPlanForTenantException für nicht zugeordnete MandantenSubstrat. Ausfallsicher schließend per Konstruktion
ResolvedPlan::policyFornon-empty-string $featureKeyRichtliniensuche auf dem aufgelösten Tarif?QuotaPolicyWirft nichtSubstrat. null bedeutet unbekanntes Feature; das Tor verweigert es
QuotaPolicynon-empty-string $featureKey, float $limit, OveragePolicy $overagePolicyLimit und Verletzungsrichtlinie je FeatureWirft nichtSubstrat. UNLIMITED = -1.0; ein Limit von 0.0 ist kein Kontingent, nicht unbegrenzt; isUnlimited(), isBlocking()
QuotaDecisionStatisch bypassed(), unlimited(), consumed()Wertobjekt für erlaubte ErgebnisseQuotaDecisionWirft nichtSubstrat. isAllowed() ist stets wahr; jede Verweigerung wirft stattdessen
UsageCounterZeilen-Momentaufnahme: Mandant, Feature, Periodengrenzen, used, limit, updatedAtUnveränderliche NutzungszeileWirft nichtSubstrat. remaining() kann negativ sein; wouldExceed() ist strikt
UsageCounterStoreInterface::getMandant, Feature, Periodengrenzen, float $limitLiest die Nutzungszeile und legt sie bei Abwesenheit mit used = 0 anUsageCounterUsageStoreUnavailableExceptionSubstrat. Gibt bei Backend-Ausfall nie einen falsy-Wert zurück
UsageCounterStoreInterface::tryConsumeMandant, Feature, Periodengrenzen, float $amount, float $limitAtomare Compare-and-Set-Reservierung innerhalb des Limits?UsageCounter (null, wenn die Reservierung das Limit verletzen würde)UsageStoreUnavailableExceptionSubstrat. Muss eine einzelne atomare Operation gegen den zugrunde liegenden Speicher sein
InMemoryUsageCounterStoreIn-Prozess-Referenzimplementierung des SpeichervertragsJe SchnittstelleJe SchnittstelleSubstrat. Nur ein einzelner Prozess; dokumentiert die Atomaritätsinvariante
QuotaEnforcementException (abstract)Basistyp jeder SubstratverweigerungIst die Werfbaren-FamilieSubstrat. Jeder Untertyp deklariert httpStatusCode()
public function checkQuota(TenantContext $tenant, SaaSPlan $plan, float $currentCu): void
public function evaluate(
TenantContext $tenant,
SaaSPlan $plan,
PlanDefinition $planDef,
float $currentCu,
): array
public function enforce(?TenantContext $tenant, string $featureKey, float $amount = 1.0): QuotaDecision
public function tryConsume(
string $tenantId,
string $featureKey,
DateTimeImmutable $periodStart,
DateTimeImmutable $periodEnd,
float $amount,
float $limit,
): ?UsageCounter;

Verweigerungstaxonomie von QuotaEnforcementGuard::enforce

AusnahmeHTTP-StatusAusgelöst bei
MissingTenantContextException401SaaS-Modus ohne authentifizierten Mandantenkontext
NoPlanForTenantException402Der Resolver findet keinen dem Mandanten zugewiesenen Tarif
UnknownFeatureException402Der aufgelöste Tarif definiert keine Richtlinie für den Feature-Schlüssel
UsageStoreUnavailableException503Der Nutzungsspeicher kann nicht gelesen oder atomar aktualisiert werden; wird auch bei einem nicht positiven $amount ausgelöst
QuotaExceededException402 (SaaS) / 403 (On-Prem)Das Kontingent einer blockierenden Richtlinie ist überschritten oder eine nebenläufige Reservierung hat den letzten Spielraum verbraucht
  • Das Standardregister liefert drei Stufen (Standard / Advanced / High Control) mit steigenden CU-Kontingenten und Fähigkeitssätzen. Eine Anfrage nach einem nicht registrierten Tarif scheitert mit einer expliziten InvalidArgumentException.
  • QuotaManager::checkQuota() wirft nur, wenn beide Bedingungen gelten: Die Richtlinie ist blockierend und die aktuelle Nutzung liegt strikt über dem enthaltenen Kontingent. Eine Budget-Alarm-Richtlinie wirft nie; Überschreitung wird über Alarme signalisiert.
  • remainingQuota() und usagePercentage() sind reine Lesungen und blockieren nie. Das verbleibende Kontingent wird bei Überschreitung negativ; der Nutzungsprozentsatz überschreitet bei Überschreitung 1.0.
  • Alarme werden in aufsteigender Schwellenreihenfolge ausgewertet: 80%-Warnung, 100%-Warnung (kritisch), dann Budget-überschritten (kritisch). Budget-überschritten ist an strikte Überschreitung gebunden; eine Nutzung von exakt 100% löst die 100%-Warnung aus, nicht Budget-überschritten.
  • Jeder Alarmtyp feuert höchstens einmal je Mandant je Abrechnungsperiode. Der Auslösezustand wird über AlertStateRepositoryInterface protokolliert, sodass die Deduplizierung so dauerhaft ist wie die gewählte Implementierung.
  • Der Deduplizierungsschlüssel bettet die UTC-Periode YYYY-MM ein. Ein neuer Kalendermonat schärft daher automatisch jeden Alarmtyp neu; für die Übergangs-Neuschärfung ist kein Clear-Aufruf erforderlich. clearAlerts() setzt die aktuelle Periode zurück, was Alarme mitten in der Periode neu schärft, etwa nach einem Tarif-Upgrade.
  • Ein Tarifdiskrepanz-Wächter in evaluate() weist einen Aufruf zurück, bei dem der übergebene Tarif und die Tarifdefinition nicht übereinstimmen, und schützt so vor einer Definition aus einer anderen Stufe als dem Tarif des Mandanten.
  • Sämtliche Periodenarithmetik ist an UTC verankert. Der Rücksetzzeitpunkt bei Kontingentüberschreitung ist der erste Tag des nächsten Kalendermonats um Mitternacht UTC; eine Soft-Stop-Antwort sollte ihn als Wiederholungshorizont ausweisen.
  • QuotaEnforcementGuard schließt im SaaS-Modus ausfallsicher. Fehlender Mandant, fehlender Tarif, unbekanntes Feature, Speicherausfall und Kontingentverletzung verweigern allesamt; nichts fällt in ein implizites Erlauben durch. Nicht-SaaS-Bereitstellungen wählen sich nur ab, indem der Wächter mit einem Nicht-SaaS-DeploymentMode konstruiert wird.
  • Blockierende Richtlinien reservieren Nutzung über UsageCounterStoreInterface::tryConsume, ein atomares Compare-and-Set. Nebenläufige Anfragen können die Nutzung nicht gemeinsam über das Limit treiben; der Verlierer des Wettlaufs erhält QuotaExceededException, obwohl die Vorprüfung bestanden wurde.
  • Unter einer Budget-Alarm-Richtlinie protokolliert der Wächter den Verbrauch nach bestem Bemühen und verweigert nie; eine Reservierung über die weiche Obergrenze hinaus protokolliert die Zeile dennoch am Limit.
  • QuotaExceededException ist bereitstellungsmodus-bewusst: SaaS-Verweigerungen bilden auf HTTP 402 mit Spec-Code SPEC-BILLING-003 ab und sind als wiederholbar markiert; On-Prem-Verweigerungen bilden auf HTTP 403 mit SPEC-LIC-001 ab.
  • Die Bibliothek gibt selbst keine HTTP-Antworten aus. Die deklarierten Statuscodes sind der Vertrag für die Edge-Schicht, die eine geworfene Verweigerung auf eine Antwort abbildet und den abrechenbaren Handler nicht aufrufen darf.
  • Nicht positives enthaltenes Kontingent. usagePercentage(), evaluate() und OverageCalculator::calculate() liefern allesamt ein Nutzungsverhältnis von 0.0, statt durch null zu teilen. Schwellenalarme feuern dann nie allein aus dem Verhältnis.
  • Budget-Alarm plus große Überschreitung. Der Manager und der Wächter geben beide erlaubte Ergebnisse zurück. Behandeln Sie das Ausbleiben einer Ausnahme nicht als Beleg dafür, innerhalb des Kontingents zu liegen; ziehen Sie OverageResult oder den Alarmstrom heran.
  • Exakt am Limit. checkQuota() bei currentCu == includedCuQuota besteht. BudgetExceeded erfordert strikte Überschreitung. UsageCounter::wouldExceed() ist ebenfalls strikt.
  • MonthlyCapReached. Das Enum deklariert diesen vierten Alarmtyp, aber BillingAlertService::evaluate() gibt ihn nie aus; seine Kandidatenliste deckt nur die drei Schwellenalarme ab. Er ist für Cap-Tracking-Emitter außerhalb dieses Moduls reserviert.
  • Doppelte Stufendefinitionen. PlanRegistry indiziert nach Stufenwert; die letzte Definition einer Stufe ersetzt frühere stillschweigend. Konstruieren Sie Register aus einer deduplizierten Liste.
  • Kein Kontingent versus unbegrenzt. Ein QuotaPolicy-Limit von 0.0 bedeutet, dass jeder Verbrauch in der Periode Überschreitung ist. Nur das negative UNLIMITED-Sentinel deaktiviert die Messung; isUnlimited() blockiert nie.
  • Nicht positive Reservierungsmenge. enforce() verweigert einen nicht positiven $amount ausfallsicher mit UsageStoreUnavailableException (503). Dies ist ein Aufruferdefekt, kein Speicherausfall.
  • Speicherausfall. Jeder Lese- oder Reservierungsfehler tritt als UsageStoreUnavailableException zutage und verweigert. Der Wächter erlaubt nie ungemessene Arbeit, während der Zähler ausgefallen ist.
  • In-Memory-Implementierungen. InMemoryAlertStateRepository und InMemoryUsageCounterStore sind nur innerhalb eines PHP-Prozesses korrekt. Bereitstellungen mit mehreren Replikaten müssen Implementierungen bereitstellen, die von einem Datenspeicher mit echter Atomarität gestützt sind; ein Read-then-Write-Speicher ist ein Defekt, der unter Last eine Überschreitung des Kontingents erlaubt.
  • FIPS-Modus. Abrechnung führt keine eigenen kryptografischen Operationen durch und hat kein FIPS-spezifisches Verhalten. Die Mandantenidentität, die sie verbraucht, muss aus einem authentifizierten Kontext stammen, dessen FIPS-Haltung mit der SaaS-Fläche dokumentiert ist.
AussageStandardKlausel
Der Statuscode 402 ist für die künftige Verwendung reserviert; er trägt keine eigene normative Anfragesemantik.RFC 9110§15.5.3
429 zeigt an, dass der Client in einem gegebenen Zeitraum zu viele Anfragen gesendet hat („Rate-Limiting”).RFC 6585§4
Retry-After gibt an, wie lange der User-Agent vor einer Folgeanfrage warten sollte.RFC 9110§10.2.3

Alle Klauseln sind paraphrasiert; NextPDF gibt keinen normativen Text wieder. NextPDF erhebt für diese Fläche keinen Anspruch auf HTTP-Protokollkonformität oder -Zertifizierung. Die von OveragePolicy::httpStatusCode() deklarierte Abbildung 402 / 429 / 200 und die Verweigerungscodes 401 / 402 / 503 des Wächters sind eine mit den obigen Klauseln abgestimmte Produktkonvention: RFC 9110 reserviert 402, sodass dessen Verwendung hier für Zahlungsverweigerung die übliche Branchenkonvention ist, nicht eine von der IETF definierte Semantik. Der Soft-Stop-Wiederholungshorizont (resetsAt) ist der Wert, den eine Edge-Schicht als Retry-After-Hinweis ausweisen sollte. Das Ausgeben tatsächlicher HTTP-Antworten, Header und des Caching-Verhaltens liegt in der Verantwortung der hostenden Anwendung.

  • Setzen Sie das Modell aus PlanRegistry::defaultRegistry(), einer OveragePolicy und einem QuotaManager zusammen; ergänzen Sie BillingAlertService mit einer dauerhaften AlertStateRepositoryInterface-Implementierung für die Alarmierung.
  • Hängen Sie QuotaEnforcementGuard in der Anfragepipeline nach der Mandantenauthentifizierung und vor dem abrechenbaren Handler ein. Fangen Sie QuotaEnforcementException und die Abrechnungs-QuotaExceededException an der Edge ab und bilden Sie httpStatusCode() auf die Antwort ab.
  • Tarifdefinitionen in diesem Modul sind die alleinige Quelle der Wahrheit für die Abrechnung; führen Sie in Ihrer Bereitstellung keine parallele Abrechnungsdefinition an anderer Stelle.
  • Die In-Memory-Implementierungen machen die gesamte Fläche ohne I/O unit-testbar. Empfohlene Grenzfalltests: Nutzung exakt am Kontingent, eine Einheit darüber, Verhältnisschwellen bei 0.8 und 1.0, der Tarifdiskrepanz-Wächter, der CAS-Wettlauf (zwei Reservierungen gegen die letzte Einheit Spielraum) und die Verweigerung bei Speicherausfall.
  • Die Kernmodellklassen tragen @since 2.2.0; das Substrat trägt @since 2.3.0. Die aktuelle Paketlinie ist 3.1.0.
  • Der Operator verantwortet die Implementierungen des Alarmzustands-Repositorys und des Nutzungsspeichers, ihre Haltbarkeit über Replikate hinweg und jede Neuschärfung von Alarmen mitten in der Periode über clearAlerts().

Diese Seite dokumentiert nur extern beobachtbares Verhalten und die unterstützte öffentliche API-Fläche. Interne Namespace-Pfade, Hilfsklassen, Mechanismustabellen, Runbook-Dateinamen und Ticket-Präfixe sind nicht im Umfang.