Enterprise edycja
Billing — szczegółowa referencja
W skrócie
Dział zatytułowany „W skrócie”Ta strona jest szczegółową referencją powierzchni rozliczeń NextPDF Enterprise. Powierzchnia ma dwie warstwy. Model rozliczeń w NextPDF\Enterprise\Billing definiuje poziomy planów, limity, polityki przekroczenia limitu oraz zdeduplikowane alerty zużycia. Substrat egzekwowania w NextPDF\Enterprise\Billing\Substrate umieszcza ten model na aktywnej ścieżce żądań — odporny na awarie (fail-closed) i bezpieczny współbieżnie. Punktami wejścia są PlanRegistry, QuotaManager, OverageCalculator, BillingAlertService oraz QuotaEnforcementGuard. Przewodnik na poziomie przepływu pracy znajduje się na stronie możliwości Billing.
Dostępność i licencjonowanie
Dział zatytułowany „Dostępność i licencjonowanie”Ta możliwość jest dostarczana w NextPDF Enterprise (nextpdf/enterprise) i aktywuje się wraz z kopertą licencyjną na poziomie Enterprise. Wdrożenie bez tego uprawnienia nie ładuje klas tej możliwości. Porównaj edycje i uzyskaj licencję.
Billing to podstawowa możliwość Enterprise bez osobnej flagi dla poszczególnych funkcji; jest dostępna po zainstalowaniu pakietu Enterprise obok pakietu Core. NextPDF Core (Apache-2.0) oraz NextPDF Pro nie mają modelu planów, limitów ani przekroczeń; ta powierzchnia nie ma odpowiednika na niższym poziomie. Uwzględnione elementy planu, limity oraz warunki komercyjne są regulowane umową licencyjną, a nie egzekwowaniem w czasie wykonywania; niniejsza referencja nie jest opinią prawną ani umowną.
Publiczna powierzchnia API
Dział zatytułowany „Publiczna powierzchnia API”Wszystkie symbole znajdują się w NextPDF\Enterprise\Billing. Wiersze oznaczone jako substrate znajdują się w NextPDF\Enterprise\Billing\Substrate. TenantContext to uwierzytelniony typ najemcy z NextPDF\Enterprise\SaaS.
| Symbol | Parametry | Domyślne zachowanie | Zwraca | Zgłasza lub kończy się błędem | Uwagi |
|---|---|---|---|---|---|
SaaSPlan (enum) | — | Poziomy planów oparte na łańcuchach: standard, advanced, high_control | — | Nie zgłasza | label() zwraca nazwę wyświetlaną |
PlanDefinition::__construct | SaaSPlan $plan, float $includedCuQuota, list<CapabilityCode> $capabilities, non-empty-string $priceTier, bool $intelligencePackIncluded, bool $privacyPackIncluded | Niezmienny obiekt wartości planu; przechowuje dane wejściowe w podanej postaci | Nowa instancja | Nie zgłasza | final readonly; promowane właściwości publiczne |
PlanDefinition::includesCapability | CapabilityCode $capability | Ścisłe sprawdzenie przynależności przez tożsamość | bool | Nie zgłasza | — |
PlanRegistry::__construct | list<PlanDefinition> $definitions | Indeksuje definicje według poziomu; ostatnia definicja dla danego poziomu wygrywa | Nowy rejestr | Nie zgłasza | Do testów oraz zestawów planów white-label |
PlanRegistry::get | SaaSPlan $plan | Kanoniczne wyszukiwanie planu | PlanDefinition | InvalidArgumentException, gdy plan nie jest zarejestrowany | — |
PlanRegistry::has | SaaSPlan $plan | Sonda rejestracji | bool | Nie zgłasza | — |
PlanRegistry::defaultRegistry (static) | — | Domyślne ustawienia produkcyjne: Standard 1,000 CU; Advanced 5,000 CU plus Intelligence Pack; High Control 20,000 CU plus Intelligence i Privacy Pack | PlanRegistry | Nie zgłasza | Używaj, chyba że warunki umowne wymagają niestandardowych definicji |
OveragePolicy (enum) | — | hard_stop, soft_stop, budget_alert | — | Nie zgłasza | httpStatusCode() odwzorowuje 402 / 429 / 200; isBlocking() jest prawdą tylko dla twardego i miękkiego zatrzymania |
QuotaManager::__construct | PlanRegistry $planRegistry, OveragePolicy $overagePolicy | Wiąże rejestr z jedną polityką | Nowa instancja | Nie zgłasza | — |
QuotaManager::checkQuota | TenantContext $tenant, SaaSPlan $plan, float $currentCu | Zwraca po cichu przy limicie lub poniżej, albo przy polityce nieblokującej | void | QuotaExceededException przy ścisłym przekroczeniu w ramach polityki blokującej; InvalidArgumentException z rejestru przy niezarejestrowanym planie | resetsAt = pierwszy dzień następnego miesiąca, północ UTC |
QuotaManager::remainingQuota | SaaSPlan $plan, float $currentCu | Czysty odczyt; nigdy nie blokuje | float | InvalidArgumentException z rejestru | Ujemny przy przekroczeniu |
QuotaManager::usagePercentage | SaaSPlan $plan, float $currentCu | Czysty odczyt; nigdy nie blokuje | float | InvalidArgumentException z rejestru | 0.0, gdy uwzględniony limit jest niedodatni; powyżej 1.0 przy przekroczeniu |
OverageCalculator::calculate | PlanDefinition $plan, float $currentCu | Oblicza niezmienny obraz przekroczenia | OverageResult | Nie zgłasza | final readonly, bezstanowy |
OverageResult | includedCu, usedCu, overageCu, usageRatio, isOverage | Niezmienny wynik obliczeń | — | Nie zgłasza | overageCu = max(0, used - included); isOverage wymaga ścisłego przekroczenia |
BillingAlertType (enum) | — | quota_warning_80, quota_warning_100, budget_exceeded, monthly_cap_reached | — | Nie zgłasza | threshold() 0.8 / 1.0 / 1.0 / 1.0; severity() warning / critical / critical / critical |
BillingAlertService::__construct | AlertStateRepositoryInterface $alertState | Wiąże magazyn deduplikacji | Nowa instancja | Nie zgłasza | — |
BillingAlertService::evaluate | TenantContext $tenant, SaaSPlan $plan, PlanDefinition $planDef, float $currentCu | Uruchamia jeszcze nieuruchomione alerty w rosnącej kolejności progów i rejestruje je | list<BillingAlertType> | InvalidArgumentException przy niezgodności planu i definicji | Klucz deduplikacji: najemca, typ, okres UTC YYYY-MM |
BillingAlertService::clearAlerts | TenantContext $tenant | Czyści stan uruchomienia najemcy dla bieżącego okresu UTC | void | Błędy zdefiniowane przez repozytorium są propagowane | Ponownie uzbraja alerty w tym samym okresie |
AlertStateRepositoryInterface | hasAlertFired(), markAlertFired(), clearForPeriod() | Trwały kontrakt utrwalania deduplikacji alertów | Zależnie od metody | Zależne od implementacji | Operator odpowiada za trwałość między replikami |
InMemoryAlertStateRepository | — | Stan uruchomienia oparty na tablicy | Zgodnie z interfejsem | Nie zgłasza | Wyłącznie cykle życia pojedynczego żądania i testy |
QuotaExceededException | Readonly currentCu, limitCu, resetsAt, tenantId, isSaaS | Odmowa limitu świadoma trybu wdrożenia | — | Jest wyjątkiem (throwable) | httpStatusCode() 402 SaaS / 403 on-prem; specCode() SPEC-BILLING-003 / SPEC-LIC-001; toErrorEnvelope() zwraca ustrukturyzowane ciało błędu |
DeploymentMode (enum) | — | saas, self_hosted_oss, local_development | — | Nie zgłasza | Substrate. enforcesQuota() jest prawdą tylko dla Saas; rezygnacja jest zawsze jawna |
QuotaEnforcementGuard::__construct | DeploymentMode, PlanResolverInterface, QuotaManager, UsageCounterStoreInterface | Składa aktywną bramę limitu | Nowa instancja | Nie zgłasza | Substrate. final readonly |
QuotaEnforcementGuard::enforce | ?TenantContext $tenant, non-empty-string $featureKey, float $amount = 1.0 | Odporna na awarie brama limitu z atomową rezerwacją | QuotaDecision (wyłącznie dozwolone wyniki) | Zobacz taksonomię odmów poniżej | Substrate. Zamontuj po uwierzytelnieniu najemcy, przed rozliczanym handlerem |
PlanResolverInterface::resolve | TenantContext $tenant | Rozwiązuje najemcę do jego planu oraz polityk poszczególnych funkcji | ResolvedPlan | NoPlanForTenantException | Substrate. Awaryjne przejście do planu domyślnego dla nieznanych najemców jest defektem |
RegistryPlanResolver | array<non-empty-string, ResolvedPlan> $plansByTenant | Resolver oparty na mapie | ResolvedPlan | NoPlanForTenantException dla niezmapowanych najemców | Substrate. Odporny na awarie z założenia |
ResolvedPlan::policyFor | non-empty-string $featureKey | Wyszukiwanie polityki na rozwiązanym planie | ?QuotaPolicy | Nie zgłasza | Substrate. null oznacza nieznaną funkcję; brama ją odrzuca |
QuotaPolicy | non-empty-string $featureKey, float $limit, OveragePolicy $overagePolicy | Limit poszczególnej funkcji i polityka naruszenia | — | Nie zgłasza | Substrate. UNLIMITED = -1.0; limit 0.0 oznacza zerowy przydział, a nie brak limitu; isUnlimited(), isBlocking() |
QuotaDecision | Statyczne bypassed(), unlimited(), consumed() | Obiekt wartości dozwolonego wyniku | QuotaDecision | Nie zgłasza | Substrate. isAllowed() jest zawsze prawdą; każda odmowa zamiast tego zgłasza wyjątek |
UsageCounter | Obraz wiersza: najemca, funkcja, granice okresu, used, limit, updatedAt | Niezmienny wiersz zużycia | — | Nie zgłasza | Substrate. remaining() może być ujemny; wouldExceed() jest ścisły |
UsageCounterStoreInterface::get | Najemca, funkcja, granice okresu, float $limit | Odczytuje wiersz zużycia, tworząc go z used = 0, gdy nie istnieje | UsageCounter | UsageStoreUnavailableException | Substrate. Nigdy nie zwraca wartości falsy przy awarii zaplecza |
UsageCounterStoreInterface::tryConsume | Najemca, funkcja, granice okresu, float $amount, float $limit | Atomowa rezerwacja compare-and-set w granicach limitu | ?UsageCounter (null, gdy rezerwacja naruszyłaby limit) | UsageStoreUnavailableException | Substrate. Musi być pojedynczą operacją atomową względem magazynu zaplecza |
InMemoryUsageCounterStore | — | Referencyjna implementacja kontraktu magazynu działająca w procesie | Zgodnie z interfejsem | Zgodnie z interfejsem | Substrate. Tylko pojedynczy proces; dokumentuje niezmiennik atomowości |
QuotaEnforcementException (abstract) | — | Typ bazowy każdej odmowy substratu | — | Jest rodziną wyjątków (throwable) | Substrate. Każdy podtyp deklaruje httpStatusCode() |
public function checkQuota(TenantContext $tenant, SaaSPlan $plan, float $currentCu): voidpublic function evaluate( TenantContext $tenant, SaaSPlan $plan, PlanDefinition $planDef, float $currentCu,): arraypublic function enforce(?TenantContext $tenant, string $featureKey, float $amount = 1.0): QuotaDecisionpublic function tryConsume( string $tenantId, string $featureKey, DateTimeImmutable $periodStart, DateTimeImmutable $periodEnd, float $amount, float $limit,): ?UsageCounter;Taksonomia odmów QuotaEnforcementGuard::enforce
| Wyjątek | Status HTTP | Zgłaszany, gdy |
|---|---|---|
MissingTenantContextException | 401 | Tryb SaaS bez uwierzytelnionego kontekstu najemcy |
NoPlanForTenantException | 402 | Resolver nie znajduje planu przypisanego do najemcy |
UnknownFeatureException | 402 | Rozwiązany plan nie definiuje polityki dla klucza funkcji |
UsageStoreUnavailableException | 503 | Magazynu zużycia nie można odczytać ani atomowo zaktualizować; zgłaszany także dla niedodatniej wartości $amount |
QuotaExceededException | 402 (SaaS) / 403 (on-prem) | Limit polityki blokującej został przekroczony lub współbieżna rezerwacja zużyła ostatni zapas |
Kontrakt zachowania
Dział zatytułowany „Kontrakt zachowania”- Domyślny rejestr dostarcza trzy poziomy (Standard / Advanced / High Control) z rosnącymi limitami CU oraz zestawami możliwości. Żądanie niezarejestrowanego planu kończy się jawnym
InvalidArgumentException. QuotaManager::checkQuota()zgłasza wyłącznie wtedy, gdy spełnione są oba warunki: polityka jest blokująca, a bieżące zużycie jest ściśle powyżej uwzględnionego limitu. Polityka alertu budżetowego nigdy nie zgłasza; przekroczenie jest sygnalizowane przez alerty.remainingQuota()orazusagePercentage()to czyste odczyty i nigdy nie blokują. Pozostały limit staje się ujemny przy przekroczeniu; procent zużycia przekracza1.0przy przekroczeniu.- Alerty są oceniane w rosnącej kolejności progów: ostrzeżenie 80%, ostrzeżenie 100% (critical), a następnie przekroczenie budżetu (critical). Przekroczenie budżetu jest bramkowane ścisłym przekroczeniem; zużycie dokładnie na poziomie 100% uruchamia ostrzeżenie 100%, a nie przekroczenie budżetu.
- Każdy typ alertu uruchamia się co najwyżej raz na najemcę na okres rozliczeniowy. Stan uruchomienia jest rejestrowany przez
AlertStateRepositoryInterface, więc deduplikacja jest tak trwała, jak wybrana implementacja. - Klucz deduplikacji osadza okres UTC
YYYY-MM. Nowy miesiąc kalendarzowy automatycznie ponownie uzbraja więc każdy typ alertu; przejście do nowego okresu nie wymaga wywołania czyszczenia.clearAlerts()czyści bieżący okres, co ponownie uzbraja alerty w trakcie okresu, na przykład po podniesieniu planu. - Zabezpieczenie niezgodności planu w
evaluate()odrzuca wywołanie, w którym dostarczony plan i definicja planu się różnią, chroniąc przed definicją z innego poziomu niż plan najemcy. - Cała arytmetyka okresów jest zakotwiczona w UTC. Momentem resetu przekroczenia limitu jest pierwszy dzień następnego miesiąca kalendarzowego o północy UTC; odpowiedź miękkiego zatrzymania powinna ogłaszać go jako horyzont ponawiania.
QuotaEnforcementGuardjest odporna na awarie (fail-closed) w trybie SaaS. Brak najemcy, brak planu, nieznana funkcja, awaria magazynu oraz naruszenie limitu — wszystko to prowadzi do odmowy; nic nie przechodzi do niejawnego zezwolenia. Wdrożenia inne niż SaaS rezygnują wyłącznie przez skonstruowanie bramy zDeploymentModeinnym niż SaaS.- Polityki blokujące rezerwują zużycie przez
UsageCounterStoreInterface::tryConsume, atomowy compare-and-set. Współbieżne żądania nie mogą wspólnie przekroczyć limitu zużycia; przegrany wyścigu otrzymujeQuotaExceededException, mimo że wstępne sprawdzenie przeszło. - W ramach polityki alertu budżetowego brama rejestruje zużycie w miarę możliwości (best-effort) i nigdy nie odmawia; rezerwacja powyżej miękkiego pułapu nadal rejestruje wiersz na poziomie limitu.
QuotaExceededExceptionjest świadomy trybu wdrożenia: odmowy SaaS odwzorowują się na HTTP 402 z kodem specyfikacjiSPEC-BILLING-003i są oznaczone jako ponawialne; odmowy on-prem odwzorowują się na HTTP 403 zSPEC-LIC-001.- Biblioteka sama nie emituje odpowiedzi HTTP. Zadeklarowane kody statusu są kontraktem dla warstwy brzegowej, która odwzorowuje zgłoszoną odmowę na odpowiedź i nie może wywoływać rozliczanego handlera.
Przypadki brzegowe i tryby awarii
Dział zatytułowany „Przypadki brzegowe i tryby awarii”- Niedodatni uwzględniony limit.
usagePercentage(),evaluate()orazOverageCalculator::calculate()zwracają współczynnik zużycia0.0zamiast dzielić przez zero. Alerty progowe nie uruchamiają się wtedy z samego współczynnika. - Alert budżetowy plus duże przekroczenie. Zarówno menedżer, jak i brama zwracają dozwolone wyniki. Nie traktuj braku wyjątku jako dowodu na mieszczenie się w limicie; sprawdź
OverageResultlub strumień alertów. - Dokładnie na poziomie limitu.
checkQuota()przycurrentCu == includedCuQuotaprzechodzi.BudgetExceededwymaga ścisłego przekroczenia.UsageCounter::wouldExceed()również jest ścisły. MonthlyCapReached. Enum deklaruje ten czwarty typ alertu, aleBillingAlertService::evaluate()nigdy go nie emituje; jego lista kandydatów obejmuje tylko trzy alerty progowe. Jest zarezerwowany dla emiterów śledzących limity poza tym modułem.- Zduplikowane definicje poziomów.
PlanRegistryindeksuje według wartości poziomu; ostatnia definicja dla danego poziomu po cichu zastępuje wcześniejsze. Konstruuj rejestry z listy pozbawionej duplikatów. - Zerowy przydział a brak limitu. Limit
QuotaPolicyrówny0.0oznacza, że każde zużycie w okresie jest przekroczeniem. Tylko ujemny wartownikUNLIMITEDwyłącza pomiar;isUnlimited()nigdy nie blokuje. - Niedodatnia wielkość rezerwacji.
enforce()odmawia niedodatniej wartości$amountw trybie odporności na awarie (fail-closed) zUsageStoreUnavailableException(503). To defekt wywołującego, a nie awaria magazynu. - Awaria magazynu. Każda awaria odczytu lub rezerwacji ujawnia się jako
UsageStoreUnavailableExceptioni skutkuje odmową. Brama nigdy nie zezwala na niezmierzoną pracę, gdy licznik jest niedostępny. - Implementacje w pamięci.
InMemoryAlertStateRepositoryorazInMemoryUsageCounterStoresą poprawne wyłącznie w obrębie jednego procesu PHP. Wdrożenia z wieloma replikami muszą dostarczyć implementacje oparte na magazynie danych z rzeczywistą atomowością; magazyn typu odczyt-następnie-zapis jest defektem, który dopuszcza przekroczenie limitu pod obciążeniem. - Tryb FIPS. Billing nie wykonuje żadnych własnych operacji kryptograficznych i nie ma zachowania specyficznego dla FIPS. Konsumowana przez niego tożsamość najemcy musi pochodzić z uwierzytelnionego kontekstu, którego stanowisko FIPS jest udokumentowane przy powierzchni SaaS.
Zgodność
Dział zatytułowany „Zgodność”| Twierdzenie | Standard | Klauzula |
|---|---|---|
| Kod statusu 402 jest zarezerwowany do przyszłego użytku; sam w sobie nie niesie żadnej normatywnej semantyki żądania. | RFC 9110 | §15.5.3 |
| 429 wskazuje, że klient wysłał zbyt wiele żądań w danym czasie („ograniczanie liczby żądań”). | RFC 6585 | §4 |
| Retry-After wskazuje, jak długo agent użytkownika powinien czekać przed wykonaniem kolejnego żądania. | RFC 9110 | §10.2.3 |
Wszystkie klauzule są sparafrazowane; NextPDF nie odtwarza tekstu normatywnego. NextPDF nie formułuje żadnego twierdzenia o zgodności z protokołem HTTP ani o certyfikacji dla tej powierzchni. Odwzorowanie 402 / 429 / 200 zadeklarowane przez OveragePolicy::httpStatusCode() oraz kody odmów bramy 401 / 402 / 503 to konwencja produktowa zgodna z powyższymi klauzulami: RFC 9110 rezerwuje 402, więc jego użycie tutaj do odmowy płatności jest powszechną konwencją branżową, a nie semantyką zdefiniowaną przez IETF. Horyzont ponawiania miękkiego zatrzymania (resetsAt) to wartość, którą warstwa brzegowa powinna udostępnić jako wskazówkę Retry-After. Emitowanie rzeczywistych odpowiedzi HTTP, nagłówków oraz zachowania buforowania jest odpowiedzialnością aplikacji hostującej.
Uwagi dla programistów
Dział zatytułowany „Uwagi dla programistów”- Złóż model z
PlanRegistry::defaultRegistry(), jednejOveragePolicyorazQuotaManager; dodajBillingAlertServicez trwałą implementacjąAlertStateRepositoryInterfacena potrzeby alertów. - Zamontuj
QuotaEnforcementGuardw potoku żądań po uwierzytelnieniu najemcy i przed rozliczanym handlerem. PrzechwytujQuotaEnforcementExceptionorazQuotaExceededExceptionz modułu rozliczeń na warstwie brzegowej i odwzorujhttpStatusCode()na odpowiedź. - Definicje planów w tym module są jedynym źródłem prawdy dla rozliczeń; nie utrzymuj równoległej definicji rozliczeń w innym miejscu wdrożenia.
- Implementacje w pamięci sprawiają, że całą powierzchnię można testować jednostkowo bez operacji wejścia/wyjścia. Zalecane testy brzegowe: zużycie dokładnie na poziomie limitu, jedną jednostkę powyżej, progi współczynnika przy 0.8 i 1.0, zabezpieczenie niezgodności planu, wyścig CAS (dwie rezerwacje o ostatnią jednostkę zapasu) oraz odmowa przy awarii magazynu.
- Klasy modelu głównego niosą
@since 2.2.0; substrat niesie@since 2.3.0. Bieżąca linia pakietu to 3.1.0. - Operator odpowiada za implementacje repozytorium stanu alertów oraz magazynu zużycia, ich trwałość między replikami, a także za wszelkie ponowne uzbrajanie alertów w trakcie okresu przez
clearAlerts().
Granica publikacji
Dział zatytułowany „Granica publikacji”Ta strona dokumentuje wyłącznie zewnętrznie obserwowalne zachowanie oraz wspieraną publiczną powierzchnię API. Wewnętrzne ścieżki przestrzeni nazw, klasy pomocnicze, tabele mechanizmów, nazwy plików runbooków oraz prefiksy zgłoszeń są poza zakresem.