Monthly Subscription App using Stripe, Cashier and Laravel 5.4 with example

Monthly Subscription App using Stripe, Cashier and Laravel 5.4 with example

Monthly Subscription App using Stripe, Cashier and Laravel 5.4 with example

In this tutorial, You will know how to configure Stripe with Laravel 5.4 and register subscription based user using the Laravel 5.4 Cashier package.

Laravel is the best back-end technology to easily configure stripe payment system.

Install Laravel 5.4 & Cashier using Composer

In the first step, you will have to setup your Laravel application in your working directory.

Now i will run the following command to install Laravel :

composer create-project --prefer-dist laravel/laravel blog

Above command will create a fresh laravel application. Now we need to configure laravel/cashier package for Stripe in our composer.json file :

Composer

.....
"require": {
        .....
        "laravel/cashier": "~7.0"
    },
    ......

Now run composer update command.

Service Provider (config/app.php)

Now we need to register service provider Laravel\Cashier\CashierServiceProvider in config/app.php file to the providers array.

....
'providers' => [
    ....,
    Laravel\Cashier\CashierServiceProvider::class,
]
    .....
Create the migrations

By default, you will get a migration file to create a users table so we will need some more columns to the users table and create a new subscriptions table for holding the information of the customer's subscription.

Create a new migration file by using artisan command and add following line of code in that file :

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

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

Now you are ready to migrate the database by running following command :

php artisan migrate
Create Stripe Plan

Now to get started with Stripe, we need to create an account first. Once you have created your stripe account then click Plans under Subscriptions.

Create Stripe Plan

I have created a diamond plan for testing, you can create your own subscription plan.

Now we need to get Stripe API access key to configure Stripe in Laravel and you can get keys from clicking on the API.

Now i will add the stripe key and secret in config/services.php file.

 'stripe' => [
        'model' => App\User::class,
        'key' =>'your_stripe_key',
        'secret' => 'your_stripe_secret',
    ],

Finally i need to add the Billable Trait on User Model.

app/User.php

use Laravel\Cashier\Billable;

class User extends Authenticatable
{
    use Billable;
}

Add Routes

I assume that you have set up the stripe configuration successfully.

Ok let's start with Laravel routes to register a subscription based user.

  1. Route::get('subscription', ['as'=>'subscription','uses'=>'HomeController@subscription']);
  2. Route::post('subscription', ['as'=>'post-subscription','uses'=>'HomeController@postSubscription']);
Add Controller

In this step, we will create a HomeController.php in following directory app/Http/Controllers.

  1. <?php
  2. namespace App\Http\Controllers;
  3. use Illuminate\Http\Request;
  4. use App\User;
  5. class HomeController extends Controller {
  6.     public function subscription(){
  7.         return view('subscription');
  8.     }
  9.     public function postSubscription(Request $request){
  10.         $user = new User;
  11.      $user->name = $request->name;    
  12.      $user->email = $request->email;
  13.      $user->password = bcrypt($request->password);
  14.      $user->save();
  15.      $user->newSubscription('main',$request->subscription)->create($request->token);
  16.      if ($user->subscribed('main')) {
  17.             return response()->json(['msg'=>'Successfully subscribed']);
  18.         }
  19.         return response()->json(['msg'=>'Oops there is something error with your input']);
  20.          
  21.     }
  22. }

In above code within postSubscription method, i add a user first then subscribe the user with selected plan.

You will notice that we are getting a token as parameter in "create" method, This token is generated from sensitive card data and you will see in the next step that how it will generate.

Subscription Page

In this step, we will create a file where users will fill their details, to subscribe a plan for the application.

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="utf-8">
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  6. <meta name="viewport" content="width=device-width, initial-scale=1">
  7. <title>Monthly Subscription App using Stripe, Cashier and Laravel 5.4 with example</title>
  8. <!-- Styles -->
  9. <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
  10. <link rel="stylesheet" href="http://formvalidation.io/vendor/formvalidation/css/formValidation.min.css">
  11. <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
  12. <script src="http://formvalidation.io/vendor/formvalidation/js/formValidation.min.js"></script>
  13. <script src="http://formvalidation.io/vendor/formvalidation/js/framework/bootstrap.min.js"></script>
  14. </head>
  15. <body>
  16. <div class="row">
  17. <form id="paymentForm" class="form-horizontal">
  18. <input type="hidden" name="_token" value="{{csrf_token()}}">
  19. <div class="form-group">
  20. <label class="col-xs-3 control-label">Subscription Plan</label>
  21. <div class="col-xs-5">
  22. <select name="subscription" class="form-control">
  23. <option value="diamond">Diamond ($20.00/month)</option>
  24. </select>
  25. </div>
  26. </div>
  27. <div class="form-group">
  28. <label class="col-xs-3 control-label">Name</label>
  29. <div class="col-xs-5">
  30. <input type="text" class="form-control" name="name" />
  31. </div>
  32. </div>
  33. <div class="form-group">
  34. <label class="col-xs-3 control-label">Email</label>
  35. <div class="col-xs-5">
  36. <input type="email" class="form-control" name="email" />
  37. </div>
  38. </div>
  39. <div class="form-group">
  40. <label class="col-xs-3 control-label">Password</label>
  41. <div class="col-xs-5">
  42. <input type="password" class="form-control" name="password" />
  43. </div>
  44. </div>
  45. <div class="form-group">
  46. <label class="col-xs-3 control-label">Credit card number</label>
  47. <div class="col-xs-5">
  48. <input type="text" class="form-control" data-stripe="number" />
  49. </div>
  50. </div>
  51. <div class="form-group">
  52. <label class="col-xs-3 control-label">Expiration</label>
  53. <div class="col-xs-3">
  54. <input type="text" class="form-control" placeholder="Month" data-stripe="exp-month" />
  55. </div>
  56. <div class="col-xs-2">
  57. <input type="text" class="form-control" placeholder="Year" data-stripe="exp-year" />
  58. </div>
  59. </div>
  60. <div class="form-group">
  61. <label class="col-xs-3 control-label">CVV</label>
  62. <div class="col-xs-2">
  63. <input type="text" class="form-control" data-stripe="cvc" />
  64. </div>
  65. </div>
  66. <div class="form-group">
  67. <div class="col-xs-9 col-xs-offset-3">
  68. <button type="submit" class="btn btn-primary">Sign Up</button>
  69. </div>
  70. </div>
  71. <input type="hidden" name="token" value="" />
  72. </form>
  73. </div>
  74. <script src="https://js.stripe.com/v2/"></script>
  75. <script>
  76. $(document).ready(function() {
  77. // Change the key to your one
  78. Stripe.setPublishableKey('your_stripe_key');
  79. $('#paymentForm')
  80. .formValidation({
  81. framework: 'bootstrap',
  82. icon: {
  83. valid: 'glyphicon glyphicon-ok',
  84. invalid: 'glyphicon glyphicon-remove',
  85. validating: 'glyphicon glyphicon-refresh'
  86. },
  87. fields: {
  88. name: {
  89. validators: {
  90. notEmpty: {
  91. message: 'The name is required'
  92. }
  93. }
  94. },
  95. email: {
  96. validators: {
  97. notEmpty: {
  98. message: 'The email is required'
  99. }
  100. }
  101. },
  102. password: {
  103. validators: {
  104. notEmpty: {
  105. message: 'The password is required'
  106. }
  107. }
  108. },
  109. ccNumber: {
  110. selector: '[data-stripe="number"]',
  111. validators: {
  112. notEmpty: {
  113. message: 'The credit card number is required'
  114. },
  115. creditCard: {
  116. message: 'The credit card number is not valid'
  117. }
  118. }
  119. },
  120. expMonth: {
  121. selector: '[data-stripe="exp-month"]',
  122. row: '.col-xs-3',
  123. validators: {
  124. notEmpty: {
  125. message: 'The expiration month is required'
  126. },
  127. digits: {
  128. message: 'The expiration month can contain digits only'
  129. },
  130. callback: {
  131. message: 'Expired',
  132. callback: function(value, validator) {
  133. value = parseInt(value, 10);
  134. var year = validator.getFieldElements('expYear').val(),
  135. currentMonth = new Date().getMonth() + 1,
  136. currentYear = new Date().getFullYear();
  137. if (value < 0 || value > 12) {
  138. return false;
  139. }
  140. if (year == '') {
  141. return true;
  142. }
  143. year = parseInt(year, 10);
  144. if (year > currentYear || (year == currentYear && value >= currentMonth)) {
  145. validator.updateStatus('expYear', 'VALID');
  146. return true;
  147. } else {
  148. return false;
  149. }
  150. }
  151. }
  152. }
  153. },
  154. expYear: {
  155. selector: '[data-stripe="exp-year"]',
  156. row: '.col-xs-3',
  157. validators: {
  158. notEmpty: {
  159. message: 'The expiration year is required'
  160. },
  161. digits: {
  162. message: 'The expiration year can contain digits only'
  163. },
  164. callback: {
  165. message: 'Expired',
  166. callback: function(value, validator) {
  167. value = parseInt(value, 10);
  168. var month = validator.getFieldElements('expMonth').val(),
  169. currentMonth = new Date().getMonth() + 1,
  170. currentYear = new Date().getFullYear();
  171. if (value < currentYear || value > currentYear + 100) {
  172. return false;
  173. }
  174. if (month == '') {
  175. return false;
  176. }
  177. month = parseInt(month, 10);
  178. if (value > currentYear || (value == currentYear && month >= currentMonth)) {
  179. validator.updateStatus('expMonth', 'VALID');
  180. return true;
  181. } else {
  182. return false;
  183. }
  184. }
  185. }
  186. }
  187. },
  188. cvvNumber: {
  189. selector: '[data-stripe="cvc"]',
  190. validators: {
  191. notEmpty: {
  192. message: 'The CVV number is required'
  193. },
  194. cvv: {
  195. message: 'The value is not a valid CVV',
  196. creditCardField: 'ccNumber'
  197. }
  198. }
  199. }
  200. }
  201. })
  202. .on('success.form.fv', function(e) {
  203. e.preventDefault();
  204. var $form = $(e.target);
  205. // Reset the token first
  206. $form.find('[name="token"]').val('');
  207. Stripe.card.createToken($form, function(status, response) {
  208. if (response.error) {
  209. alert(response.error.message);
  210. } else {
  211. // Set the token value
  212. $form.find('[name="token"]').val(response.id);
  213. // Or using Ajax
  214. $.ajax({
  215. // You need to change the url option to your back-end endpoint
  216. url: "{{route('post-subscription')}}",
  217. data: $form.serialize(),
  218. method: 'POST',
  219. dataType: 'json'
  220. }).success(function(data) {
  221. alert(data.msg);
  222. // Reset the form
  223. $form.formValidation('resetForm', true);
  224. });
  225. }
  226. });
  227. });
  228. });
  229. </script>
  230. </body>
  231. </html>

Using stripe, we do not need to save sensitive card data, we pass these details to the Stripe server and stripe server will return a token if we do not get any error.

Phone: (+91) 8800417876
Noida, 201301