تخطَّ إلى المحتوى
getnextpdf.com

Enterprise الإصدار

الفوترة — مرجع متعمّق

هذه الصفحة هي المرجع المتعمّق لسطح الفوترة في ⁨NextPDF Enterprise⁩. يتألف هذا السطح من طبقتين. يُعرّف نموذج الفوترة في NextPDF\Enterprise\Billing طبقاتِ الخطط والحصص وسياسات التجاوز وتنبيهات الاستخدام المُزال منها التكرار. أمّا ركيزة الفرض في NextPDF\Enterprise\Billing\Substrate فتضع هذا النموذج على مسار الطلب الحيّ، مُغلقةً عند الفشل وآمنةً في التزامن. نقاط الدخول هي PlanRegistry وQuotaManager وOverageCalculator وBillingAlertService وQuotaEnforcementGuard. للاطّلاع على الدليل على مستوى سير العمل، راجع صفحة قدرة الفوترة.

تُشحن هذه القدرة ضمن ⁨NextPDF Enterprise⁩ (nextpdf/enterprise) وتُفعَّل بمظروف ترخيص من طبقة ⁨Enterprise⁩. لا يُحمِّل أيّ نشرٍ يفتقر إلى ذلك الاستحقاق أصنافَ هذه القدرة. قارن بين الإصدارات واحصل على ترخيص.

الفوترة قدرةٌ أساسية في ⁨Enterprise⁩ بلا رايةٍ مستقلّة لكل ميزة؛ فهي متاحة بمجرّد تثبيت حزمة ⁨Enterprise⁩ إلى جانب حزمة ⁨Core⁩. لا يملك ⁨NextPDF Core⁩ (⁨Apache-2.0⁩) ولا ⁨NextPDF Pro⁩ أيّ نموذجٍ للخطط أو الحصص أو التجاوز؛ ولا يوجد لهذا السطح مكافئٌ في طبقةٍ أدنى. تخضع مشمولات الخطط والحصص والشروط التجارية لاتفاقية الترخيص، لا لفرضٍ في زمن التشغيل؛ وهذا المرجع ليس رأياً قانونياً أو تعاقدياً.

تقع جميع الرموز ضمن NextPDF\Enterprise\Billing. أمّا الصفوف المُعلَّمة بـ substrate فتقع ضمن NextPDF\Enterprise\Billing\Substrate. وTenantContext هو نوع المستأجر المُصادَق عليه من NextPDF\Enterprise\SaaS.

الرمزالمعاملاتالسلوك الافتراضيالقيمة المُعادةيرمي أو يفشل بـملاحظات
SaaSPlan (⁨enum⁩)طبقات خطط مسنودة بسلاسل نصية: standard وadvanced وhigh_controlلا يرميتُعيد label() الاسم المعروض
PlanDefinition::__constructSaaSPlan $plan، float $includedCuQuota، list<CapabilityCode> $capabilities، non-empty-string $priceTier، bool $intelligencePackIncluded، bool $privacyPackIncludedكائن قيمة خطة غير قابل للتغيير؛ يخزّن المدخلات كما هينسخة جديدةلا يرميfinal readonly؛ خصائص عامة مُرقّاة
PlanDefinition::includesCapabilityCapabilityCode $capabilityفحص عضوية بهوية صارمةboolلا يرمي
PlanRegistry::__constructlist<PlanDefinition> $definitionsيفهرس التعريفات حسب الطبقة؛ ويفوز آخر تعريف لكل طبقةسِجِلّ جديدلا يرميلأغراض الاختبار ومجموعات الخطط ذات العلامة البيضاء
PlanRegistry::getSaaSPlan $planبحث قانوني عن الخطةPlanDefinitionInvalidArgumentException عندما تكون الخطة غير مُسجّلة
PlanRegistry::hasSaaSPlan $planفحص التسجيلboolلا يرمي
PlanRegistry::defaultRegistry (⁨static⁩)الإعدادات الافتراضية للإنتاج: ⁨Standard⁩ بـ 1,000 CU؛ و⁨Advanced⁩ بـ 5,000 CU إضافةً إلى ⁨Intelligence Pack⁩؛ و⁨High Control⁩ بـ 20,000 CU إضافةً إلى ⁨Intelligence Pack⁩ و⁨Privacy Pack⁩PlanRegistryلا يرمياستخدمها ما لم تستلزم الشروط التعاقدية تعريفات مخصّصة
OveragePolicy (⁨enum⁩)hard_stop وsoft_stop وbudget_alertلا يرميتُطابق httpStatusCode() القيم ⁨402 / 429 / 200⁩؛ وisBlocking() صحيحة للإيقاف الصارم واللين فقط
QuotaManager::__constructPlanRegistry $planRegistry، OveragePolicy $overagePolicyيربط السِّجِلّ بسياسة واحدةنسخة جديدةلا يرمي
QuotaManager::checkQuotaTenantContext $tenant، SaaSPlan $plan، float $currentCuيعود بصمت عند الحصة أو دونها، أو في ظل سياسة غير حاجبةvoidQuotaExceededException عند التجاوز الصارم في ظل سياسة حاجبة؛ وInvalidArgumentException من السِّجِلّ عند خطة غير مُسجّلةresetsAt = أول يوم من الشهر التالي، منتصف الليل بتوقيت ⁨UTC⁩
QuotaManager::remainingQuotaSaaSPlan $plan، float $currentCuقراءة صِرفة؛ لا تحجب أبداًfloatInvalidArgumentException من السِّجِلّسالبة عند التجاوز
QuotaManager::usagePercentageSaaSPlan $plan، float $currentCuقراءة صِرفة؛ لا تحجب أبداًfloatInvalidArgumentException من السِّجِلّ0.0 عندما تكون الحصة المشمولة غير موجبة؛ وأعلى من 1.0 عند التجاوز
OverageCalculator::calculatePlanDefinition $plan، float $currentCuيحسب لقطة تجاوز غير قابلة للتغييرOverageResultلا يرميfinal readonly، بلا حالة
OverageResultincludedCu، usedCu، overageCu، usageRatio، isOverageنتيجة حساب غير قابلة للتغييرلا يرميoverageCu = max(0, used - included)؛ وisOverage يتطلّب تجاوزاً صارماً
BillingAlertType (⁨enum⁩)quota_warning_80 وquota_warning_100 وbudget_exceeded وmonthly_cap_reachedلا يرميthreshold() بالقيم ⁨0.8 / 1.0 / 1.0 / 1.0⁩؛ وseverity() بالقيم ⁨warning / critical / critical / critical⁩
BillingAlertService::__constructAlertStateRepositoryInterface $alertStateيربط مخزن إزالة التكرارنسخة جديدةلا يرمي
BillingAlertService::evaluateTenantContext $tenant، SaaSPlan $plan، PlanDefinition $planDef، float $currentCuيُطلق التنبيهات التي لم تُطلَق بعد بترتيب تصاعدي للعتبة ويسجّلهاlist<BillingAlertType>InvalidArgumentException عند عدم تطابق الخطة/التعريفمفتاح إزالة التكرار: المستأجر، والنوع، وفترة YYYY-MM بتوقيت ⁨UTC⁩
BillingAlertService::clearAlertsTenantContext $tenantيمسح حالة الإطلاق للمستأجر للفترة الحالية بتوقيت ⁨UTC⁩voidتنتشر الإخفاقات المُعرَّفة في المستودعيعيد تسليح التنبيهات ضمن الفترة نفسها
AlertStateRepositoryInterfacehasAlertFired()، markAlertFired()، clearForPeriod()عقد استمرارية دائم لإزالة تكرار التنبيهاتحسب كل دالةمُعرَّف حسب التنفيذالمُشغِّل يملك الديمومة عبر النسخ المتماثلة
InMemoryAlertStateRepositoryحالة إطلاق مسنودة بمصفوفةحسب الواجهةلا يرميلدورات حياة الطلب الواحد والاختبارات فقط
QuotaExceededExceptioncurrentCu وlimitCu وresetsAt وtenantId وisSaaS للقراءة فقطرفض حصة واعٍ بوضع النشرهو الكائن القابل للرميhttpStatusCode() بالقيمة 402 لـ ⁨SaaS⁩ / 403 للنشر المحلي؛ وspecCode() بالقيمة SPEC-BILLING-003 / SPEC-LIC-001؛ وتُنتج toErrorEnvelope() جسم خطأ مُهيكلاً
DeploymentMode (⁨enum⁩)saas وself_hosted_oss وlocal_developmentلا يرميSubstrate. enforcesQuota() صحيحة لـ Saas فقط؛ والانسحاب صريح دائماً
QuotaEnforcementGuard::__constructDeploymentMode، PlanResolverInterface، QuotaManager، UsageCounterStoreInterfaceيُجمّع بوابة الحصة الحيّةنسخة جديدةلا يرميSubstrate. final readonly
QuotaEnforcementGuard::enforce?TenantContext $tenant، non-empty-string $featureKey، float $amount = 1.0بوابة حصة مُغلقة عند الفشل مع حجز ذرّيQuotaDecision (النتائج المسموح بها فقط)راجع تصنيف الرفض أدناهSubstrate. رَكِّبها بعد مصادقة المستأجر، وقبل المعالج القابل للفوترة
PlanResolverInterface::resolveTenantContext $tenantيحلّ المستأجر إلى خطته وسياساته لكل ميزةResolvedPlanNoPlanForTenantExceptionSubstrate. اعتماد خطة افتراضية احتياطية للمستأجرين غير المعروفين عيبٌ برمجي
RegistryPlanResolverarray<non-empty-string, ResolvedPlan> $plansByTenantمُحلّل مسنود بخريطةResolvedPlanNoPlanForTenantException للمستأجرين غير المُخطَّطينSubstrate. مُغلق عند الفشل بحكم البناء
ResolvedPlan::policyFornon-empty-string $featureKeyبحث عن السياسة في الخطة المُحلَّلة?QuotaPolicyلا يرميSubstrate. تعني null ميزة غير معروفة؛ وترفضها البوابة
QuotaPolicynon-empty-string $featureKey، float $limit، OveragePolicy $overagePolicyحدّ لكل ميزة وسياسة عند الخرقلا يرميSubstrate. UNLIMITED = -1.0؛ والحدّ 0.0 يعني سماحاً صِفرياً، لا لامحدوداً؛ isUnlimited()، isBlocking()
QuotaDecisionدوال ساكنة bypassed() وunlimited() وconsumed()كائن قيمة لنتيجة مسموح بهاQuotaDecisionلا يرميSubstrate. isAllowed() صحيحة دائماً؛ وكل رفضٍ يرمي بدلاً من ذلك
UsageCounterلقطة صف: المستأجر، والميزة، وحدود الفترة، used، limit، updatedAtصف استخدام غير قابل للتغييرلا يرميSubstrate. قد تكون remaining() سالبة؛ وwouldExceed() صارمة
UsageCounterStoreInterface::getالمستأجر، والميزة، وحدود الفترة، float $limitيقرأ صف الاستخدام، مُنشئاً إياه بـ used = 0 عند غيابهUsageCounterUsageStoreUnavailableExceptionSubstrate. لا يُعيد أبداً قيمة زائفة عند فشل الواجهة الخلفية
UsageCounterStoreInterface::tryConsumeالمستأجر، والميزة، وحدود الفترة، float $amount، float $limitحجز ذرّي بالمقارنة والتعيين ضمن الحدّ?UsageCounter (null عندما يخرق الحجز الحدّ)UsageStoreUnavailableExceptionSubstrate. يجب أن يكون عملية ذرّية واحدة ضدّ المخزن الداعم
InMemoryUsageCounterStoreتنفيذ مرجعي داخل العملية لعقد المخزنحسب الواجهةحسب الواجهةSubstrate. عملية واحدة فقط؛ يوثّق ثابت الذرّية
QuotaEnforcementException (⁨abstract⁩)النوع الأساس لكل رفضٍ في الركيزةهو عائلة الكائنات القابلة للرمي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يُرفع عند
MissingTenantContextException401وضع ⁨SaaS⁩ بلا سياق مستأجر مُصادَق عليه
NoPlanForTenantException402لا يجد المُحلّل خطة مُسنَدة إلى المستأجر
UnknownFeatureException402لا تُعرّف الخطة المُحلَّلة أيّ سياسة لمفتاح الميزة
UsageStoreUnavailableException503تعذُّر قراءة مخزن الاستخدام أو تحديثه ذرّياً؛ ويُرفع أيضاً عند $amount غير موجب
QuotaExceededException402 (⁨SaaS⁩) / 403 (النشر المحلي)تجاوز حصة سياسة حاجبة، أو استهلاك حجزٍ متزامن لآخر متّسع
  • يشحن السِّجِلّ الافتراضي ثلاث طبقات (⁨Standard⁩ / ⁨Advanced⁩ / ⁨High Control⁩) بحصص CU ومجموعات قدرات متزايدة. ويفشل طلب خطة غير مُسجّلة بـ InvalidArgumentException صريح.
  • لا ترفع QuotaManager::checkQuota() إلا عند تحقّق الشرطين معاً: السياسة حاجبة، والاستخدام الحالي أعلى بصرامة من الحصة المشمولة. ولا ترفع سياسة تنبيه الميزانية أبداً؛ ويُبلَّغ عن التجاوز عبر التنبيهات.
  • remainingQuota() وusagePercentage() قراءتان صِرفتان ولا تحجبان أبداً. وتصبح الحصة المتبقية سالبة عند التجاوز؛ وتتجاوز نسبة الاستخدام 1.0 عند التجاوز.
  • تُقيَّم التنبيهات بترتيب تصاعدي للعتبة: تحذير 80%، وتحذير 100% (حَرِج)، ثم تجاوز-الميزانية (حَرِج). ويُشترَط في تجاوز-الميزانية تجاوزٌ صارم؛ والاستخدام عند 100% بالضبط يُطلق تحذير 100%، لا تجاوز-الميزانية.
  • يُطلَق كل نوع تنبيه مرّة واحدة على الأكثر لكل مستأجر لكل فترة فوترة. وتُسجَّل حالة الإطلاق عبر AlertStateRepositoryInterface، ومن ثمّ فإن إزالة التكرار دائمة بقدر ديمومة التنفيذ المُختار.
  • يتضمّن مفتاح إزالة التكرار فترة YYYY-MM بتوقيت ⁨UTC⁩. لذا يعيد شهرٌ تقويمي جديد تسليح كل نوع تنبيه تلقائياً؛ ولا يلزم أيّ استدعاء مسحٍ لإعادة التسليح عند التدوير. وتمسح clearAlerts() الفترة الحالية، فتعيد تسليح التنبيهات في منتصف الفترة، مثلاً بعد ترقية خطة.
  • يرفض حارس عدم-تطابق-الخطة في evaluate() أيّ استدعاءٍ تختلف فيه الخطة المُقدَّمة عن تعريف الخطة، حمايةً من تعريفٍ من طبقةٍ مختلفة عن خطة المستأجر.
  • تُرسى جميع حسابات الفترة على توقيت ⁨UTC⁩. ولحظة إعادة تعيين تجاوز الحصة هي أول يوم من الشهر التقويمي التالي عند منتصف الليل بتوقيت ⁨UTC⁩؛ وينبغي لاستجابة الإيقاف اللين الإعلان عنها بوصفها أفق إعادة المحاولة.
  • QuotaEnforcementGuard مُغلق عند الفشل في وضع ⁨SaaS⁩. فالمستأجر المفقود، والخطة المفقودة، والميزة غير المعروفة، وانقطاع المخزن، وخرق الحصة تُرفض جميعها؛ ولا شيء يمرّ إلى سماحٍ ضمني. ولا تنسحب النشرات غير الـ ⁨SaaS⁩ إلا ببناء الحارس بوضع DeploymentMode غير الـ ⁨SaaS⁩.
  • تحجز السياسات الحاجبة الاستخدام عبر UsageCounterStoreInterface::tryConsume، وهي مقارنة-وتعيين ذرّية. ولا تستطيع الطلبات المتزامنة مجتمعةً دفع الاستخدام فوق الحدّ؛ ويتلقّى خاسر السباق QuotaExceededException حتى وإن نجح الفحص المسبق.
  • في ظل سياسة تنبيه الميزانية، يسجّل الحارس الاستهلاك بأفضل جهد ولا يرفض أبداً؛ ويظلّ الحجز الذي يتجاوز السقف اللين يسجّل الصف عند الحدّ.
  • QuotaExceededException واعٍ بوضع النشر: تُطابَق رفوض ⁨SaaS⁩ بـ ⁨HTTP⁩ 402 برمز المواصفة SPEC-BILLING-003 وتُعلَّم قابلة لإعادة المحاولة؛ وتُطابَق رفوض النشر المحلي بـ ⁨HTTP⁩ 403 بـ SPEC-LIC-001.
  • لا تُصدر المكتبة استجابات ⁨HTTP⁩ بنفسها. ورموز الحالة المُصرَّح بها هي العقد لطبقة الحافة، التي تُطابق رفضاً مرميّاً باستجابة ويجب ألّا تستدعي المعالج القابل للفوترة.

الحالات الحدّية وأنماط الفشل

قسم بعنوان «الحالات الحدّية وأنماط الفشل»
  • حصة مشمولة غير موجبة. تُنتج usagePercentage() وevaluate() وOverageCalculator::calculate() جميعها نسبة استخدام 0.0 بدلاً من القسمة على صفر. ومن ثمّ لا تُطلَق تنبيهات العتبة من النسبة وحدها أبداً.
  • تنبيه الميزانية مع تجاوزٍ كبير. يُعيد المدير والحارس كلاهما نتائج مسموحاً بها. لا تعامل غياب الاستثناء دليلاً على البقاء ضمن الحصة؛ راجع OverageResult أو تدفّق التنبيهات.
  • عند الحدّ تماماً. تنجح checkQuota() عند currentCu == includedCuQuota. ويتطلّب BudgetExceeded تجاوزاً صارماً. وUsageCounter::wouldExceed() صارمة أيضاً.
  • MonthlyCapReached. يُصرّح التعداد بنوع التنبيه الرابع هذا، لكن BillingAlertService::evaluate() لا تُصدره أبداً؛ فقائمة مرشّحيها تغطي التنبيهات الثلاثة للعتبة فقط. وهو محجوز لمُصدِرات تتبّع السقف خارج هذه الوحدة.
  • تعريفات طبقة مكرّرة. يفهرس PlanRegistry حسب قيمة الطبقة؛ ويستبدل آخر تعريف لطبقةٍ ما سابقيه بصمت. ابنِ السِّجِلّات من قائمة مُزال منها التكرار.
  • السماح الصِفري مقابل اللامحدود. حدّ QuotaPolicy بقيمة 0.0 يعني أن كل استهلاك في الفترة تجاوز. ولا يُعطّل القياسَ إلا الحارس السالب UNLIMITED؛ وisUnlimited() لا تحجب أبداً.
  • مقدار حجز غير موجب. ترفض enforce() أيّ $amount غير موجب بالإغلاق عند الفشل عبر UsageStoreUnavailableException (503). وهذا عيبٌ في المُستدعِي، لا انقطاعٌ في المخزن.
  • انقطاع المخزن. يظهر أيّ فشلٍ في القراءة أو الحجز بوصفه UsageStoreUnavailableException ويرفض. ولا يسمح الحارس أبداً بعملٍ غير مقيسٍ بينما القياس معطّل.
  • التنفيذات في الذاكرة. InMemoryAlertStateRepository وInMemoryUsageCounterStore صحيحان ضمن عملية ⁨PHP⁩ واحدة فقط. ويجب على النشرات متعدّدة النسخ توفير تنفيذاتٍ مسنودة بمخزن بيانات ذي ذرّية حقيقية؛ فالمخزن الذي يقرأ ثم يكتب عيبٌ يسمح بتجاوز الحصة تحت الحِمل.
  • وضع FIPS. لا تُجري الفوترة أيّ عمليات تعمية خاصة بها ولا سلوكاً خاصاً بـ FIPS. ويجب أن تنشأ هوية المستأجر التي تستهلكها من سياقٍ مُصادَق عليه يُوثَّق وضعه في FIPS مع سطح ⁨SaaS⁩.
الادعاءالمعيارالبند
رمز الحالة 402 محجوز للاستخدام المستقبلي؛ ولا يحمل دلالات طلب معيارية خاصة به.RFC 9110§15.5.3
يشير 429 إلى أن العميل أرسل طلبات أكثر من اللازم في مدة زمنية معيّنة («تحديد المعدّل»).RFC 6585§4
يشير Retry-After إلى المدة التي ينبغي لوكيل المستخدم انتظارها قبل تقديم طلب متابعة.RFC 9110§10.2.3

جميع البنود مُعاد صياغتها؛ ولا يستنسخ ⁨NextPDF⁩ النص المعياري. لا يقدّم ⁨NextPDF⁩ أيّ ادعاء مطابقة أو اعتماد لبروتوكول ⁨HTTP⁩ بشأن هذا السطح. إن مطابقة ⁨402 / 429 / 200⁩ التي تُصرّح بها OveragePolicy::httpStatusCode() ورموز الرفض ⁨401 / 402 / 503⁩ لدى الحارس هي عُرفٌ للمنتج متوائم مع البنود أعلاه: يحجز RFC 9110 الرمز 402، ومن ثمّ فإن استخدامه هنا لرفض الدفع هو العُرف الصناعي الشائع، لا دلالة مُعرّفة من IETF. وأفق إعادة المحاولة للإيقاف اللين (resetsAt) هو القيمة التي ينبغي لطبقة الحافة إظهارها بوصفها إرشاد Retry-After. أمّا إصدار استجابات ⁨HTTP⁩ الفعلية والترويسات وسلوك التخزين المؤقت فهو مسؤولية التطبيق المُضيف.

  • ركّب النموذج من PlanRegistry::defaultRegistry() وسياسة OveragePolicy واحدة وQuotaManager؛ وأضف BillingAlertService مع تنفيذ دائم لـ AlertStateRepositoryInterface للتنبيه.
  • رَكِّب QuotaEnforcementGuard في مسار الطلب بعد مصادقة المستأجر وقبل المعالج القابل للفوترة. التقط QuotaEnforcementException وQuotaExceededException الخاص بالفوترة عند الحافة وطابِق httpStatusCode() بالاستجابة.
  • تعريفات الخطط في هذه الوحدة هي المصدر الوحيد للحقيقة في الفوترة؛ لا تُبقِ تعريف فوترة موازياً في مكانٍ آخر من نشرك.
  • تجعل التنفيذات في الذاكرة السطح كله قابلاً لاختبار الوحدة دون إدخال/إخراج. الاختبارات الحدّية المُوصى بها: الاستخدام عند الحصة تماماً، ووحدة واحدة فوقها، وعتبات النسبة عند ⁨0.8⁩ و⁨1.0⁩، وحارس عدم-تطابق-الخطة، وسباق CAS (حجزان ضدّ آخر وحدة متّسع)، ورفض انقطاع المخزن.
  • تحمل أصناف النموذج الأساسية @since 2.2.0؛ وتحمل الركيزة @since 2.3.0. وخطّ الحزمة الحالي هو 3.1.0.
  • يملك المُشغِّل تنفيذات مستودع حالة التنبيه ومخزن الاستخدام، وديمومتها عبر النسخ المتماثلة، وأيّ إعادة تسليح للتنبيهات في منتصف الفترة عبر clearAlerts().

توثّق هذه الصفحة السلوك القابل للملاحظة خارجياً وسطح واجهة البرمجة العامة المدعوم فقط. أمّا مسارات فضاء الأسماء الداخلية، وأصناف المساعدة، وجداول الآليات، وأسماء ملفات كتيّب التشغيل، وبادئات التذاكر فخارج النطاق.