Creating Stripe Setup Intents With Laravel API and VueJS SPA
Part 5 of 7 in Using Laravel Cashier with VueJS SPA and Laravel Passport APIWe are making progress in implementing our Subscription component! Next up, we have to get our setup intent.
When I first started implementing Stripe into applications for recurring transactions, the concept of the set up intent was a little confusing to me. In the Laravel 6.x Cashier docs (https://laravel.com/docs/6.x/billing#payment-methods) it states that “A “Setup Intent” indicates to Stripe the intention to charge a customer’s payment method.” What kind of made sense to me was to look at the setup intent as “hey, we need to save your card because you intend on letting us charge it for our service at a certain rate”. It sounds a lot more complicated than it is, but Stripe and Laravel make it a breeze. We are just going to put it in an API + SPA form.
The setup intent will be sent to Stripe along with the credit card info so Stripe will store the information on behalf of the user and return the proper data to us to store locally so we can charge accordingly.
Before you can actually charge for a subscription and create that subscription, you have to have a card on file. In this tutorial we are taking the first steps to getting there! The next step will be actually persisting the card and getting the payment ID so we can charge the customer accordingly.
1. Add API Route To Grab Setup Intent
This is right away where this tutorial diverges from the official Laravel documentation. In the official Laravel 6.x docs on Storing Payment Methods (https://laravel.com/docs/6.x/billing#storing-payment-methods), the documentation goes through returning a blade view in a call and response type structure, with a variable that is the setup intent token. We don’t have that available in our Single Page Application, so we have to do it a little bit differently.
As long as you have Laravel Passport installed (https://laravel.com/docs/6.x/passport), and configured, open up routes/api.php
. All of the API routes we are using in this course will be behind the auth:api
middleware which requires a valid authentication token to be present. This is VERY important so no matter how you secure the routes (group, middleware in constructor, etc) make sure they are behind the auth:api
middleware.
I like putting all of my secure API routes within a secure route group. I find it easier to organize and view so I can place secure routes in that group easier.
This is what my routes/api.php file looks like:
Route::group(['prefix' => 'v1', 'middleware' => 'auth:api'], function(){
});
If you are implementing in an existing application, you might have a ton of existing routes. Just make sure the routes we add next are blocked by the auth:api
middleware. I also have a v1
prefix for my API so it makes versioning easer. That’s a different subject entirely, but you won’t have to worry about that in the scope of this course.
Next, add the following route within your secure route group:
Route::get('/user/setup-intent', 'API\UserController@getSetupIntent');
2 things to note. The route will resolve to a GET
request on /api/v1/user/setup-intent
with the v1
prefix that I have (may be different on your API) and I am using an API
namespaced UserController.php
. I put this in a controller and specifically namespaced it for API usage. It will respond on the method getSetupIntent()
.
2. Add getSetupIntent method to UserController
Now we need to handle the response to the /api/v1/user/setup-intent
route. Open up your controller, in this case the API\UserController
and add the following code:
namespace App\Http\Controllers\API;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
class UserController extends Controller
{
/**
* Creates an intent for payment so we can capture the payment
* method for the user.
*
* @param Request $request The request data from the user.
*/
public function getSetupIntent( Request $request ){
return $request->user()->createSetupIntent();
}
}
The createSetupIntent()
is a trait on the User
model that returns the setup intent object client_secret
. We will pass an object back to our SPA (unlike in the documentation where it binds it to a blade view). Since we are calling our API, this returns a beautiful, JSON formatted response that we can easily integrate into our SPA.
3. Using the Setup Intent in the VueJS SPA
Let’s switch back to the SubscriptionManagement.vue
component that we started making in Using Stripe Elements in a VueJS Component.
First, let’s add a local variable to our data()
object named intentToken
. This will house the response from the API.
data(){
return {
...
intentToken: ''
}
},
Next, let’s add a method that loads the intent from the API and add this to the methods
object in the local component:
/*
Loads the payment intent key for the user to pay.
*/
loadIntent(){
axios.get('/api/v1/user/setup-intent')
.then( function( response ){
this.intentToken = response.data;
}.bind(this));
},
This will take the response and save it to the local intentToken variable so we can attach it to the request to Stripe in the next tutorial.
Finally, let’s look at our mounted()
lifecycle hook. In there, we will add a call to the loadIntent()
method like this:
mounted(){
...
this.loadIntent();
},
Now when our component is mounted, we will load the intent to setup a new payment. There are a LOT of different UX things you can do with this. Maybe you don’t want to display the form right away to add a payment, that way you don’t need a setup intent. If that’s the case, don’t call the this.loadIntent()
from inside mounted
. Call it when you want the user to begin adding their payment.
The flow of getting the setup intent from within the SPA is as follows:
- Our Subscription Management component is
mounted()
- We call the
loadIntent()
method which loads the setup intent from/api/v1/user/setup-intent
. - Upon completion, this token (the object returned) is stored to the local
intentToken
variable. Theclient_secret
key will be used when we call the Stripe API.
A few final notes. Since we are getting a client_secret
from the API for the setup intent, it’s good to have protections in place (such as being behind the auth:api
middleware). It’s also okay to note that we are saving this to a local variable. Our Stripe client secret is still housed in our .env
server side. Even in the examples provided, this intent token is stored in the HTML.
Let me know your thoughts and if you have any questions in the comment section below! In the next tutorial we will be actually saving the payment and creating a subscription!