跳到內容
getnextpdf.com

Enterprise 版本

Billing — 深入參考

本頁是 NextPDF Enterprise 計費介面的深入參考。此介面分為兩層。位於 NextPDF\Enterprise\Billing 的計費模型定義方案層級、配額、超額原則,以及去重後的用量警示。位於 NextPDF\Enterprise\Billing\Substrate 的強制執行基底將該模型置於實際請求路徑上,採 fail-closed 且具並行安全性。進入點為 PlanRegistryQuotaManagerOverageCalculatorBillingAlertServiceQuotaEnforcementGuard。工作流程層級的指南請參閱Billing 能力頁面

此能力隨 NextPDF Enterprisenextpdf/enterprise)提供,並以 Enterprise 層級的授權封套啟用。未持有該授權的部署不會載入此能力的類別。比較版本並取得授權

Billing 是一項基礎的 Enterprise 能力,沒有獨立的逐功能旗標;只要將 Enterprise 套件與 Core 套件一同安裝即可使用。NextPDF Core(Apache-2.0)與 NextPDF Pro 皆沒有任何方案、配額或超額模型;此介面沒有任何較低層級的等價物。方案包含項目、配額與商業條款由授權合約規範,而非由執行階段強制執行;本參考不是法律或合約意見。

所有符號都位於 NextPDF\Enterprise\Billing 之下。標記為 substrate 的列位於 NextPDF\Enterprise\Billing\Substrate 之下。TenantContext 是來自 NextPDF\Enterprise\SaaS 的已認證租戶型別。

符號參數預設行為回傳拋出或失敗於備註
SaaSPlan(enum)字串支撐的方案層級:standardadvancedhigh_control不會拋出label() 回傳顯示名稱
PlanDefinition::__constructSaaSPlan $planfloat $includedCuQuotalist<CapabilityCode> $capabilitiesnon-empty-string $priceTierbool $intelligencePackIncludedbool $privacyPackIncluded不可變的方案值物件;依原樣儲存輸入新實例不會拋出final readonly;提升的 public 屬性
PlanDefinition::includesCapabilityCapabilityCode $capability嚴格身分成員檢查bool不會拋出
PlanRegistry::__constructlist<PlanDefinition> $definitions依層級索引定義;每個層級以最後一筆定義為準新註冊表不會拋出供測試與白標方案集使用
PlanRegistry::getSaaSPlan $plan規範的方案查找PlanDefinition方案未註冊時拋出 InvalidArgumentException
PlanRegistry::hasSaaSPlan $plan註冊探測bool不會拋出
PlanRegistry::defaultRegistry(static)正式環境預設值:Standard 1,000 CU;Advanced 5,000 CU 外加 Intelligence Pack;High Control 20,000 CU 外加 Intelligence 與 Privacy PackPlanRegistry不會拋出除非合約條款要求自訂定義,否則使用此項
OveragePolicy(enum)hard_stopsoft_stopbudget_alert不會拋出httpStatusCode() 對應 402 / 429 / 200;isBlocking() 僅對 hard 與 soft stop 為真
QuotaManager::__constructPlanRegistry $planRegistryOveragePolicy $overagePolicy將註冊表繫結至單一原則新實例不會拋出
QuotaManager::checkQuotaTenantContext $tenantSaaSPlan $planfloat $currentCu在配額內(含剛好達標)或非阻擋原則下靜默回傳void在阻擋原則下嚴格超額時拋出 QuotaExceededException;方案未註冊時由註冊表拋出 InvalidArgumentExceptionresetsAt = 下個月第一天,午夜 UTC
QuotaManager::remainingQuotaSaaSPlan $planfloat $currentCu純讀取;絕不阻擋float註冊表 InvalidArgumentException超額時為負值
QuotaManager::usagePercentageSaaSPlan $planfloat $currentCu純讀取;絕不阻擋float註冊表 InvalidArgumentException包含配額為非正值時為 0.0;超額時高於 1.0
OverageCalculator::calculatePlanDefinition $planfloat $currentCu計算不可變的超額快照OverageResult不會拋出final readonly,無狀態
OverageResultincludedCuusedCuoverageCuusageRatioisOverage不可變的計算結果不會拋出overageCu = max(0, used - included)isOverage 需嚴格超額
BillingAlertType(enum)quota_warning_80quota_warning_100budget_exceededmonthly_cap_reached不會拋出threshold() 為 0.8 / 1.0 / 1.0 / 1.0;severity() 為 warning / critical / critical / critical
BillingAlertService::__constructAlertStateRepositoryInterface $alertState繫結去重儲存新實例不會拋出
BillingAlertService::evaluateTenantContext $tenantSaaSPlan $planPlanDefinition $planDeffloat $currentCu依門檻遞增順序觸發尚未觸發的警示並加以記錄list<BillingAlertType>方案/定義不符時拋出 InvalidArgumentException去重鍵:租戶、類型、UTC YYYY-MM 週期
BillingAlertService::clearAlertsTenantContext $tenant清除該租戶在目前 UTC 週期的觸發狀態void由 repository 定義的失敗會向外傳播在同一週期內重新武裝警示
AlertStateRepositoryInterfacehasAlertFired()markAlertFired()clearForPeriod()耐久的警示去重持久化合約依各方法而定由實作定義運維人員負責跨複本的耐久性
InMemoryAlertStateRepository以陣列支撐的觸發狀態依介面而定不會拋出僅供單一請求生命週期與測試
QuotaExceededException唯讀的 currentCulimitCuresetsAttenantIdisSaaS感知部署模式的配額拒絕即為該可拋出物httpStatusCode() 為 402 SaaS / 403 on-prem;specCode()SPEC-BILLING-003 / SPEC-LIC-001toErrorEnvelope() 產生結構化的錯誤主體
DeploymentMode(enum)saasself_hosted_osslocal_development不會拋出Substrate. enforcesQuota() 僅對 Saas 為真;退出一律為明確指定
QuotaEnforcementGuard::__constructDeploymentModePlanResolverInterfaceQuotaManagerUsageCounterStoreInterface組裝實際的配額閘門新實例不會拋出Substrate. final readonly
QuotaEnforcementGuard::enforce?TenantContext $tenantnon-empty-string $featureKeyfloat $amount = 1.0fail-closed 的配額閘門,具原子式預留QuotaDecision(僅允許結果)參見下方的拒絕分類Substrate. 掛載於租戶認證之後、計費處理常式之前
PlanResolverInterface::resolveTenantContext $tenant將租戶解析為其方案與逐功能原則ResolvedPlanNoPlanForTenantExceptionSubstrate. 為未知租戶提供預設方案回退是一種缺陷
RegistryPlanResolverarray<non-empty-string, ResolvedPlan> $plansByTenant以 map 支撐的解析器ResolvedPlan對未對應的租戶拋出 NoPlanForTenantExceptionSubstrate. 依建構本質即為 fail-closed
ResolvedPlan::policyFornon-empty-string $featureKey在已解析方案上查找原則?QuotaPolicy不會拋出Substrate. null 代表未知功能;閘門會拒絕它
QuotaPolicynon-empty-string $featureKeyfloat $limitOveragePolicy $overagePolicy逐功能的上限與違規原則不會拋出Substrate. UNLIMITED = -1.00.0 的上限代表零額度,而非無限制;isUnlimited()isBlocking()
QuotaDecision靜態方法 bypassed()unlimited()consumed()允許結果的值物件QuotaDecision不會拋出Substrate. isAllowed() 恆為真;每次拒絕改以拋出處理
UsageCounter列快照:租戶、功能、週期界限、usedlimitupdatedAt不可變的用量列不會拋出Substrate. remaining() 可能為負;wouldExceed() 為嚴格判定
UsageCounterStoreInterface::get租戶、功能、週期界限、float $limit讀取用量列,不存在時以 used = 0 建立UsageCounterUsageStoreUnavailableExceptionSubstrate. 後端失敗時絕不回傳假值
UsageCounterStoreInterface::tryConsume租戶、功能、週期界限、float $amountfloat $limit在上限內進行原子式 compare-and-set 預留?UsageCounter(預留會突破上限時為 nullUsageStoreUnavailableExceptionSubstrate. 必須是對後端儲存的單一原子操作
InMemoryUsageCounterStore儲存合約的行程內參考實作依介面而定依介面而定Substrate. 僅限單一行程;用於記載原子性不變式
QuotaEnforcementException(abstract)每個 substrate 拒絕的基底型別即為該可拋出物族系Substrate. 每個子型別都宣告 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;

QuotaEnforcementGuard::enforce 的拒絕分類

例外HTTP 狀態引發時機
MissingTenantContextException401SaaS 模式下沒有已認證的租戶情境
NoPlanForTenantException402解析器找不到指派給該租戶的方案
UnknownFeatureException402已解析方案未為該功能鍵定義任何原則
UsageStoreUnavailableException503用量儲存無法讀取或原子更新;$amount 為非正值時亦會引發
QuotaExceededException402(SaaS)/ 403(on-prem)阻擋原則的配額被超過,或並行預留耗盡了最後的餘裕
  • 預設註冊表隨附三個層級(Standard / Advanced / High Control),具遞增的 CU 配額與能力集合。對未註冊方案的請求會以明確的 InvalidArgumentException 失敗。
  • QuotaManager::checkQuota() 只在兩個條件同時成立時才引發:原則為阻擋型,且目前使用量嚴格高於包含配額。預算警示原則絕不引發;超額改由警示傳達。
  • remainingQuota()usagePercentage() 是純讀取,絕不阻擋。超額時剩餘配額會轉為負值;超額時使用百分比會超過 1.0
  • 警示依門檻遞增順序評估:80% 警告、100% 警告(critical),接著是預算超額(critical)。預算超額以嚴格超額為閘門;剛好達到 100% 的使用量會觸發 100% 警告,而非預算超額。
  • 每種警示類型每個租戶每個計費週期最多觸發一次。觸發狀態透過 AlertStateRepositoryInterface 記錄,因此去重的耐久程度取決於所選實作。
  • 去重鍵內嵌 UTC YYYY-MM 週期。因此新的日曆月份會自動重新武裝每種警示類型;換期重新武裝無需任何清除呼叫。clearAlerts() 清除目前週期,會在週期中途重新武裝警示,例如在方案升級之後。
  • evaluate() 中的方案不符防護會拒絕「所提供方案與方案定義不一致」的呼叫,以防範來自與租戶方案不同層級的定義。
  • 所有週期運算皆錨定於 UTC。配額超額的重設瞬時是下一個日曆月份第一天的午夜 UTC;soft-stop 回應應將其公告為重試期限。
  • QuotaEnforcementGuard 在 SaaS 模式下為 fail-closed。缺少租戶、缺少方案、未知功能、儲存中斷與配額突破全都會拒絕;沒有任何情況會落入隱含的允許。非 SaaS 部署只能透過以非 SaaS 的 DeploymentMode 建構閘門來退出。
  • 阻擋原則透過 UsageCounterStoreInterface::tryConsume(一種原子式 compare-and-set)預留用量。並行請求無法合力將用量推過上限;競態中落敗的一方即使通過預先檢查,仍會收到 QuotaExceededException
  • 在預算警示原則下,閘門盡力記錄消耗且絕不拒絕;超過軟性上限的預留仍會將該列記錄在上限值。
  • QuotaExceededException 感知部署模式:SaaS 拒絕對應到 HTTP 402、規格碼 SPEC-BILLING-003,並標記為可重試;on-prem 拒絕對應到 HTTP 403、SPEC-LIC-001
  • 此程式庫本身不發出 HTTP 回應。所宣告的狀態碼是給邊緣層的合約,邊緣層負責將拋出的拒絕對應為回應,且不得呼叫計費處理常式。
  • 非正的包含配額。 usagePercentage()evaluate()OverageCalculator::calculate() 都會產生 0.0 的使用比率,而非除以零。此時門檻警示不會僅因比率而觸發。
  • 預算警示加上大幅超額。 管理員與閘門都會回傳允許結果。不要將「沒有例外」視為「在配額內」的證明;請查閱 OverageResult 或警示串流。
  • 剛好達到上限。 checkQuota()currentCu == includedCuQuota 時通過。BudgetExceeded 需嚴格超額。UsageCounter::wouldExceed() 同樣為嚴格判定。
  • MonthlyCapReached 此 enum 宣告了這第四種警示類型,但 BillingAlertService::evaluate() 從不發出它;其候選清單只涵蓋三種門檻警示。它保留給此模組外的上限追蹤發出者使用。
  • 重複的層級定義。 PlanRegistry 依層級值索引;某層級的最後一筆定義會靜默取代先前的定義。請以去重後的清單建構註冊表。
  • 零額度相對於無限制。 QuotaPolicy 的上限為 0.0 代表該週期內的每次消耗都是超額。只有負的 UNLIMITED 哨兵值會停用計量;isUnlimited() 絕不阻擋。
  • 非正的預留數量。 enforce() 會以 fail-closed 方式拒絕非正的 $amount,拋出 UsageStoreUnavailableException(503)。這是呼叫端的缺陷,而非儲存中斷。
  • 儲存中斷。 任何讀取或預留失敗都會以 UsageStoreUnavailableException 呈現並拒絕。當計量器停擺時,閘門絕不允許未計量的作業。
  • 記憶體內實作。 InMemoryAlertStateRepositoryInMemoryUsageCounterStore 僅在單一 PHP 行程內正確。多複本部署必須提供以具備真正原子性的資料儲存為後端的實作;先讀後寫的儲存是一種缺陷,會在高負載下允許超出配額。
  • FIPS 模式。 Billing 本身不進行任何加密運算,也沒有任何 FIPS 專屬行為。它所消耗的租戶身分必須源自一個已認證的情境,而該情境的 FIPS 態勢隨 SaaS 介面一併記載。
主張標準條款
402 狀態碼保留供未來使用;它本身不帶任何規範性的請求語意。RFC 9110§15.5.3
429 表示用戶端在一段給定時間內送出了過多請求(「速率限制」)。RFC 6585§4
Retry-After 指出使用者代理在發出後續請求前應等待多久。RFC 9110§10.2.3

所有條款皆為改寫;NextPDF 不重現規範性文字。NextPDF 未對此介面做出任何 HTTP 協定一致性或認證主張。 OveragePolicy::httpStatusCode() 所宣告的 402 / 429 / 200 對應,以及閘門的 401 / 402 / 503 拒絕碼,都是與上述條款對齊的產品慣例:RFC 9110 保留 402,因此此處將其用於付款拒絕屬於常見的業界慣例,而非 IETF 定義的語意。soft-stop 的重試期限(resetsAt)是邊緣層應作為 Retry-After 指引呈現的值。發出實際的 HTTP 回應、標頭與快取行為是託管應用程式的責任。

  • PlanRegistry::defaultRegistry()、單一 OveragePolicy 與一個 QuotaManager 組成模型;若需警示,再加上搭配耐久 AlertStateRepositoryInterface 實作的 BillingAlertService
  • QuotaEnforcementGuard 掛載於請求管線中租戶認證之後、計費處理常式之前。在邊緣捕捉 QuotaEnforcementException 與計費的 QuotaExceededException,並將 httpStatusCode() 對應到回應。
  • 此模組中的方案定義是計費的單一事實來源;請勿在部署的其他處維護任何平行的計費定義。
  • 記憶體內實作讓整個介面無需 I/O 即可進行單元測試。建議的邊界測試:使用量剛好達到配額、超過一個單位、比率門檻在 0.8 與 1.0、方案不符防護、CAS 競態(兩筆預留爭奪最後一個單位的餘裕),以及儲存中斷拒絕。
  • 核心模型類別標記 @since 2.2.0;substrate 標記 @since 2.3.0。目前的套件版本線為 3.1.0。
  • 運維人員負責警示狀態 repository 與用量儲存的實作、其跨複本的耐久性,以及透過 clearAlerts() 進行的任何週期中途警示重新武裝。

本頁僅記載外部可觀察的行為與受支援的公開 API 介面。內部命名空間路徑、輔助類別、機制表、runbook 檔名與工單前綴皆不在範圍內。