Stripe - Upgrading a subscription synchronously (immediately charge for a proration without webhooks)

risafj

Risa Fujii

Posted on May 3, 2019

Stripe - Upgrading a subscription synchronously (immediately charge for a proration without webhooks)

I was doing some research on how upgrading subscriptions works on Stripe. Stripe's default system is to charge the customer for the proration cost in their next invoice, along with the fee for the upcoming billing cycle (docs).

For example, if I upgraded my subscription to some service from the standard plan ($5/month) to the premium plan ($10/month) at the midpoint of my billing cycle, my invoice on the first of the next month would be something like $12.50. $10 for the charge for the next month, $5 for the premium plan portion I started using half a month ago, and $2.50 refund for the unused portion of the standard plan after the upgrade.

But this billing system could be confusing for customers, and I wanted to see if there was a way to charge customers immediately for the proration ($2.50 in our example above). There was no prorate_now flag or anything, but it could be done manually. Here are the high level steps and a gist to walk you through it. The code will most likely require customization for your use case.

Note: I used Stripe's test environment for this as the docs suggest.

Prerequisites

  • Add the stripe gem to your Rails app by adding the line below to your Gemfile and running bundle.
gem 'stripe'
Enter fullscreen mode Exit fullscreen mode
  • Assign your Stripe publishable and secret keys to environment variables, and create config/initializers/stripe.rb (you can get your keys from the Stripe dashboard):
Rails.configuration.stripe = {
  publishable_key: ENV['STRIPE_PUBLISHABLE_KEY'],
  secret_key:      ENV['STRIPE_SECRET_KEY']
  }

Stripe.api_key = Rails.configuration.stripe[:secret_key]
Enter fullscreen mode Exit fullscreen mode
  • In the Stripe test environment, you need to create at least one product, two plans, and a customer who is subscribed to the lower tier plan. These can be created from the Stripe dashboard, or by sending requests to their API; I've linked the relevant docs for making API requests in the previous sentence.

  • The customer also needs to have a credit card so they can be charged (not a real one, but a mock card for testing purposes). If you created the customer via the API, a mock card was probably auto-created for you. You can make sure of this by going to the dashboard and checking that customer's details.

High level steps

  1. Create a preview invoice, which simulates what a customer's next invoice would look like if they upgraded now
  2. Calculate the proration cost from the preview invoice
  3. Create a new one-off invoice with that amount
  4. Charge the customer immediately
  5. Check if the invoice has been paid, then upgrade the customer with proration disabled (otherwise, they will be charged twice for the upgrade!)

Note: Creating a preview invoice does not create an actual invoice, so you don't have to worry about billing the customer accidentally (explained in the docs).

Gist as a proof of concept

The test below follows the steps I described above. The customer is subscribed to my cheaper plan, which costs JPY 100 per month, but they want to upgrade to the plan that costs JPY 1,000, so we charge them for the difference before upgrading them. I also have a User model and a Plan model in my Rails app (which correspond to customer and plan respectively in Stripe) that I occasionally refer to in my test.

And now for the actual code:

I will also link the relevant parts of the Stripe docs, because I personally found it a chore to sift through the detailed but scattered docs.

Read about...

  • creating a preview invoice and calculating the proration cost here
  • creating an one-off invoice here
  • synchronously paying an invoice here
  • checking if an invoice was paid successfully here
  • upgrading a customer with proration disabled here

Please let me know in the comments if any part of the code is unclear, or if there are alternative ways to do this. Thank you!

💖 💪 🙅 🚩
risafj
Risa Fujii

Posted on May 3, 2019

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related