Creating Stripe Setup Intents With Laravel API and VueJS SPA

Part 5 of 7 in Using Laravel Cashier with VueJS SPA and Laravel Passport API
Dan Pastori avatar
Dan Pastori December 17th, 2019

We 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:

  1. Our Subscription Management component is mounted()
  2. We call the loadIntent() method which loads the setup intent from /api/v1/user/setup-intent .
  3. Upon completion, this token (the object returned) is stored to the local intentToken variable. The client_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!

Keep Reading
View the course View the Course Using Laravel Cashier with VueJS SPA and Laravel Passport API
Up Next → Managing Stripe Payment Methods in VueJS SPA and Laravel API

Support future content

The Ultimate Guide to Building APIs and Single-Page Applications with Laravel + VueJS + Capacitor book cover.

Psst... any earnings that we make off of our book is being reinvested to bringing you more content. If you like what you read, consider getting our book or get sweet perks by becoming a sponsor.

Written By Dan

Dan Pastori avatar Dan Pastori

Builder, creator, and maker. Dan Pastori is a Laravel certified developer with over 10 years experience in full stack development. When you aren't finding Dan exploring new techniques in programming, catch him at the beach or hiking in the National Parks.

Like this? Subscribe

We're privacy advocates. We will never spam you and we only want to send you emails that you actually want to receive. One-click unsubscribes are instantly honored.