AppDividend
Latest Code Tutorials

Laravel Cashier Braintree Payment Gateway Tutorial With Example

87

Get real time updates directly on you device, subscribe now.

In this tutorial, we will see Laravel Cashier Braintree Payment Gateway Tutorial With Example We will build a Subscription based Platform in which the user can choose a plan, and according to that, he will be charged. We use Braintree for this demo, and we will discuss the stripe in the next tutorial. So, we will build a simple payment gateway in which a user can charge according to their selected plan. It is a straightforward system to work with, and for that, we need to create a developer account at Braintree. So, if you have not created yet, then please go to this link.

Laravel Cashier Braintree Payment Gateway

Integrate payment gateway in any web application used to be a very tough task. But nowadays, there are lots of SDKs available to work with that is why it is effortless to integrate any payment gateway to any web application. Now, you can find the Official Laravel Documentation to integrate Braintree here.

Braintree Caveats

For many operations, the Stripe and Braintree implementations of the Cashier function is the same. Both services provide the subscription billing with the credit cards, but Braintree also supports the payments via PayPal. We will not use Paypal for this example. However, Braintree also lacks some features that are supported by the Stripe. You should keep the following things in mind when deciding to use Stripe or Braintree:

  • Braintree supports PayPal while Stripe does not.
  • Braintree does not support the increment and decrement methods on subscriptions. This is a Braintree limitation, not a Cashier limitation.
  • Braintree does not support percentage based discounts. This is a Braintree limitation, not a Cashier limitation.

Let us start this small project by installing the Laravel Framework. I am using Laravel 5.7 for this example.

Step 1: Install and configure Laravel

Install Laravel using the following command.

laravel new subscription

Now, go inside the project folder and configure the database inside the .env file.

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=subscription
DB_USERNAME=root
DB_PASSWORD=root

Also, install the npm dependencies using the following command.

npm install

Compile the CSS and JS files.

npm run dev

Also, create the standard authentication using the following command.

php artisan make:auth

Step 2: Install and configure Braintree package

Next step is to install the Laravel Cashier package. So let us install that.

composer require "laravel/cashier-braintree":"~2.0"

Next step is to register the Laravel\Cashier\CashierServiceProvider  service provider in your config/app.php configuration file.

...
Laravel\Cashier\CashierServiceProvider::class,
...

Now, we need to create and modify the database migrations.

First, edit the create_users_table.php file and add the following fields inside the schema.

Schema::table('users', function ($table) {
    $table->string('braintree_id')->nullable();
    $table->string('paypal_email')->nullable();
    $table->string('card_brand')->nullable();
    $table->string('card_last_four')->nullable();
    $table->timestamp('trial_ends_at')->nullable();
});

So, our final create_users_table.php file looks like below.

<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateUsersTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
            $table->string('email')->unique();
            $table->timestamp('email_verified_at')->nullable();
            $table->string('password');
            $table->string('braintree_id')->nullable();
            $table->string('paypal_email')->nullable();
            $table->string('card_brand')->nullable();
            $table->string('card_last_four')->nullable();
            $table->timestamp('trial_ends_at')->nullable();
            $table->rememberToken();
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('users');
    }
}

Also, we need to create two more migrations file. To create the files, hit the following commands.

php artisan make:migration create_plans_table
php artisan make:migration create_subscriptions_table

Now, write the following schemas inside it.

<?php

// create_subscriptions_table.php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateSubsriptionsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('subsriptions', function (Blueprint $table) {
          $table->increments('id');
          $table->integer('user_id');
          $table->string('name');
          $table->string('braintree_id');
          $table->string('braintree_plan');
          $table->integer('quantity');
          $table->timestamp('trial_ends_at')->nullable();
          $table->timestamp('ends_at')->nullable();
          $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('subsriptions');
    }
}

And the following is plans table schema.

<?php

// create_plans_table.php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreatePlansTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('plans', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
            $table->string('slug')->unique();
            $table->string('braintree_plan');
            $table->float('cost');
            $table->text('description')->nullable();
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('plans');
    }
}

Now go to the terminal and create the tables using the following command.

php artisan migrate

It will create the tables.

 

Laravel Cashier Braintree Payment Gateway Tutorial With Example

Related Posts
1 of 41

Now, add the Billable Trait inside the User.php model.

// User.php

use Laravel\Cashier\Billable;

class User extends Authenticatable
{
    use Billable;
}

Step 3: Grab and add API keys for Braintree

Now, if you have not created a developer account at Braintree, then please create one.

After that, you will be redirected to the Dashboard and then go to the Account >> My User and click the View Authorizations link under the API Keys, Tokenization Keys, Encryption Keys section.

 

API Keys, Tokenization Keys, Encryption Keys

After clicking the View Authorizations link, you will be redirected to your Sandbox API Keys page.

 

Laravel Braintree Payment Gateway Example Tutorial

Now, you need to click the View links inside the table, and now you can see your following keys and data.

  1. Your Public Key
  2. Your Private Key
  3. Your Environment
  4. Your Merchant Id

You need to add these keys inside your .env file.

BRAINTREE_ENV=sandbox
BRAINTREE_MERCHANT_ID=your merchant id
BRAINTREE_PUBLIC_KEY=your public key
BRAINTREE_PRIVATE_KEY=your private key

Next step is that you should configure the following options inside your services.php file.

// services.php

'braintree' => [
    'model'  => App\User::class,
    'environment' => env('BRAINTREE_ENV'),
    'merchant_id' => env('BRAINTREE_MERCHANT_ID'),
    'public_key' => env('BRAINTREE_PUBLIC_KEY'),
    'private_key' => env('BRAINTREE_PRIVATE_KEY'),
],

Then you should add the following Braintree SDK calls to your AppServiceProvider’s boot method.

// AppServiceProvider.php

use Braintree_Configuration;

public function boot()
{
      Braintree_Configuration::environment(env('BRAINTREE_ENV'));
      Braintree_Configuration::merchantId(env('BRAINTREE_MERCHANT_ID'));
      Braintree_Configuration::publicKey(env('BRAINTREE_PUBLIC_KEY'));
      Braintree_Configuration::privateKey(env('BRAINTREE_PRIVATE_KEY'));
}

You can also set the currency. Dollar($) is by default.

You can change the default currency by calling the Cashier::useCurrency method from within the boot method of one of your service providers. In our case we have used the AppServiceProvider. The useCurrency method accepts two string parameters: the currency and the currency’s symbol.

// AppServiceProvider.php

use Laravel\Cashier\Cashier;

Cashier::useCurrency('eur', '€');

Step 4: Create plans on Braintree dashboard

Now, for this example, we will create only two plans. You can create as per your requirement. You can find the plans inside your dashboard on the left sidebar.

I have created two plans.

  1. Basic
  2. Professional

 

Laravel Subscription Based System

Also, you need to add the plans manually inside the plans table inside MySQL.

 

Laravel Braintree Example

Make sure both plans have the same names as on MySQL and Braintree dashboard.

Step 5: Display plans on Frontend

First, define the routes for our application inside the routes >> web.php file.

// web.php

Route::group(['middleware' => 'auth'], function() {
    Route::get('/home', 'HomeController@index')->name('home');
    Route::get('/plans', 'PlanController@index')->name('plans.index');
});

We have taken the auth middleware to protect the routes related to payment and home.

Now, create the Plan.php model and PlanController.php file.

php artisan make:model Plan
php artisan make:controller PlanController

Define the index method inside the PlanController.

// PlanController.php

use App\Plan;

public function index()
{
        $plans = Plan::all();
        return view('plans.index', compact('plans'));
}

Now, inside the resources >> views folder, create one folder called plans and inside that folder, create one file called index.blade.php file. Write the following code.

@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-12">
            <div class="card">
                <div class="card-header">Plans</div>
                <div class="card-body">
                    <ul class="list-group">
                        @foreach($plans as $plan)
                        <li class="list-group-item clearfix">
                            <div class="pull-left">
                                <h5>{{ $plan->name }}</h5>
                                <h5>${{ number_format($plan->cost, 2) }} monthly</h5>
                                <h5>{{ $plan->description }}</h5>
                                
                                <a href="" class="btn btn-outline-dark pull-right">Choose</a>
                                
                            </div>
                        </li>
                        @endforeach
                    </ul>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

Save the file and navigate to the URL: http://subscription.test/plans. If you have not registered yet, then please register one user in our application.

You will see something like this.

Laravel Payment Example

 

So, we have successfully displayed the plans from the database. Now, the next step is when a user chooses the plan, you will redirect to a page from where a user can be charged for that plan.

Step 6: Show the plan

So, when the user chooses the plan, we need to redirect the user to a particular plan page.

Define one more route inside the routes >> web.php file.

// web.php

Route::group(['middleware' => 'auth'], function() {
    Route::get('/home', 'HomeController@index')->name('home');
    Route::get('/plans', 'PlanController@index')->name('plans.index');
    Route::get('/plan/{plan}', 'PlanController@show')->name('plans.show');
});

Now, by default, RouteModelBinding works with the ID of the model. But we will not pass the ID to show the particular plan instead we will pass the slug. So we need to define it inside the Plan.php model. Also, define the fillable fields as well.

<?php

// Plan.php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Plan extends Model
{
	protected $fillable = [
		'name',
		'slug',
		'braintree_plan',
		'cost',
		'description'
	];

	public function getRouteKeyName()
	{
		return 'slug';
	}
}

Here, we have defined the function called getRouteKeyName. So based on this function, now we can fetch the record based on the slug and not based on the ID. That is why we have taken the slug field as a unique field in the database.

Now, define the show() function inside the PlanController.php file.

// PlanController.php

public function show(Plan $plan, Request $request)
{
     return view('plans.show', compact('plan'));
}

Next step is to create a view file called show.blade.php inside the resources >> views >> plans folder. Add the following code inside the show.blade.php file.

@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-12">
            <div class="card">
                <div class="card-header">{{ $plan->name }}</div>
                <div class="card-body">
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

So, here, we will display the payment form.

Step 7: Display the Payment Form

For displaying the Payment Form, we will use the Drop-in UI. You can find the complete documentation here. We are using Version 2 of the Drop-in UI. There is version 3 but let us stick with version 2 for this example. Now, we are implementing the Client Side configuration. Configure the container and from where the Drop-in UI will add the payment method nonce. Make sure to replace CLIENT_AUTHORIZATION with your generated client token.

Now, we need to include the External JS files in our project. For that, we need to modify the resources >> views >> layouts >> app.blade.php file.

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <!-- CSRF Token -->
    <meta name="csrf-token" content="{{ csrf_token() }}">

    <title>{{ config('app.name', 'Laravel') }}</title>

    <!-- Fonts -->
    <link rel="dns-prefetch" href="//fonts.gstatic.com">
    <link href="https://fonts.googleapis.com/css?family=Nunito" rel="stylesheet" type="text/css">

    <!-- Styles -->
    <link href="{{ asset('css/app.css') }}" rel="stylesheet">
</head>
<body>
    <div id="app">
        <nav class="navbar navbar-expand-md navbar-light navbar-laravel">
            <div class="container">
                <a class="navbar-brand" href="{{ url('/') }}">
                    {{ config('app.name', 'Laravel') }}
                </a>
                <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="{{ __('Toggle navigation') }}">
                    <span class="navbar-toggler-icon"></span>
                </button>

                <div class="collapse navbar-collapse" id="navbarSupportedContent">
                    <!-- Left Side Of Navbar -->
                    <ul class="navbar-nav mr-auto">

                    </ul>

                    <!-- Right Side Of Navbar -->
                    <ul class="navbar-nav ml-auto">
                        <!-- Authentication Links -->
                        @guest
                            <li class="nav-item">
                                <a class="nav-link" href="{{ route('login') }}">{{ __('Login') }}</a>
                            </li>
                            <li class="nav-item">
                                @if (Route::has('register'))
                                    <a class="nav-link" href="{{ route('register') }}">{{ __('Register') }}</a>
                                @endif
                            </li>
                        @else
                          <li class="nav-item">      
                            <a class="nav-link" href="{{ route('plans.index') }}">{{ __('Plans') }}</a>
                          </li>
                            <li class="nav-item dropdown">
                                <a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" v-pre>
                                    {{ Auth::user()->name }} <span class="caret"></span>
                                </a>

                                <div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdown">
                                    <a class="dropdown-item" href="{{ route('logout') }}"
                                       onclick="event.preventDefault();
                                                     document.getElementById('logout-form').submit();">
                                        {{ __('Logout') }}
                                    </a>

                                    <form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;">
                                        @csrf
                                    </form>
                                </div>
                            </li>
                        @endguest
                    </ul>
                </div>
            </div>
        </nav>

        <main class="py-4">
            @yield('content')
        </main>
    </div>
    <!-- Scripts -->
    <script src="{{ asset('js/app.js') }}"></script>
    @yield('scripts');
</body>
</html>

Here, we have defined one navigation link called plans and also add one section for scripts. So, now we can add the Javascript per pagewise using @yield directive.

Also, we need to fetch the Client Token for authorization. So we will fetch that token from Backend using AJAX request.

So, define one route inside the web.php file.

// web.php

Route::group(['middleware' => 'auth'], function() {
    Route::get('/home', 'HomeController@index')->name('home');
    Route::get('/plans', 'PlanController@index')->name('plans.index');
    Route::get('/plan/{plan}', 'PlanController@show')->name('plans.show');
    Route::get('/braintree/token', 'BraintreeTokenController@index')->name('token');
});

Create a new Controller called BraintreeTokenController using the following command.

php artisan make:controller BraintreeTokenController

Write the following code inside the BraintreeTokenController.php file.

<?php

// BraintreeTokenController.php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Braintree_ClientToken;

class BraintreeTokenController extends Controller
{
    public function index()
    {
        return response()->json([
            'data' => [
                'token' => Braintree_ClientToken::generate()
            ]
        ]);
    }
}

The index() function returns the ClientToken to the Clientside JS file, and now we can authorize our Laravel application with the Braintree developer account.

So, write the final code inside the show.blade.php file.

@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-12">
            <div class="card">
                <div class="card-header">{{ $plan->name }}</div>
                <div class="card-body">
                <form method="post" action="{{ route('subscription.create') }}">
                    @csrf
                    <div id="dropin-container"></div>
                    <hr />
                    <input type="hidden" name="plan" value="{{ $plan->id }}" />
                    <button type="submit" class="btn btn-outline-dark d-none" id="payment-button">Pay</button>
                </form>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

@section('scripts')
<script src="https://js.braintreegateway.com/js/braintree-2.32.1.min.js"></script>
<script>
    jQuery.ajax({
        url: "{{ route('token') }}",
    })
    .done(function(res) {
        braintree.setup(res.data.token, 'dropin', {
            container: 'dropin-container',
            onReady: function() {
                jQuery('#payment-button').removeClass('d-none')
            }
        });
    });
</script>
@endsection

So, when the document is loaded, we will send an Ajax request to the Laravel server and get the Client Auth token. From that token, included js will generate the Payment Form. We defined the form like this.

<form method="post" action="">
        @csrf
        <div id="dropin-container"></div>
        <hr />
        <input type="hidden" name="plan" value="{{ $plan->id }}" />
        <button type="submit" class="btn btn-outline-dark d-none" id="payment-button">Pay</button>
</form>

Also, write the Javascript like this.

@section('scripts')
<script src="https://js.braintreegateway.com/js/braintree-2.32.1.min.js"></script>
<script>
    jQuery.ajax({
        url: "{{ route('token') }}",
    })
    .done(function(res) {
        braintree.setup(res.data.token, 'dropin', {
            container: 'dropin-container',
            onReady: function() {
                jQuery('#payment-button').removeClass('d-none')
            }
        });
    });
</script>
@endsection

So, when the request succeeds, it will return the token, and we use that token to create a Payment Form. The form looks like this.

 

Laravel 5.7 Payment Tutorial With Example

So, here we need to enter the two details. You can enter the details like below inputs.

  1. Card Number: 4242 4242 4242 4242
  2. Expiration Date: 10/21 or whatever you like

These are dummy details, but these details generally used in sandbox account to check the application.

Right now nothing will happen because we need to define the form action to store the data inside the database tables. So let us define the post route.

Step 8: Save the plan details

Define the final route inside the web.php file.

<?php

// web.php

Route::get('/', function () {
    return view('welcome');
});

Auth::routes();

Route::group(['middleware' => 'auth'], function() {
    Route::get('/home', 'HomeController@index')->name('home');
    Route::get('/plans', 'PlanController@index')->name('plans.index');
    Route::get('/plan/{plan}', 'PlanController@show')->name('plans.show');
    Route::get('/braintree/token', 'BraintreeTokenController@index')->name('token');
    Route::post('/subscription', 'SubscriptionController@create')->name('subscription.create');
});

We have defined the post route for the subscription details. Now, create SubscriptionController using the following command.

php artisan make:controller SubscriptionController

Inside that controller, we need to define one function called create().

<?php

// SubscriptionController.php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Plan;

class SubscriptionController extends Controller
{
    public function create(Request $request, Plan $plan)
    {
        $plan = Plan::findOrFail($request->get('plan'));
        
        $request->user()
            ->newSubscription('main', $plan->braintree_plan)
            ->create($request->payment_method_nonce);
        
        return redirect()->route('home')->with('success', 'Your plan subscribed successfully');
    }
}

First, we have to fetch the plan according to the id. Then we need to pass that plan to the subscribedToPlan() function.

So, here we have used the Billable trait’s subscribedToPlan() method and pass the first parameter plan and the second parameter main.

We are creating a new Subscription, and if the payment made successfully then, it would redirect to the HomePage with a  success message.

Write the following code inside the home.blade.php file.

@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            @if(session()->get('success'))
                <div class="alert alert-success">
                    {{ session()->get('success') }}
                </div>
            @endif
            <div class="card">
                <div class="card-header">Dashboard</div>

                <div class="card-body">
                    @if (session('status'))
                        <div class="alert alert-success" role="alert">
                            {{ session('status') }}
                        </div>
                    @endif

                    You are logged in!
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

So, if your all of the configurations are right, then you should go to any of the plans and try to subscribe to the plan. If you are redirecting to the homepage, then you are almost done.

 

Laravel Saas Example

You can see that one database entry inside the subscriptions table is there.

 

Laravel Saas Tutorial

That means, we have successfully subscribed the Professional plan. Also, there is updation on the user’s table. We have already added some more fields.

You can see inside your Dashboard that Sales and Transaction volume is there. So, our Laravel Cashier Braintree Payment Gateway Tutorial With Example is almost complete.

Step 9: Security Tweaks

Now, we need to keep in mind one thing that if the user is already subscribed to one plan, then we need the user to prevent to choose that plan. So, we need to add one condition on the choose button.

So, add the condition inside the index.blade.php file.

@if(!auth()->user()->subscribedToPlan($plan->braintree_plan, 'main'))
     <a href="{{ route('plans.show', $plan->slug) }}" class="btn btn-outline-dark pull-right">Choose</a>
@endif

Also, we need to add the condition inside the PlanController.php file’s show() function.

// PlanController.php

public function show(Plan $plan, Request $request)
{
    if($request->user()->subscribedToPlan($plan->braintree_plan, 'main')) {
            return redirect()->route('home')->with('success', 'You have already subscribed the plan');
    }
    return view('plans.show', compact('plan'));
}

Also, we need to do the same thing inside the SubscriptionController’s create() method.

// SubscriptionController.php

public function create(Request $request, Plan $plan)
{
        if($request->user()->subscribedToPlan($plan->braintree_plan, 'main')) {
            return redirect()->route('home');
        }

        $plan = Plan::findOrFail($request->get('plan'));
        
        $request->user()
            ->newSubscription('main', $plan->braintree_plan)
            ->create($request->payment_method_nonce);
        
        return redirect()->route('home')->with('success', 'Your plan subscribed successfully');
}

Save the file, and now you are good to go. I am putting this Laravel Cashier Braintree Payment Gateway Tutorial’s whole code on Github.

Finally, Laravel Cashier Braintree Payment Gateway Tutorial With Example article is over. There are still so many things that we can do with the project. But for basic understanding, this is enough. Thanks.

Github Code

 

Leave A Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.