Processing Stripe subscriptions using Webhooks

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 into incomplete if the initial payment attempt fails. [...] . Once the first invoice is paid, the subscription moves into an active state. If the first invoice is not paid within 23 hours, the subscription transitions to incomplete_expired. This is a terminal state, the open invoice will be voided and no further invoices will be generated.
If subscription collection_method=charge_automatically it becomes past_due when payment to renew it fails and canceled or unpaid (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 status active. 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.