ল্যারাভেলে স্ট্রাইপ পেমেন্ট গেটওয়ে ইন্টিগ্রেশন

এর আগে আমরা একটা ল্যারাভেলে মিনিমাল ইকমার্স অ্যাপ তৈরি করেছি। যেখানে অর্ডার করা পর্যন্ত কমপ্লিট করেছি। কিন্তু পেমেন্ট অপশন যোগ করিনি। একেক দেশে একেক পেমেন্ট গেটওয়ে রয়েছে। আবার প্রতিটা গেটওয়ের ইন্টিগ্রেশনও ভিন্ন। যে জন্য এই স্টেপ প্রজেক্ট অনুযায়ী ভিন্ন হবে। বিশ্বব্যাপী স্ট্রাইপ জনপ্রিয়। তো আমরা দেখব কিভাবে স্ট্রাইপ পেমেন্টগেটওয়ে নিয়ে কাজ করা যায়। কিভাবে অর্ডারের পেমেন্ট স্ট্যাটাস আপডেট করা তা দেখব।

প্রধানত দুই ভাবে স্ট্রাইপ পেমেন্ট গেটওয়ে যোগ করা যায়। একটা হচ্ছে স্ট্রাইপ হোস্টেড, আরেকটা হচ্ছে সেলফ হোস্টেড। এখানে ইন্টিগ্রেশনের বিস্তারিত গাইড দেওয়া আছে। ল্যারাভেল পিএইচপি ফ্রেমওয়ার্ক। এর মানে হচ্ছে পিএইচপির গাইড ফলো করে ল্যারাভেলে ইন্টিগ্রেট করা যাবে।

বিদ্রঃ স্ট্রাইপ ইন্ট্রিগ্রেট করার জন্য একটা একাউন্ট খুলব। বাংলাদেশে স্ট্রাইপ নেই। তবে একটা একাউন্ট খুলে টেস্ট মুডে আমরা কাজ করতে পারব।

প্রথমে দেখি কিভাবে স্ট্রাইপ হোস্টেড পেমেন্ট ফরম যোগ করা যায়ঃ

এর জন্য একটা ল্যারাভেল প্রজেক্ট তৈরি করে নিব।

একটা কন্ট্রোলার তৈরি করি StripeHostedController নামে। আপনি যে কোন নাম ব্যবহার করতে পারেন।

php artisan make:controller StripeHostedController

এই StripeHostedController কন্ট্রোলারে আমরা তিনটা মেথড যোগ করব। index, checkout এবং success নামে। এর আগে রুট গুলো আপডেট করে নেই।

রুট ফাইল web.php ওপেন করে use App\Http\Controllers\StripeHostedController; ইম্পোর্ট করে নিব। এরপর নিচের রুট গুলো যোগ করবঃ

Route::get('/checkout', [StripeHostedController::class, 'index'])->name('index');
Route::post('/checkout', [StripeHostedController::class, 'checkout'])->name('checkout');
Route::get('/success', [StripeHostedController::class, 'success'])->name('success');

স্ট্রাইপ সিক্রেট কী সেটআপ

স্ট্রাইপে লগিন করার পর ড্যাশবোর্ড থেকে Secret key কপি করব।

ল্যারাভেলের .env ফাইলে এই সিক্রেট কী যোগ করবঃ

STRIPE_SECRET=sk_test_51O0VHBJFMmOd.... 

লেআউটঃ

লেয়াউটে শুধু একটা বাটন যোগ করলেই হবে।

            <form action="/checkout" method="POST">
              @csrf
                <button type="submit">Checkout</button>
            </form>

এই বাটনে ক্লিক করলে স্ট্রাইপের পেমেন্ট পেইজে নিয়ে যাবে। একটু সুন্দর করে দেখাতে চাইলে views ফোল্ডারের ভেতর checkout.blade.php ফাইল তৈরি করে এভাবে লিখতে পারিঃ

<!doctype html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="https://cdn.tailwindcss.com/"></script>
</head>

<body>
    <nav class="bg-gray-800">
        <div class="mx-auto max-w-7xl px-2 sm:px-6 lg:px-8">
            <h1 class="p-5 text-white">Stripe Hosted Payment Demo</h1>
        </div>

    </nav>
    <main class="mx-auto max-w-7xl px-4 py-6 sm:px-6 lg:px-8">

        <div class="block  p-6 text-center border border-gray-200 rounded-lg shadow">

            <h2 class="mb-2 text-2xl">Awesome Product</h2>
            <p>Price: 8.00</p>

            <form action="/checkout" method="POST">
              @csrf
                <button type="submit"
                    class="text-white bg-blue-700 hover:bg-blue-800  rounded-lg text-sm my-10 px-5 py-2.5 me-2 mb-2">Checkout</button>
            </form>
        </div>

    </main>
</body>
</html>

আউটপুট এমন দেখাবেঃ

পেমেন্ট কমপ্লিট হলে একটা সাকসেস মেসেজ দেখাতে পারি। আর তার জন্য views ফোল্ডারে success.blade.php ফাইল তৈরি করে এভাবে লিখতে পারিঃ

<!doctype html>
<html>

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="https://cdn.tailwindcss.com/"></script>
</head>

<body>
    <nav class="bg-gray-800">
        <div class="mx-auto max-w-7xl px-2 sm:px-6 lg:px-8">
            <h1 class="p-5 text-white">Stripe Hosted Payment Demo</h1>
        </div>

    </nav>
    <main class="mx-auto max-w-7xl px-4 py-6 sm:px-6 lg:px-8">

        <div class="block p-6 text-center border border-gray-200 rounded-lg shadow">

            <h2 class="mb-2 text-2xl">Thank You for Your Order! </h2>

            <p class="m-10">Your order has been successfully placed, and our team is already working hard to ensure
                everything is perfect for you!</p>

            <a href="/" class="text-white bg-blue-700 hover:bg-blue-800  rounded-lg text-sm  px-5 py-2.5  ">Go
                Back to Home</a>
        </div>
    </main>
</body>
</html>

এবার StripeHostedController.php ফাইল ওপেন করব। index মেথডে কোন কাজ নেই। শুধু checkout ভিউ দেখানোঃ

    public function index(){
        return view('checkout');
    }

checkout মেথড এভাবে লিখবঃ

       public function checkout(){
   // Set your Stripe secret key
        Stripe::setApiKey(env('STRIPE_SECRET'));
        $checkout_session = \Stripe\Checkout\Session::create([
            'line_items' =>[
                [
                    'price_data' => [
                        'currency'  => 'usd',
                        'product_data' =>[
                            'name' => 'Product name',
                        ],
                     'unit_amount' => '800' // Amount in cents (e.g., $8.00)
                    ],
                    'quantity' => 1,
                ],
            ],
            'mode' => 'payment',
            'success_url' => route('success'),
            'cancel_url' => route('index')
        ]);
        return redirect()->away($checkout_session -> url);
    }

এখানে সিক্রেট কী সেট করেছি .env ফাইলে STRIPE_SECRET ভ্যারিয়েবলের ডেটা থেকেঃ Stripe::setApiKey(env('STRIPE_SECRET'));

এছাড়া চেকআউট করার সময় line_items ভ্যারিয়েবলের মধ্যে প্রোডাক্ট ফরমেশন গুলো দিতে হয়। একাধিক প্রোডাক্ট থাকলে এখানে একাধিক প্রোডাক্ট ইনফরমেশন গুলো পাস করতে পারব। উপরের উদাহরণে আমরা হার্ড কোড করে এই ডেটা গুলো পাস করেছি। পরবর্তীতে দেখব কিভাবে ভ্যারিয়েবল থেকে এই ডেটা পাস করা যায়।

এখানে success_url এবং cancel_url পাস করা যাবে। পেমেন্ট কমপ্লিট হলে success_url এ রিডারেক্ট করবে। StripeHostedController এ success মেথড এভাবে লিখিঃ

    public function success(){
        // update order status here.
        // then return the view.
        return view('success');
    }

যখন পেমেন্ট কমপ্লিট হবে এবং success মেথড কল হবে, তখন আমরা প্রোডাক্টের পেমেন্ট স্ট্যাটাস পরিবর্তন করতে পারব।

এখানে একটা সমস্যা তৈরি হবে। যদি পেমেন্ট কমপ্লিট হয়ে এই success রুটে ফিরে না আসে, তাহলে কিন্তু অর্ডার স্ট্যাটাস পরিবর্তন করা যাবে না। এর সমাধানও রয়েছে। এর সমাধান হচ্ছে স্ট্রাইপের ওয়েবহুক। যা দিয়ে পেমেন্ট সাকসেস হওয়ার পর কেউ ব্রাউজার ক্লোজ করলেও প্রোডাক্ট স্ট্যাটাস পরিবর্তন করতে পারবে। এই পুরা বিষয় গুলো Stripe Complete Checkout Process in Laravel ভিডিও থেকে জানা যাবে। কিভাবে ফুল পেমেন্ট প্রসেস কাজ করে, তা ভিডিও থেকে বুঝতে পারবেন।

এই পর্যায় আমরা checkout বাটনে ক্লিক করে পেমেন্ট কমপ্লিট করতে পারব। টেস্ট করার জন্য টেস্ট কার্ড ব্যবহার করে পেমেন্ট কমপ্লিট করা যাবে। এরপর সাকসেস পেইজে নিয়ে যাবে।

যেমন 4242 4242 4242 4242 নাম্বার, যে কোন ফিউচারের তারিখ এবং যে কোন নাম্বার CVC হিসেবে ইনপুট দিলে পেমেন্ট সাকসেস হবে।

উপরে দেখেছি কিভাবে স্ট্রাইপ হোস্টেড পেমেন্ট ফরম ব্যবহার করা যায়।

সেলফ হোস্টেড পেমেন্ট ফরম

এবার দেখি সেলফ হোস্টেড ফরম কিভাবে ব্যবহার করা যায়। যদিও আমরা বলছি সেলফ হোস্টেড ফরম, আসলে এখানেও স্ট্রাইপ ফরমটা আমাদের ওয়েব পেইজে ইনজেক্ট করবে। তো রিডারেক্ট করে স্ট্রাইপ পেইজে না নিয়ে নিজেদের ওয়েব পেইজে সব কাজ করা যাবে। এটাই সুবিধা।

একই ল্যারাভেল প্রজেক্টেই কাজ করতে পারি। তার জন্য আরেকটা কন্ট্রোলার তৈরি করে নেই। যেমন PaymentController:

php artisan make:controller PaymentController

PaymentController এর কোড এভাবে লিখবঃ

<?php

namespace App\Http\Controllers;

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

class PaymentController extends Controller
{
    public function showForm()
    {
        // Set your Stripe secret key
        Stripe::setApiKey(env('STRIPE_SECRET'));

        // Create a PaymentIntent with the amount and currency
        $paymentIntent = PaymentIntent::create([
            'amount' => 800, // Amount in cents (e.g., $8.00)
            'currency' => 'usd',
            'automatic_payment_methods' => [
                'enabled' => true,
            ],
        ]);

        // Pass the client_secret to the view
        return view('payment-form', ['clientSecret' => $paymentIntent->client_secret]);
    }

    public function processPayment(Request $request)
    {
        // Set your Stripe secret key
        Stripe::setApiKey(config('services.stripe.secret'));

        try {
            // Retrieve the payment intent using the provided ID
            $paymentIntent = PaymentIntent::retrieve($request->input('payment_intent_id'));

            // Confirm the payment intent
            $paymentIntent->confirm();

            return response()->json(['success' => true, 'paymentIntent' => $paymentIntent]);
        } catch (\Exception $e) {
            return response()->json(['success' => false, 'error' => $e->getMessage()]);
        }
    }
}

দুইটা রুট তৈরি করে নিব web.php ফাইলেঃ

Route::get('/payment', [PaymentController::class, 'showForm']);
Route::post('/payment', [PaymentController::class, 'processPayment']);

একটা ফরম তৈরি করি payment-form.blade.php নামে। যেখানে পেমেন্ট ফরম দেখাবোঃ

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Stripe Payment</title>

<style>
    /* Reset some default browser styles */
body, html {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
    font-family: Arial, sans-serif;
}

/* Center the form on the page */
body {
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100vh;
    background-color: #f7f7f7;
}

#payment-form {
    background: #ffffff;
    padding: 20px;
    border-radius: 8px;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
    max-width: 400px;
    width: 100%;
}

h1 {
    font-size: 24px;
    margin-bottom: 20px;
    color: #333;
    text-align: center;
}

input, button {
    width: 100%;
    padding: 10px;
    margin: 10px 0;
    border: 1px solid #ccc;
    border-radius: 4px;
    box-sizing: border-box;
}

input:focus, button:focus {
    outline: none;
    border-color: #007bff;
}

button {
    background-color: #007bff;
    color: #ffffff;
    border: none;
    cursor: pointer;
    font-size: 16px;
}

button:hover {
    background-color: #0056b3;
}

#card-element {
    margin: 10px 0;
}
    </style>

</head>
<body>
    <form id="payment-form">

        <h2 class="mb-2 text-2xl">Awesome Product</h2>
        <p>Price: 8.00</p>
        <input type="text" id="card-holder-name" placeholder="Cardholder Name">
        <div id="card-element"></div> {{--   will inject by Stripe --}}
        <button id="card-button" data-secret="{{ $clientSecret }}">Pay</button>
    </form>

    <script src="https://js.stripe.com/v3/"></script>
    <script>
        const stripe = Stripe('{{ env('STRIPE_KEY') }}');
        const elements = stripe.elements();
        const cardElement = elements.create('card');
        cardElement.mount('#card-element');

        const cardButton = document.getElementById('card-button');
        const clientSecret = cardButton.dataset.secret;

        cardButton.addEventListener('click', async (e) => {
            e.preventDefault();

            const { error, paymentIntent } = await stripe.confirmCardPayment(clientSecret, {
                payment_method: {
                    card: cardElement,
                    billing_details: {
                        name: document.getElementById('card-holder-name').value,
                    },
                },
            });

            if (error) {
                alert(`Payment failed: ${error.message}`);
            } else if (paymentIntent.status === 'succeeded') {
                alert('Payment successful!');
            }
        });
    </script>
</body>
</html>

এখানে পেমেন্ট সাকসেস হলে জাভাস্ক্রিপ্টে এলার্ট দেখাবে। আমরা চাইলে কোড নিজের মত করে মডিফাই করে নিতে পারব।

Leave a Reply