Pular para o conteúdo
getnextpdf.com

Enterprise edição

Billing — Referência Profunda

Esta página é a referência detalhada da superfície de billing do NextPDF Enterprise. A superfície tem duas camadas. O modelo de billing em NextPDF\Enterprise\Billing define níveis de plano, cotas, políticas de excedente e alertas de uso deduplicados. O substrate de imposição em NextPDF\Enterprise\Billing\Substrate coloca esse modelo no caminho de requisição ativo, fail-closed e seguro para concorrência. Os pontos de entrada são PlanRegistry, QuotaManager, OverageCalculator, BillingAlertService e QuotaEnforcementGuard. Para o guia em nível de fluxo de trabalho, consulte a página de capacidade de Billing.

Esta capacidade é entregue no NextPDF Enterprise (nextpdf/enterprise) e é ativada com um envelope de licença de nível Enterprise. Uma implantação sem esse direito não carrega as classes da capacidade. Compare edições e obtenha uma licença.

O Billing é uma capacidade base do Enterprise, sem uma flag por recurso separada; ele fica disponível assim que o pacote Enterprise é instalado ao lado do pacote Core. O NextPDF Core (Apache-2.0) e o NextPDF Pro não têm modelo de plano, cota ou excedente; esta superfície não tem equivalente em nível inferior. As inclusões de plano, as cotas e os termos comerciais são regidos pelo contrato de licença, não pela imposição em tempo de execução; esta referência não é uma opinião jurídica ou contratual.

Todos os símbolos residem em NextPDF\Enterprise\Billing. As linhas marcadas com substrate residem em NextPDF\Enterprise\Billing\Substrate. TenantContext é o tipo de tenant autenticado de NextPDF\Enterprise\SaaS.

SímboloParâmetrosComportamento padrãoRetornaLança ou falha comNotas
SaaSPlan (enum)Níveis de plano com backing de string: standard, advanced, high_controlNão lançalabel() retorna o nome de exibição
PlanDefinition::__constructSaaSPlan $plan, float $includedCuQuota, list<CapabilityCode> $capabilities, non-empty-string $priceTier, bool $intelligencePackIncluded, bool $privacyPackIncludedObjeto de valor de plano imutável; armazena as entradas como fornecidasNova instânciaNão lançafinal readonly; propriedades públicas promovidas
PlanDefinition::includesCapabilityCapabilityCode $capabilityVerificação de pertencimento por identidade estritaboolNão lança
PlanRegistry::__constructlist<PlanDefinition> $definitionsIndexa definições por nível; a última definição por nível prevaleceNovo registroNão lançaPara testes e conjuntos de planos white-label
PlanRegistry::getSaaSPlan $planConsulta canônica de planoPlanDefinitionInvalidArgumentException quando o plano não está registrado
PlanRegistry::hasSaaSPlan $planSondagem de registroboolNão lança
PlanRegistry::defaultRegistry (static)Padrões de produção: Standard 1,000 CU; Advanced 5,000 CU mais Intelligence Pack; High Control 20,000 CU mais Intelligence e Privacy PacksPlanRegistryNão lançaUse a menos que os termos contratuais exijam definições personalizadas
OveragePolicy (enum)hard_stop, soft_stop, budget_alertNão lançahttpStatusCode() mapeia 402 / 429 / 200; isBlocking() é true apenas para hard stop e soft stop
QuotaManager::__constructPlanRegistry $planRegistry, OveragePolicy $overagePolicyVincula o registro a uma políticaNova instânciaNão lança
QuotaManager::checkQuotaTenantContext $tenant, SaaSPlan $plan, float $currentCuRetorna silenciosamente com uso igual ou abaixo da cota, ou sob uma política de não bloqueiovoidQuotaExceededException em excedente estrito sob uma política de bloqueio; InvalidArgumentException do registro em um plano não registradoresetsAt = primeiro dia do mês seguinte, meia-noite UTC
QuotaManager::remainingQuotaSaaSPlan $plan, float $currentCuLeitura pura; nunca bloqueiafloatInvalidArgumentException do registroNegativa em excedente
QuotaManager::usagePercentageSaaSPlan $plan, float $currentCuLeitura pura; nunca bloqueiafloatInvalidArgumentException do registro0.0 quando a cota incluída é não positiva; acima de 1.0 em excedente
OverageCalculator::calculatePlanDefinition $plan, float $currentCuCalcula um snapshot imutável de excedenteOverageResultNão lançafinal readonly, sem estado
OverageResultincludedCu, usedCu, overageCu, usageRatio, isOverageResultado de cálculo imutávelNão lançaoverageCu = max(0, used - included); isOverage requer excedente estrito
BillingAlertType (enum)quota_warning_80, quota_warning_100, budget_exceeded, monthly_cap_reachedNão lançathreshold() 0.8 / 1.0 / 1.0 / 1.0; severity() warning / critical / critical / critical
BillingAlertService::__constructAlertStateRepositoryInterface $alertStateVincula o armazenamento de deduplicaçãoNova instânciaNão lança
BillingAlertService::evaluateTenantContext $tenant, SaaSPlan $plan, PlanDefinition $planDef, float $currentCuDispara alertas ainda não disparados em ordem crescente de limiar e os registralist<BillingAlertType>InvalidArgumentException em divergência de plano/definiçãoChave de dedup: tenant, tipo, período UTC YYYY-MM
BillingAlertService::clearAlertsTenantContext $tenantLimpa o estado de disparo do tenant para o período UTC atualvoidFalhas definidas pelo repositório se propagamRearma os alertas dentro do mesmo período
AlertStateRepositoryInterfacehasAlertFired(), markAlertFired(), clearForPeriod()Contrato de persistência durável de deduplicação de alertasPor métodoDefinido pela implementaçãoO operador é dono da durabilidade entre réplicas
InMemoryAlertStateRepositoryEstado de disparo com backing de arrayConforme a interfaceNão lançaApenas ciclos de vida de requisição única e testes
QuotaExceededExceptionSomente leitura: currentCu, limitCu, resetsAt, tenantId, isSaaSNegação de cota ciente do modo de implantaçãoÉ o throwablehttpStatusCode() 402 SaaS / 403 on-prem; specCode() SPEC-BILLING-003 / SPEC-LIC-001; toErrorEnvelope() produz um corpo de erro estruturado
DeploymentMode (enum)saas, self_hosted_oss, local_developmentNão lançaSubstrate. enforcesQuota() é true apenas para Saas; o opt-out é sempre explícito
QuotaEnforcementGuard::__constructDeploymentMode, PlanResolverInterface, QuotaManager, UsageCounterStoreInterfaceMonta o portão de cota ativoNova instânciaNão lançaSubstrate. final readonly
QuotaEnforcementGuard::enforce?TenantContext $tenant, non-empty-string $featureKey, float $amount = 1.0Portão de cota fail-closed com reserva atômicaQuotaDecision (apenas resultados permitidos)Consulte a taxonomia de negação abaixoSubstrate. Monte após a autenticação do tenant, antes do handler faturável
PlanResolverInterface::resolveTenantContext $tenantResolve um tenant para seu plano e políticas por recursoResolvedPlanNoPlanForTenantExceptionSubstrate. Um fallback de plano padrão para tenants desconhecidos é um defeito
RegistryPlanResolverarray<non-empty-string, ResolvedPlan> $plansByTenantResolvedor com backing de mapaResolvedPlanNoPlanForTenantException para tenants não mapeadosSubstrate. Fail-closed por construção
ResolvedPlan::policyFornon-empty-string $featureKeyConsulta de política no plano resolvido?QuotaPolicyNão lançaSubstrate. null significa recurso desconhecido; o guarda o nega
QuotaPolicynon-empty-string $featureKey, float $limit, OveragePolicy $overagePolicyLimite por recurso e política de violaçãoNão lançaSubstrate. UNLIMITED = -1.0; um limite 0.0 é permissão zero, não ilimitado; isUnlimited(), isBlocking()
QuotaDecisionEstáticos bypassed(), unlimited(), consumed()Objeto de valor de resultado permitidoQuotaDecisionNão lançaSubstrate. isAllowed() é sempre true; toda negação lança em vez disso
UsageCounterSnapshot de linha: tenant, recurso, limites de período, used, limit, updatedAtLinha de uso imutávelNão lançaSubstrate. remaining() pode ser negativo; wouldExceed() é estrito
UsageCounterStoreInterface::getTenant, recurso, limites de período, float $limitLê a linha de uso, criando-a com used = 0 quando ausenteUsageCounterUsageStoreUnavailableExceptionSubstrate. Nunca retorna um valor falsy em falha de backend
UsageCounterStoreInterface::tryConsumeTenant, recurso, limites de período, float $amount, float $limitReserva atômica de compare-and-set dentro do limite?UsageCounter (null quando a reserva violaria o limite)UsageStoreUnavailableExceptionSubstrate. Deve ser uma única operação atômica contra o armazenamento subjacente
InMemoryUsageCounterStoreImplementação de referência em processo do contrato de armazenamentoConforme a interfaceConforme a interfaceSubstrate. Apenas processo único; documenta o invariante de atomicidade
QuotaEnforcementException (abstract)Tipo base de toda negação do substrateÉ a família de throwablesSubstrate. Cada subtipo declara 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;

Taxonomia de negação de QuotaEnforcementGuard::enforce

ExceçãoStatus HTTPLevantada quando
MissingTenantContextException401Modo SaaS sem contexto de tenant autenticado
NoPlanForTenantException402O resolvedor não encontra plano atribuído ao tenant
UnknownFeatureException402O plano resolvido não define política para a chave de recurso
UsageStoreUnavailableException503O armazenamento de uso não pode ser lido ou atualizado atomicamente; também levantada para um $amount não positivo
QuotaExceededException402 (SaaS) / 403 (on-prem)A cota de uma política de bloqueio é excedida, ou uma reserva concorrente consumiu a última folga
  • O registro padrão traz três níveis (Standard / Advanced / High Control) com cotas de CU e conjuntos de capacidades crescentes. Uma solicitação de plano não registrado falha com um InvalidArgumentException explícito.
  • QuotaManager::checkQuota() levanta apenas quando ambas as condições se sustentam: a política é de bloqueio e o uso atual está estritamente acima da cota incluída. Uma política de alerta de orçamento nunca levanta; o excedente é sinalizado por meio de alertas.
  • remainingQuota() e usagePercentage() são leituras puras e nunca bloqueiam. A cota restante fica negativa em excedente; o percentual de uso ultrapassa 1.0 em excedente.
  • Os alertas são avaliados em ordem crescente de limiar: aviso de 80%, aviso de 100% (critical), depois orçamento-excedido (critical). O orçamento-excedido é condicionado a excedente estrito; um uso de exatamente 100% dispara o aviso de 100%, não o orçamento-excedido.
  • Cada tipo de alerta dispara no máximo uma vez por tenant por período de billing. O estado de disparo é registrado por meio de AlertStateRepositoryInterface, de modo que a deduplicação é tão durável quanto a implementação escolhida.
  • A chave de deduplicação embute o período UTC YYYY-MM. Um novo mês civil, portanto, rearma automaticamente cada tipo de alerta; nenhuma chamada de limpeza é necessária para o rearmamento na virada. clearAlerts() limpa o período atual, o que rearma os alertas no meio do período, por exemplo após um upgrade de plano.
  • Um guarda de divergência de plano em evaluate() rejeita uma chamada em que o plano fornecido e a definição de plano divergem, protegendo contra uma definição de um nível diferente do plano do tenant.
  • Toda a aritmética de período é ancorada em UTC. O instante de reset de cota excedida é o primeiro dia do mês civil seguinte à meia-noite UTC; uma resposta de soft-stop deve anunciá-lo como o horizonte de nova tentativa.
  • QuotaEnforcementGuard é fail-closed no modo SaaS. Tenant ausente, plano ausente, recurso desconhecido, indisponibilidade do armazenamento e violação de cota — todos negam; nada escapa para uma permissão implícita. Implantações não SaaS só fazem opt-out construindo o guarda com um DeploymentMode não SaaS.
  • Políticas de bloqueio reservam uso por meio de UsageCounterStoreInterface::tryConsume, um compare-and-set atômico. Requisições concorrentes não podem, coletivamente, empurrar o uso além do limite; o perdedor da corrida recebe QuotaExceededException mesmo que a pré-verificação tenha passado.
  • Sob uma política de alerta de orçamento, o guarda registra o consumo em best-effort e nunca nega; uma reserva além do teto flexível ainda registra a linha no limite.
  • QuotaExceededException é ciente do modo de implantação: negações em SaaS mapeiam para HTTP 402 com o spec code SPEC-BILLING-003 e são marcadas como retryable; negações on-prem mapeiam para HTTP 403 com SPEC-LIC-001.
  • A biblioteca não emite respostas HTTP por conta própria. Os códigos de status declarados são o contrato para a camada de borda, que mapeia uma negação lançada para uma resposta e não deve invocar o handler faturável.
  • Cota incluída não positiva. usagePercentage(), evaluate() e OverageCalculator::calculate() produzem uma razão de uso de 0.0 em vez de dividir por zero. Os alertas de limiar então nunca disparam apenas com base na razão.
  • Alerta de orçamento mais excedente grande. O gerenciador e o guarda retornam resultados permitidos. Não trate a ausência de uma exceção como prova de estar dentro da cota; consulte OverageResult ou o fluxo de alertas.
  • Exatamente no limite. checkQuota() com currentCu == includedCuQuota passa. BudgetExceeded requer excedente estrito. UsageCounter::wouldExceed() também é estrito.
  • MonthlyCapReached. O enum declara este quarto tipo de alerta, mas BillingAlertService::evaluate() nunca o emite; sua lista de candidatos cobre apenas os três alertas de limiar. Ele é reservado para emissores de rastreamento de cap fora deste módulo.
  • Definições de nível duplicadas. PlanRegistry indexa pelo valor do nível; a última definição de um nível substitui silenciosamente as anteriores. Construa registros a partir de uma lista deduplicada.
  • Permissão zero versus ilimitado. Um limite 0.0 em QuotaPolicy significa que todo consumo no período é excedente. Apenas o sentinela negativo UNLIMITED desativa a medição; isUnlimited() nunca bloqueia.
  • Valor de reserva não positivo. enforce() nega um $amount não positivo de forma fail-closed com UsageStoreUnavailableException (503). Isso é um defeito do chamador, não uma indisponibilidade do armazenamento.
  • Indisponibilidade do armazenamento. Qualquer falha de leitura ou de reserva aparece como UsageStoreUnavailableException e nega. O guarda nunca permite trabalho sem medição enquanto o medidor está fora do ar.
  • Implementações em memória. InMemoryAlertStateRepository e InMemoryUsageCounterStore são corretas apenas dentro de um único processo PHP. Implantações com múltiplas réplicas devem fornecer implementações apoiadas por um datastore com atomicidade real; um armazenamento de ler-depois-escrever é um defeito que permite ultrapassar a cota sob carga.
  • Modo FIPS. O Billing não realiza operações criptográficas próprias e não tem comportamento específico de FIPS. A identidade de tenant que ele consome deve originar-se de um contexto autenticado cuja postura de FIPS é documentada com a superfície de SaaS.
AfirmaçãoPadrãoCláusula
O código de status 402 é reservado para uso futuro; ele não carrega semântica de requisição normativa própria.RFC 9110§15.5.3
429 indica que o cliente enviou requisições demais em um dado período de tempo (“rate limiting”).RFC 6585§4
Retry-After indica quanto tempo o user agent deve esperar antes de fazer uma requisição de acompanhamento.RFC 9110§10.2.3

Todas as cláusulas são parafraseadas; o NextPDF não reproduz texto normativo. O NextPDF não faz nenhuma afirmação de conformidade ou certificação de protocolo HTTP para esta superfície. O mapeamento 402 / 429 / 200 declarado por OveragePolicy::httpStatusCode() e os códigos de negação 401 / 402 / 503 do guarda são uma convenção de produto alinhada com as cláusulas acima: a RFC 9110 reserva o 402, portanto seu uso aqui como negação de pagamento é a convenção comum da indústria, não uma semântica definida pelo IETF. O horizonte de nova tentativa do soft-stop (resetsAt) é o valor que uma camada de borda deve expor como orientação de Retry-After. Emitir respostas HTTP reais, cabeçalhos e comportamento de cache é responsabilidade da aplicação hospedeira.

  • Componha o modelo a partir de PlanRegistry::defaultRegistry(), uma OveragePolicy e um QuotaManager; adicione BillingAlertService com uma implementação durável de AlertStateRepositoryInterface para os alertas.
  • Monte QuotaEnforcementGuard no pipeline de requisição após a autenticação do tenant e antes do handler faturável. Capture QuotaEnforcementException e o QuotaExceededException do billing na borda e mapeie httpStatusCode() para a resposta.
  • As definições de plano neste módulo são a única fonte de verdade para o billing; não mantenha uma definição de billing paralela em outro lugar da sua implantação.
  • As implementações em memória tornam toda a superfície testável em unidade sem I/O. Testes de fronteira recomendados: uso exatamente na cota, uma unidade acima, limiares de razão em 0.8 e 1.0, o guarda de divergência de plano, a corrida de CAS (duas reservas contra a última unidade de folga) e a negação por indisponibilidade do armazenamento.
  • As classes do modelo core carregam @since 2.2.0; o substrate carrega @since 2.3.0. A linha de pacote atual é 3.1.0.
  • O operador é dono das implementações do repositório de estado de alerta e do armazenamento de uso, da sua durabilidade entre réplicas e de qualquer rearmamento de alerta no meio do período por meio de clearAlerts().

Esta página documenta apenas o comportamento observável externamente e a superfície pública de API suportada. Caminhos de namespace internos, classes auxiliares, tabelas de mecanismos, nomes de arquivos de runbook e prefixos de tickets estão fora do escopo.