Miguel Piedrafita

Miguel Piedrafita

Upgrading the Blogcast billing system for the new EU legislation

In case you weren't aware, new PSD2 regulation by the European Union will require Strong Customer Authentication (SCA) for many online payments beginning September 14. This is the tale of upgrading a Laravel application support SCA using the new Laravel Cashier release (v10).

Blogcast doesn't follow the traditional Software as a Service (SaaS) billing model. Instead of billing a flat fee each month, it bills a flat fee every time a user converts an article. Before the upgrade, Blogcast asked for a payment method on registration, then billed it from the server each time a new article was created.

Unfortunately, this system is no longer possible with SCA, as it may require additional authentication from the cardholder at any time, which isn't always possible when creating charges from the server. This update also blocks single charges (charges not related to a subscription) from using stored payment methods, making server charges impossible, again. Keep in mind these limitations are due to the way Stripe implemented the legislation into their platform and not explicitly to the legislation.

With my current implementation deemed impossible, I had to brainstorm a new one. I didn't want to change my usage-based billing model but wanted to use some kind of subscription-like implementation as it's what both Cashier and Stripe are optimized for. Fortunately, Stripe offers metered usage subscriptions, that is, subscriptions that allow you to bill per unit. I created a new subscription and set the billing period to days instead of months. This way, users would be billed at the end of the day instead of monthly, making this system similar to the old one.

The only thing left now was updating the backend code to record usage instead of billing users directly. Fortunately, I could reuse some code I already had from a different application, so it was a matter of minutes instead of hours. Here's the code to increment the usage record of a specific user:

\Stripe\UsageRecord::create([
	// number of units to increment
	'quantity'          => 2,
	// when was this charge made?
	'timestamp'         => now()->timestamp,
	// if you skip this it'll override the units instead of adding them
	'action'            => 'increment',
	// id of the user's subscription, you can get it using Stripe's API
	'subscription_item' => $user->subscription()->asStripeSubscription()->items->data[0]['id'],
]);

Now, Stripe will handle off-session billing by themselves, emailing the users if they need extra authentication. The other advantage to the old system is that there's only one charge per day instead of one per article, making it easier for the users to recognize the charges when they show up in their bank account.

As easy as it may seem, there were a lot of changes involved in making everything work, from updating the frontend to handle the new Stripe Elements methods to updating all the automated tests.

Screenshot of the PR that implements this changes into Blogcast
Final Pull Request that implemented this changes

There are still some before the legislation is passed on September 14, but if your application processes payments online, you should definitely start looking into it. It's already hard to convince people to pay for your application, and the last thing anyone wants is a bad payment flow getting in the way.