Lewati ke konten
getnextpdf.com

Enterprise edisi

Billing — Referensi Mendalam

Halaman ini adalah referensi mendalam untuk permukaan billing NextPDF Enterprise. Permukaan ini memiliki dua lapisan. Model billing di NextPDF\Enterprise\Billing mendefinisikan tier plan, kuota, kebijakan overage, dan alert penggunaan yang terdeduplikasi. Substrat penegakan di NextPDF\Enterprise\Billing\Substrate menempatkan model tersebut pada jalur permintaan langsung, secara fail-closed dan aman terhadap konkurensi. Titik masuknya adalah PlanRegistry, QuotaManager, OverageCalculator, BillingAlertService, dan QuotaEnforcementGuard. Untuk panduan tingkat alur kerja, lihat halaman kapabilitas Billing.

Kapabilitas ini hadir dalam NextPDF Enterprise (nextpdf/enterprise) dan aktif dengan envelope lisensi tier Enterprise. Deployment tanpa entitlement tersebut tidak memuat kelas-kelas kapabilitas ini. Bandingkan edisi dan dapatkan lisensi.

Billing adalah kapabilitas dasar Enterprise tanpa flag per-fitur terpisah; ia tersedia begitu paket Enterprise terpasang berdampingan dengan paket Core. NextPDF Core (Apache-2.0) dan NextPDF Pro tidak memiliki model plan, kuota, atau overage; permukaan ini tidak memiliki padanan di tier yang lebih rendah. Inklusi plan, kuota, dan ketentuan komersial diatur oleh perjanjian lisensi, bukan oleh penegakan runtime; referensi ini bukan opini hukum atau kontraktual.

Semua simbol berada di bawah NextPDF\Enterprise\Billing. Baris yang ditandai substrate berada di bawah NextPDF\Enterprise\Billing\Substrate. TenantContext adalah tipe tenant terautentikasi dari NextPDF\Enterprise\SaaS.

SimbolParameterPerilaku defaultMengembalikanMelempar atau gagal denganCatatan
SaaSPlan (enum)Tier plan berbasis string: standard, advanced, high_controlTidak melemparlabel() mengembalikan nama tampilan
PlanDefinition::__constructSaaSPlan $plan, float $includedCuQuota, list<CapabilityCode> $capabilities, non-empty-string $priceTier, bool $intelligencePackIncluded, bool $privacyPackIncludedObjek nilai plan yang immutable; menyimpan input apa adanyaInstance baruTidak melemparfinal readonly; properti publik yang dipromosikan
PlanDefinition::includesCapabilityCapabilityCode $capabilityPengecekan keanggotaan berbasis identitas ketatboolTidak melempar
PlanRegistry::__constructlist<PlanDefinition> $definitionsMengindeks definisi berdasarkan tier; definisi terakhir per tier yang menangRegistry baruTidak melemparUntuk pengujian dan set plan white-label
PlanRegistry::getSaaSPlan $planPencarian plan kanonikPlanDefinitionInvalidArgumentException ketika plan tidak terdaftar
PlanRegistry::hasSaaSPlan $planPemeriksaan registrasiboolTidak melempar
PlanRegistry::defaultRegistry (statis)Default produksi: Standard 1.000 CU; Advanced 5.000 CU plus Intelligence Pack; High Control 20.000 CU plus Intelligence dan Privacy PackPlanRegistryTidak melemparGunakan kecuali ketentuan kontraktual memerlukan definisi kustom
OveragePolicy (enum)hard_stop, soft_stop, budget_alertTidak melemparhttpStatusCode() memetakan 402 / 429 / 200; isBlocking() bernilai true hanya untuk hard dan soft stop
QuotaManager::__constructPlanRegistry $planRegistry, OveragePolicy $overagePolicyMengikat registry ke satu kebijakanInstance baruTidak melempar
QuotaManager::checkQuotaTenantContext $tenant, SaaSPlan $plan, float $currentCuKembali secara diam-diam pada atau di bawah kuota, atau di bawah kebijakan non-blockingvoidQuotaExceededException pada overage ketat di bawah kebijakan blocking; InvalidArgumentException dari registry pada plan yang tidak terdaftarresetsAt = hari pertama bulan berikutnya, tengah malam UTC
QuotaManager::remainingQuotaSaaSPlan $plan, float $currentCuPembacaan murni; tidak pernah memblokirfloatInvalidArgumentException dari registryNegatif saat overage
QuotaManager::usagePercentageSaaSPlan $plan, float $currentCuPembacaan murni; tidak pernah memblokirfloatInvalidArgumentException dari registry0.0 ketika kuota yang disertakan non-positif; di atas 1.0 saat overage
OverageCalculator::calculatePlanDefinition $plan, float $currentCuMenghitung snapshot overage yang immutableOverageResultTidak melemparfinal readonly, stateless
OverageResultincludedCu, usedCu, overageCu, usageRatio, isOverageHasil perhitungan yang immutableTidak melemparoverageCu = max(0, used - included); isOverage memerlukan overage ketat
BillingAlertType (enum)quota_warning_80, quota_warning_100, budget_exceeded, monthly_cap_reachedTidak melemparthreshold() 0.8 / 1.0 / 1.0 / 1.0; severity() warning / critical / critical / critical
BillingAlertService::__constructAlertStateRepositoryInterface $alertStateMengikat penyimpanan deduplikasiInstance baruTidak melempar
BillingAlertService::evaluateTenantContext $tenant, SaaSPlan $plan, PlanDefinition $planDef, float $currentCuMemicu alert yang belum terpicu dalam urutan ambang menaik dan mencatatnyalist<BillingAlertType>InvalidArgumentException pada ketidakcocokan plan/definisiKunci dedup: tenant, tipe, periode YYYY-MM UTC
BillingAlertService::clearAlertsTenantContext $tenantMembersihkan status-terpicu tenant untuk periode UTC saat inivoidKegagalan yang didefinisikan repository merambatMengaktifkan kembali alert dalam periode yang sama
AlertStateRepositoryInterfacehasAlertFired(), markAlertFired(), clearForPeriod()Kontrak persistensi deduplikasi-alert yang durablePer metodeDidefinisikan implementasiOperator memiliki durabilitas lintas replika
InMemoryAlertStateRepositoryStatus-terpicu berbasis arrayPer antarmukaTidak melemparHanya untuk siklus hidup single-request dan pengujian
QuotaExceededExceptioncurrentCu, limitCu, resetsAt, tenantId, isSaaS yang readonlyPenolakan kuota yang sadar mode deploymentAdalah objek yang dilemparhttpStatusCode() 402 SaaS / 403 on-prem; specCode() SPEC-BILLING-003 / SPEC-LIC-001; toErrorEnvelope() menghasilkan body error terstruktur
DeploymentMode (enum)saas, self_hosted_oss, local_developmentTidak melemparSubstrate. enforcesQuota() bernilai true hanya untuk Saas; opt-out selalu eksplisit
QuotaEnforcementGuard::__constructDeploymentMode, PlanResolverInterface, QuotaManager, UsageCounterStoreInterfaceMenyusun gerbang kuota langsungInstance baruTidak melemparSubstrate. final readonly
QuotaEnforcementGuard::enforce?TenantContext $tenant, non-empty-string $featureKey, float $amount = 1.0Gerbang kuota fail-closed dengan reservasi atomikQuotaDecision (hanya hasil yang diizinkan)Lihat taksonomi penolakan di bawahSubstrate. Pasang setelah autentikasi tenant, sebelum handler yang ditagih
PlanResolverInterface::resolveTenantContext $tenantMenyelesaikan tenant ke plan dan kebijakan per-fiturnyaResolvedPlanNoPlanForTenantExceptionSubstrate. Fallback plan-default untuk tenant tak dikenal adalah cacat
RegistryPlanResolverarray<non-empty-string, ResolvedPlan> $plansByTenantResolver berbasis mapResolvedPlanNoPlanForTenantException untuk tenant yang tidak dipetakanSubstrate. Fail-closed secara konstruksi
ResolvedPlan::policyFornon-empty-string $featureKeyPencarian kebijakan pada plan yang telah diselesaikan?QuotaPolicyTidak melemparSubstrate. null berarti fitur tak dikenal; gerbang menolaknya
QuotaPolicynon-empty-string $featureKey, float $limit, OveragePolicy $overagePolicyBatas per-fitur dan kebijakan pelanggaranTidak melemparSubstrate. UNLIMITED = -1.0; batas 0.0 adalah nol izin, bukan tak terbatas; isUnlimited(), isBlocking()
QuotaDecisionStatik bypassed(), unlimited(), consumed()Objek nilai hasil-yang-diizinkanQuotaDecisionTidak melemparSubstrate. isAllowed() selalu true; setiap penolakan melempar sebagai gantinya
UsageCounterSnapshot baris: tenant, fitur, batas periode, used, limit, updatedAtBaris penggunaan yang immutableTidak melemparSubstrate. remaining() dapat negatif; wouldExceed() bersifat ketat
UsageCounterStoreInterface::getTenant, fitur, batas periode, float $limitMembaca baris penggunaan, membuatnya dengan used = 0 ketika tidak adaUsageCounterUsageStoreUnavailableExceptionSubstrate. Tidak pernah mengembalikan nilai falsy pada kegagalan backend
UsageCounterStoreInterface::tryConsumeTenant, fitur, batas periode, float $amount, float $limitReservasi compare-and-set atomik dalam batas?UsageCounter (null ketika reservasi akan melanggar batas)UsageStoreUnavailableExceptionSubstrate. Harus berupa satu operasi atomik terhadap penyimpanan pendukung
InMemoryUsageCounterStoreImplementasi referensi in-process dari kontrak penyimpananPer antarmukaPer antarmukaSubstrate. Hanya satu proses; mendokumentasikan invarian atomisitas
QuotaEnforcementException (abstrak)Tipe dasar dari setiap penolakan substrateAdalah keluarga objek yang dilemparSubstrate. Setiap subtipe mendeklarasikan 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;

Taksonomi penolakan QuotaEnforcementGuard::enforce

ExceptionStatus HTTPDiangkat ketika
MissingTenantContextException401Mode SaaS tanpa konteks tenant yang terautentikasi
NoPlanForTenantException402Resolver tidak menemukan plan yang ditetapkan untuk tenant
UnknownFeatureException402Plan yang diselesaikan tidak mendefinisikan kebijakan untuk kunci fitur
UsageStoreUnavailableException503Penyimpanan penggunaan tidak dapat dibaca atau diperbarui secara atomik; juga diangkat untuk $amount non-positif
QuotaExceededException402 (SaaS) / 403 (on-prem)Kuota kebijakan blocking terlampaui, atau reservasi konkuren menghabiskan sisa headroom terakhir
  • Registry default menghadirkan tiga tier (Standard / Advanced / High Control) dengan kuota CU dan set kapabilitas yang meningkat. Permintaan plan yang tidak terdaftar gagal dengan InvalidArgumentException eksplisit.
  • QuotaManager::checkQuota() mengangkat exception hanya ketika kedua kondisi terpenuhi: kebijakan bersifat blocking, dan penggunaan saat ini secara ketat di atas kuota yang disertakan. Kebijakan budget-alert tidak pernah mengangkat exception; overage disinyalkan melalui alert.
  • remainingQuota() dan usagePercentage() adalah pembacaan murni dan tidak pernah memblokir. Sisa kuota menjadi negatif saat overage; persentase penggunaan melampaui 1.0 saat overage.
  • Alert dievaluasi dalam urutan ambang menaik: warning 80%, warning 100% (critical), lalu budget-exceeded (critical). Budget-exceeded digerbangi oleh overage ketat; penggunaan tepat 100% memicu warning 100%, bukan budget-exceeded.
  • Setiap tipe alert terpicu paling banyak sekali per tenant per periode billing. Status-terpicu dicatat melalui AlertStateRepositoryInterface, sehingga deduplikasi sedurable implementasi yang dipilih.
  • Kunci deduplikasi menyematkan periode YYYY-MM UTC. Bulan kalender baru karenanya mengaktifkan kembali setiap tipe alert secara otomatis; tidak diperlukan panggilan clear untuk pengaktifan-ulang rollover. clearAlerts() membersihkan periode saat ini, yang mengaktifkan kembali alert di tengah periode, misalnya setelah upgrade plan.
  • Pengaman ketidakcocokan-plan dalam evaluate() menolak panggilan di mana plan yang diberikan dan definisi plan tidak sesuai, melindungi dari definisi tier yang berbeda dari plan tenant.
  • Semua aritmetika periode berjangkar ke UTC. Instan reset kuota-terlampaui adalah hari pertama bulan kalender berikutnya pada tengah malam UTC; respons soft-stop sebaiknya mengiklankannya sebagai horizon retry.
  • QuotaEnforcementGuard bersifat fail-closed dalam mode SaaS. Tenant hilang, plan hilang, fitur tak dikenal, penyimpanan mati, dan pelanggaran kuota semuanya menolak; tidak ada yang lolos ke izin implisit. Deployment non-SaaS hanya opt-out dengan menyusun gerbang menggunakan DeploymentMode non-SaaS.
  • Kebijakan blocking mereservasi penggunaan melalui UsageCounterStoreInterface::tryConsume, sebuah compare-and-set atomik. Permintaan konkuren tidak dapat secara kolektif mendorong penggunaan melampaui batas; pihak yang kalah dalam balapan menerima QuotaExceededException meski pra-pengecekan lolos.
  • Di bawah kebijakan budget-alert, gerbang mencatat konsumsi secara best-effort dan tidak pernah menolak; reservasi yang melewati batas lunak tetap mencatat baris pada batasnya.
  • QuotaExceededException sadar mode deployment: penolakan SaaS memetakan ke HTTP 402 dengan kode spesifikasi SPEC-BILLING-003 dan ditandai dapat dicoba ulang; penolakan on-prem memetakan ke HTTP 403 dengan SPEC-LIC-001.
  • Library tidak memancarkan respons HTTP sendiri. Kode status yang dideklarasikan adalah kontrak untuk lapisan edge, yang memetakan penolakan yang dilempar ke respons dan tidak boleh memanggil handler yang ditagih.
  • Kuota disertakan non-positif. usagePercentage(), evaluate(), dan OverageCalculator::calculate() semuanya menghasilkan rasio penggunaan 0.0 alih-alih membagi dengan nol. Alert ambang kemudian tidak pernah terpicu dari rasio semata.
  • Budget-alert plus overage besar. Manager dan gerbang keduanya mengembalikan hasil yang diizinkan. Jangan perlakukan ketiadaan exception sebagai bukti berada dalam kuota; konsultasikan OverageResult atau aliran alert.
  • Tepat pada batas. checkQuota() pada currentCu == includedCuQuota lolos. BudgetExceeded memerlukan overage ketat. UsageCounter::wouldExceed() juga bersifat ketat.
  • MonthlyCapReached. Enum mendeklarasikan tipe alert keempat ini, tetapi BillingAlertService::evaluate() tidak pernah memancarkannya; daftar kandidatnya hanya mencakup tiga alert ambang. Ia dicadangkan untuk emitter pelacakan-cap di luar modul ini.
  • Definisi tier duplikat. PlanRegistry mengindeks berdasarkan nilai tier; definisi terakhir untuk sebuah tier secara diam-diam menggantikan yang sebelumnya. Bangun registry dari daftar yang terdeduplikasi.
  • Nol-izin versus tak terbatas. Batas QuotaPolicy sebesar 0.0 berarti setiap konsumsi dalam periode adalah overage. Hanya sentinel UNLIMITED yang negatif yang menonaktifkan metering; isUnlimited() tidak pernah memblokir.
  • Jumlah reservasi non-positif. enforce() menolak $amount non-positif secara fail-closed dengan UsageStoreUnavailableException (503). Ini adalah cacat pemanggil, bukan penyimpanan mati.
  • Penyimpanan mati. Setiap kegagalan pembacaan atau reservasi muncul sebagai UsageStoreUnavailableException dan menolak. Gerbang tidak pernah mengizinkan pekerjaan yang tak-termeter saat meter mati.
  • Implementasi in-memory. InMemoryAlertStateRepository dan InMemoryUsageCounterStore hanya benar dalam satu proses PHP. Deployment multi-replika harus menyediakan implementasi yang didukung datastore dengan atomisitas nyata; penyimpanan read-then-write adalah cacat yang memperbolehkan over-quota di bawah beban.
  • Mode FIPS. Billing tidak melakukan operasi kriptografis sendiri dan tidak memiliki perilaku khusus FIPS. Identitas tenant yang dikonsumsinya harus berasal dari konteks terautentikasi yang postur FIPS-nya didokumentasikan bersama permukaan SaaS.
KlaimStandarKlausul
Kode status 402 dicadangkan untuk penggunaan mendatang; ia tidak membawa semantik permintaan normatif tersendiri.RFC 9110§15.5.3
429 menandakan klien telah mengirim terlalu banyak permintaan dalam rentang waktu tertentu (“rate limiting”).RFC 6585§4
Retry-After menunjukkan berapa lama user agent sebaiknya menunggu sebelum membuat permintaan lanjutan.RFC 9110§10.2.3

Semua klausul diparafrasekan; NextPDF tidak mereproduksi teks normatif. NextPDF tidak membuat klaim konformitas atau sertifikasi protokol-HTTP untuk permukaan ini. Pemetaan 402 / 429 / 200 yang dideklarasikan oleh OveragePolicy::httpStatusCode() dan kode penolakan 401 / 402 / 503 dari gerbang adalah konvensi produk yang selaras dengan klausul di atas: RFC 9110 mencadangkan 402, sehingga penggunaannya sebagai penolakan-pembayaran di sini adalah konvensi industri yang umum, bukan semantik yang didefinisikan IETF. Horizon retry soft-stop (resetsAt) adalah nilai yang sebaiknya ditampilkan lapisan edge sebagai panduan Retry-After. Memancarkan respons HTTP aktual, header, dan perilaku caching adalah tanggung jawab aplikasi host.

  • Susun modelnya dari PlanRegistry::defaultRegistry(), satu OveragePolicy, dan sebuah QuotaManager; tambahkan BillingAlertService dengan implementasi AlertStateRepositoryInterface yang durable untuk pemberian alert.
  • Pasang QuotaEnforcementGuard dalam pipeline permintaan setelah autentikasi tenant dan sebelum handler yang ditagih. Tangkap QuotaEnforcementException dan QuotaExceededException billing di edge dan petakan httpStatusCode() ke respons.
  • Definisi plan dalam modul ini adalah satu-satunya sumber kebenaran untuk billing; jangan memelihara definisi billing paralel di tempat lain dalam deployment Anda.
  • Implementasi in-memory membuat seluruh permukaan dapat diuji-unit tanpa I/O. Pengujian batas yang direkomendasikan: penggunaan tepat pada kuota, satu unit di atasnya, ambang rasio pada 0.8 dan 1.0, pengaman ketidakcocokan-plan, balapan CAS (dua reservasi terhadap unit headroom terakhir), dan penolakan penyimpanan-mati.
  • Kelas model inti membawa @since 2.2.0; substrate membawa @since 2.3.0. Lini paket saat ini adalah 3.1.0.
  • Operator memiliki implementasi alert-state repository dan usage-store, durabilitasnya lintas replika, dan setiap pengaktifan-ulang alert di tengah periode melalui clearAlerts().

Halaman ini hanya mendokumentasikan perilaku yang dapat diamati secara eksternal dan permukaan API publik yang didukung. Path namespace internal, kelas helper, tabel mekanisme, nama file runbook, dan prefiks tiket berada di luar cakupan.