Salta ai contenuti
getnextpdf.com

Enterprise edizione

Billing — Riferimento approfondito

Questa pagina è il riferimento approfondito per la superficie di billing di NextPDF Enterprise. La superficie ha due layer. Il modello di billing in NextPDF\Enterprise\Billing definisce i tier di piano, le quote, le politiche di overage e gli avvisi di utilizzo deduplicati. Il substrate di applicazione in NextPDF\Enterprise\Billing\Substrate colloca quel modello sul percorso di richiesta attivo, fail-closed e sicuro per la concorrenza. I punti d’ingresso sono PlanRegistry, QuotaManager, OverageCalculator, BillingAlertService e QuotaEnforcementGuard. Per la guida a livello di workflow, vedere la pagina della capability Billing.

Questa capability è distribuita in NextPDF Enterprise (nextpdf/enterprise) e si attiva con un envelope di licenza di livello Enterprise. Una distribuzione priva di tale entitlement non carica le classi della capability. Confronta le edizioni e ottieni una licenza.

Il billing è una capability Enterprise di base senza un flag per-feature separato; è disponibile una volta installato il pacchetto Enterprise accanto al pacchetto Core. NextPDF Core (Apache-2.0) e NextPDF Pro non dispongono di alcun modello di piano, quota o overage; questa superficie non ha un equivalente di livello inferiore. Le inclusioni dei piani, le quote e i termini commerciali sono regolati dal contratto di licenza, non dall’applicazione in fase di esecuzione; questo riferimento non è un parere legale o contrattuale.

Tutti i simboli risiedono sotto NextPDF\Enterprise\Billing. Le righe contrassegnate substrate risiedono sotto NextPDF\Enterprise\Billing\Substrate. TenantContext è il tipo tenant autenticato di NextPDF\Enterprise\SaaS.

SimboloParametriComportamento predefinitoRestituisceSolleva o fallisce conNote
SaaSPlan (enum)Tier di piano basati su stringa: standard, advanced, high_controlNon sollevalabel() restituisce il nome visualizzato
PlanDefinition::__constructSaaSPlan $plan, float $includedCuQuota, list<CapabilityCode> $capabilities, non-empty-string $priceTier, bool $intelligencePackIncluded, bool $privacyPackIncludedValue object immutabile del piano; memorizza gli input così come fornitiNuova istanzaNon sollevafinal readonly; proprietà pubbliche promosse
PlanDefinition::includesCapabilityCapabilityCode $capabilityControllo di appartenenza per identità strettaboolNon solleva
PlanRegistry::__constructlist<PlanDefinition> $definitionsIndicizza le definizioni per tier; vince l’ultima definizione per tierNuovo registryNon sollevaPer i test e gli insiemi di piani white-label
PlanRegistry::getSaaSPlan $planRicerca canonica del pianoPlanDefinitionInvalidArgumentException quando il piano non è registrato
PlanRegistry::hasSaaSPlan $planSonda di registrazioneboolNon solleva
PlanRegistry::defaultRegistry (static)Impostazioni predefinite di produzione: Standard 1,000 CU; Advanced 5,000 CU più Intelligence Pack; High Control 20,000 CU più Intelligence e Privacy PackPlanRegistryNon sollevaDa usare a meno che i termini contrattuali non richiedano definizioni personalizzate
OveragePolicy (enum)hard_stop, soft_stop, budget_alertNon sollevahttpStatusCode() mappa 402 / 429 / 200; isBlocking() è true solo per hard e soft stop
QuotaManager::__constructPlanRegistry $planRegistry, OveragePolicy $overagePolicyLega il registry a una politicaNuova istanzaNon solleva
QuotaManager::checkQuotaTenantContext $tenant, SaaSPlan $plan, float $currentCuRitorna in silenzio a quota o sotto quota, oppure sotto una politica non bloccantevoidQuotaExceededException in caso di overage stretto sotto una politica bloccante; InvalidArgumentException dal registry per un piano non registratoresetsAt = primo giorno del mese successivo, mezzanotte UTC
QuotaManager::remainingQuotaSaaSPlan $plan, float $currentCuLettura pura; non blocca maifloatInvalidArgumentException dal registryNegativa in overage
QuotaManager::usagePercentageSaaSPlan $plan, float $currentCuLettura pura; non blocca maifloatInvalidArgumentException dal registry0.0 quando la quota inclusa è non positiva; superiore a 1.0 in overage
OverageCalculator::calculatePlanDefinition $plan, float $currentCuCalcola uno snapshot immutabile dell’overageOverageResultNon sollevafinal readonly, senza stato
OverageResultincludedCu, usedCu, overageCu, usageRatio, isOverageRisultato di calcolo immutabileNon sollevaoverageCu = max(0, used - included); isOverage richiede un overage stretto
BillingAlertType (enum)quota_warning_80, quota_warning_100, budget_exceeded, monthly_cap_reachedNon sollevathreshold() 0.8 / 1.0 / 1.0 / 1.0; severity() warning / critical / critical / critical
BillingAlertService::__constructAlertStateRepositoryInterface $alertStateLega lo store di deduplicazioneNuova istanzaNon solleva
BillingAlertService::evaluateTenantContext $tenant, SaaSPlan $plan, PlanDefinition $planDef, float $currentCuAttiva gli avvisi non ancora attivati in ordine crescente di soglia e li registralist<BillingAlertType>InvalidArgumentException in caso di mismatch tra piano e definizioneChiave di dedup: tenant, tipo, periodo UTC YYYY-MM
BillingAlertService::clearAlertsTenantContext $tenantAzzera lo stato di attivazione del tenant per il periodo UTC correntevoidI guasti definiti dal repository si propaganoRi-arma gli avvisi nello stesso periodo
AlertStateRepositoryInterfacehasAlertFired(), markAlertFired(), clearForPeriod()Contratto di persistenza durevole per la deduplicazione degli avvisiPer metodoDefinito dall’implementazioneL’operatore è responsabile della durabilità tra le repliche
InMemoryAlertStateRepositoryStato di attivazione basato su arrayPer interfacciaNon sollevaSolo cicli di vita a richiesta singola e test
QuotaExceededExceptionIn sola lettura: currentCu, limitCu, resetsAt, tenantId, isSaaSRifiuto di quota consapevole della modalità di distribuzioneÈ il throwablehttpStatusCode() 402 SaaS / 403 on-prem; specCode() SPEC-BILLING-003 / SPEC-LIC-001; toErrorEnvelope() produce un corpo di errore strutturato
DeploymentMode (enum)saas, self_hosted_oss, local_developmentNon sollevaSubstrate. enforcesQuota() è true solo per Saas; l’opt-out è sempre esplicito
QuotaEnforcementGuard::__constructDeploymentMode, PlanResolverInterface, QuotaManager, UsageCounterStoreInterfaceAssembla il gate di quota attivoNuova istanzaNon sollevaSubstrate. final readonly
QuotaEnforcementGuard::enforce?TenantContext $tenant, non-empty-string $featureKey, float $amount = 1.0Gate di quota fail-closed con prenotazione atomicaQuotaDecision (solo esiti consentiti)Vedere la tassonomia dei rifiuti più sottoSubstrate. Da montare dopo l’autenticazione del tenant, prima dell’handler fatturabile
PlanResolverInterface::resolveTenantContext $tenantRisolve un tenant nel suo piano e nelle politiche per-featureResolvedPlanNoPlanForTenantExceptionSubstrate. Un fallback a piano predefinito per tenant sconosciuti è un difetto
RegistryPlanResolverarray<non-empty-string, ResolvedPlan> $plansByTenantResolver basato su mappaResolvedPlanNoPlanForTenantException per tenant non mappatiSubstrate. Fail-closed per costruzione
ResolvedPlan::policyFornon-empty-string $featureKeyRicerca della politica sul piano risolto?QuotaPolicyNon sollevaSubstrate. null indica una feature sconosciuta; la guardia la rifiuta
QuotaPolicynon-empty-string $featureKey, float $limit, OveragePolicy $overagePolicyLimite per-feature e politica di violazioneNon sollevaSubstrate. UNLIMITED = -1.0; un limite 0.0 è zero allowance, non illimitato; isUnlimited(), isBlocking()
QuotaDecisionStatici bypassed(), unlimited(), consumed()Value object di esito consentitoQuotaDecisionNon sollevaSubstrate. isAllowed() è sempre true; ogni rifiuto solleva invece un’eccezione
UsageCounterSnapshot di riga: tenant, feature, limiti del periodo, used, limit, updatedAtRiga di utilizzo immutabileNon sollevaSubstrate. remaining() può essere negativo; wouldExceed() è stretto
UsageCounterStoreInterface::getTenant, feature, limiti del periodo, float $limitLegge la riga di utilizzo, creandola con used = 0 quando assenteUsageCounterUsageStoreUnavailableExceptionSubstrate. Non restituisce mai un valore falsy in caso di guasto del backend
UsageCounterStoreInterface::tryConsumeTenant, feature, limiti del periodo, float $amount, float $limitPrenotazione atomica compare-and-set entro il limite?UsageCounter (null quando la prenotazione violerebbe il limite)UsageStoreUnavailableExceptionSubstrate. Deve essere una singola operazione atomica sullo store di supporto
InMemoryUsageCounterStoreImplementazione di riferimento in-process del contratto dello storePer interfacciaPer interfacciaSubstrate. Solo processo singolo; documenta l’invariante di atomicità
QuotaEnforcementException (abstract)Tipo base di ogni rifiuto del substrateÈ la famiglia di throwableSubstrate. Ogni sottotipo dichiara 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;

Tassonomia dei rifiuti di QuotaEnforcementGuard::enforce

EccezioneStato HTTPSollevata quando
MissingTenantContextException401Modalità SaaS senza contesto tenant autenticato
NoPlanForTenantException402Il resolver non trova alcun piano assegnato al tenant
UnknownFeatureException402Il piano risolto non definisce alcuna politica per la chiave di feature
UsageStoreUnavailableException503Lo store di utilizzo non può essere letto o aggiornato atomicamente; sollevata anche per un $amount non positivo
QuotaExceededException402 (SaaS) / 403 (on-prem)La quota di una politica bloccante è superata, oppure una prenotazione concorrente ha consumato l’ultimo margine
  • Il registry predefinito include tre tier (Standard / Advanced / High Control) con quote CU e insiemi di capability crescenti. Una richiesta per un piano non registrato fallisce con un InvalidArgumentException esplicito.
  • QuotaManager::checkQuota() solleva solo quando valgono entrambe le condizioni: la politica è bloccante e l’utilizzo corrente è strettamente superiore alla quota inclusa. Una politica budget-alert non solleva mai; l’overage è segnalato tramite avvisi.
  • remainingQuota() e usagePercentage() sono letture pure e non bloccano mai. La quota rimanente diventa negativa in overage; la percentuale di utilizzo supera 1.0 in overage.
  • Gli avvisi sono valutati in ordine crescente di soglia: warning all’80%, warning al 100% (critical), poi budget-exceeded (critical). Budget-exceeded è subordinato all’overage stretto; un utilizzo esattamente al 100% attiva il warning al 100%, non budget-exceeded.
  • Ogni tipo di avviso si attiva al massimo una volta per tenant per periodo di fatturazione. Lo stato di attivazione è registrato tramite AlertStateRepositoryInterface, così che la deduplicazione sia durevole quanto l’implementazione scelta.
  • La chiave di deduplicazione incorpora il periodo UTC YYYY-MM. Un nuovo mese di calendario ri-arma quindi automaticamente ogni tipo di avviso; nessuna chiamata di clear è necessaria per il ri-armamento al rollover. clearAlerts() azzera il periodo corrente, ri-armando gli avvisi a metà periodo, per esempio dopo un upgrade di piano.
  • Una guardia di mismatch del piano in evaluate() rifiuta una chiamata in cui il piano fornito e la definizione del piano non concordano, proteggendo dal caso di una definizione appartenente a un tier diverso dal piano del tenant.
  • Tutta l’aritmetica dei periodi è ancorata a UTC. L’istante di reset della quota superata è il primo giorno del mese di calendario successivo a mezzanotte UTC; una risposta soft-stop dovrebbe pubblicizzarlo come orizzonte di ritentativo.
  • QuotaEnforcementGuard è fail-closed in modalità SaaS. Tenant mancante, piano mancante, feature sconosciuta, indisponibilità dello store e violazione di quota rifiutano tutti; nulla ricade in un consenso implicito. Le distribuzioni non-SaaS si escludono solo costruendo la guardia con un DeploymentMode non-SaaS.
  • Le politiche bloccanti prenotano l’utilizzo tramite UsageCounterStoreInterface::tryConsume, un compare-and-set atomico. Le richieste concorrenti non possono spingere collettivamente l’utilizzo oltre il limite; chi perde la corsa riceve QuotaExceededException anche se il pre-controllo era passato.
  • Sotto una politica budget-alert la guardia registra il consumo best-effort e non rifiuta mai; una prenotazione oltre il tetto morbido registra comunque la riga al limite.
  • QuotaExceededException è consapevole della modalità di distribuzione: i rifiuti SaaS mappano su HTTP 402 con spec code SPEC-BILLING-003 e sono contrassegnati come ritentabili; i rifiuti on-prem mappano su HTTP 403 con SPEC-LIC-001.
  • La libreria non emette essa stessa risposte HTTP. I codici di stato dichiarati sono il contratto per il layer di edge, che mappa un rifiuto sollevato su una risposta e non deve invocare l’handler fatturabile.
  • Quota inclusa non positiva. usagePercentage(), evaluate() e OverageCalculator::calculate() producono tutti un rapporto di utilizzo di 0.0 anziché una divisione per zero. Gli avvisi di soglia non si attivano quindi mai dal solo rapporto.
  • Budget-alert più un overage elevato. Sia il manager sia la guardia restituiscono esiti consentiti. Non interpretare l’assenza di un’eccezione come prova di essere entro la quota; consultare OverageResult o lo stream degli avvisi.
  • Esattamente al limite. checkQuota() con currentCu == includedCuQuota passa. BudgetExceeded richiede un overage stretto. Anche UsageCounter::wouldExceed() è stretto.
  • MonthlyCapReached. L’enum dichiara questo quarto tipo di avviso, ma BillingAlertService::evaluate() non lo emette mai; la sua lista di candidati copre solo i tre avvisi di soglia. È riservato agli emitter di tracciamento del cap esterni a questo modulo.
  • Definizioni di tier duplicate. PlanRegistry indicizza per valore di tier; l’ultima definizione per un tier sostituisce silenziosamente quelle precedenti. Costruire i registry a partire da una lista deduplicata.
  • Zero allowance contro illimitato. Un limite QuotaPolicy di 0.0 significa che ogni consumo nel periodo è overage. Solo il sentinel negativo UNLIMITED disabilita il metering; isUnlimited() non blocca mai.
  • Importo di prenotazione non positivo. enforce() rifiuta un $amount non positivo in modo fail-closed con UsageStoreUnavailableException (503). Questo è un difetto del chiamante, non un’indisponibilità dello store.
  • Indisponibilità dello store. Qualsiasi guasto di lettura o prenotazione emerge come UsageStoreUnavailableException e rifiuta. La guardia non consente mai lavoro non misurato mentre il meter è inattivo.
  • Implementazioni in memoria. InMemoryAlertStateRepository e InMemoryUsageCounterStore sono corretti solo all’interno di un singolo processo PHP. Le distribuzioni multi-replica devono fornire implementazioni supportate da un datastore con atomicità reale; uno store read-then-write è un difetto che permette il superamento della quota sotto carico.
  • Modalità FIPS. Il billing non esegue alcuna operazione crittografica propria e non ha alcun comportamento specifico FIPS. L’identità del tenant che consuma deve provenire da un contesto autenticato la cui postura FIPS è documentata con la superficie SaaS.
AffermazioneStandardClausola
Il codice di stato 402 è riservato per usi futuri; non porta alcuna semantica normativa di richiesta propria.RFC 9110§15.5.3
429 indica che il client ha inviato troppe richieste in un dato intervallo di tempo (“rate limiting”).RFC 6585§4
Retry-After indica quanto tempo lo user agent dovrebbe attendere prima di effettuare una richiesta successiva.RFC 9110§10.2.3

Tutte le clausole sono parafrasate; NextPDF non riproduce testo normativo. NextPDF non avanza alcuna affermazione di conformità o certificazione del protocollo HTTP per questa superficie. La mappatura 402 / 429 / 200 dichiarata da OveragePolicy::httpStatusCode() e i codici di rifiuto 401 / 402 / 503 della guardia sono una convenzione di prodotto allineata con le clausole di cui sopra: la RFC 9110 riserva il 402, quindi il suo uso qui per il rifiuto di pagamento è la comune convenzione di settore, non una semantica definita dall’IETF. L’orizzonte di ritentativo soft-stop (resetsAt) è il valore che un layer di edge dovrebbe esporre come indicazione Retry-After. L’emissione delle effettive risposte HTTP, delle intestazioni e del comportamento di caching è responsabilità dell’applicazione ospitante.

  • Comporre il modello a partire da PlanRegistry::defaultRegistry(), una OveragePolicy e un QuotaManager; aggiungere BillingAlertService con un’implementazione durevole di AlertStateRepositoryInterface per gli avvisi.
  • Montare QuotaEnforcementGuard nella pipeline di richiesta dopo l’autenticazione del tenant e prima dell’handler fatturabile. Intercettare QuotaEnforcementException e la QuotaExceededException del billing all’edge e mappare httpStatusCode() sulla risposta.
  • Le definizioni dei piani in questo modulo sono l’unica fonte di verità per il billing; non mantenere una definizione di billing parallela altrove nella distribuzione.
  • Le implementazioni in memoria rendono l’intera superficie testabile a livello unitario senza I/O. Test di confine consigliati: utilizzo esattamente alla quota, un’unità sopra, soglie di rapporto a 0.8 e 1.0, la guardia di mismatch del piano, la corsa CAS (due prenotazioni contro l’ultima unità di margine) e il rifiuto per indisponibilità dello store.
  • Le classi del modello core portano @since 2.2.0; il substrate porta @since 2.3.0. La linea di pacchetto corrente è 3.1.0.
  • L’operatore è responsabile delle implementazioni del repository dello stato degli avvisi e dello usage-store, della loro durabilità tra le repliche e di qualsiasi ri-armamento degli avvisi a metà periodo tramite clearAlerts().

Questa pagina documenta esclusivamente il comportamento osservabile dall’esterno e la superficie dell’API pubblica supportata. I percorsi di namespace interni, le classi helper, le tabelle dei meccanismi, i nomi di file dei runbook e i prefissi dei ticket sono fuori ambito.