Laravel 12 Stripe Integration: A Step-by-Step Guide for Secure Payments

Author

Kritim Yantra

Jun 03, 2025

Laravel 12 Stripe Integration: A Step-by-Step Guide for Secure Payments

In today's digital world, accepting online payments is crucial for any web application. Laravel 12, the latest version of the popular PHP framework, makes integrating Stripe—one of the most secure and developer-friendly payment processors—remarkably simple. Whether you're building an e-commerce store, SaaS application, or any platform requiring payments, this guide will walk you through the entire process.

Why Choose Stripe with Laravel?

  • Security: Stripe handles all sensitive payment information, reducing your PCI compliance burden
  • Global Reach: Accept payments from customers in over 135 currencies
  • Developer Experience: Clean API and excellent documentation
  • Laravel Compatibility: Official Cashier package simplifies integration

Prerequisites

Before we begin, ensure you have:

  1. Laravel 12 installed (run php artisan --version to check)
  2. Composer installed
  3. A Stripe account (sign up here)
  4. Basic Laravel knowledge (routes, controllers, views)

Step 1: Install Stripe PHP Library

First, let's install the required Stripe package via Composer:

composer require stripe/stripe-php

Step 2: Configure Stripe Keys

Add your Stripe keys to your .env file:

STRIPE_KEY=your_stripe_publishable_key
STRIPE_SECRET=your_stripe_secret_key

You can find these in your Stripe Dashboard under "Developers" > "API keys".

Step 3: Create the Payment Controller

Generate a new controller to handle payments:

php artisan make:controller PaymentController

Now, let's add some methods to our PaymentController.php:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Stripe\Stripe;
use Stripe\PaymentIntent;

class PaymentController extends Controller
{
    public function showPaymentForm()
    {
        return view('payment');
    }

    public function processPayment(Request $request)
    {
        Stripe::setApiKey(env('STRIPE_SECRET'));

        try {
            $paymentIntent = PaymentIntent::create([
                'amount' => $request->amount * 100, // Convert to cents
                'currency' => 'usd',
                'description' => 'Payment for product/service',
                'payment_method' => $request->payment_method,
                'confirm' => true,
                'return_url' => route('payment.success'),
            ]);

            return redirect()->route('payment.success');
            
        } catch (\Exception $e) {
            return back()->withErrors(['error' => $e->getMessage()]);
        }
    }

    public function paymentSuccess()
    {
        return view('payment_success');
    }
}

Step 4: Set Up Routes

Add these routes to your web.php:

use App\Http\Controllers\PaymentController;

Route::get('/payment', [PaymentController::class, 'showPaymentForm'])->name('payment.form');
Route::post('/process-payment', [PaymentController::class, 'processPayment'])->name('payment.process');
Route::get('/payment/success', [PaymentController::class, 'paymentSuccess'])->name('payment.success');

Step 5: Create the Payment Form View

Create a new file resources/views/payment.blade.php:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Payment Form</title>
    <link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
    <script src="https://js.stripe.com/v3/"></script>
</head>
<body class="bg-gray-100">
    <div class="container mx-auto px-4 py-8">
        <div class="max-w-md mx-auto bg-white rounded-xl shadow-md overflow-hidden md:max-w-2xl">
            <div class="p-8">
                <div class="uppercase tracking-wide text-sm text-indigo-500 font-semibold">Payment Information</div>
                <form id="payment-form" action="{{ route('payment.process') }}" method="POST" class="mt-6">
                    @csrf
                    <div class="mb-4">
                        <label class="block text-gray-700 text-sm font-bold mb-2" for="amount">
                            Amount (USD)
                        </label>
                        <input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" 
                               id="amount" name="amount" type="number" min="1" step="0.01" placeholder="10.00" required>
                    </div>
                    
                    <div class="mb-4">
                        <label class="block text-gray-700 text-sm font-bold mb-2" for="card-element">
                            Credit or debit card
                        </label>
                        <div id="card-element" class="p-3 border rounded">
                            <!-- Stripe Elements will be inserted here -->
                        </div>
                        <div id="card-errors" role="alert" class="text-red-500 text-xs italic mt-2"></div>
                    </div>
                    
                    <button id="submit-button" class="w-full bg-indigo-500 hover:bg-indigo-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline">
                        Pay Now
                    </button>
                </form>
            </div>
        </div>
    </div>

    <script>
        const stripe = Stripe('{{ env("STRIPE_KEY") }}');
        const elements = stripe.elements();
        const cardElement = elements.create('card');
        
        cardElement.mount('#card-element');
        
        const form = document.getElementById('payment-form');
        const submitButton = document.getElementById('submit-button');
        
        form.addEventListener('submit', async (event) => {
            event.preventDefault();
            submitButton.disabled = true;
            
            const {paymentMethod, error} = await stripe.createPaymentMethod({
                type: 'card',
                card: cardElement,
            });
            
            if (error) {
                document.getElementById('card-errors').textContent = error.message;
                submitButton.disabled = false;
            } else {
                const hiddenInput = document.createElement('input');
                hiddenInput.setAttribute('type', 'hidden');
                hiddenInput.setAttribute('name', 'payment_method');
                hiddenInput.setAttribute('value', paymentMethod.id);
                form.appendChild(hiddenInput);
                
                form.submit();
            }
        });
    </script>
</body>
</html>

Step 6: Create the Success View

Create resources/views/payment_success.blade.php:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Payment Successful</title>
    <link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
</head>
<body class="bg-gray-100">
    <div class="container mx-auto px-4 py-8">
        <div class="max-w-md mx-auto bg-white rounded-xl shadow-md overflow-hidden md:max-w-2xl">
            <div class="p-8 text-center">
                <svg class="mx-auto h-12 w-12 text-green-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" />
                </svg>
                <h2 class="mt-4 text-2xl font-bold text-gray-900">Payment Successful!</h2>
                <p class="mt-2 text-gray-600">Thank you for your payment. Your transaction has been completed successfully.</p>
                <div class="mt-6">
                    <a href="/" class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
                        Return Home
                    </a>
                </div>
            </div>
        </div>
    </div>
</body>
</html>

Step 7: Test Your Integration

Before going live, test your integration using Stripe's test cards:

  • Successful payment: 4242 4242 4242 4242
  • Failed payment: 4000 0000 0000 0002

Advanced Features to Consider

  1. Webhooks: Handle asynchronous events like completed payments
  2. Subscriptions: Implement recurring billing
  3. Payment Methods: Accept various payment methods (Apple Pay, Google Pay)
  4. 3D Secure: Add extra authentication for European customers

Security Best Practices

  1. Never store credit card information in your database
  2. Always use HTTPS
  3. Keep your Stripe API keys secure
  4. Regularly update your dependencies

Conclusion

Congratulations! You've successfully integrated Stripe payments into your Laravel 12 application. This implementation provides a secure, professional payment experience for your users. Remember to test thoroughly before going live and consider implementing additional features like email receipts or order tracking to enhance the user experience.

For more advanced implementations, check out the Laravel Cashier package which provides even more Stripe integration features out of the box.

Happy coding! 🚀

LIVE MENTORSHIP ONLY 5 SPOTS

Laravel Mastery
Coaching Class Program

KritiMyantra

Transform from beginner to Laravel expert with our personalized Coaching Class starting June 11, 2025. Limited enrollment ensures focused attention.

Daily Sessions

1-hour personalized coaching

Real Projects

Build portfolio applications

Best Practices

Industry-standard techniques

Career Support

Interview prep & job guidance

Total Investment
$200
Duration
30 hours
1h/day

Enrollment Closes In

Days
Hours
Minutes
Seconds
Spots Available 5 of 10 remaining
Next cohort starts:
June 11, 2025

Join the Program

Complete your application to secure your spot

Application Submitted!

Thank you for your interest in our Laravel mentorship program. We'll contact you within 24 hours with next steps.

What happens next?

  • Confirmation email with program details
  • WhatsApp message from our team
  • Onboarding call to discuss your goals

Tags

Comments

No comments yet. Be the first to comment!

Please log in to post a comment:

Sign in with Google

Related Posts