Laravel Cashier has released today with support for SCA in Stripe, and removing support for Braintree.
A big shout out has to go to Dries Vints though, for what has been nothing less than almost a total re-write of the package and it's documentation.
Fundamental changes to flow
Previous versions of Cashier stepped in AFTER you'd generated a payment source token on the client side - either by using Stripe Elements or the now defunkt popup Checkout.
The old workflow was something like this:
- Generate a payment source token on the client side - by having the user enter their card details.
- Pass this token to Cashier to begin a subscription, or update the payment method.
- Profit $$$
The new way is a little more complicated, and has to take into account:
- 3D secure Version 2 (where customers may be asked by the bank to confirm their purchase using 2-factor authentication methods)
- Payment confirmations, where they have to confirm their payment by entering their details again.
- Off-session payment confirmations, where the bank decides a recurring payment may need confirmation, and the user is not directly accessing your website at the time.
Because off-session confirmations and confirmations can add extra time to the process, you can no longer directly verify that a payment has been completed during the space of one server request as you have been able to do with Cashier in the past.
So the new flow looks something like this when someone is purchasing a subscription:
Generate an "Intent" (in this case, a
SetupIntent) which allows stripe to track the status of a payment or payment method.
You can either manually attach this payment method to a customer, or pass it to the subscription create method where it will it attach it for you.
The payment will either complete, require 3D Secure confirmation, require a payment confirmation, or fail for standard card-processing reasons.
(If 3D Secure is required) the customer follows this process, stripe elements will create a popup window for this, and it will either confirm the payment, fail, or also require a confirmation.
(If a confirmation is required) you can choose to either tell the user that a confirmation is required, and let Stripe send them an email, or you can redirect them to the confirmation route that ships with cashier yourself. Either way, the subscription will still not be active.
If the payment is successful, and all confirmations and extra steps that were required are completed successfully, stripe will send a webhook to your application which will confirm the subscription.
Looks daunting doesn't it? But not that much has changed except that:
There is an extra entity to create at first, the
SetupIntent, this is done by calling
$user->createSetupIntent(), so all the hard work is done for you!
You need to pass the publishable key of this intent to the Strip Elements library, and use
stripe.handleCardPaymentfrom the JS SDK rather than
Stripe.createTokenthat you may previously have been using.
Webhooks are now the main way in which a subscription will be confirmed, so you have to be able to test these locally.
Because subscriptions make recurring charges in the background, sometimes the payment provider or bank may want extra confirmation that it's okay to charge this amount again.
When this happens, Cashier will send a webhook () to your app, which will trigger an email to the customer that they require additional confirmation.
To get your app to handle this, add the handler to your
You can also, I believe, get Stripe to handle the email and confirmation payment via billing emails.
If this confirmation is not provided, it will act like the payment has failed and move on to the dunning process you've specified in your Stripe settings.
Webhooks, and testing them locally.
Webhooks can be a pain, Stripe has some great tooling to let you see the payloads it sends to your application and inspect them, but it also brings up one big problem - testing webhooks locally.
The official docs mention the fact that you can use the
valet share command to test webhooks, this will give you an Ngrok URL that securely tunnels traffic into your local copy of your app, meaning you can not only receive webhooks locally, but also inspect their content using Ngrok's tools.
Another tool i use for ease of use (as it gives you a more fixed URL) is Ultrahook - it's also handy if you don't happen to be using Laravel Valet to run your sites locally.