Ir al contenido
getnextpdf.com

Enterprise edición

Facturación — Referencia detallada

Esta página es la referencia detallada de la superficie de facturación de NextPDF Enterprise. La superficie tiene dos capas. El modelo de facturación en NextPDF\Enterprise\Billing define niveles de plan, cuotas, políticas de excedente y alertas de uso deduplicadas. El sustrato de aplicación en NextPDF\Enterprise\Billing\Substrate sitúa ese modelo en la ruta de solicitud en vivo, fail-closed y seguro ante concurrencia. Los puntos de entrada son PlanRegistry, QuotaManager, OverageCalculator, BillingAlertService y QuotaEnforcementGuard. Para la guía a nivel de flujo de trabajo, consulte la página de la funcionalidad de Facturación.

Esta funcionalidad se incluye en NextPDF Enterprise (nextpdf/enterprise) y se activa con un sobre de licencia de nivel Enterprise. Un despliegue sin ese derecho no carga las clases de la funcionalidad. Comparar ediciones y obtener una licencia.

La facturación es una funcionalidad base de Enterprise sin indicador por característica independiente; está disponible una vez que el paquete Enterprise se instala junto al paquete Core. NextPDF Core (Apache-2.0) y NextPDF Pro no tienen modelo de plan, cuota ni excedente; esta superficie no tiene equivalente en niveles inferiores. Las inclusiones de plan, las cuotas y los términos comerciales se rigen por el acuerdo de licencia, no por la aplicación en tiempo de ejecución; esta referencia no es una opinión legal ni contractual.

Todos los símbolos residen en NextPDF\Enterprise\Billing. Las filas marcadas como sustrato residen en NextPDF\Enterprise\Billing\Substrate. TenantContext es el tipo de inquilino autenticado de NextPDF\Enterprise\SaaS.

SímboloParámetrosComportamiento predeterminadoDevuelveLanza o falla conNotas
SaaSPlan (enum)Niveles de plan respaldados por cadenas: standard, advanced, high_controlNo lanzalabel() devuelve el nombre para mostrar
PlanDefinition::__constructSaaSPlan $plan, float $includedCuQuota, list<CapabilityCode> $capabilities, non-empty-string $priceTier, bool $intelligencePackIncluded, bool $privacyPackIncludedObjeto de valor de plan inmutable; almacena las entradas tal cualNueva instanciaNo lanzafinal readonly; propiedades públicas promovidas
PlanDefinition::includesCapabilityCapabilityCode $capabilityComprobación de pertenencia por identidad estrictaboolNo lanza
PlanRegistry::__constructlist<PlanDefinition> $definitionsIndexa las definiciones por nivel; gana la última definición por nivelNuevo registroNo lanzaPara pruebas y conjuntos de planes de marca blanca
PlanRegistry::getSaaSPlan $planBúsqueda canónica de planPlanDefinitionInvalidArgumentException cuando el plan no está registrado
PlanRegistry::hasSaaSPlan $planSondeo de registroboolNo lanza
PlanRegistry::defaultRegistry (static)Valores predeterminados de producción: Standard 1,000 CU; Advanced 5,000 CU más Intelligence Pack; High Control 20,000 CU más Intelligence y Privacy PacksPlanRegistryNo lanzaUtilícelo salvo que los términos contractuales exijan definiciones personalizadas
OveragePolicy (enum)hard_stop, soft_stop, budget_alertNo lanzahttpStatusCode() asigna 402 / 429 / 200; isBlocking() es verdadero solo para hard stop y soft stop
QuotaManager::__constructPlanRegistry $planRegistry, OveragePolicy $overagePolicyVincula el registro a una políticaNueva instanciaNo lanza
QuotaManager::checkQuotaTenantContext $tenant, SaaSPlan $plan, float $currentCuRetorna silenciosamente al alcanzar o estar por debajo de la cuota, o bajo una política no bloqueantevoidQuotaExceededException en excedente estricto bajo una política bloqueante; InvalidArgumentException del registro ante un plan no registradoresetsAt = primer día del mes siguiente, medianoche UTC
QuotaManager::remainingQuotaSaaSPlan $plan, float $currentCuLectura pura; nunca bloqueafloatInvalidArgumentException del registroNegativa en excedente
QuotaManager::usagePercentageSaaSPlan $plan, float $currentCuLectura pura; nunca bloqueafloatInvalidArgumentException del registro0.0 cuando la cuota incluida es no positiva; por encima de 1.0 en excedente
OverageCalculator::calculatePlanDefinition $plan, float $currentCuCalcula una instantánea de excedente inmutableOverageResultNo lanzafinal readonly, sin estado
OverageResultincludedCu, usedCu, overageCu, usageRatio, isOverageResultado de cálculo inmutableNo lanzaoverageCu = max(0, used - included); isOverage requiere excedente estricto
BillingAlertType (enum)quota_warning_80, quota_warning_100, budget_exceeded, monthly_cap_reachedNo lanzathreshold() 0.8 / 1.0 / 1.0 / 1.0; severity() warning / critical / critical / critical
BillingAlertService::__constructAlertStateRepositoryInterface $alertStateVincula el almacén de deduplicaciónNueva instanciaNo lanza
BillingAlertService::evaluateTenantContext $tenant, SaaSPlan $plan, PlanDefinition $planDef, float $currentCuDispara las alertas aún no disparadas en orden ascendente de umbral y las registralist<BillingAlertType>InvalidArgumentException ante discrepancia de plan/definiciónClave de deduplicación: inquilino, tipo, periodo UTC YYYY-MM
BillingAlertService::clearAlertsTenantContext $tenantBorra el estado de disparo del inquilino para el periodo UTC actualvoidLos fallos definidos por el repositorio se propaganRearma las alertas dentro del mismo periodo
AlertStateRepositoryInterfacehasAlertFired(), markAlertFired(), clearForPeriod()Contrato de persistencia duradera para la deduplicación de alertasSegún el métodoDefinido por la implementaciónEl operador es responsable de la durabilidad entre réplicas
InMemoryAlertStateRepositoryEstado de disparo respaldado por arraySegún la interfazNo lanzaSolo para ciclos de vida de una sola solicitud y pruebas
QuotaExceededExceptioncurrentCu, limitCu, resetsAt, tenantId, isSaaS de solo lecturaDenegación de cuota consciente del modo de despliegueEs el lanzablehttpStatusCode() 402 SaaS / 403 on-prem; specCode() SPEC-BILLING-003 / SPEC-LIC-001; toErrorEnvelope() produce un cuerpo de error estructurado
DeploymentMode (enum)saas, self_hosted_oss, local_developmentNo lanzaSustrato. enforcesQuota() es verdadero solo para Saas; la exclusión es siempre explícita
QuotaEnforcementGuard::__constructDeploymentMode, PlanResolverInterface, QuotaManager, UsageCounterStoreInterfaceEnsambla la compuerta de cuota en vivoNueva instanciaNo lanzaSustrato. final readonly
QuotaEnforcementGuard::enforce?TenantContext $tenant, non-empty-string $featureKey, float $amount = 1.0Compuerta de cuota fail-closed con reserva atómicaQuotaDecision (solo resultados permitidos)Véase la taxonomía de denegación más abajoSustrato. Móntela tras la autenticación del inquilino, antes del controlador facturable
PlanResolverInterface::resolveTenantContext $tenantResuelve un inquilino a su plan y políticas por característicaResolvedPlanNoPlanForTenantExceptionSustrato. Un plan de reserva predeterminado para inquilinos desconocidos es un defecto
RegistryPlanResolverarray<non-empty-string, ResolvedPlan> $plansByTenantResolutor respaldado por mapaResolvedPlanNoPlanForTenantException para inquilinos no mapeadosSustrato. Fail-closed por construcción
ResolvedPlan::policyFornon-empty-string $featureKeyBúsqueda de política en el plan resuelto?QuotaPolicyNo lanzaSustrato. null significa característica desconocida; la compuerta la deniega
QuotaPolicynon-empty-string $featureKey, float $limit, OveragePolicy $overagePolicyLímite por característica y política de incumplimientoNo lanzaSustrato. UNLIMITED = -1.0; un límite 0.0 es asignación cero, no ilimitado; isUnlimited(), isBlocking()
QuotaDecisionEstáticos bypassed(), unlimited(), consumed()Objeto de valor de resultado permitidoQuotaDecisionNo lanzaSustrato. isAllowed() es siempre verdadero; toda denegación lanza en su lugar
UsageCounterInstantánea de fila: inquilino, característica, límites del periodo, used, limit, updatedAtFila de uso inmutableNo lanzaSustrato. remaining() puede ser negativo; wouldExceed() es estricto
UsageCounterStoreInterface::getInquilino, característica, límites del periodo, float $limitLee la fila de uso y la crea con used = 0 cuando no existeUsageCounterUsageStoreUnavailableExceptionSustrato. Nunca devuelve un valor falsy ante un fallo del backend
UsageCounterStoreInterface::tryConsumeInquilino, característica, límites del periodo, float $amount, float $limitReserva atómica de comparar y establecer dentro del límite?UsageCounter (null cuando la reserva incumpliría el límite)UsageStoreUnavailableExceptionSustrato. Debe ser una única operación atómica contra el almacén subyacente
InMemoryUsageCounterStoreImplementación de referencia en proceso del contrato del almacénSegún la interfazSegún la interfazSustrato. Solo un proceso; documenta el invariante de atomicidad
QuotaEnforcementException (abstract)Tipo base de toda denegación del sustratoEs la familia de lanzablesSustrato. 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;

Taxonomía de denegación de QuotaEnforcementGuard::enforce

ExcepciónEstado HTTPSe lanza cuando
MissingTenantContextException401Modo SaaS sin contexto de inquilino autenticado
NoPlanForTenantException402El resolutor no encuentra ningún plan asignado al inquilino
UnknownFeatureException402El plan resuelto no define ninguna política para la clave de característica
UsageStoreUnavailableException503El almacén de uso no puede leerse ni actualizarse atómicamente; también se lanza ante un $amount no positivo
QuotaExceededException402 (SaaS) / 403 (on-prem)Se supera la cuota de una política bloqueante, o una reserva concurrente consumió el último margen
  • El registro predeterminado incluye tres niveles (Standard / Advanced / High Control) con cuotas de CU y conjuntos de capacidades crecientes. Una solicitud de plan no registrado falla con un InvalidArgumentException explícito.
  • QuotaManager::checkQuota() lanza solo cuando se cumplen ambas condiciones: la política es bloqueante y el uso actual está estrictamente por encima de la cuota incluida. Una política de alerta de presupuesto nunca lanza; el excedente se señaliza mediante alertas.
  • remainingQuota() y usagePercentage() son lecturas puras y nunca bloquean. La cuota restante se vuelve negativa en excedente; el porcentaje de uso supera 1.0 en excedente.
  • Las alertas se evalúan en orden ascendente de umbral: aviso al 80%, aviso al 100% (crítico) y luego presupuesto superado (crítico). El presupuesto superado depende de un excedente estricto; un uso de exactamente el 100% dispara el aviso del 100%, no el de presupuesto superado.
  • Cada tipo de alerta se dispara como máximo una vez por inquilino y por periodo de facturación. El estado de disparo se registra mediante AlertStateRepositoryInterface, por lo que la deduplicación es tan duradera como la implementación elegida.
  • La clave de deduplicación incorpora el periodo UTC YYYY-MM. Por tanto, un nuevo mes natural rearma automáticamente cada tipo de alerta; no se requiere ninguna llamada de borrado para el rearmado por renovación. clearAlerts() borra el periodo actual, lo que rearma las alertas a mitad del periodo, por ejemplo tras una mejora de plan.
  • Un protector de discrepancia de plan en evaluate() rechaza una llamada en la que el plan proporcionado y la definición de plan no coinciden, protegiendo frente a una definición de un nivel distinto al del plan del inquilino.
  • Toda la aritmética de periodos está anclada a UTC. El instante de restablecimiento de cuota superada es el primer día del mes natural siguiente a medianoche UTC; una respuesta de soft-stop debería anunciarlo como el horizonte de reintento.
  • QuotaEnforcementGuard es fail-closed en modo SaaS. Inquilino ausente, plan ausente, característica desconocida, caída del almacén e incumplimiento de cuota deniegan todos; nada se filtra hacia una autorización implícita. Los despliegues no SaaS se excluyen únicamente construyendo la compuerta con un DeploymentMode no SaaS.
  • Las políticas bloqueantes reservan el uso mediante UsageCounterStoreInterface::tryConsume, un comparar y establecer atómico. Las solicitudes concurrentes no pueden empujar colectivamente el uso más allá del límite; el perdedor de la carrera recibe QuotaExceededException aunque la comprobación previa haya pasado.
  • Bajo una política de alerta de presupuesto, la compuerta registra el consumo con el mejor esfuerzo y nunca deniega; una reserva por encima del techo flexible aún registra la fila en el límite.
  • QuotaExceededException es consciente del modo de despliegue: las denegaciones SaaS se asignan a HTTP 402 con el código de especificación SPEC-BILLING-003 y se marcan como reintentables; las denegaciones on-prem se asignan a HTTP 403 con SPEC-LIC-001.
  • La biblioteca no emite respuestas HTTP por sí misma. Los códigos de estado declarados son el contrato para la capa de borde, que asigna una denegación lanzada a una respuesta y no debe invocar el controlador facturable.
  • Cuota incluida no positiva. usagePercentage(), evaluate() y OverageCalculator::calculate() producen todos una relación de uso 0.0 en lugar de dividir por cero. Las alertas por umbral no se disparan entonces solo por la relación.
  • Alerta de presupuesto con gran excedente. Tanto el gestor como la compuerta devuelven resultados permitidos. No trate la ausencia de una excepción como prueba de estar dentro de la cuota; consulte OverageResult o el flujo de alertas.
  • Justo en el límite. checkQuota() con currentCu == includedCuQuota pasa. BudgetExceeded requiere excedente estricto. UsageCounter::wouldExceed() también es estricto.
  • MonthlyCapReached. El enum declara este cuarto tipo de alerta, pero BillingAlertService::evaluate() nunca lo emite; su lista de candidatos cubre solo las tres alertas por umbral. Está reservado para emisores de seguimiento de topes fuera de este módulo.
  • Definiciones de nivel duplicadas. PlanRegistry indexa por valor de nivel; la última definición de un nivel reemplaza silenciosamente a las anteriores. Construya los registros a partir de una lista deduplicada.
  • Asignación cero frente a ilimitado. Un límite de QuotaPolicy de 0.0 significa que todo consumo en el periodo es excedente. Solo el centinela negativo UNLIMITED desactiva la medición; isUnlimited() nunca bloquea.
  • Importe de reserva no positivo. enforce() deniega un $amount no positivo en modo fail-closed con UsageStoreUnavailableException (503). Esto es un defecto del llamante, no una caída del almacén.
  • Caída del almacén. Cualquier fallo de lectura o reserva se manifiesta como UsageStoreUnavailableException y deniega. La compuerta nunca permite trabajo sin medir mientras el medidor está caído.
  • Implementaciones en memoria. InMemoryAlertStateRepository e InMemoryUsageCounterStore son correctas únicamente dentro de un solo proceso PHP. Los despliegues multirréplica deben proporcionar implementaciones respaldadas por un almacén de datos con atomicidad real; un almacén de leer-luego-escribir es un defecto que permite superar la cuota bajo carga.
  • Modo FIPS. La facturación no realiza operaciones criptográficas propias y no tiene comportamiento específico de FIPS. La identidad de inquilino que consume debe originarse en un contexto autenticado cuya postura FIPS se documenta con la superficie SaaS.
AfirmaciónEstándarCláusula
El código de estado 402 está reservado para uso futuro; no conlleva semántica normativa de solicitud propia.RFC 9110§15.5.3
429 indica que el cliente ha enviado demasiadas solicitudes en un periodo de tiempo determinado («limitación de tasa»).RFC 6585§4
Retry-After indica cuánto tiempo debería esperar el agente de usuario antes de realizar una solicitud de seguimiento.RFC 9110§10.2.3

Todas las cláusulas están parafraseadas; NextPDF no reproduce texto normativo. NextPDF no formula ninguna afirmación de conformidad ni certificación de protocolo HTTP para esta superficie. La asignación 402 / 429 / 200 declarada por OveragePolicy::httpStatusCode() y los códigos de denegación 401 / 402 / 503 de la compuerta son una convención de producto alineada con las cláusulas anteriores: RFC 9110 reserva el 402, por lo que su uso aquí para denegación de pago es la convención habitual del sector, no una semántica definida por el IETF. El horizonte de reintento de soft-stop (resetsAt) es el valor que una capa de borde debería exponer como orientación de Retry-After. Emitir las respuestas HTTP reales, las cabeceras y el comportamiento de caché es responsabilidad de la aplicación anfitriona.

  • Componga el modelo a partir de PlanRegistry::defaultRegistry(), una OveragePolicy y un QuotaManager; añada BillingAlertService con una implementación duradera de AlertStateRepositoryInterface para las alertas.
  • Monte QuotaEnforcementGuard en la canalización de solicitudes tras la autenticación del inquilino y antes del controlador facturable. Capture QuotaEnforcementException y la QuotaExceededException de facturación en el borde y asigne httpStatusCode() a la respuesta.
  • Las definiciones de plan de este módulo son la única fuente de verdad para la facturación; no mantenga una definición de facturación paralela en otro lugar de su despliegue.
  • Las implementaciones en memoria hacen que toda la superficie sea comprobable con pruebas unitarias sin E/S. Pruebas de límite recomendadas: uso exactamente en la cuota, una unidad por encima, umbrales de relación en 0.8 y 1.0, el protector de discrepancia de plan, la carrera CAS (dos reservas contra la última unidad de margen) y la denegación por caída del almacén.
  • Las clases del modelo central llevan @since 2.2.0; el sustrato lleva @since 2.3.0. La línea de paquete actual es 3.1.0.
  • El operador es responsable de las implementaciones del repositorio de estado de alertas y del almacén de uso, de su durabilidad entre réplicas y de cualquier rearmado de alertas a mitad de periodo mediante clearAlerts().

Esta página documenta únicamente el comportamiento observable externamente y la superficie de API pública admitida. Las rutas de espacio de nombres internas, las clases auxiliares, las tablas de mecanismos, los nombres de archivo de runbook y los prefijos de tique quedan fuera de alcance.