Processing Stripe subscriptions using Webhooks
We are implementing paid plans on Koliseo (free, professional, enterprise) using Stripe Subscriptions. Long story short, this is the output from Stripe CLI when a user switches from a free to a paid plan using Stripe Checkout:
2020-09-05 08:33:23 --> charge.succeeded [evt_1HNuxiIsiuZOrhXKxQmevmbT]
2020-09-05 08:33:24 --> checkout.session.completed [evt_1HNuxiIsiuZOrhXKONxVGGbd]
2020-09-05 08:33:25 --> payment_method.attached [evt_1HNuxjIsiuZOrhXKscXrsakA]
2020-09-05 08:33:26 --> customer.created [evt_1HNuxjIsiuZOrhXKgSoPUfDh]
2020-09-05 08:33:26 --> customer.updated [evt_1HNuxjIsiuZOrhXKAir5XYoH]
2020-09-05 08:33:27 --> invoice.created [evt_1HNuxjIsiuZOrhXKg8xaZ5d1]
2020-09-05 08:33:27 --> customer.subscription.created [evt_1HNuxjIsiuZOrhXKbYYkPk1Q]
2020-09-05 08:33:27 --> invoice.updated [evt_1HNuxjIsiuZOrhXKGq6vZa28]
2020-09-05 08:33:27 --> invoice.finalized [evt_1HNuxjIsiuZOrhXKcEv05sQd]
2020-09-05 08:33:27 --> customer.subscription.updated [evt_1HNuxjIsiuZOrhXK02nsFP3Z]
2020-09-05 08:33:27 --> payment_intent.succeeded [evt_1HNuxkIsiuZOrhXKZjT2uVKb]
2020-09-05 08:33:27 --> payment_intent.created [evt_1HNuxkIsiuZOrhXK1xgDiZmi]
2020-09-05 08:33:27 --> invoice.updated [evt_1HNuxjIsiuZOrhXKDdu0cjuD]
2020-09-05 08:33:27 --> invoice.payment_succeeded [evt_1HNuxjIsiuZOrhXKs4jUhfc1]
2020-09-05 08:33:27 --> invoice.paid [evt_1HNuxjIsiuZOrhXKZSRMp12V]
The documentation for Stripe event types lists hundreds of these with unclear relationships: should we listen for payment_intent.succeded
, invoice.payment_needed
, invoice.finalized
, or customer.subscription.created
?
Subscription events
We are focusing on subscription events for the following cases:
- Create new subscription: move from free to a paid plan.
- Upgrade/Downgrade subscription: move between professional and enterprise plans
- Cancel subscription: move from paid to a free plan on Koliseo, or cancel the subscription on the Stripe Customer Portal
- Unpaid subscription: payment rejected or expired credit card
To create / update subscriptions, we launch Stripe Checkout sessions from our server-side (with client-side Checkout we could not modify an existing subscription for upgrade/downgrade).
When payment is not arriving
Creating and canceling is easy, but what happens when a customer stops paying? For this, the Stripe documentation for subscriptions is super useful:
a subscription moves intoincomplete
if the initial payment attempt fails. [...] . Once the first invoice is paid, the subscription moves into anactive
state. If the first invoice is not paid within 23 hours, the subscription transitions toincomplete_expired
. This is a terminal state, the open invoice will be voided and no further invoices will be generated.
If subscriptioncollection_method=charge_automatically
it becomespast_due
when payment to renew it fails andcanceled
orunpaid
(depending on your subscriptions settings) when Stripe has exhausted all payment retry attempts.
So, wait while the customer receives reminders and tries to fix the situation. If this is not fixable, the subscription will be updated following the configuration under Settings > Subscriptions and emails
, and we receive the corresponding event.
During this process Stripe sends emails with a link to Koliseo that we just redirect to the Stripe Customer Portal, where the user can introduce the new means of payment.
TL;DR The event list
After some research, we ended up listening to the following list of events:
- Create new subscription:
customer.subscription.created
. Unfortunately, the payment is not yet confirmed, which brings us to the next event. - Upgrade/Downgrade subscription:
customer.subscription.updated
with statusactive
. Also triggered when the payment is confirmed for the first time. - Cancel subscription:
customer.subscription.deleted
- Unpaid subscription:
customer.subscription.updated
with one of these status:unpaid
,canceled
,incomplete_expired
The rest is some happy hacking :)
We hope this is useful to others implementing similar subscription plans with Stripe. If you find these tips useful, make sure to follow us on Twitter.