Files
carmanagement/B2B_SUBSCRIPTION_EXECUTION_PLAN.md
T
2026-05-25 03:12:19 -04:00

26 KiB

B2B Subscription Execution Plan

1. Objective

Build a reliable B2B subscription management system that supports:

  • Free trials
  • Paid subscriptions
  • Payment pending states
  • Grace periods
  • Payment retry logic
  • Suspension
  • Cancellation
  • Reactivation
  • Admin overrides
  • Auditable subscription lifecycle events
  • Entitlement-based access control

The system must avoid ambiguous subscription states and must separate billing status from product access.


2. Guiding Principles

2.1 Subscription Status Is Not Access

A subscription status describes the billing lifecycle.

Access should be determined by an entitlement layer.

Example:

subscription.status = "past_due"
entitlement.access_level = "limited"

Do not scatter billing checks throughout the product.


2.2 Events Are the Source of Truth

Every important lifecycle action must create an immutable event.

Examples:

subscription.created
trial.started
trial.ended
payment.succeeded
payment.failed
subscription.suspended
subscription.canceled
subscription.reactivated

The current subscription state may be stored directly for performance, but every change must be explainable through historical events.


2.3 Webhooks Are Unreliable Until Reconciled

Billing provider webhooks may be delayed, duplicated, or arrive out of order.

The system must support:

  • Idempotent webhook processing
  • Event deduplication
  • Provider reconciliation jobs
  • Manual admin review

3. Core Status Model

Use the following subscription statuses:

trialing
active
payment_pending
past_due
suspended
canceled
expired
paused

Optional advanced statuses:

refunded
chargeback

Avoid adding more statuses unless there is a real operational need. Status bloat is how billing systems become folklore.


4. Status Definitions

Status Meaning
trialing Customer is in a free or paid trial period.
active Customer has a valid paid subscription.
payment_pending Payment is expected but has not completed.
past_due Payment is overdue after the pending window.
suspended Access is blocked due to unresolved billing issue.
canceled Subscription has been canceled and will not renew.
expired Trial or subscription ended without renewal.
paused Subscription is intentionally paused.

5.1 Trial Policy

Default policy:

Trial length: 14 days
Payment method required: Yes
Trial eligibility: One trial per company or billing account
Trial conversion: Automatic

Trial rules:

  • A customer may start one trial per company workspace.
  • A valid payment method should be required before trial activation.
  • Trial start and end dates must be stored.
  • Trial users should receive notifications before conversion.
  • Trial abuse checks should be applied at account and company level.

Recommended fields:

{
  "trial_start_at": "2026-05-25T00:00:00Z",
  "trial_end_at": "2026-06-08T00:00:00Z",
  "trial_used": true,
  "payment_method_required": true
}

5.2 Trial Ending Policy

At trial end:

Condition Result
Payment succeeds Move to active
Payment fails Move to payment_pending
No payment method exists Move to expired or require payment method before trial starts

Recommended rule:

If payment method is required, no payment method means trial cannot start.

This is stricter, but cleaner.


5.3 Active Subscription Policy

An active subscription means:

  • Payment is valid
  • Current billing period is open
  • Customer has access based on purchased plan
  • Renewal should occur automatically

Fields:

{
  "status": "active",
  "current_period_start": "2026-06-08T00:00:00Z",
  "current_period_end": "2026-07-08T00:00:00Z",
  "cancel_at_period_end": false
}

5.4 Payment Pending Policy

Payment pending begins when payment is required but incomplete.

Triggers:

  • Trial conversion payment failed
  • Renewal payment failed
  • Upgrade payment requires confirmation
  • Payment provider marks payment as incomplete
  • Bank transfer or invoice payment is awaiting settlement

Default B2B policy:

Payment pending timeout: 7 days
Access during payment pending: Full access
Notifications: Day 0, Day 3, Day 6

B2B payments often involve finance teams, approvals, cards expiring, invoice routing, and other thrilling artifacts of corporate civilization.


5.5 Past Due Policy

After the payment pending window expires:

payment_pending → past_due

Default policy:

Past due duration: 7 days
Access: Limited access
Admin visibility: Required
Customer notification: Required

Limited access may include:

  • Read-only access
  • No new projects
  • No exports
  • No premium actions
  • Existing data remains accessible

5.6 Suspension Policy

After the past due window expires:

past_due → suspended

Default policy:

Suspension starts: 14 days after first failed payment
Access: Blocked or read-only
Data retention: Continue retaining account data
Admin notification: Required

Suspended accounts should not be deleted.

Suspension means:

  • Billing issue is unresolved
  • Customer can still update payment method
  • Customer can still contact support
  • Admins can manually extend access if needed

5.7 Cancellation Policy

After the suspension window expires:

suspended → canceled

Default B2B cancellation policy:

Cancel after: 30 days unpaid
Access: Removed
Renewal: Disabled
Data: Retained according to data retention policy

Cancellation should be final for that subscription instance, but the customer may create a new subscription or reactivate if allowed.


6. Full Lifecycle Flow

none
  ↓
trialing
  ↓
active
  ↓
payment_pending
  ↓
past_due
  ↓
suspended
  ↓
canceled

Alternative trial expiration flow:

none
  ↓
trialing
  ↓
expired

Reactivation flow:

payment_pending / past_due / suspended / canceled
  ↓
payment succeeds
  ↓
active

7. Transition Table

Current Status Event New Status Notes
none Trial started trialing Customer begins trial
none Paid subscription started payment_pending First payment required
payment_pending Payment succeeded active Grant paid access
trialing Trial ended and payment succeeded active Convert trial
trialing Trial ended and payment failed payment_pending Start recovery flow
trialing Trial ended without payment method expired No access
active Renewal payment succeeded active Extend period
active Renewal payment failed payment_pending Start dunning
payment_pending Payment succeeded active Restore normal billing
payment_pending Timeout reached past_due Payment unresolved
past_due Payment succeeded active Restore access
past_due Timeout reached suspended Restrict access
suspended Payment succeeded active Restore access
suspended Timeout reached canceled End subscription
active User cancels active with cancel_at_period_end = true Access until period end
active Admin cancels immediately canceled Used for fraud, abuse, or special cases
canceled Customer reactivates payment_pending Payment required
expired Customer subscribes payment_pending Payment required
paused Resume date reached active Resume subscription

8. Timeout Policy

Recommended B2B timeline:

Day Status Access Action
0 payment_pending Full Payment fails, notify billing owner
3 payment_pending Full Retry payment, send reminder
6 payment_pending Full Final warning before past due
7 past_due Limited Restrict risky actions
14 suspended Read-only or blocked Notify admins and billing owner
30 canceled No access Cancel subscription

Recommended constants:

{
  "payment_pending_timeout_days": 7,
  "past_due_timeout_days": 7,
  "suspension_timeout_days": 16,
  "cancel_after_days_unpaid": 30
}

9. Payment Retry Policy

Use a configurable retry schedule.

Recommended retry schedule:

{
  "retry_schedule_days": [0, 3, 6, 10, 14],
  "max_retry_attempts": 5
}

Retry rules:

  • Retry immediately after first failure.
  • Retry again after 3 days.
  • Retry again before moving to past due.
  • Retry again before suspension.
  • Retry one final time before cancellation.
  • Stop retries if the customer cancels.
  • Stop retries if invoice is manually voided.
  • Stop retries if subscription is canceled.

10. Access Control Policy

Access must be derived through entitlements.

Access Levels

full
limited
read_only
none

Mapping

Subscription Status Access Level
trialing full
active full
payment_pending full
past_due limited
suspended read_only or none
canceled none
expired none
paused read_only

Entitlement Decision Example

{
  "workspace_id": "workspace_123",
  "subscription_id": "sub_123",
  "subscription_status": "past_due",
  "access_level": "limited",
  "reason": "payment_overdue",
  "valid_until": "2026-06-22T00:00:00Z"
}

11. Data Model

11.1 Subscriptions Table

CREATE TABLE subscriptions (
  id UUID PRIMARY KEY,
  workspace_id UUID NOT NULL,
  customer_id UUID NOT NULL,
  plan_id UUID NOT NULL,

  status TEXT NOT NULL,

  billing_interval TEXT NOT NULL,
  current_period_start TIMESTAMP,
  current_period_end TIMESTAMP,

  trial_start_at TIMESTAMP,
  trial_end_at TIMESTAMP,

  payment_pending_since TIMESTAMP,
  payment_due_at TIMESTAMP,

  past_due_since TIMESTAMP,
  suspended_at TIMESTAMP,

  cancel_at_period_end BOOLEAN DEFAULT FALSE,
  canceled_at TIMESTAMP,
  ended_at TIMESTAMP,

  retry_count INTEGER DEFAULT 0,
  max_retry_count INTEGER DEFAULT 5,

  created_at TIMESTAMP NOT NULL DEFAULT NOW(),
  updated_at TIMESTAMP NOT NULL DEFAULT NOW()
);

11.2 Subscription Events Table

CREATE TABLE subscription_events (
  id UUID PRIMARY KEY,
  subscription_id UUID NOT NULL,
  workspace_id UUID NOT NULL,

  event_type TEXT NOT NULL,
  source TEXT NOT NULL,
  payload JSONB NOT NULL,

  occurred_at TIMESTAMP NOT NULL,
  created_at TIMESTAMP NOT NULL DEFAULT NOW()
);

11.3 Invoices Table

CREATE TABLE invoices (
  id UUID PRIMARY KEY,
  subscription_id UUID NOT NULL,
  workspace_id UUID NOT NULL,

  provider_invoice_id TEXT,
  status TEXT NOT NULL,

  amount_due INTEGER NOT NULL,
  amount_paid INTEGER DEFAULT 0,
  currency TEXT NOT NULL,

  due_at TIMESTAMP,
  paid_at TIMESTAMP,
  failed_at TIMESTAMP,
  voided_at TIMESTAMP,

  created_at TIMESTAMP NOT NULL DEFAULT NOW(),
  updated_at TIMESTAMP NOT NULL DEFAULT NOW()
);

11.4 Payment Attempts Table

CREATE TABLE payment_attempts (
  id UUID PRIMARY KEY,
  invoice_id UUID NOT NULL,
  subscription_id UUID NOT NULL,

  provider_payment_id TEXT,
  status TEXT NOT NULL,

  failure_code TEXT,
  failure_message TEXT,

  attempted_at TIMESTAMP NOT NULL,
  created_at TIMESTAMP NOT NULL DEFAULT NOW()
);

11.5 Entitlements Table

CREATE TABLE entitlements (
  id UUID PRIMARY KEY,
  workspace_id UUID NOT NULL,
  subscription_id UUID NOT NULL,

  feature_key TEXT NOT NULL,
  access_level TEXT NOT NULL,

  starts_at TIMESTAMP NOT NULL,
  ends_at TIMESTAMP,

  created_at TIMESTAMP NOT NULL DEFAULT NOW(),
  updated_at TIMESTAMP NOT NULL DEFAULT NOW()
);

12. Event Catalog

Subscription Events

subscription.created
subscription.updated
subscription.activated
subscription.payment_pending
subscription.past_due
subscription.suspended
subscription.canceled
subscription.expired
subscription.reactivated
subscription.paused
subscription.resumed

Trial Events

trial.started
trial.ending_soon
trial.ended
trial.converted
trial.expired

Payment Events

payment.started
payment.succeeded
payment.failed
payment.requires_action
payment.retry_scheduled
payment.retry_failed
payment.retry_exhausted

Invoice Events

invoice.created
invoice.finalized
invoice.paid
invoice.payment_failed
invoice.voided
invoice.refunded

Risk Events

chargeback.created
chargeback.resolved
fraud.review_required
admin.override_created

13. Notification Plan

13.1 Trial Notifications

Timing Message
Trial start Welcome and trial end date
7 days before trial end Reminder
3 days before trial end Conversion reminder
1 day before trial end Final reminder
Trial converted Payment receipt and activation
Trial expired Upgrade prompt

13.2 Payment Failure Notifications

Timing Recipient Message
Day 0 Billing owner Payment failed
Day 3 Billing owner + admins Payment retry failed
Day 6 Billing owner + admins Final warning before past due
Day 7 Billing owner + admins Account is past due
Day 14 Billing owner + admins Account suspended
Day 30 Billing owner + admins Subscription canceled

Every notification should include:

  • Company name
  • Plan name
  • Amount due
  • Due date
  • Payment update link
  • Consequence of non-payment
  • Support contact

14. Admin Console Requirements

Admins must be able to:

  • View subscription status
  • View payment history
  • View invoice history
  • View retry attempts
  • View lifecycle events
  • Extend trial
  • Extend grace period
  • Retry payment
  • Cancel immediately
  • Cancel at period end
  • Suspend account
  • Reactivate account
  • Grant temporary access
  • Change plan
  • Add internal notes

All admin actions must create audit events.

Audit record:

{
  "admin_id": "admin_123",
  "action": "extend_grace_period",
  "reason": "Enterprise customer requested invoice processing time",
  "previous_state": "past_due",
  "new_state": "payment_pending",
  "created_at": "2026-06-15T00:00:00Z"
}

15. Webhook Processing Requirements

Webhook processor must support:

  • Signature verification
  • Idempotency keys
  • Event deduplication
  • Out-of-order event handling
  • Retry-safe processing
  • Dead-letter queue
  • Manual replay

Webhook events to handle:

customer.subscription.created
customer.subscription.updated
customer.subscription.deleted
invoice.created
invoice.finalized
invoice.paid
invoice.payment_failed
payment_intent.succeeded
payment_intent.payment_failed
charge.dispute.created
charge.refunded

Processing rule:

Never trust webhook order.
Always compare provider timestamp, local state, and event history.

16. Scheduled Jobs

Create scheduled jobs for:

Job Frequency Purpose
Trial ending reminder job Daily Notify customers before trial ends
Trial expiration job Hourly Expire or convert ended trials
Payment retry job Hourly Retry failed invoices
Payment pending timeout job Hourly Move stale pending payments to past due
Past due timeout job Hourly Move past due subscriptions to suspended
Suspension timeout job Daily Cancel long-unpaid subscriptions
Provider reconciliation job Daily Compare local state with billing provider
Entitlement sync job Hourly Ensure access matches subscription state

17. API Requirements

Create Trial

POST /subscriptions/trials

Request:

{
  "workspace_id": "workspace_123",
  "plan_id": "plan_pro",
  "payment_method_id": "pm_123"
}

Response:

{
  "subscription_id": "sub_123",
  "status": "trialing",
  "trial_end_at": "2026-06-08T00:00:00Z"
}

Start Paid Subscription

POST /subscriptions

Request:

{
  "workspace_id": "workspace_123",
  "plan_id": "plan_pro",
  "payment_method_id": "pm_123"
}

Cancel Subscription

POST /subscriptions/{subscription_id}/cancel

Request:

{
  "mode": "period_end",
  "reason": "customer_requested"
}

Reactivate Subscription

POST /subscriptions/{subscription_id}/reactivate

Request:

{
  "payment_method_id": "pm_123"
}

Get Entitlements

GET /workspaces/{workspace_id}/entitlements

Response:

{
  "workspace_id": "workspace_123",
  "access_level": "full",
  "features": [
    {
      "feature_key": "advanced_reports",
      "enabled": true
    }
  ]
}

18. Policy Configuration

Create a centralized subscription policy config.

{
  "trial": {
    "enabled": true,
    "duration_days": 14,
    "requires_payment_method": true,
    "one_trial_per_workspace": true,
    "one_trial_per_company": true
  },
  "payment": {
    "payment_pending_timeout_days": 7,
    "retry_schedule_days": [0, 3, 6, 10, 14],
    "max_retry_attempts": 5
  },
  "past_due": {
    "timeout_days": 7,
    "access_level": "limited"
  },
  "suspension": {
    "cancel_after_days": 16,
    "access_level": "read_only"
  },
  "cancellation": {
    "default_customer_cancel_mode": "period_end",
    "admin_cancel_mode": "immediate"
  },
  "access": {
    "trialing": "full",
    "active": "full",
    "payment_pending": "full",
    "past_due": "limited",
    "suspended": "read_only",
    "canceled": "none",
    "expired": "none",
    "paused": "read_only"
  },
  "notifications": {
    "trial_ending_days_before": [7, 3, 1],
    "payment_failure_days_after": [0, 3, 6],
    "past_due_days_after": [7],
    "suspension_days_after": [14],
    "cancellation_days_after": [30]
  }
}

19. Implementation Phases

Phase 1: Foundation

Goals

  • Define status model
  • Create subscription tables
  • Create event table
  • Create basic policy config
  • Implement basic subscription service

Deliverables

  • Subscription schema
  • Event schema
  • Subscription status enum
  • Policy config
  • Subscription creation endpoint
  • Trial creation endpoint

Acceptance Criteria

  • A workspace can start a trial.
  • A workspace can start a paid subscription.
  • Subscription status is persisted.
  • Every status change creates an event.

Phase 2: Billing Provider Integration

Goals

  • Integrate billing provider
  • Create customer records
  • Create subscriptions
  • Track invoices
  • Track payment attempts

Deliverables

  • Billing provider adapter
  • Webhook processor
  • Invoice model
  • Payment attempt model
  • Payment success handling
  • Payment failure handling

Acceptance Criteria

  • Successful payment activates subscription.
  • Failed payment moves subscription to payment_pending.
  • Webhooks are idempotent.
  • Duplicate webhooks do not duplicate events.

Phase 3: Trial Conversion

Goals

  • Convert trials automatically
  • Handle trial expiration
  • Notify users before trial end

Deliverables

  • Trial ending reminder job
  • Trial conversion job
  • Trial expiration handling
  • Trial notification templates

Acceptance Criteria

  • Trial converts to active after successful payment.
  • Trial moves to payment_pending after failed payment.
  • Trial moves to expired if payment is not available.
  • Trial ending reminders are sent on schedule.

Phase 4: Payment Recovery

Goals

  • Implement dunning flow
  • Retry failed payments
  • Enforce payment pending timeout
  • Enforce past due timeout

Deliverables

  • Payment retry job
  • Payment pending timeout job
  • Past due timeout job
  • Notification schedule
  • Admin visibility for failed payments

Acceptance Criteria

  • Failed payments are retried on schedule.
  • payment_pending becomes past_due after timeout.
  • past_due becomes suspended after timeout.
  • Customers and admins receive notifications.

Phase 5: Entitlements

Goals

  • Separate subscription status from access
  • Create entitlement service
  • Enforce access levels in product

Deliverables

  • Entitlement model
  • Entitlement service
  • Access decision API
  • Feature flag integration
  • Middleware or guard layer

Acceptance Criteria

  • active and trialing users receive full access.
  • past_due users receive limited access.
  • suspended users receive read-only or no access.
  • Product code does not directly hardcode billing statuses.

Phase 6: Cancellation and Reactivation

Goals

  • Support customer cancellation
  • Support admin cancellation
  • Support reactivation
  • Support cancel-at-period-end

Deliverables

  • Cancel subscription endpoint
  • Reactivate subscription endpoint
  • Period-end cancellation job
  • Admin cancellation flow

Acceptance Criteria

  • Customer cancellation defaults to period end.
  • Admin cancellation can happen immediately.
  • Reactivation requires successful payment.
  • Canceled subscriptions do not renew.

Phase 7: Admin Console

Goals

  • Give support and finance teams operational control
  • Add audit logging for all manual actions

Deliverables

  • Subscription detail page
  • Invoice history
  • Payment attempt history
  • Event timeline
  • Manual retry action
  • Extend trial action
  • Extend grace period action
  • Suspend action
  • Reactivate action
  • Cancel action

Acceptance Criteria

  • Admins can inspect complete subscription history.
  • Admin actions are audited.
  • High-risk actions require a reason.
  • Manual overrides do not silently mutate state.

Phase 8: Reconciliation and Hardening

Goals

  • Prevent billing drift
  • Detect inconsistent states
  • Make webhook processing resilient

Deliverables

  • Provider reconciliation job
  • Dead-letter queue
  • Webhook replay tool
  • State consistency checks
  • Alerting

Acceptance Criteria

  • Local subscription state matches billing provider state.
  • Failed webhooks are visible and replayable.
  • Inconsistent states generate alerts.
  • Reconciliation does not break valid admin overrides.

20. State Invariants

The system must enforce these rules:

A canceled subscription cannot renew.
A canceled subscription cannot become active without reactivation.
A subscription cannot be both active and canceled.
A trial cannot be active after trial_end_at.
A payment success must be idempotent.
A failed payment must not duplicate payment attempts.
A workspace cannot have multiple active subscriptions for the same product unless explicitly allowed.
Entitlements must be recalculated after every subscription status change.
Every manual override must create an audit event.

21. Monitoring and Alerts

Track these metrics:

trial_started_count
trial_conversion_rate
payment_failed_count
payment_recovered_count
payment_pending_count
past_due_count
suspended_count
canceled_count
reactivated_count
webhook_failure_count
webhook_duplicate_count
reconciliation_mismatch_count

Alert on:

Webhook processing failures
Spike in payment failures
Subscriptions stuck in payment_pending
Subscriptions stuck in past_due
Provider/local state mismatch
Entitlement sync failures
Unexpected cancellation spike

22. Testing Plan

Unit Tests

Test:

  • Status transitions
  • Policy config
  • Retry schedule
  • Entitlement mapping
  • Cancellation rules
  • Reactivation rules

Integration Tests

Test:

  • Payment success webhook
  • Payment failure webhook
  • Duplicate webhook
  • Out-of-order webhook
  • Trial conversion
  • Payment retry
  • Suspension
  • Cancellation

End-to-End Tests

Test:

Start trial → trial ends → payment succeeds → active
Start trial → trial ends → payment fails → payment_pending → past_due → suspended → canceled
Active subscription → renewal fails → payment_pending → payment succeeds → active
Active subscription → user cancels → active until period end → canceled
Suspended subscription → payment succeeds → active

23. Rollout Plan

Step 1: Internal Testing

  • Enable for internal workspaces only.
  • Use test billing provider mode.
  • Simulate webhooks.
  • Validate event history.

Step 2: Limited Beta

  • Enable for selected B2B customers.
  • Monitor payment failures.
  • Monitor state transitions.
  • Review support tickets.

Step 3: Full Launch

  • Enable for all new B2B customers.
  • Keep legacy subscriptions on old system temporarily.
  • Run reconciliation daily.

Step 4: Migration

  • Migrate old subscriptions into new status model.
  • Generate synthetic migration events.
  • Validate entitlements.
  • Confirm invoice state.
  • Freeze legacy billing writes.

24. Migration Plan

For each existing subscription:

  1. Identify current provider status.
  2. Map provider status to internal status.
  3. Create internal subscription record.
  4. Create current entitlement record.
  5. Import current invoice state.
  6. Create subscription.migrated event.
  7. Run reconciliation check.
  8. Validate workspace access.

Example mapping:

Provider State Internal Status
trialing trialing
active active
incomplete payment_pending
past_due past_due
unpaid suspended
canceled canceled

25. Risks

Risk Mitigation
Duplicate webhooks Idempotency keys and event deduplication
Out-of-order webhooks Compare timestamps and current state
Incorrect access removal Entitlement layer and audit logs
Customers canceled too early Grace period and admin override
Trial abuse Company-level trial eligibility checks
Billing provider mismatch Daily reconciliation
Admin misuse Audit logs and approval rules
Status sprawl Strict status model

26. Final Default Policy

Use this as the baseline B2B policy:

Trial lasts 14 days.
Payment method is required before trial starts.
Trial converts automatically if payment succeeds.
If payment fails, subscription becomes payment_pending.
Payment pending lasts 7 days with full access.
After 7 unpaid days, subscription becomes past_due with limited access.
After 14 unpaid days, subscription becomes suspended.
After 30 unpaid days, subscription is canceled.
Customer cancellation takes effect at period end.
Admin cancellation can be immediate.
Reactivation requires successful payment.
Chargebacks suspend access immediately.
Access is controlled through entitlements.
All transitions are event-driven and audited.

27. Definition of Done

The B2B subscription system is complete when:

  • All subscription statuses are implemented.
  • All transition rules are enforced.
  • Billing provider webhooks are idempotent.
  • Failed payments enter recovery flow.
  • Trial conversion works automatically.
  • Entitlements control product access.
  • Admins can override safely.
  • Every lifecycle change has an audit event.
  • Reconciliation detects billing drift.
  • Monitoring and alerts are live.
  • End-to-end tests cover the full lifecycle.