Entries for May 18, 2024
-
Stripe Subscription States
This is a quick note on Subscription states on Stripe. Subscriptions are objects which track products with recurring payments. Stripe docs on Subscriptions are very comprehensive, but for some reason they don’t include a state diagram that shows the transitions between different states of a subscription. They do have one for Invoices, so maybe this post will inspire them to add one.
As of May 2024, the API has 8 values for
Subscription.status:incomplete: This is the initial state of a subscription. It means that the subscription has been created but the first payment has not been made yet.incomplete_expired: The first payment was not made within 23 hours of creating the subscription.trialing: The subscription is in a trial period.active: The subscription is active and the customer is being billed according to the subscription’s billing schedule.past_due: The subscription has unpaid invoices.unpaid: The subscription has been canceled due to non-payment.canceled: The subscription has been canceled by the customer or due to non-payment.paused: The subscription is paused and will not renew.
At any given time, a
Customer’s subscription can be in one of these states. The following diagram shows the transitions between these states.stateDiagram classDef alive fill:#28a745,color:white,font-weight:bold,stroke-width:2px classDef dead fill:#dc3545,color:white,font-weight:bold,stroke-width:2px classDef suspended fill:#ffc107,color:#343a40,font-weight:bold,stroke-width:2px active:::alive trialing:::alive incomplete:::suspended past_due:::suspended unpaid:::suspended paused:::suspended canceled:::dead incomplete_expired:::dead [*] --> incomplete: Create Subscription trialing --> active: Trial ended, first<br>payment succeeded incomplete --> trialing: Started trial incomplete --> incomplete_expired: Payment not made<br>within 23 hours incomplete --> active: Payment<br>succeeded active --> past_due: Automatic payment<br>failed trialing --> past_due: Trial ended<br>payment failed past_due --> unpaid: Retry limit<br>reached past_due --> canceled: Retry limit reached<br>or subscription canceled past_due --> active: Payment<br>succeeded trialing --> paused: Trial ended without<br>default payment method paused --> active: First payment<br>made active --> unpaid: Automatic payment disabled,<br>manual intervention required unpaid --> active: Payment<br>succeeded unpaid --> canceled: Subscription<br>canceled active --> canceled: Subscription<br>canceled paused --> canceled: Subscription<br>canceled trialing --> canceled: Subscription<br>canceled incomplete_expired --> [*] canceled --> [*]Stripe doesn’t comment on these states further and leaves their interpretation to the developer. This is probably because each company might interpret these states differently. For example, a user skipping a payment and becoming
past_duemight not warrant disabling a service for some companies, while others might want to disable services immediately. Stripe’s API is built to be agnostic of these decisions.Regardless of how you interpret these 8 states, you will most likely end up generalizing them into 3 categories:
ALIVE,SUSPENDED, andDEAD. The colors in the diagram above represent these categories:ALIVE: The subscription is active and payments are being made. States:active,trialing.SUSPENDED: The subscription is not active but can be reactivated. States:incomplete,past_due,unpaid,paused.DEAD: The subscription is not active and cannot be reactivated. Such subscriptions are effectively deleted. States:canceled,incomplete_expired.
While
DEADstates are unambiguous, your company might differ in what is consideredALIVEandSUSPENDED. For example, you might considerpast_dueasALIVEif you don’t want to disable services immediately after a payment failure.If you collapse the 8 states into these categories, you get the following diagram:
stateDiagram direction TB; classDef alive fill:#28a745,color:white,font-weight:bold,stroke-width:2px classDef dead fill:#dc3545,color:white,font-weight:bold,stroke-width:2px classDef suspended fill:#ffc107,color:#343a40,font-weight:bold,stroke-width:2px ALIVE:::alive SUSPENDED:::suspended DEAD:::dead state ALIVE { active trialing trialing-->active } state DEAD { canceled incomplete_expired } state SUSPENDED { incomplete past_due unpaid paused past_due-->unpaid } [*] --> SUSPENDED: Create<br>Subscription SUSPENDED --> ALIVE: Payment succeeded<br>or trial started ALIVE --> SUSPENDED: Payment<br>failed SUSPENDED --> DEAD: Subscription canceled<br>or checkout expired ALIVE --> DEAD: Subscription<br>canceled DEAD --> [*]The distinction is important, because Stripe doesn’t make it crystal clear what kind of subscriptions can come back from the dead and end up charging the customers multiple times. If you are not limiting the number of subscriptions per customer, this is something you should be aware of. Practically, this means that you block the customer from creating a new subscription if they already have an
ALIVEorSUSPENDEDsubscription.DEADsubscriptions can be ignored.