WORK STREAM
Payments-Driven Entitlements
Every complication in subscription commerce traces to one decision: the system commits state before payment confirms. PDE inverts the order — confirm payment first, then commit. Every downstream problem disappears.
A customer upgrades their plan. The system commits the subscription change, provisions entitlements, publishes events to ~50 downstream services, then attempts payment. On failure, it tries to roll everything back. The reversal logic is a parallel inverse of the forward path — every step that committed state needs an undo step.
Subscription written, entitlements provisioned, events published. Then payment attempted. On failure: undo subscription, revoke entitlements, compensating events. 196 rollbacks/day.
PaymentIntent created and confirmed before any subscription exists. If payment fails or requires 3DS, there is nothing to roll back. Zero rollbacks by construction.
Steps 1–4 have zero committed state. If anything fails, just stop. Only the Commit step creates hard-to-reverse state, and it only runs after payment is confirmed. The green zone is not a safety net — it is the absence of anything that needs one.
| Step | What | Status | Timeline | Depends On |
|---|---|---|---|---|
| 1 | Unify payment collection — one path regardless of origin | In progress | ~Feb | — |
| 2a | default_incomplete for subscription creates | Shipped (gated) | Q1 | Step 1 |
| 2b | Refactor discounts out of update params | Shipped | Q1 | — |
| 2c | Refactor subscription schedules out of update params | Pending | ~Mar | Step 2b |
| 2d | pending_if_incomplete for subscription updates | Pending | ~Mar | Step 2c |
| 3 | Grant features after payment — planned-state struct, split MakeObjectProductsChanges into plan+commit | Pending | ~Apr | Step 2d |
| 4 | Remove reversal logic — delete rollback machinery, remove WorkflowID/Phase1 | Pending | ~Apr | Step 3 |
Step 3 is the structural pivot. Today, MakeObjectProductsChanges does everything in one call — compute the change, write to Stripe, write to DB, provision entitlements. After Step 3, it splits into plan (compute what would change) and commit (write it all after payment confirms). The planned-state struct bridges the gap.
Compute the subscription diff, proration, entitlement changes. Write nothing. Return a planned-state struct that captures the intended outcome.
After payment confirms, apply the planned state: Stripe subscription, DB records, entitlement provisioning, PGQ events. One atomic sequence, no undo needed.
| Payment Result | Session | Action | State Committed? |
|---|---|---|---|
| Succeeds | Any | Commit inline | Yes — after payment |
| Requires action (3DS) | On-session | Workflow → authenticate → commit | Yes — after auth |
| Requires action | Off-session | Void invoice | No |
| Requires new PM | On-session | Workflow → new PM → commit | Yes — after new PM |
| Requires new PM | Off-session | Void invoice | No |
| Hard failure | Any | Void invoice | No |
| Component | Purpose | Why Unreachable |
|---|---|---|
| Rollback Processor | Entire rollback machinery — undo Stripe writes, revert DB, deprovision entitlements | Payment confirms before commit; nothing to undo |
| ApplyImmediateSubscriptionChanges | Temporal workflow for immediate (non-scheduled) subscription mutations | Already removed (BILLACCT-946) |
| probation/ package | Interim account state while payment outcome was pending | Confirm-before-commit eliminates the pending window |
| WorkflowID / Phase1 OPE fields | Tracked rollback state so the system knew what to undo on failure | Never written in the new flow — no rollback to track |
| Pre-computed rollback in PLAN | changes_calculator PLAN stage pre-computes rollback data for each mutation | No rollback means no rollback data to compute |
The goal is structural: make rollbacks impossible by construction, not by better rollback code. Each step shipped independently moves the system closer to confirm-then-commit.