is your webhook publicly accessible?
have you added it to the except array in verifycsrf middleware?
is the route permitted for guest access?
Does the stripe console show that they tried to send a webhook?
Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.
I'm implementing a stripe checkout for my client who's a limousine booking company. I need to insert data into the database after a successful Stripe transaction but it appears that my webhook is not being called at all. I've been trying to figure this out for 5 hours but couldn't. Any help is much appreciated! As far as code, here is my endpoint in web.php:
Route::post('/stripe/webhook', [StripeWebHookHandler::class, 'handleWebhook']);
Here's my function in my BookingController:
public function bookRide(Request $request)
{
/*
These prices are for if the user selects point-to-point
the prices are:
sedan add that to the price of the sedan per mile + 20%
mini suv: ,
suv: 0,
For the van, no matter what service, the customer needs to call you email notification
*/
$admin = User::find(1);
$data = $request->only([
'first_name', 'last_name', 'email', 'phone_number', 'service_type',
'pickup_date_time', 'pickup_location', 'dropoff_location',
'num_passengers', 'num_luggage', 'vehicle', 'calculated_distance',
'calculated_duration',
'booster_seat_addon',
]);
$validate = $this->validateBooking($data);
if ($validate->fails()) {
return redirect()->route('reservation')->withErrors($validate);
}
$product = $this->stripe->products->retrieve($request->vehicle);
$prices = $this->stripe->prices->all(['product' => $product->id]);
$price = $prices->data[0]->unit_amount;
$distance = floatval($request->calculated_distance);
$totalPrice = 0;
$surCharge = 0.20; // 20% surcharge
// Calculate pricing based on requested pricing model
$basePrice = 0; // initialize to <?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Stripe\Stripe;
use Stripe\Webhook;
use Stripe\StripeClient;
use Stripe\Event;
class StripeWebhookHandler extends Controller
{
protected $stripe;
public function __construct(){
Stripe::setApiKey(env('STRIPE_SECRET_KEY'));
$this->stripe = new StripeClient(env('STRIPE_SECRET_KEY'));
}
public function handleWebhook(Request $request){
$payload = $request->getContent();
$event = null;
try{
$event = Event::constructFrom(
json_decode($payload, true)
);
}
catch(\UnexpectedValueException $e){
return redirect('reservation')->with('EXCEPTION_CAUGHT', 'Unexpected error occurred while checking out');
}
// Handle events
switch($event->type){
case 'payment_intent.succeeded':
return 'PAYMENT_INTENT_SUCCESS';
break;
case 'payment_intent.failed':
return 'PAYMENT_INTENT_FAILED';
break;
case 'payment_intent.created':
return 'PAYMENT_INTENT_CREATED';
break;
case 'checkout.session.completed':
return 'CHECKOUT_SESSION_COMPLETED';
break;
case 'charge.succeeded':
return 'CHARGE_SUCCEEDED';
break;
case 'charge.failed':
return 'CHARGE_FAILED';
break;
}
}
}
by default
switch($product->name){
case 'Sedan':
$basePrice = 8000; // 8000 cents is
break;
case 'Mini Suv':
$basePrice = 9000; // 9000 cents is
break;
case 'Suv':
$basePrice = 10000; // 10000 cents is 0
break;
}
if ($request->service_type == 'Hourly') {
if ($request->num_hours < 4 || $product->name !== 'Hourly Service') {
return redirect()->back()->withInput()->with('INVALID_HOURLY_SELECTION', "Minimum hours for hourly service must be at least 4, or you have chosen a vehicle other than 'Hourly Service' ");
}
$totalPrice = $price * $request->num_hours;
} else {
$mileageCost = $price * $distance;
$totalPrice = $basePrice + $mileageCost;
$totalPrice +=$totalPrice * $surCharge;
}
if ($request->booster_seat_addon == 1) {
$totalPrice += 2000; // Add for the booster seat addon
}
$encryptedPrice = Crypt::encryptString($totalPrice);
$basePath = config('app.url');
$checkoutSessionId = null;
if($product->name === 'Van'){
$data = [
'bookingID'=>$this->bookingID,
'checkout_session_id' => null,
'name'=>$data['first_name']. ' '.$data['last_name'],
'email'=>$data['email'],
'phone_number'=>$data['phone_number'],
'service_type'=>$data['service_type'],
'pickup_date_time'=>date('F jS, Y \a\t g:i A', strtotime($data['pickup_date_time'])),
'pickup_location'=>$data['pickup_location'],
'dropoff_location'=>$data['dropoff_location'],
'num_passengers'=>$data['num_passengers'],
'num_luggage'=>$data['num_luggage'],
'calculated_distance'=>$distance,
'calculated_duration'=>$request->calculated_duration,
'booster_seat_addon'=>$data['booster_seat_addon'] ? TRUE : FALSE,
'total_price'=>0.00,
'vehicle'=>$data['vehicle']
];
Notification::route('mail', env('MAIL_FROM_ADDRESS'))->notify(new VanBooked($this->bookingID, $data));
Notification::route('mail', $request->email)->notify(new QuoteRequired($this->bookingID, $data));
Bookings::create($data);
return redirect('limo-booked?name='.$data['name'].'&email='.$data['email'].'');
}
$checkout_session = $this->stripe->checkout->sessions->create([
'payment_method_types' => ['card'],
'line_items' => [[
'price_data' => [
'currency' => 'usd',
'product_data' => [
'name' => $product->name,
'images'=>[$product->images[0]],
'description' => $product->description,
],
'unit_amount' => intval($totalPrice),
],
'quantity' => 1,
]],
'mode' => 'payment',
'success_url' => $basePath . ':8000/booking-success?name='.$request->first_name.$request->last_name.'&email=' . $data['email'] . '&price=' . $encryptedPrice.'&reference_id='.uniqid().'',
'cancel_url' => route('reservation'),
]);
$data = [
'bookingID'=>$this->bookingID,
'checkout_session_id' => $checkout_session->id, // Use the ternary operator to set the value
'name'=>$data['first_name']. ' '.$data['last_name'],
'email'=>$data['email'],
'phone_number'=>$data['phone_number'],
'service_type'=>$data['service_type'],
'pickup_date_time'=>date('F jS, Y \a\t g:i A', strtotime($data['pickup_date_time'])),
'pickup_location'=>$data['pickup_location'],
'dropoff_location'=>$data['dropoff_location'],
'num_passengers'=>$data['num_passengers'],
'num_luggage'=>$data['num_luggage'],
'calculated_distance'=>$distance,
'calculated_duration'=>$request->calculated_duration,
'booster_seat_addon'=>$data['booster_seat_addon'] ? TRUE : FALSE,
'total_price'=>$totalPrice,
'vehicle'=>$data['vehicle']
];
return redirect()->away($checkout_session->url);
$webhookEventType = $webhookHandler->handleWebhook($request);
switch($webhookEventType){
case 'PAYMENT_INTENT_SUCCESS':
case 'PAYMENT_INTENT_CREATED':
case 'CHECKOUT_SESSION_COMPLETED':
case 'CHARGE_SUCCEEDED':
Bookings::create($data);
Notification::route('mail', $data['email'])->notify(new ServiceBooked($data));
$admin->notify(new LimoBooked($data, $admin->name));
break;
case 'PAYMENT_INTENT_FAILED':
case 'CHARGE_FAILED':
return redirect('reservation')->with('EXCEPTION_CAUGHT', 'Something went wrong while processing your transaction');
break;
}
}
I have also imported the stripe webhook controller into my BookingController class like so:
use App\Http\Controllers\StripeWebhookHandler;
Here is my stripe webhook handler class:
LARACASTS_SNIPPET_PLACEHOLDERis your webhook publicly accessible?
have you added it to the except array in verifycsrf middleware?
is the route permitted for guest access?
Does the stripe console show that they tried to send a webhook?
Please or to participate in this conversation.