Laravel Stripe Payment Gateway Integration Tutorial With Example

Laravel Stripe Payment Example Tutorial

Laravel Stripe Payment Gateway Integration Tutorial With Example is today’s leading topic. We will use the Cashier package to integrate Stripe Payment Gateway in Laravel. The demand for Saas based platform is increasing day by day and nowadays building a subscription-based system is universal. So to make it easy on the backend like Laravel, we do need some packages that can help us build scalable, securable, and reliable web applications. Laravel Cashier makes it very simple to integrate Payment Gateway like Stripe. So let us see how we can integrate stripe in Laravel on Subscription based system.

Laravel Stripe Payment Gateway Integration

Let us first install and configure Laravel 5.7

Step 1: Install and configure Laravel 5.7

Type the following command.

laravel new stripesub

Go to the project folder and open the project in an editor. I am using VSCode.

cd stripesub && code .

Install the js dependencies using the following command.

npm install

Install the Cashier package for Stripe dependencies.

composer require laravel/cashier

Also, create an authentication scaffold.

php artisan make:auth

Step 2: Create an essential database migrations

Before using Cashier, we’ll also need to prepare the database. We need to add several columns to your users’ table and create new subscriptions and plans table to hold all of our customer’s subscriptions.

So, first go to the create_users_table.php and add the following schema.

$table->string('stripe_id')->nullable()->collation('utf8mb4_bin');
$table->string('card_brand')->nullable();
$table->string('card_last_four', 4)->nullable();
$table->timestamp('trial_ends_at')->nullable();

Your schema now looks like this.

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('stripe_id')->nullable()->collation('utf8mb4_bin');
         $table->string('card_brand')->nullable();
         $table->string('card_last_four', 4)->nullable();
         $table->timestamp('trial_ends_at')->nullable();
         $table->rememberToken();
         $table->timestamps();
     });
}

Now, create two more migrations files.

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

Now, write the schema on both of the files.

First, add the following schema inside the create_subscriptions_table.php file.

public function up()
    {
        Schema::create('subscriptions', function (Blueprint $table) {
            $table->increments('id');
            $table->unsignedInteger('user_id');
            $table->string('name');
            $table->string('stripe_id')->collation('utf8mb4_bin');
            $table->string('stripe_plan');
            $table->integer('quantity');
            $table->timestamp('trial_ends_at')->nullable();
            $table->timestamp('ends_at')->nullable();
            $table->timestamps();
        });
    }

Write the following schema inside the create_plans_table.php file.

public function up()
    {
        Schema::create('plans', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
            $table->string('slug')->unique();
            $table->string('stripe_plan');
            $table->float('cost');
            $table->text('description')->nullable();
            $table->timestamps();
        });
    }

Save the file and go to the terminal and migrate tables.

php artisan migrate

Now, add two plans manually on the plans table like this.

 

Laravel Stripe Payment Gateway Integration Tutorial With Example

Step 3: Get and Set Stripe API Keys

First, you need to create an account on Stripe. You can do it on here. Then after login to your account, you will find a Test dashboard.

Now, click on the Developers link on the left sidebar. In the submenu, you can find the API keys item. Click on that item, and you will be redirected to this page.

 

Stripe API keys

Here, you can find the Publishable Key or Stripe Key and Secret Key.

You need to add these keys inside the .env file in our project.

// .env

STRIPE_KEY=your key here
STRIPE_SECRET=your secret here

Finally, you should configure your Stripe key in your services.php configuration file. You can retrieve your Stripe API keys from the Stripe control panel.

// services.php

'stripe' => [
    'model'  => App\User::class,
    'key' => env('STRIPE_KEY'),
    'secret' => env('STRIPE_SECRET'),
],

Also, you need to add the Billable Trait inside the User.php model.

// User.php

use Laravel\Cashier\Billable;

class User extends Authenticatable
{
    use Billable;
}

Step 4: Create Plans on Stripe Dashboard

You can create a plan through Laravel but that will take some time, and our motto is to accept the payment through stripe. So we will create the plans manually on Stripe Dashboard.

First, go to the Billing >> Products link and right now, there are no products available. So we need to create two products. Here products mean plans. So we will create two plans.

  1. Basic
  2. Professional

Create and assign the values same as we have assigned the values on the Plans table in our Laravel application. After creating two plans, your products look like this.

 

Laravel Subscription Based System Using Stripe Payment

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></h5>
                                <h5>$ monthly</h5>
                                <h5></h5>
                                <a href="" class="btn btn-outline-dark pull-right">Choose</a>
                            </div>
                        </li>
                        @endforeach
                    </ul>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

Now, register one user on Laravel application and go to this URL: https://ift.tt/2SsY5nd

You will find the plans like this.

 

Laravel Saas Tutorial

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.

<?php

// Plan.php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Plan extends Model
{
    protected $fillable = [
        'name',
        'slug',
        'stripe_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"></div>
                <div class="card-body">
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

Now, we will display the Payment Form on this page.

Step 7: Display the Payment Form

For this example, I am using Card Element. You can find it on official Stripe Documentation. It is securely collect sensitive card details using Elements, our pre-built UI components.

 

Stripe Payment Form Example

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="">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <!-- CSRF Token -->
    <meta name="csrf-token" content="">

    <title></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="" 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="">
                    
                </a>
                <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="">
                    <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=""></a>
                            </li>
                            <li class="nav-item">
                                @if (Route::has('register'))
                                    <a class="nav-link" href=""></a>
                                @endif
                            </li>
                        @else
                          <li class="nav-item">      
                            <a class="nav-link" href=""></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>
                                     <span class="caret"></span>
                                </a>

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

                                    <form id="logout-form" action="" 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=""></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.

Next step is to write the Card Element 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="">
                <p>You will be charged $ for  Plan</p>
            </div>
            <div class="card">
                <form action="#" method="post" id="payment-form">
                    @csrf                    
                    <div class="form-group">
                        <div class="card-header">
                            <label for="card-element">
                                Enter your credit card information
                            </label>
                        </div>
                        <div class="card-body">
                            <div id="card-element">
                            <!-- A Stripe Element will be inserted here. -->
                            </div>
                            <!-- Used to display form errors. -->
                            <div id="card-errors" role="alert"></div>
                            <input type="hidden" name="plan" value="" />
                        </div>
                    </div>
                    <div class="card-footer">
                        <button class="btn btn-dark" type="submit">Pay</button>
                    </div>
                </form>
            </div>
        </div>
    </div>
</div>
@endsection
@section('scripts')
<script src="https://js.stripe.com/v3/"></script>
<script>
    // Create a Stripe client.
var stripe = Stripe('');

// Create an instance of Elements.
var elements = stripe.elements();

// Custom styling can be passed to options when creating an Element.
// (Note that this demo uses a wider set of styles than the guide below.)
var style = {
  base: {
    color: '#32325d',
    lineHeight: '18px',
    fontFamily: '"Helvetica Neue", Helvetica, sans-serif',
    fontSmoothing: 'antialiased',
    fontSize: '16px',
    '::placeholder': {
      color: '#aab7c4'
    }
  },
  invalid: {
    color: '#fa755a',
    iconColor: '#fa755a'
  }
};

// Create an instance of the card Element.
var card = elements.create('card', {style: style});

// Add an instance of the card Element into the `card-element` <div>.
card.mount('#card-element');

// Handle real-time validation errors from the card Element.
card.addEventListener('change', function(event) {
  var displayError = document.getElementById('card-errors');
  if (event.error) {
    displayError.textContent = event.error.message;
  } else {
    displayError.textContent = '';
  }
});

// Handle form submission.
var form = document.getElementById('payment-form');
form.addEventListener('submit', function(event) {
  event.preventDefault();

  stripe.createToken(card).then(function(result) {
    if (result.error) {
      // Inform the user if there was an error.
      var errorElement = document.getElementById('card-errors');
      errorElement.textContent = result.error.message;
    } else {
      // Send the token to your server.
      stripeTokenHandler(result.token);
    }
  });
});

// Submit the form with the token ID.
function stripeTokenHandler(token) {
  // Insert the token ID into the form so it gets submitted to the server
  var form = document.getElementById('payment-form');
  var hiddenInput = document.createElement('input');
  hiddenInput.setAttribute('type', 'hidden');
  hiddenInput.setAttribute('name', 'stripeToken');
  hiddenInput.setAttribute('value', token.id);
  form.appendChild(hiddenInput);

  // Submit the form
  form.submit();
}
</script>
@endsection

Here, we have used the Card element and Javascript to display the form.

Now, add the link to this page inside the index.blade.php file.

// index.blade.php

<a href="" class="btn btn-outline-dark pull-right">Choose</a>

So, it will be redirected to the page like this.

 

Stripe Payment Gateway Integration in Laravel

So, here we need to enter the following 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 from the future of today’s date
  3. CVC: whatever you like
  4. Zipcode: 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 subscriptions and accept payment

Define the final 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::post('/subscription', 'SubscriptionController@create')->name('subscription.create');
});

We have defined the POST route for subscriptions. Now, create SubscriptionController using the following command.

php artisan make:controller SubscriptionController

Now, add a POST route inside the show.blade.php file when we post the data to the server.

// show.blade.php

<form action="" method="post" id="payment-form">

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->stripe_plan)
            ->create($request->stripeToken);
        
        return redirect()->route('home')->with('success', 'Your plan subscribed successfully');
    }
}

Here, we are creating the subscription for registered User and charge him.

First, we have to fetch the plan according to the id. Then we need to pass that plan to the newSubscription() function and create a subscription.

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, 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">
                    
                </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">
                            
                        </div>
                    @endif

                    Welcome to the Dashboard
                </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.

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

 

Laravel Saas Example

Your user’s table also has been updated as you can see some field’s details is updated.

You can check the Stripe Dashboard as well. As we have subscribed the 50$ Professional Plan.

 

Stripe Payment Example

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->stripe_plan, 'main'))
    <a href="" 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->stripe_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->stripe_plan, 'main')) {
            return redirect()->route('home')->with('success', 'You have already subscribed the plan');
        }
        $plan = Plan::findOrFail($request->get('plan'));
        
        $request->user()
            ->newSubscription('main', $plan->stripe_plan)
            ->create($request->stripeToken);
        
        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 Stripe Payment Gateway Integration Tutorial’s whole code on Github.

Finally, Laravel Stripe Payment Gateway Integration Tutorial 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

The post Laravel Stripe Payment Gateway Integration Tutorial With Example appeared first on AppDividend.

via Planet MySQL
Laravel Stripe Payment Gateway Integration Tutorial With Example

Laravel Cashier Braintree Payment Gateway Tutorial With Example

Laravel 5.6 Firebase Example Tutorial

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

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></h5>
                                <h5>$ monthly</h5>
                                <h5></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"></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="">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <!-- CSRF Token -->
    <meta name="csrf-token" content="">

    <title></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="" 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="">
                    
                </a>
                <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="">
                    <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=""></a>
                            </li>
                            <li class="nav-item">
                                @if (Route::has('register'))
                                    <a class="nav-link" href=""></a>
                                @endif
                            </li>
                        @else
                          <li class="nav-item">      
                            <a class="nav-link" href=""></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>
                                     <span class="caret"></span>
                                </a>

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

                                    <form id="logout-form" action="" 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=""></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"></div>
                <div class="card-body">
                <form method="post" action="">
                    @csrf
                    <div id="dropin-container"></div>
                    <hr />
                    <input type="hidden" name="plan" value="" />
                    <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: "",
    })
    .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="" />
        <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: "",
    })
    .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">
                    
                </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">
                            
                        </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="" 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

 

The post Laravel Cashier Braintree Payment Gateway Tutorial With Example appeared first on AppDividend.

via Planet MySQL
Laravel Cashier Braintree Payment Gateway Tutorial With Example

Carol Danvers Is a Noble Warrior Hero in the New Captain Marvel Trailer 


Carol eats Skrulls for breakfast
GIF: Disney
Trailer FrenzyA special place to find the newest trailers for movies and TV shows you’re craving.  

The first trailer for Captain Marvel was big but we hadn’t seen anything yet. Now the second trailer is here, and the worlds of Carol Danvers have exploded onto the scene in ways that are sure to make your own head explode.

Earth, Space, Skrulls, Nick Fury—they are all here and then some in the new trailer for Captain Marvel. Check it out.

That trailer is incredible and, if you believe the rumors, there may be more to come from Marvel later this week. Yes, we mean that. And yet, Anna Boden and Ryan Fleck’s cosmic origin story looks so good, it almost makes us forget we’re getting two Carol-starring movies next year.

Captain Marvel, which stars Brie Larson, Samuel L. Jackson, Ben Mendelsohn, Djimon Hounsou, Lee Pace, Lashana Lynch, Gemma Chan, Rune Temte, Algenis Perez Soto, Mckenna Grace, Annette Bening, Clark Gregg, and Jude Law, opens March 8.


For more, make sure you’re following us on our new Instagram @io9dotcom. 


via Gizmodo
Carol Danvers Is a Noble Warrior Hero in the New Captain Marvel Trailer 

Database Backup Encryption – Best Practices


Offsite backup storage should be a critical part of any organisation’s disaster recovery plan. The ability to store data in a separate physical location, where it could survive a catastrophic event which destroys all the data in your primary data center, ensures your data survival and continuity of your organisation. A Cloud storage service is quite a good method to store offsite backups. No matter if you are using a cloud provider or if you are just copying data to an external data center, the backup encryption is a must in such cases. In one of our previous blogs, we discussed several methods of encrypting your backups. Today we will focus on some best practices around backup encryption.

Ensure that your secrets are safe

To encrypt and decrypt your data you have to use some sort of a password or a key. Depending on the encryption method (symmetrical or asymmetrical), it can be one secret for both encryption and decryption or it can be a public key for encryption and a private key for decryption. What is important, you should keep those safe. If you happen to use asymmetric encryption, you should focus on the private key, the one you will use for decrypting backups.

You can store keys in a key management system or a vault – there are numerous options on the market to pick from like Amazon’s KMS or Hashicorp’s Vault. Even if you decide not to use those solutions, you still should apply generic security practices like to ensure that only the correct users can access your keys and passwords. You should also consider preparing your backup scripts in a way that you will not expose keys or passwords in the list of running processes. Ideally, put them in the file instead of passing them as an argument to some commands.

Consider asymmetric encryption

The main difference between symmetric and asymmetric encryption is that while using symmetric encryption for both encryption and decryption, you use a single key or password. This requires higher security standards on both ends of the process. You have to make sure that the host on which you encrypt the data is very secure as a leak of the symmetric encryption key will allow the access to all of your encrypted backups.

On the other hand, if you use asymmetric encryption, you have two keys: the public key for encrypting the data and the private key for decryption. This makes things so much easier – you don’t really have to care about the public key. Even if it would be compromised, it will still not allow for any kind of access to the data from backups. You have to focus on the security of the private key only. It is easier – you are most likely encrypting backups on a daily basis (if not more frequent) while restore happens from time to time, making it feasible to store the private key in more secure location (even on a dedicated physical device). Below is a very quick example on how you can use gpg to generate a key pair and use it to encrypt data.

First, you have to generate the keys:

root@vagrant:~# gpg --gen-key
gpg (GnuPG) 1.4.20; Copyright (C) 2015 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

gpg: directory `/root/.gnupg' created
gpg: new configuration file `/root/.gnupg/gpg.conf' created
gpg: WARNING: options in `/root/.gnupg/gpg.conf' are not yet active during this run
gpg: keyring `/root/.gnupg/secring.gpg' created
gpg: keyring `/root/.gnupg/pubring.gpg' created
Please select what kind of key you want:
   (1) RSA and RSA (default)
   (2) DSA and Elgamal
   (3) DSA (sign only)
   (4) RSA (sign only)
Your selection?
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (2048) 4096
Requested keysize is 4096 bits
Please specify how long the key should be valid.
         0 = key does not expire
      <n>  = key expires in n days
      <n>w = key expires in n weeks
      <n>m = key expires in n months
      <n>y = key expires in n years
Key is valid for? (0)
Key does not expire at all
Is this correct? (y/N) y

You need a user ID to identify your key; the software constructs the user ID
from the Real Name, Comment and Email Address in this form:
    "Heinrich Heine (Der Dichter) <heinrichh@duesseldorf.de>"

Real name: Krzysztof Ksiazek
Email address: my@backups.cc
Comment: Backup key
You selected this USER-ID:
    "Krzysztof Ksiazek (Backup key) <my@backups.cc>"

Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? o
You need a Passphrase to protect your secret key.

This created both public and private keys. Next, you want to export your public key to use for encrypting the data:

gpg --armor --export my@backups.cc > mybackupkey.asc

Next, you can use it to encrypt your backup.

root@vagrant:~# xtrabackup  --backup --stream=xbstream  | gzip | gpg -e --armor -r my@backups.cc -o /backup/pgp_encrypted.backup

Finally, an example how you can use your primary key (in this case it’s stored in the local key ring) to decrypt your backups:

root@vagrant:/backup# gpg -d /backup/pgp_encrypted.backup | gunzip | xbstream -x
encryption: using gcrypt 1.6.5

You need a passphrase to unlock the secret key for
user: "Krzysztof Ksiazek (Backup key) <my@backups.cc>"
4096-bit RSA key, ID E047CD69, created 2018-11-19 (main key ID BC341551)

gpg: gpg-agent is not available in this session
gpg: encrypted with 4096-bit RSA key, ID E047CD69, created 2018-11-19
      "Krzysztof Ksiazek (Backup key) <my@backups.cc>"

Rotate your encryption keys

No matter what kind of encryption you implemented, symmetric or asymmetric, you have to think about the key rotation. First of all, it is very important to have a mechanism in place to rotate the keys. This might be useful in case of a security breach, and you would have to quickly change keys that you use for backup encryption and decryption. Of course, in case of a security breach, you need to consider what is going to happen with the old backups which were encrypted using compromised keys. They have been compromised although they still may be useful and required as per Recovery Point Objective. There are couple of options including re-encrypting them or moving them to a non-compromised localization.

Speed up the encryption process by parallelizing it

If you have an option to implement parallelization of the encryption process, consider it. Encryption performance mostly depends on the CPU power, thus allowing more CPU cores to work in parallel to encrypt the file should result in much smaller encryption times. Some of the encryption tools give such option. One of them is xtrabackup which has an option to use embedded encryption and parallelize the process.

What you are looking for is either “–encrypt-key” or “–encrypt-key-file” options which enable embedded encryption. While doing that you can also define “–encrypt-threads” and “–encrypt-chunk-size”. Second increases a working buffer for encryption, first defines how many threads should be used for encryption.

Of course, this is just one of the solutions you can implement. You can achieve this using shell tools. An example below:

root@vagrant:~# files=2 ; mariabackup --user=root --backup --pass=pass --stream=xbstream  |split -b 60M - backup ; ls backup* |  parallel -j ${files} --workdir "$(pwd)" 'echo "encrypting {}" ; openssl  enc -aes-256-cbc -salt -in "{}" -k mypass > "111{}"'

This is by no means a perfect solution as you have to know in advance how big, more or less, the backup will be to split it to predefined number of files matching the parallelization level you want to achieve (if you want to use 2 CPU cores, you should have two files, if you want to use 4 cores, 4 files etc). It also requires disk space that is twice the size of the backup, as at first it generates multiple files using split and then encryption creates another set of encrypted files. On the other hand, if your data set size is acceptable and you would like to improve encryption performance, that’s an option you can consider. To decrypt the backup you will have to decrypt each of the individual files and then use ‘cat’ to join them together.

Test your backups

No matter how you are going to implement the backup encryption, you have to test it. First of all, all backups have to be tested, encrypted or not. Backups may not be complete, or may suffer from some type of corruption. You cannot be sure that your backup can be restored until you actually perform the restore. That’s why regular backup verification is a must. Encryption adds more complexity to the backup process. Issues may show up at the encryption time, again – bugs or glitches may corrupt the encrypted files. Once encrypted, the question is then if it is possible to decrypt it and restore?

You should have a restore test process in place. Ideally, the restore test would be executed after each backup. As a minimum, you should test your backups a couple of times per year. Definitely you have to test it as soon as a change in the backup process had been introduced. Have you added compression to the backup? Did you change the encryption method? Did you rotate the encryption key? All of those actions may have some impact on your ability to actually restore your backup. Therefore you should make sure you test the whole process after every change.

ClusterControl can automate the verification process, both on demand or scheduled after every backup.

To verify an existing backup, you just need to pick the one from the list, click on “Restore” option and then go through the restore wizard. First, you need to verify which backup you want to restore.

Then, on the next step, you should pick the restore and verify option.

You need to pass some information about the host on which you want to test the restore. It has to be accessible via SSH from the ClusterControl instance. You may decide to keep the restore test server up and running (and then dump some partial data from it if you wanted to go for a partial restore) or shut it down.

The final step is all about verifying if you made the correct choices. If yes, you can start the backup verification job.

If the verification completed successfully, you will see that the backup is marked as verified on the list of the backups.

If you want to automate this process, it is also possible with ClusterControl. When scheduling the backup you can enable backup verification:

This adds another step in the backup scheduling wizard.

Here you again have to define the host which you want to use for backup restore tests, decide if you want to install the software on it (or maybe you already have it done), if you want to keep the restore server up and whether you want to test the backup immediately after it is completed or maybe you want to wait a bit.


via Planet MySQL
Database Backup Encryption – Best Practices

The Inventory’s 2018 Stocking Stuffer Gift Guide: 150 Genuinely Useful Gifts For Under $20

Picking out big, pricey gifts is easy. Finding inexpensive, smaller stuff for stockings, office gift exchanges, and third cousins twice removed is where gift-buying season gets tricky. That’s why we compiled this list of 150 Inventory-recommended (and actually useful) stocking stuffer ideas, all for $20 or less.*

Everything you see here is either a Kinja Deals bestseller, a reader-favorite from Kinja Co-Op, something we’ve written about on The Inventory, or a product that we use and love ourselves.

Advertisement

*Due to daily pricing fluctuations, these might not all be under $20 at all times, but they’re all frequently available for that price, and don’t often go much higher.


1. Spigen Magnetic Phone Kickstand

2. MicroSD Cards

3. Monopod

4. Tensun World Travel Adapter

I used this on a trip to France and England this year, and love that it includes four USB ports, in addition to an outlet. – Shep

5. Monet Phone Wallets

6. Magic Mouse Grips

7. USB Heated Mouse Pad Mouse Hand Warmer

I don’t know about yours, but our office is frigid. Finally, here’s solution to cold, computer-using hands. – Chelsea

8. Anker PowerCore 5000 Battery Pack

9. Qi Charging Pads

Once you have a wirelessly charging phone, you can literally never have enough of these things.

10. Magnetic Phone Desk Mount

11. Sparkr Mini Electric Lighter 2.0 | 12. Tacklife Flexible Neck Electric Lighter

13. USB Hub

I have a keyboard, mouse and some USB Christmas trees plugged in right now. – Elizabeth.

14. PortaPow 3rd Gen Data Blocker

So Android Auto will leave you alone when you just want to charge your phone in the car. – Elizabeth

15. Smart Light Bulbs

16. Velcro Cable Ties

17. HDTV Bias Lighting

18. Anker PowerLine 3-in-1 MicroUSB/USB-C/Lightning Charging Cables

19. Phone Stand

20. Rechargeable Batteries

21. Tiny Wall Charger

22. Miniature Surge Protector

23. Magnetic Waterproof Phone Pouch

24. Bluetooth Headphone Charging Bag

25. HDTV Antenna

26. Nite Ize Original Gear Ties

27. USB Power Receptacles

28. Satechi Stick-Anywhere Magnetic Phone Mounts

29. Under-Desk Headphone Hook

30. Magnetic Parts Tray

31. Succulent Candles | 32. Kikkerland Potted Pen Stand

I have a talent for killing any and all plants, but these plant-like knick knacks are both cute and immune to my flora-murdering ways. – Chelsea

33. Clipa2 – The Instant Bag Hanger

I have this and use it all the time to hang my bag on shopping carts/restaurant chairs. – Elizabeth

34. Mudder Washi Masking Tape Collection

Washi tape has so many crafty, decorative uses. I love this festive, gold-patterned pack. – Chelsea

35. Luggage Scale

It’ll pay for itself if it saves your giftee from a single overweight baggage fee. – Shep

36. Signature K9 Heavy Leather Dog Leash

37. Pet Bowl Mat

38. Post-it Extreme Notes

39. Packing Cubes

40. Drill Brush

41. Luminoodle Click

42. Toilet Light

43. Under-Bed Motion Lights

44. Anker LED Flashlights

45. Delta Showerhead

46. HAMMERHEAD 4V Lithium Rechargeable Screwdriver

47. Candles

48. Copper String Lights

49. OXO Over-The-Door Folding Hook

50. Meguiar’s Ultimate Fast Finish

It does 80% of the work of waxing, without taking an entire afternoon. – Shep

51. Slice Box Cutter

52. Umbra Casa Tissue Box

53. Mkono Self Watering Globe Plant Water Bulb

54. Neat-O Chrome-Plate Steel Large Suction Cup Sponge Holder

Leaving a wet sponge in the sink or on the counter always felt wrong to me. This holder sticks right onto the side of your sink, and let’s your soaked sponge dry in peace. – Chelsea

55. Two Trees Botanicals

56. Microfiber Cleaning Mop Slippers

You may or may not be able to find me sliding around my apartment floor wearing these fashionable mops on my feet on a Saturday night. Yes, there is music playing. Yes, I promise I have a life. – Chelsea

57. TEKTON Mini 6-Inch x 1-1/2-Inch Ratchet Bar Clamp

Most people don’t need clamps very often, but everyone should have at least one in their toolbox. I’ve used this to help repair some MDF furniture that started to split after a move. -Shep

58. Sugru

59. Accutire Digital Tire Pressure Gauge

A top seller on Amazon for a reason. It does one thing, and does it perfectly.

60. TubShroom the Revolutionary Tub Drain Protector Hair Catcher

My long hair has clogged many a drain, but I’ve said goodbye to snaking since I bought this little guy. – Chelsea

61. Label Maker

62. 3M Doorstops

63. Squatty Potty

64. GiR Spatula

65. Knife Sharpener

66. Instant Pot Ceramic Non-Stick Interior Coated Inner Cooking Pot

67. Happy Sales Steel Kitchen Garbage Sink Strainer

No more forks in the disposal! – Elizabeth

68. HQY Magnet-Automatic Beer Bottle Opener

69. OXO Jigger

Easy-to-read measurements inside, dishwasher safe, and the spout means you won’t spill anything when you pour. I use this any time I make cocktails – Shep

70. Takeya Cold Brew Coffee Maker

71. Lakemint Zoodle Chef Vegetable Spiralizer

I once tried a diet that forced me to eliminate all carbs, but I missed pasta so freaking much. This spiralizer, when used on a zucchini or squash, almost made me forget about spaghetti. Almost. – Chelsea

72. Taco Holders

They hold tacos…I don’t really know what else to add. – Shep

73. Tomorrow’s Kitchen Silicone Utensil Rest

The age-old problem of where to set your dirty utensils while you cook, solved. It’s even dishwasher safe – Shep

74. Herb Scissors

I use these any time I have to chop something herby. – Elizabeth

75. Chef’n VeggiChop Hand-Powered Food Chopper

And this when I have to deal with onions. – Elizabeth

76. MASTER COOK Kabob Skewers

Even when you aren’t using them for kabobs, they work as marshmallow sticks when you’re making s’mores. But you should make more kabobs. – Shep

77. ingenuiTEA Bottom-Dispensing Teapot

78. Copper Reusable Straws | 79. Silicone Reusable Straws

80. SiliconeZone 2-Cup Measuring Cube, Green/White

This thing is a bunch of measuring devices in one and fits better in cabinets than round cups. – Elizabeth

81. IR Thermometer

82. Cast Iron Chainmail

83. Contigo Travel Mug

84. Amazon Truffle Spread

85. Collapsible Microwave Cover

86. Cooking Gloves

 
87. Steel Tumbler

88. Williams Sonoma West Blade Citrus Zester

89. Dash Egg Cooker

90. OXO Can Opener

91. OXO Pour-Over Coffee Dripper

92. Kitchen Scale

93. Frozen Drink Glasses

94. Paring Knife

95. Thermos Can Insulators

96. Rubbermaid Easy Find Lid Storage Sets

97. Joseph Joseph Cutting Board

98. Five Pounds of Gummy Bears

99. Magnetic Knife Strip

100. Hanging Toiletry Kit

101. Shoe Bags

102. Plus Pen Style Compact Twiggy Scissors with Cover

I keep these in my bag because you need scissors way more often than you’d think. Legit used them today. – Elizabeth

103. Mini Air Compressor

104. Magnetic Smartphone Vent Mounts

 
105. A Tiny Car Charger

106. DenTek On-the-Go Flossers

Travel floss for when you get popcorn in your teeth at the movies. – Elizabeth

107. BEARZ Compact Pocket Blanket

108. UCO Stormproof Matches

109. LifeStraw

110. Gerber Shard

111. Repel 100 Bug Spray

112. UCO Candle Lantern

113. USB Fan

114. HEROCLIP Carabiner

115. IMAK Compression Eye Mask

116. No-Pressure Eye Masks

For people who have trouble sleeping unless it’s totally dark. – Elizabeth

117. Melatonin Gummies

118. Marvy Shampoo Brush and Scalp Invigorator

I think we can all agree that the best part of a haircut is the scalp massage during the shampoo. This little tool allows you to recreate the feeling at home in the shower. – Chelsea

119. OXO Mouthwash Bottle

120. Dreamfarm Tapi

121. Upper Canada Softest Plush Spa Headband

If you own a face mask, you should also own this headband. Because getting mask out of your hair is the worst. – Chelsea

122. Hearos Ear Plugs

123. Oars + Alps Natural Solid Face Wash

124. Garnier SkinActive Micellar Cleansing Water

The fastest and easiest way to wash your face – Elizabeth

125. Bio-Oil Multiuse Skincare Oil

My dry skin requires that I moisturize in winter, but sometimes, lotion just feels too gloppy. Bio-Oil is a non-greasy alternative that I cannot recommend enough. – Chelsea

126. Queen Helene Mint Julep Masque

Mask newbs and mask veterans alike will enjoy this delightful minty breakout zapper. – Chelsea

127. Iron Lion Soap

128. Mario Badescu Glycolic Foaming Cleanser

129. Tiger Balm White Ointment

I rub some Tiger Balm on my nose when I feel a cold coming on. And sometimes I find myself sniffing it even when I’m not congested. – Chelsea

130. Burt’s Bees Tinted Lip Balm

131. skyn ICELAND Hydro Cool Firming Eye Gel

132. Paula’s Choice Lipscreen SPF 50

133. Nose Hair Trimmer

134. Vinyl Records

135. Fabric Shaver

136. Hanes Ultimate Men’s Comfort Flex Fit Boxer Briefs

137. Dritz Thermal Thimbles

These have saved me so much pain when using a hot glue gun. – Elizabeth

138. Umbra Poise Large Jewelry Tray

This two-tiered tray is interesting enough to not be an eyesore, and cool enough to disguise the fact that I’m kind of a slob when it comes to my jewelry. – Chelsea

139. Whitmor Clip and Drip Hanger

Clothes last longer when they air dry, and this thing lets you hang a ton of socks, underwear, and even larger clothes from your shower rod. – Shep

140. Dryer Balls

141. NOMATIC BASICS Wallet

142. Aerie Boybriefs

143. Portable Stain Removers

144. Strap Saver

145. A Big-Ass Mouse Pad

146. USB SNES Controller

147. Pixel Pals

148. Cards Against Humanity Storage Box

If you can’t fit all your expansion packs in your original CAH box anymore, this is the solution. – Shep

149. Plus-Plus Blocks

150. Big Bubble Wands


Many of these products were also included in last year’s guide, but you can check it out for even more ideas.



via Lifehacker
The Inventory’s 2018 Stocking Stuffer Gift Guide: 150 Genuinely Useful Gifts For Under $20

Improve Your Ruger 22 Charger with Adaptive Tactical Rimfire Accessories


Adaptive Tactical's Rimfire Accessories line for the Ruger 22 Charger
Adaptive Tactical’s Rimfire Accessories line for the Ruger 22 Charger

Nampa, Idaho (Ammoland.com) – Adaptive Tactical, LLC, manufacturers of innovative firearm stocks and accessories, can take your factory Ruger 22 Charger or 22 Charger Takedown and improve its performance, all while increasing its accuracy and versatility through several products from its Rimfire Accessories line. The Tac-Hammer Ruger 22 Charger Barrel/Rail Combo, Tac-Hammer Ruger 22 Charger Takedown Barrel/Rail Combo and TK22C Ruger 22 Charger Takedown stock are the ideal upgrades for a plinker’s dream firearm.

Adaptive Tactical Tac-Hammer Ruger 22 Charger barrel/rail combo
Adaptive Tactical Tac-Hammer Ruger 22 Charger barrel/rail combo

The Tac-Hammer Ruger 22 Charger Barrel/Rail Combo and Tac-Hammer Ruger 22 Charger Takedown Barrel/Rail Combo will increase accuracy and improve the performance and portability of the Ruger 22 Charger when upgrading from the original factory barrel. The Rigid-Core Barrel delivers the benefits of a bull barrel’s larger diameter and related stiffness – greater accuracy and repeatability of the shot – without the added weight. A rigid-core, stepped barrel design is encased within a post-tension, aluminum shroud, offering the rigidity of a bull barrel with a significant weight reduction. The Tac-Hammer Package includes barrel, cantilevered optic rail and front threaded compensator. The cantilevered rail design ensures consistent zeroing for Takedown firearms.

Specifications:

Barrel: P4140 Chromoly steel with heat and rust resistant coating
Shroud: 6061 aluminum with durable Cerakote® coating
Top Rail/Compensator: 6061 aluminum with color matched Cerakote
Twist: 1:16
Threaded Barrel End: ½ x 28 (fits common compensators or suppressors)
Barrel OD: 0.92”
Barrel Length:

Without Compensator:

With Compensator:

Exposed Barrel w/ Comp:

 

9”

10.125”

8.875”

The Tac-Hammer TK22C Ruger 22 Charger Takedown Stock is made from a durable, all season polymer construction that provides increased versatility. The removable barrel insert provides a custom fit for both standard tapered barrels and 0.92” bull barrels. The pistol grip is designed to fit the spring-loaded TacTRED™ Monopod for increased stability (sold separately). It includes a custom firearm sling, which improves stability when shooting. Simply attach to the stock’s rear loop and shoulder; aim is improved due to full extension along the arm. It also features an integrated rear single point sling attachment loop and front bipod swivel mount.

For more information on Adaptive Tactical, visit www.adaptivetactical.com or “Like” us on Facebook and “Follow” us on Instagram.


About Adaptive Tactical, LLC:Adaptive Tactical Logo

Adaptive Tactical’s design team, a proven leader in firearm stock and accessory innovation, led the way in award winning recoil dampening shotgun and rifle stocks and accessories. Manufacturers of the popular Sidewinder Venom™ mag-fed shotgun system and ADTAC stock systems, Adaptive offers products focused on improving speed, performance and versatility for military, LE, defense, range and competition applications. www.adaptivetactical.com


via AmmoLand.com
Improve Your Ruger 22 Charger with Adaptive Tactical Rimfire Accessories

The Truth About AR-15 Rifles


The Truth About the AR-15 Rifle

courtesy Nick Leghorn for TTAG

[Ed: Nick originally wrote this less than a week after the Sandy Hook shooting, but it’s still just as relevant today.]

In the wake of recent events, it’s obvious and unfortunate that the vast majority of pundits have no idea what they’re talking about when it comes to guns. Especially with a firearm like the AR-15 (a.k.a., “assault rifle”). Scanning the press coverage, there’s no end of misinformation about the ArmaLite Rifle (that’s what AR means, not “assault rifle”) design and why it’s the most popular rifle in the United States. Hopefully I can put some of that right.

Versatility

Before the AR-15 rifle made its way onto the market, gun owners needed to buy a different gun for each caliber and application.

Whether they wanted inexpensive target shooting (with cheap ammo like .22 LR) or deer hunting (with a more substantial caliber like .308 Winchester), shooters had to buy a different firearm for each use. That made changing calibers expensive, time-consuming, and generally a one-way process.

Shooters were also stuck with their rifle’s ergonomics. If the stock was too long or too short there wasn’t much they could do short of paying a gunsmith to modify their firearm. The same was true if you didn’t like the rifle’s trigger or its sights. Changing just about anything was a major pain in the butt.

With an AR-15, however, gun owners don’t need a qualified gunsmith to modify or customize their gun. The average shooter can order the parts they need online and perform the work themselves with little more than a screwdriver, a wrench, a hammer and a YouTube tutorial. [Click here for a handy how-to.]

In fact, there’s only one part of the gun that an owner has to buy through a gun shop: the “receiver” or lower (above). That’s the serialized part of an AR pattern rifle. Technically, as far as the ATF is concerned, that is the gun. I’ve assembled all of my own AR-15 rifles from scratch, having purchased only the receivers through gun stores.

Everything about the AR-15 platform can be swapped out to fit the specific end-user and their intended use. Long-range shooters might add a longer barrel and more powerful scope for increased accuracy. Those interested in home defense might choose a shorter barrel, a red dot and add a flashlight to the gun. You can even change the grip and fore end to fit your hand exactly and make shooting more comfortable.

Hunting

Gun control advocates, the media and many politicians are fixated on the idea that AR-15s are “military weapons” or “weapons of war” that “have no place on our streets.” We hear again that they’re not suitable for hunting.

Not true.

Hundreds of thousands of hunters use the AR-15 platform which is often sold in complete configurations specifically designed for hunting. The gun is rugged, reliable, portable and accurate. What’s more, the ability to quickly and easily change the rifle’s caliber offers American hunters a huge advantage.

deer hunting with AR-15 rifle

courtesy ar15.com

I use an AR-15 that fires the relatively new 300 AAC Blackout round for hunting in Texas. When deer aren’t in season I swap my AR’s upper receiver for one that shoots the much cheaper .22 LR cartridge. This kind of caliber swap cuts down on costs and makes hunters more accurate (since they can afford to practice with their hunting rifle all year long).

Self-defense

The AR-15 is the civilian version of the M-16 rifle, as adopted by the U.S. armed forces. The M-16 was developed in the wake of World War II. Generals wanted a rifle that would allow U.S. servicemen to put rounds on target accurately at extreme distances (as they did with the M1 Garand in WWII).

That’s the reason the rifle originally came with a bulky stock and precision “aperture” sights. The Powers That Be wanted their troops to take precise, aimed shots from the shoulder. So despite what the media would have you believe, the AR-15 was not designed to “spray” bullets. It was originally created as a precision rifle.

And despite plenty of misinformation to the contrary, civilian AR-15 rifles are semi-automatic. That means one round per trigger pull. Actual fully automatic machine guns are rare as hens teeth and prohibitively expensive thanks to regulation that goes back as far as 1934.

A great offensive weapon also makes a great defensive weapon. The AR-15 is an easy-to-use and effective rifle for personal and home defense. If someone was defending say, a school, and they were positioned at the end of a corridor, an AR-15 would give them the speed, repeatability (i.e. ammunition capacity) and/or accuracy they’d need to eliminate a lethal threat. Or threats.

Which is why so many Americans depend on the AR-15 for the self-defense. It’s also the reason that police rely on AR-15s to counter active shooters.

 


via The Truth About Guns
The Truth About AR-15 Rifles

Black Friday Deal: Seagate 8TB Hard Drive for $129

Black Friday Deal: Seagate 8TB Hard Drive for $129

By Leave a Comment

This Seagate 8TB USB 3.0 external hard drive is $129.99 (reg. $159.99) for Black Friday. Check it out here at B&H Photo and Amazon.

Also, check out more hard drive deals here on Amazon.

Watch for more deals this weekend on the Black Friday and Cyber Monday Camera Deals page.

Subscribe to the Photography Bay Deals Newsletter to catch great deals like this and other photo gear deals daily. Sign up here.

via Photography Bay
Black Friday Deal: Seagate 8TB Hard Drive for $129

The Mad Scientists at Jell-O Have Figured Out How to Make Edible Slime


Your body is probably still digesting yesterday’s fantastic feast (and bracing for leftovers) but it’s already time to start planning next year’s Thanksgiving dinner, and now that Kraft Foods has seemingly done the impossible and invented edible slime, grandma’s Jell-O salad will never be the same.

Not content with just re-engineering the classic Jell-O formula so that the dessert can be used to make jiggly toy building blocks, a team of food scientists hidden away somewhere in Kraft Foods’ secret laboratories asked a question no one has ever asked before: what if slime was edible? Now this is not to imply that slime has never been ingested before; there’s undoubtedly countless children who’ve sampled their gooey play toy out of curiosity, or to prove their playground bravado. But Jell-O’s new DIY slime—part of its recent ‘Play’ line—is completely edible without the risk of going blind or needing your stomach pumped after snacking.

Whipping up a batch is apparently as easy as just adding water to an included mix and stirring for 30 seconds, at which point you’ll be left with a brightly colored non-Newtonian fluid that you can squeeze, squish, massage, and tear with your hands for about an hour before you’ll need to add more water to keep it fluid. There will be two flavors available at launch: pink (unicorn-themed) strawberry and green (monster-themed) lime, but there’s no word on how much each container will cost when they hit stores next month.

So what’s in the slime? According to the listing on Amazon, it contains “modified food starch, sugar, gelatin, contains less than 2% of adipic acid, disodium phosphate, sodium citrate, artificial flavor, fumaric acid, red 40.” Nothing we haven’t seen in other highly-processed snacks before, and a distinct lack of real monster or unicorn ingredients, which some might find disappointing. But those people have obviously forgotten how to have fun with their food.

[Amazon via Comicbook.com]


via Gizmodo
The Mad Scientists at Jell-O Have Figured Out How to Make Edible Slime