This article was peer reviewed by Younes Rafie and Wern Ancheta. Thanks to all of SitePoint’s peer reviewers for making SitePoint content the best it can be!
Subscriptions to services online are something extremely common – from subscribing to music streaming services to tutorial sites to access premium content.
With Laravel 5, we saw the introduction of Laravel Cashier, an official Laravel package to help developers manage Stripe’s and Braintree’s subscription billing services without writing most of the boilerplate subscription billing code.
Stripe and Braintree are payment platforms that make it easy to accept payments in your app or website.
In this tutorial, we will be building a dummy Courses site with Braintree subscriptions. In the process, we will learn how to use the various methods offered by .
In the first part of this extensive two-part series, we are going to:
- Set up Laravel Cashier
- Sign up to the Braintree sandbox (For production apps we use the main Braintree service)
- Create plans on Braintree
- Create an artisan command to sync the online plans with our database
- Allow users to subscribe to a plan
In part two, we will:
- Add the ability to swap plans
- Create middleware to protect some routes based on the subscription status
- Protect premium courses from users with basic subscription
- Learn how to cancel and resume subscriptions
- Add Braintree notifications to a variety of the application’s events via webhooks
The complete code for part one can be found here.
Creating the Application
We will start with a fresh Laravel installation.
Preparing the Database
Next, we have to set up the database before running any migrations. Let’s use MySQL – it’s easy to use, easy to configure, and included in a well-built development environment like Homestead Improved. After setting up the database, I have my file looking like this:
The next step is adding authentication to our application.
Laravel introduced a built in Authentication module in version 5.2 which made this extremely easy.
By running the command, everything related to authentication will be generated for us i.e. the views, controllers and the routes mapping to the controller actions.
To be able to sign up and log in, we need to have tables to hold the user data. If we look inside , we’ll notice that the generated Laravel app came with migrations for creating the and tables. Let’s run these migrations:
If we navigate to , we can now sign up new users. Links for signing up and logging in are also present in the navbar.
Setting up Cashier
With the users table in place, we can now add Cashier. Since we’ll be using Braintree for this tutorial, let’s require the package:
Then, we register the in our :
Next, we have to pull in the trait in the model so as to be able to call the various cashier methods on a user:
Then we add extra columns to the table for billing purposes. We will also create a table to handle all of our subscriptions:
Open the newly generated migration and change the method to this:
Let’s now create the model and migration:
Open the migration and tweak the up method to this:
With this set up, run the Artisan command to create the table and add the extra columns to the table:
At this point, we have our Laravel side set up. Time to wire things up on the Braintree end. We will be using the Braintree Sandbox since this is not a production app. For those who don’t have a Sandbox account, sign up here, then log in.
Once inside the dashboard, generate a new API key in order to be able to use Braintree’s API in our app:
After generating the key, we also get the , and . With these in place, we need to set up configuration in our Laravel App so we can communicate with the Braintree API. Open your file and set the keys alongside their corresponding values. I have my file looking like this:
Then we add the Braintree configuration to our file:
As the final step before communicating with Braintree’s API, let’s add the following Braintree SDK calls to our service provider’s method. We will use the helper to pull in the values we set in our file. Note that we also have to import the class into our , else we won’t be able to call the various methods from the class:
The default Cashier currency is United States Dollars (USD). We can change the default currency by calling the method from within the method of one of the service providers. The method accepts two string parameters: the currency and the currency’s symbol. In this tutorial, we will just stick to USD. I have a commented out line of code illustrating how to change to Euros for example.
I must admit that setting up Cashier took a while but once done with this, we find ourselves in a position to start processing payments and manage subscriptions.
Creating Plans and Syncing Them to Our Database
Our next step is creating the plans. For this tutorial, we will be creating two: a Basic and a Premium plan. Let’s head over to the Braintree dashboard and do that. Note the trial period is optional but I set mine to 14 days:
Repeat the same process to create a premium plan but set the amount to 20 USD for a subscription.
When done creating the plans on Braintree, we can then create a plans table to store the plans locally. Let’s generate a model and a migration:
Update the method in your migration to this:
Create the table by running the migration:
Next, we want to populate the table with the data we set on Braintree. Hard coding the plans into our table is allowed but I find it a bit tedious, especially when the plan information online keeps changing. To simplify the process, we will create an artisan command to sync with the plans online and update our database:
For those not on Laravel 5.3+ run:
In , we need to change the signature value and also add a description for the command:
We then register the command to the kernel so as to be able to run it from the terminal:
If we now run , the plan-syncing command will be visible from the list of available commands:
Then the big question, what should happen after running this command? The command is supposed to clear the data in the table, then populate the table with the Plan data available online. This logic should be placed in the method inside :
Note we have to pull in the and namespaces so as to be able to call methods statically from these classes.
We should then navigate to the model and add the , , , and to the list of mass assignable attributes. If we don’t do this, we will get a when trying to update the attributes:
We can then run our command to see if everything is working as expected:
For those planning to push the app to production, it’s always a good idea to set the site to maintenance mode before syncing plans then bring the site up once the syncing process is over. In this case I would have done something like this:
Let’s now display the plans on a page. We will start by creating a route:
Then, we create a and add an action:
The index action should return a view listing all the plans:
Now let’s set up the view. Create a plans folder before creating the view:
In the view, paste the following code:
We’ve used the helper to help us display the amount to two decimal places. As it stands, the button does not lead anywhere, but we’ll fix that in a moment. If we now visit , all the plans will be displayed. Update the navbar to include a link pointing to the plans:
Your view should now look like this:
Our next step is creating the payment form where users will fill in their credit card details before subscribing to a plan.
Card Payment Form
We are going to use Braintree’s Drop-in UI for this. More documentation on how to customize the Drop-in UI can be found here.
First things first, we create a route pointing to the view. We also want to make it such that only authenticated users can perform operations involving subscriptions. Let’s create a route group and add the auth middleware. It is in this group where all routes pointing to subscription related activities will fall:
Then create the action inside our :
Since we want to pass in the slug value in the URL instead of the Plan ID, we have to override the method on the Eloquent model. By default, when using Route Model Binding in Laravel, it’s the that is returned. Let’s change that so we can have the slug value returned instead:
The “show” URL should now be in this format: . Time we created the view holding the payment form:
We can test things out by visiting or , depending on the plan name. Let’s also make the button in the plans index page point to the view:
At this point, the view is very basic and does not allow customers to enter credit card details. To load Braintree’s Drop-in UI, let’s require after the content section:
Then make sure the script is loaded in the app:
Update the show view to include a form. The is required for this to work but we’ll handle that later:
If we visit or click on the button in the plans listing page, we should be taken to a view that looks like this:
We don’t, however, want the button to be present when the payment form has not yet loaded. To achieve this, we are going to add a hidden class to the button, then remove the hidden class once the payment form shows up:
Let’s now create a new controller responsible for generating the token. We will then use ajax to set the generated token as the value. Note that needs a client token generated by the Braintree server SDK for the payment form to be loaded:
Then, inside the controller, we create a action which returns a JSON response containing the token:
Don’t forget to pull the namespace into this controller; otherwise, the method responsible for creating the token will error.
We then update our routes making it such that we can access a URL containing the JSON response from the method:
Try visiting and a token value will be displayed on the page. Our next step is pulling in this token value using AJAX into the show view. Update the code in the braintree section:
In the code block above, we are making a request to the URL in order to obtain a JSON response containing the token. This token is what we then set as the . Once the payment form loads, we remove the hidden class from our button making it visible to our customers.
With this in place, we can try reloading the page and we should see a payment form that looks like this:
It’s time we made the form come to life so users can subscribe to a plan. Let’s update our form:
The form will make a POST request to the URL – we are yet to create a route and a controller action to handle that. We also added a hidden input to our form. This will help the know the plan we are subscribing to. Then, to protect users against Cross-Site Request Forgery when submitting the form, we’ve used Laravel’s helper to generate a CSRF token.
Let’s create the route and controller responsible for subscribing users:
Inside the controller, add a action. This is the action responsible for creating and adding new subscriptions to the database:
What we are doing inside the method is getting the plan from the value we passed in the hidden input. Once we get the plan, we call the method on the currently logged in user. This method came with the trait that we required in the model. The first argument passed to the method should be the name of the subscription. For this app, we only offer monthly subscriptions and thus called our subscription . The second argument is the specific Braintree plan the user is subscribing to.
The method accepts the that we generate from Braintree as its argument. It’s the method that will begin the subscription as well as update our database with the customer ID and other relevant billing information.
After subscribing the user, we redirect them to the homepage. We will implement basic flash messaging for notifications later.
We can now confirm that things are working by filling in the form with a test card, then navigating to the Braintree dashboard to see if the card was billed. For the card number, use and set the date to a future date. Something like this:
If everything went as expected, a new payment should be reflected in your braintree dashboard. A new record will also be added to the subscriptions table.
We’ve come a long way to get to this point but users now have the ability to subscribe to a plan. We covered basic Braintree integration with Laravel, plan creation, building of commands for syncing and fetching the token, and more – everything you need to get started with Braintree subscriptions.
In the next part, we’ll prevent users from signing up to the same plan twice, amongst other things. Stay tuned.
Chris is a software developer at Andela. He has worked with both Rails and Laravel and blogs to share a few tips. Chris also loves traveling.
Lets for arguments sake say this is our database columns : username, password and finally accessLevel
With the create method you pass through an Array of post data lets take this example :
now with this approach we are not assigning input to specific fields in the database like we would using the save() approach so i can easily modify the html add a form element with the id accessLevel ( with a lucky guess or something ) and set it equal to 1 and that for instance that could resemble admin access.
With the save() method we assign exactly what we want to store where thus there cannot be a mass assignment even if post data is added to the request it will just be discarded.
If however you are not passing Input::all() to the create() method and simply passing input::only() i think it might be something with the design regarding the create method. I'm not to sure about this, but whether you pass input::all() or input::only(), the create() method still gets and array so i think it won't be able to differentiate between the 2 thus the mass assignment exception is added and $fillable or $gauderd array is needed within the model to discard unwanted post data.
This is just my thought and how i understand the subject. take it with a pinch of salt.
Just wanting to add i treat Laravel like a TV remote, i press the button and the channel changes, i'm not too concerned how the remote does what it does all i know is i get the channel i want. :) if you really want to know why i would suggest having a look at the create() method and see if you can gather why this is the case.