In order to work with subscriptions, we first need to understand what exactly is the flow for successful or failed subscription. Once we understand the flow, we can work on further steps like provisioning access to product and so on.
The following is the flow of events for a successful subscription from the implementation perspective:
activeand the invoice status is set to
The pseudo-code for a failed subscription flow looks like so:
activeand invoice to be
incomplete_expiredand the invoice becomes void.
past_dueand attempt to recover payment based on the retry rules that we set in Stripe dashboard. We will discuss about retry rules in next section.
unpaidand don't cancel the subscription.
When an invoice payment with an iDEAL, Sofort, Bancontact, credit or debit card fails, Stripe retries payments according to our Dashboard's automatic collection settings. To avoid bank fees, Stripe doesn't retry invoice payments made with ACH debit, BECS debit, or SEPA debit.
We can choose the schedule of retries, or we can allow Stripe's Smart Retries feature to optimize the schedule and number of retries.
First let's talk about Smart Retries.
Using machine learning, Smart Retries chooses optimal times to retry failed payment attempts to increase the chance of successfully paying an invoice. The Smart Retries feature attempts to retry the customer's charge up to four times within a customizable period. We can override this behavior, for all invoices, in our Dashboard's automatic collection settings by disabling Smart Retries and defining our own custom retry rules.
In order to avoid failures in payments due to problems in already provided payment info of the customer, Stripe provides us the option to send email regarding keeping payment info up to date.
We can send mail for two of the following cases by toggling either of them or both in dashboard:
We are also provided with the option to send a webhook event for upcoming renewal events, before n-days of the renewal.
Stripe allows us to configure the steps we'd like to take when charging a customer's payment method fails.
This is how the dashboard would look like:
We can delegate the retry logic to Smart Retries feature in Stripe or we can set our own custom retry schedule which can be set to retry 1,3,5 or 7 days after the previous attempt.
If the payment attempt or retries fail, then we toggle the option named
Customer emails to send email regarding card payment fails.
Another important setting over here is, what to do when all retries for a
particular payment fails. In that case for a subscription we can either leave
the subscription as is, mark as
unpaid or cancel the subscription. Similarly
for an invoice, we can leave it as is or mark as
The most straightforward way to provide a customer access to a product from the
implementation perspective is to check whether the status of the subscription is
active or not. We have to make use of Stripe webhook events like
payment_intent.failed to explicitly check
whether the subscription is still active or not, and orient our backend logic to
take into account the subscription status and allow access to the product.
If our application uses Stripe checkout, then we don't need to explicitly create a subscription nor customer, since Stripe itself takes care of that.
If we weren't using Stripe checkout, then we'd have to leverage both the
Subscription and Customer API's to complete the first time payment process.
Which means, in this case we would have to invoke the
create API call using
these API's for creating the Customer as well as the Subscription during the
first time payment process.
Steps to provision access to product for first time subscription, if using Stripe checkout:
checkout.session.completedwebhook event from Stripe.
statusof the subscription object received is
active, then allow access to our product.
If not using Stripe checkout, then the steps are as follows:
Let's take an example where Oliver uses monthly billed plans, where he uses a $10 plan for half a month and then decides to switch to $20 plan. The first thing Stripe will check is whether the billing periods of the current subscription and switched subscription are same or not. If billing periods are same, then the invoice will be charged, with or without prorations, on the same billing dates. However, take the case where we switch the customer from a monthly plan to an yearly plan. In that case the billing date is updated to the billing date of the switched subscription.
So in the above scenario, where Oliver switched to another monthly plan itself, the billing would only take place at the start of the next month, given that both plans have same billing period. In more simpler terms, we have to pay the rest of the amount for switching to the new expensive plan only after the current month gets over, rather than immediately.
There are two ways to bill Oliver during that switch:
proration_behavior: 'none'while switching the subscription.
In Stripe, the term proration is used to denote the amount that needs to be collected, when say switching from one plan to another. In the above example, the customer upgrades from a $10 per month subscription to a $20 subscription, and the customer is charged the prorated amounts for the time spent on each option. Assuming the change occurred halfway through the billing period, the customer is billed an additional $5: -5 USD for unused time on the initial price, and 10 USD for the remaining time on the new price. The prorated amount is calculated as soon as the API updates the subscription to a new plan. The current billing period’s start and end timestamps are used to calculate the cost of the subscription before and after the change.
Let's take one more example to solidify our understanding on how a proration is calculated by Stripe. Say Oliver subscribed to a $30 plan whose billing period is 30 days. Let's say that he used 1 day from that plan and is switching to an yearly plan priced at $60. Then stripe will charge him for the n-number of days he used current plan, using an amount calculated from the current plan and charge for the rest of the m-number of days based on the pricing of the newly switched to plan. Here Oliver will be charged $1 for the 1-day he used in $30 plan and for the rest of the 29 days, the amount will be calculated from $60 plan, and a final bill will be generated either immediately or at the end of current billing period, depending on our configuration of the subscription. We will take a look into how to configure the billing of a subscription in the following sections.
It can get confusing at times if we let Stripe decide the billing date when switching between plans. The most sanest way to bill the customer during a switch is to immediately attempt the payment. Meaning, if they're switching from a $10 plan to a $20 plan, then immediately attempt payment for the prorated amount, without taking into consideration the billing period.
Stripe does this automatically if the billing periods of the plans differ. Example: If we switch from a monthly plan to an yearly plan, then payment will be attempted immediately at the moment of switch.
But what if we want to switch from a monthly standard plan to a monthly pro plan? Usually Stripe will attempt the payment only at the next billing period. But this can often create problems if the customers card declines payment during next billing period. So it's better to attempt immediate payment and make sure the customer is able to pay for the features of the new plan.
In order to attempt immediate payment at all times, we need to set the proration
behavior to always invoice. From the implementation perspective, we need to set
Let's say that in our application, we are attempting immediate payment with proration. Therefore from a customers point of view, it's essential to show them the amount that they need to pay extra, when switching to a more expensive subscription plan. We can preview the effects of switching a subscription, including a preview of what proration will take place, by retrieving the upcoming invoice. Because Stripe prorates to the second, prorated amounts can change between the time they’re previewed and the time the update is made. To ensure that the actual proration is calculated exactly the same as the previewed proration, we should pass a proration date, which should be the datetime at which we make the API call to retrieve upcoming invoice, parameter when doing the actual subscription update. The recommended way to get only the prorations being previewed is to consider only the items whose starting period is same as the subscription proration date, of the upcoming invoice object.
The following is the pseudo-code for switching to another subscription:
always_invoicefor immediate payment attempt.
itemsto the ID of the
itemsdata in the retrieved subscription and the
priceto be the ID of the new price to be switched to.
customer.subscription.updatedand check the
statusof the subscription object. If
active, then we have switched the subscription.