StatusIN PROGRESS — Act 1 Phase 2 active, Continuation planned
DriverJared Goguen
Repossubscriptions-api
Staffing1 engineer, partial quarter

The old model committed state before payment confirmed — provision entitlements, publish events, attempt payment, then try to rollback on failure. This commit-then-pay ordering is the root cause of state drift, duplicate charges, and entitlement inconsistency.

The Ordering Inversion

Commit state → attempt payment → rollback on failure. Each rollback is a window for drift. ~200 rollbacks/day at the peak.

The PM Gate

The system blocks users without a valid payment method from any subscription change. Returns HTTP 402 / error 1210. Legitimate users can't subscribe.

Rollback Complexity

7 components involved in undoing state on failure: rollback calculator, Stripe rollback, OPE rollback, fallback calculator, schedule re-creation. Each is a window for partial state.

Checkout Fragmentation

Three checkout systems: Multisku, Unified Checkout, Billing Profile. Different payment flows, different error handling, different PM management. Consolidation blocked on the PM gate.

Q1 delivers the foundation: Stripe parameter migration across all payment paths, then the continuation workflow that flips entitlement provisioning to payment success. Together they unlock PM Gate removal and UC expansion in Q2.

Act 1 confirm-before-commit payment-paths stripe-params FOUNDATION
Continuation Workflow defer-entitlements payment-success dead-code-deletion THE FLIP
Deferred Collection remove-pm-gate client-secrets subscribe-first UNLOCKED
Unified Checkout one-component card-paypal-3ds stripe-js UNLOCKED
Stripe API default-incomplete pending-if-incomplete payment-intents PAYMENT BEHAVIOR
enables unlocks unlocks payment_behavior params
Q1 2026
Act 1: Stripe Params
Late Q1
Continuation Workflow
Q2
PM Gate Removal
Q2
UC Expansion

Four Stripe API call sites in subscriptions-api create or modify subscriptions. Each one needs a specific set of Stripe parameters to enforce confirm-before-commit ordering. For each call site: set the right parameters, handle the new error types, and verify payment confirmation happens before state changes.

#PathWhat It DoesPhaseParams
1New subscription creationStripe.Subscription.Create with payment_behavior1 ✓default_incomplete
2Subscription update (change)Stripe.Subscription.Update with pending_if_incomplete2pending_if_incomplete
3Schedule eliminationExisting schedules converted to immediate updates2pending_if_incomplete
4Payment collection (on + off session)PaymentIntent — on/off session collapsed1 ✓(already done)
Phase 1 · New Subscription Creation

payment_behavior: default_incomplete. New subscriptions create an incomplete invoice with a PaymentIntent. Frontend collects payment via Stripe.js. Subscription activates only on payment success.

Phase 2 · Subscription Updates

pending_if_incomplete on updates. Schedule elimination: convert future-dated changes to immediate updates with pending payment. Conflict handling: what happens when two updates race?

Two states. Today we're in State A. The continuation workflow moves us to State B. The difference is when entitlements are provisioned — before payment or after.

Apply Changes save-phases cancel-phases sync-stripe ENTRY
Continuation Workflow payment-pending customer-present WAIT FOR PAYMENT
Success Path provision-entitlements publish-events set-billing-anchor COMMIT
Failure Path nothing-provisioned nothing-to-undo no-rollback NO-OP
Deleted Code rollback-calculator rollback-changes-async rollback-multi-sub DEAD
starts payment succeeds payment fails no longer called
StageWhat Changes
CommitNEW — gains provision. Entitlements, events, billing anchor set HERE instead of at Apply start.
RollbackDELETED — nothing was provisioned, so rollback is a no-op. Code deleted.

Save Phases, Cancel Phases, Sync Stripe, Signal Loop, and Decision Engine are unchanged.

Safety: Feature Flag + Gradual Rollout

Flag gates the continuation path. Ramp by account segment. Kill switch: revert to eager entitlement if metrics degrade. No code deleted until flag is 100% for 2+ weeks.

Success Criteria

0 rollbacks triggered. 0 entitlements granted without payment confirmation. Payment success rate ≥ baseline. Measured for 2 weeks at 100% before deletion manifest executes.

What dies when the flip happens. Each deletion is gated on the continuation workflow working correctly for all payment paths.

ComponentWhat It Does TodayWhy It Dies
rollback_calculator.goComputes what to undo on payment failureNothing was provisioned
RollbackChangesAsyncAsync rollback of subscription state changesNothing to undo
RollBackMultiSubscriptionUpdateMulti-sub rollback coordinationNothing to undo
Fallback calculatorFallback when rollback partially failsNo partial state
Schedule re-creationRecreates schedules after failed paymentSchedules eliminated (Act 1)
OPE rollbackRolls back entitlement changesEntitlements never changed
Stripe rollbackReverts Stripe subscription to prior stateStripe sub in pending state, auto-expires
7
components deleted simultaneously
0
rollbacks needed after the flip
0
partial state windows
#InvariantProperty
I1No entitlement without paymentEvery active subscription has a corresponding successful PaymentIntent. No entitlement granted before payment confirmation.
I1 · Enforcement

Inline: commit() verifies PaymentIntent succeeded before granting entitlements. Batch: BQ query for active subs without successful PaymentIntent. Alert: entitlements_granted_without_payment counter — PagerDuty if > 0.

W1
W2
W3
W4
W5
W6
W7
W8
W9
W10
W11
W12
W13
W14
Smart Checkout Q1
Act 1 · Phase 1 (New Subs)
Act 1 · Phase 2 (Updates)
Continuation Workflow
#DecisionOwnerBy WhenStatusRecommendationRisk if Wrong
1Phase 2 schedule conflicts — racing updates?EngineeringQ1 (PDE P2)OPENSecond update fails or queuesMedium — race conditions
2Event ordering — consumers assume Apply-time events?EngineeringQ1 (flip)OPENAudit all downstream consumersHigh — silent downstream break
RiskImpactOwnerMitigation
Workflow abandonmentMediumEngine leadLost struct = pending Stripe sub. Need cleanup job.

Q1 proves payment-before-entitlement on every subscription path. With that foundation in place, the PM Gate can be removed — the gate exists because the old model couldn't guarantee payment, but the continuation workflow makes payment confirmation structural. Unified Checkout can expand because checkout is unified behind a single Stripe.js component, no longer blocked by the gate.

PM Gate Removal, UC Expansion, and the full Smart Checkout orchestration.

−141
tickets/month eliminated by Continuation