If you are running an online store or some kind of paid service, you probably need to accept card payments on your application. Stripe is one of the popular payment gateways which accept credit or debit card payments online. In this article, we study Stripe payment gateway integration in Laravel.
For integrating Stripe gateway in Laravel we are going to use the Omnipay library which is popular among the developers. I am going to integrate the Payment Intents API with Omnipay.
The benefits of using Stripe Payment Intents API are as follows.
- Automatic authentication handling
- No double charges
- No idempotency key issues
- Support for Strong Customer Authentication (SCA) and similar regulatory changes
The cards which require SCA or two-factor authentication will charge only after the customer validates the purchase.
Having said that, let’s learn Stripe integration in Laravel.
Get API Keys for Stripe Integration
First, you need to create a Stripe account if you don’t have one already. From your Stripe dashboard, grab the secret and publishable key. You will get these keys from the Developers->API Keys page.
I’d recommend first testing the code with sandbox credentials. It is better to test the transactions with sandbox keys. If everything works well then go for live keys.
Basic Setup in Laravel for Stripe Integration
To start accepting online payments using Stripe, you need to perform a basic setup.
We will require stripe keys during API integration so add these keys to the .env
file.
STRIPE_PUBLISHABLE_KEY=PASTE_PUBLISHABLE_KEY
STRIPE_SECRET_KEY=PASTE_SECRET_KEY
STRIPE_CURRENCY=USD
Whenever you add new constants in the environment file, you should clear the configuration cache.
php artisan config:cache
When it comes to online payments, one should store the transaction details in the database. For this, create a migration file using the command:
php artisan make:migration create_payments_table
In the migration, edit the up
method as follow:
public function up()
{
Schema::create('payments', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('payment_id');
$table->string('payer_email');
$table->float('amount', 10, 2);
$table->string('currency');
$table->string('payment_status');
$table->timestamps();
});
}
Next, run the below command to execute the migration.
php artisan migrate
We will require an HTML form where a user can enter their card details and other information. Create a payment.blade.php
file and add the code below to it.
<link rel="stylesheet" href="{{ asset('/css/style.css') }}" />
<script src="https://js.stripe.com/v3/"></script>
@if ($message = Session::get('success'))
<div class="success">
<strong>{{ $message }}</strong>
</div>
@endif
@if ($message = Session::get('error'))
<div class="error">
<strong>{{ $message }}</strong>
</div>
@endif
<form action="{{ url('charge') }}" method="post" id="payment-form">
<div class="form-row">
<p><input type="text" name="amount" placeholder="Enter Amount" /></p>
<p><input type="email" name="email" placeholder="Enter Email" /></p>
<label for="card-element">
Credit or debit card
</label>
<div id="card-element">
<!-- A Stripe Element will be inserted here. -->
</div>
<!-- Used to display form errors. -->
<div id="card-errors" role="alert"></div>
</div>
<p><button>Submit Payment</button></p>
{{ csrf_field() }}
</form>
<script>
var publishable_key = '{{ env('STRIPE_PUBLISHABLE_KEY') }}';
</script>
<script src="{{ asset('/js/card.js') }}"></script>
In the blade file, I included CSS and JS files which I would define in the next steps. I also added success and error messages which would display after completing a payment. Next, define the routes as follows.
routes/web.php
Route::get('/payment', 'PaymentController@index');
Route::post('/charge', 'PaymentController@charge');
Route::get('/confirm', 'PaymentController@confirm');
I will create the PaymentController
in the later part of the tutorial.
Generate Stripe Card Elements
Stripe provides its own prebuilt UI components that collect customer card data securely without handling sensitive data. The card details are converted to ‘Token’ which then needs to be sent to your servers. Using this ‘Token’ you can charge the customers. This is a way secure as your application doesn’t need to store or interact with customer card details.
In the blade, we included card.js
file. Create this JS file under the public/js
directory and add the below code to it.
js/card.js
// Create a Stripe client.
var stripe = Stripe(publishable_key);
// 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',
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();
}
The blade file has also included a stylesheet style.css
. Create style.css
inside the public/css
folder. This CSS will have the below code.
css/style.css
.StripeElement {
box-sizing: border-box;
height: 40px;
padding: 10px 12px;
border: 1px solid transparent;
border-radius: 4px;
background-color: white;
box-shadow: 0 1px 3px 0 #e6ebf1;
-webkit-transition: box-shadow 150ms ease;
transition: box-shadow 150ms ease;
}
.StripeElement--focus {
box-shadow: 0 1px 3px 0 #cfd7df;
}
.StripeElement--invalid {
border-color: #fa755a;
}
.StripeElement--webkit-autofill {
background-color: #fefde5 !important;
}
When you visit the ‘/payment’ route you will see a form with built-in UI elements for the card. Stripe is providing different kinds of UI elements for building a checkout form with credit card details. Read more about it in their documentation.
Stripe Payment Gateway Integration in Laravel
We set all required configurations. Now we can go ahead and integrate the Stripe payment gateway in Laravel. Run the command below to install the Omnipay library in your project.
composer require league/omnipay omnipay/stripe
To call the blade file and charge the transaction create a PaymentController
using the artisan command:
php artisan make:controller PaymentController
As we should store transaction details in the database, create a model Payment
which is associated with the payments table in the database.
php artisan make:model Payment
Stripe Payment Intents API
The Payment Intents API handles the checkout flow and triggers additional authentication steps if required. Your customers might get redirected to their bank page for the authentication process. Once they complete the purchase, the customer will redirect to the URL set by the application. In our case, we set the redirect URL to the route('confirm')
.
The PaymentController
will have the following code which follows the Payment Intents API to charge the transaction and insert the transaction details into the database.
PaymentController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Omnipay\Omnipay;
use App\Models\Payment;
class PaymentController extends Controller
{
public $gateway;
public $completePaymentUrl;
public function __construct()
{
$this->gateway = Omnipay::create('Stripe\PaymentIntents');
$this->gateway->setApiKey(env('STRIPE_SECRET_KEY'));
$this->completePaymentUrl = url('confirm');
}
public function index()
{
return view('payment');
}
public function charge(Request $request)
{
if($request->input('stripeToken'))
{
$token = $request->input('stripeToken');
$response = $this->gateway->authorize([
'amount' => $request->input('amount'),
'currency' => env('STRIPE_CURRENCY'),
'description' => 'This is a X purchase transaction.',
'token' => $token,
'returnUrl' => $this->completePaymentUrl,
'confirm' => true,
])->send();
if($response->isSuccessful())
{
$response = $this->gateway->capture([
'amount' => $request->input('amount'),
'currency' => env('STRIPE_CURRENCY'),
'paymentIntentReference' => $response->getPaymentIntentReference(),
])->send();
$arr_payment_data = $response->getData();
$this->store_payment([
'payment_id' => $arr_payment_data['id'],
'payer_email' => $request->input('email'),
'amount' => $arr_payment_data['amount']/100,
'currency' => env('STRIPE_CURRENCY'),
'payment_status' => $arr_payment_data['status'],
]);
return redirect("payment")->with("success", "Payment is successful. Your payment id is: ". $arr_payment_data['id']);
}
elseif($response->isRedirect())
{
session(['payer_email' => $request->input('email')]);
$response->redirect();
}
else
{
return redirect("payment")->with("error", $response->getMessage());
}
}
}
public function confirm(Request $request)
{
$response = $this->gateway->confirm([
'paymentIntentReference' => $request->input('payment_intent'),
'returnUrl' => $this->completePaymentUrl,
])->send();
if($response->isSuccessful())
{
$response = $this->gateway->capture([
'amount' => $request->input('amount'),
'currency' => env('STRIPE_CURRENCY'),
'paymentIntentReference' => $request->input('payment_intent'),
])->send();
$arr_payment_data = $response->getData();
$this->store_payment([
'payment_id' => $arr_payment_data['id'],
'payer_email' => session('payer_email'),
'amount' => $arr_payment_data['amount']/100,
'currency' => env('STRIPE_CURRENCY'),
'payment_status' => $arr_payment_data['status'],
]);
return redirect("payment")->with("success", "Payment is successful. Your payment id is: ". $arr_payment_data['id']);
}
else
{
return redirect("payment")->with("error", $response->getMessage());
}
}
public function store_payment($arr_data = [])
{
$isPaymentExist = Payment::where('payment_id', $arr_data['payment_id'])->first();
if(!$isPaymentExist)
{
$payment = new Payment;
$payment->payment_id = $arr_data['payment_id'];
$payment->payer_email = $arr_data['payer_email'];
$payment->amount = $arr_data['amount'];
$payment->currency = env('STRIPE_CURRENCY');
$payment->payment_status = $arr_data['payment_status'];
$payment->save();
}
}
}
For the sandbox mode, Stripe provides dummy credit cards to test a transaction.
Once you are done with a testing sandbox mode, replace your test API keys with the live one and your application will start accepting real payments automatically.
That’s it! You are done with the Stripe payment gateway integration in Laravel. I would like to hear your thoughts and suggestions in the comment section below.
Related Articles
- PayPal Payment Gateway Integration in Laravel
- Authorize.Net Payment Gateway Integration in Laravel
- PayKun Payment Gateway Integration in Laravel
If you liked this article, then please subscribe to our YouTube Channel for video tutorials.
Can you integrate stripe payment gateway in my website can we use stripe for recurring payments??
thanks, you are amazing!
Hello,
I got the error message “Your card’s expiration date is incomplete”. I want to enable expiry month, year and cvv number so that user can fill it. Please guide me.
The form has the fields you mentioned. You have to fill the expiry month, year in MM/YY format. You can also refer to our video on same topic https://www.youtube.com/watch?v=K7_xjj99UUo
It does not handle authentication… User can not pay with 3D secury cards…
I updated the article. It now supports 3D secure cards.
Not getting omnipay class
Hi,
I have an issue. i followed all the process but finally i am getting this error
Invalid API Key provided: publisha***_key
Can you pls help
Hi,
I followed your tutorial but I got this error:
Please call Stripe() with your publishable key. You used an empty string
Can you please help
In your view file change the quotes
var publishable_key = “{{ env(‘STRIPE_PUBLISHABLE_KEY’) }}”;
Hi! Great and easy tutorial, but I have an issue with:
I follow you in every step but the Stripe card doesn’t appear :-(.
Do you have any suggestion?
Thank you
Is there any JavaScript error showing in console tab?
Yes, and it was about the publish key that (I don’t know why) was unable to read from the .env file.
I’ve copied/pasted and now it’s everything running.
Thank you for the tutorial (stay coding!) and I hope that this post would help someone else 😀
Try to avoid hardcoding env() credentials. It is not safe since they can be easily accessed by others.
While deploying the Laravel project on the production server, you must set doc root to the public folder. It restricted accessing the `.env` file outside the world.
i really appreciate for making things simple for many of us
Thank you.
Thanks. I’ve been looking for a good stripe tutorial. this will come handy in my next project.
hello ,
My name is animesh mana from kolkata,India.i am a php developer. in API related coding i always take help from here.i like this website.