عاملها، حاکمیت، حقوق و صورتحساب · فصل ۳ — Invoice، Subscription، Self-hosted و SaaS · 291
Subscription, Grace و Dunning
agent را صرفاً automation میبیند و authority، memory، retry، cost و human escalation را صریح نمیکند. موضوع جاری: «Subscription, Grace و Dunning». میتواند برای «Subscription, Grace و Dunning» authority، memory/failure boundary و escalation/evidence را در یک agent محدود تعریف کند.
خلاصهٔ درس
agent را صرفاً automation میبیند و authority، memory، retry، cost و human escalation را صریح نمیکند. موضوع جاری: «Subscription, Grace و Dunning».
میتواند برای «Subscription, Grace و Dunning» authority، memory/failure boundary و escalation/evidence را در یک agent محدود تعریف کند.
خروجی این درس
میتواند برای «Subscription, Grace و Dunning» authority، memory/failure boundary و escalation/evidence را در یک agent محدود تعریف کند.
پیشنیازها
شکست پرداخت، مجوز قطع دسترسی نیست
یک اشتراک ماهانه دو ساعت قبل از تمدید با شکست پرداخت روبهرو میشود. اگر سامانه فقط past_due یا payment_failed را ببیند و همان را مستقیم به entitlement تبدیل کند، ممکن است دسترسی را زودتر از سیاست محصول قطع کند، retry غیرمجاز انجام دهد یا grace را بینهایت نگه دارد. این درس Subscription Entitlement, Grace, Retry & Dunning State Machine v1 را میسازد تا subscription، invoice/payment، retry، dunning، grace، entitlement و escalation هرکدام state و authority مستقل داشته باشند.
مرز درس و مسیر تصمیم
AGN-013 مالک recovery و entitlement policy پس از شکست collection است. AGN-012 مرجع invoice/payment خارجی را پوشش میدهد و AGN-014 release/support self-hosted را؛ این درس outcome آنها را جذب نمیکند. ابتدا failure را طبقهبندی کن، سپس retry eligibility و attempt budget را ببند، grace را با مرز زمانی روشن محاسبه کن، notification/dunning را اعمال کن و در پایان entitlement و customer action را از سیاست محصول نتیجه بگیر؛ نه از status خام provider.
مدل ذهنی: پنج ماشین حالت که نباید یکی شوند
پنج state را جدا نگه دار: Contract/Subscription, Collection, Retry/Dunning, Grace, Entitlement. invoice.payment_failed فقط evidence درباره collection است. attempt_count و next_payment_attempt میتوانند evidence retry باشند. اما هیچکدام بهتنهایی نمیگویند کاربر باید FULL، LIMITED، SUSPENDED یا REVOKED باشد. آن تصمیم به policy version و grace/entitlement rule نیاز دارد.
فیلدهای پایه State Machine
هویت با subscription_id و invoice_id شروع میشود. collection با payment_state و failure_class ثبت میشود. retry با retry_eligible, attempt_count, next_attempt_at, retry_policy_ref بسته میشود. ارتباط با مشتری از dunning_policy_ref و notification_state میآید. grace با grace_policy_ref, grace_started_at, grace_expires_at محدود میشود. access با entitlement_state, entitlement_reason و customer_action_required توضیح داده میشود. recovery_state, policy_version و audit_ref مسیر تصمیم را بازسازیپذیر میکنند.
Retryability، grace و entitlement سه سؤال متفاوتاند
در مستندات فعلی Stripe، hard-decline-like failure خودکار retry نمیشود مگر payment method جدید فراهم شود؛ در عین حال schedule و telemetry retry میتواند همچنان وجود داشته باشد. بنابراین next_payment_attempt یا افزایش attempt_count را نباید کورکورانه به «retry واقعاً مجاز و قابل اجراست» تبدیل کرد. در MAKNOW ابتدا failure classification و local retry policy بسته میشود. سپس grace و entitlement جداگانه محاسبه میشوند. حتی یک retryable failure هم الزاماً FULL access نمیدهد؛ policy میتواند LIMITED access در grace را مقرر کند.
مثال پایه: failure قابل retry در grace هفتروزه
اشتراک SUB-M-001 با invoice INV-1001 شکست retryable دارد. policy حداکثر سه attempt و grace هفتروزه را مجاز میکند. attempt اول ثبت شده، next attempt آینده است، failure notice ارسال شده و entitlement در طول grace برابر LIMITED است. State machine نتیجه RECOVERY_REVIEW_READY میدهد؛ نه بهخاطر status provider، بلکه چون retry eligibility، budget، grace boundary، entitlement reason و audit همزمان بستهاند.
حالا failure را NON_RETRYABLE کن اما retry_eligible=true و next_attempt_at را نگه دار. نتیجه باید HOLD_NONRETRYABLE_RETRY_CONFLICT باشد. همچنین اگر grace منقضی شده ولی entitlement بدون override هنوز FULL است، نتیجه باید HOLD شود.
Failure modeهای مهم
- provider status = access policy:
past_dueنباید مستقیماً entitlement را تعیین کند. - non-retryable + retry scheduled: contradiction است.
- attempt budget بینهایت: recovery bounded نیست.
- grace بدون expiry: اگر policy واقعاً NO_GRACE نیست، state قابل audit نیست.
- grace منقضی + access قبلی: entitlement باید recompute شود.
- duplicate failure پس از recovery: نباید dunning جدید ایجاد کند.
- manual override بدون owner/ref/expiry: استثنا authority ندارد.
- کپی رفتار monthly به annual/no-grace: transfer واقعی انجام نشده است.
Retry، customer action یا grace exit
اگر failure retryable است و budget باقی مانده، مسیر RETRY_SCHEDULED میتواند review-ready شود. اگر non-retryable است، retry خاموش میشود و مسیر به CUSTOMER_ACTION_REQUIRED میرود. اگر grace تمام شده و payment هنوز failed است، entitlement باید طبق policy به LIMITED/SUSPENDED/REVOKED منتقل شود و recovery میتواند EXHAUSTED یا cancellation path بگیرد. هیچکدام از اینها صرفاً از vendor status مشتق نمیشود.
پل فرد باتجربه: event idempotency و policy versioning
اگر با billing webhookها آشنا هستی، روی دو چیز تمرکز کن: idempotency و policy versioning. duplicate event بعد از recovery نباید دوباره notice یا dunning بسازد. همچنین تغییر retry/grace policy فقط باید روی تصمیمهایی اعمال شود که به همان policy_version و effective-time تعلق دارند. state بدون audit ref یا policy version قابلیت توضیحپذیری ندارد.
تمرین هدایتشده و مستقل
ابتدا baseline را اجرا کن و توضیح بده چرا LIMITED access در grace از payment state جداست. سپس failure را non-retryable کن، grace را منقضی کن و attempt را بالاتر از budget ببر؛ blocker دقیق هر mutation را نام ببر. در تمرین مستقل، recovery را موفق کن و یک duplicate payment_failed پس از آن وارد کن؛ باید escalation جدید سرکوب شود.
چه چیزی باید قابل بازسازی باشد؟
Reviewer باید بتواند subscription/invoice identity، payment/failure state، retry eligibility و budget، next attempt، dunning/notification، grace start/end، entitlement reason، customer action، recovery state، policy version و audit ref را از روی evidence ببیند. اگر پاسخ entitlement فقط «چون Stripe گفت past_due» باشد، evidence ناقص است.
Transfer: annual، بدون grace و non-retryable
برای اشتراک دوم، billing سالانه است، grace وجود ندارد و hard-decline-like failure به payment method جدید نیاز دارد. baseline ماهانه oracle نیست. State machine باید retry را خاموش کند، next_attempt_at=NONE بگذارد، customer action را لازم بداند، entitlement را طبق no-grace policy به SUSPENDED ببرد و cancellation/escalation را از policy جدید محاسبه کند. نتیجه review-ready فقط وقتی مجاز است که transfer واقعاً recompute شده باشد.
Fact / Inference / Design و freshness
FACT: Stripe Billing retry و telemetryهایی مانند invoice.payment_failed, attempt_count, next_payment_attempt را مستند میکند و hard declineها را بدون payment method جدید قابل retry خودکار نمیداند. FACT: Stripe retries، customer notifications و automations/dunning را قابلیتهای جدا معرفی میکند. INFERENCE: جداکردن recovery از entitlement از تبدیل failure موقت به access decision مبهم جلوگیری میکند. DESIGN: تعداد retry، grace window، notification sequence، entitlement/cancellation و manual override authority متعلق به سیاست محصول MAKNOW است.
منابع provider practice-evolving هستند و باید تا 2027-01-01 یا زودتر در صورت تغییر semantics بازبینی شوند.
ارزیابی
پاسخنامهی این درس در بستهی آموزش وجود ندارد؛ خودت جواب بده و صادقانه علامت بزن. «نیاز به مرور» درس را به صف تکرار میفرستد.
AGN-013-S1 retrieval
Explain why payment/provider status cannot directly define entitlement.
AGN-013-S2 application
Build the baseline state machine for the monthly retryable failure.
AGN-013-S3 diagnosis
Change failure_class to NON_RETRYABLE while retry_eligible remains true and next_attempt_at remains scheduled.
AGN-013-S4 transfer
Recompute annual/no-grace/hard-decline behavior without copying monthly behavior.
ملاکهای ارزیابی (rubric)
A sufficient submission preserves the lesson boundary, completes all required evidence dimensions, rejects provider-status shortcuts and contradictory retry/grace states, uses a bounded policy version/audit trail, and recomputes the transfer case without copying the baseline result. Any unsupported success state, infinite retry/grace behavior, or hidden manual exception is insufficient.
منابع و تازگی
مطالعهٔ موارد (case studies)
فایلهای آزمایشگاه
در همین پوشه python -B run_checks.py را اجرا کنید. همه رکوردها synthetic هستند؛ هیچ پرداخت، اشتراک، entitlement یا پیام مشتری واقعی تغییر نمیکند و هیچ credential تولیدی استفاده نمیشود.