Confirming successful Stripe Checkout payments in Laravel
I have a VueJS/Laravel app that uses Stripe Checkout to accept card payments (using Checkout rather than a custom implementation is the simplest way to ensure compliance with EU online payment regulations).
The Checkout documentation (https://stripe.com/docs/payments/checkout/accept-a-payment#testing) points out that the redirect to a success URL should not be relied on for confirming payment, and provides a few methods for doing so properly.
I assume that the best option in my case is to use Webhooks. However, I have no idea how to use them in Laravel. Would they, along with a package like laravel-webhook-client (https://github.com/spatie/laravel-webhook-client) be the best solution?
Yeah @braunson I've found and installed that. I'm still not sure what to actually do in the handler though.
For now I've worked out a system where I store the Stripe session ID and use that to verify a genuine visit to the success page.
Your guide is https://stripe.com/docs/webhooks
So webhooks just take in the "success" from the Stripe end, what you do with it or need to do with it is up to you.
.......
You need to setup a webhook on a Laravel endpoint that's publicly accessible, then go to Stripe and set the Webhook URL for your user to that URL.
Next look at the package, follow the steps to install and this part is important...
Route::stripeWebhooks('webhook-route-configured-at-the-stripe-dashboard');
Finally, take care of the routing: At the Stripe dashboard you must configure at what url Stripe webhooks should hit your app. In the routes file of your app you must pass that route to
Route::stripeWebhooks
This you would update with the webhook url you put in Stripe as the webhook.
Then under the package's Usage it shows how to handle webhook requests using jobs or an event listener (also displayed in the package Usage area of the docs). You can specify 'events' to specific jobs.
You'll want to likely use the events checkout.session.completed or checkout.session.async_payment_succeeded (both taken from the Stripe API event types docs linked above) depending on what you are looking to do.
So assuming you want to use Listeners for the event checkout.session.completed you'd do this:
- Create an event listener
- Add the event -> listener relation in the Event Service Provider
- Setup the webhook URL in your routes file.
- Update your
EventServiceProviderand add this..
/**
* The event listener mappings for the application.
*
* @var array
*/
protected $listen = [
'stripe-webhooks::checkout.session.completed' => [
App\Listeners\StripeCheckoutSessionCompleted::class,
],
];
- Create this
app\Listeners\StripeCheckoutSessionCompleted.phpwith the following code:
<?php
namespace App\Listeners;
use Illuminate\Contracts\Queue\ShouldQueue;
use Spatie\WebhookClient\Models\WebhookCall;
class StripeCheckoutSessionCompleted implements ShouldQueue
{
public function handle(WebhookCall $webhookCall)
{
// do your work here
// you can access the payload of the webhook call with `$webhookCall->payload`
}
}
Update the handle method with your logic..
We highly recommend that you make the event listener queueable, as this will minimize the response time of the webhook requests. This allows you to handle more Stripe webhook requests and avoid timeouts.
- Setup the webhook URL in your routes file
Route::stripeWebhooks('stripe-webhooks');
- Go to Stripe and update the webhook endpoint to be
yourapp.com/stripe-webhooks
Now your done.
@Braunson I'm starting with laravel-stripe-webhooks on my local dev, and am confused as to what to put in the Stripe dashboard for the webhook URL and how to configure the local route.
If I replace 'webhook-route-configured-at-the-stripe-dashboard' with 'stripe-webhooks' in the route, how do I put that into the my Stripe dashboard and where does that route go? What is expected for the name in the route and then where is that name pointing to, as I don't see a controller and method involved to take on that route.
I know the Stripe CLI takes over the webhook messages and directs them to the local dev instance endpoint, but I still am not sure what to put in the Dashboard as my endpoint. The Spatie instructions do not address this, so I feel like I'm missing a crucial point.
Can you help clarify please?
@Hondaman900 for local webhooks you just setup a temporary listener and use the CLI to forward events. You use stripe listen --forward-to localhost:4242/api/webhook where the route is whatever you've set up in Laravel.
Yeah, I've got it all set up with the package you mentioned. The bit I'm not of sure is what to do in the handler. It seems like Stripe just carries on to the success page regardless of what I do in there.
@TimeSocks I think you have already solved this but for anyone who lands here from Google
Here is how to handle this (this is not the only way of course) :
public function checkout_success(Request $request) { Stripe::setApiKey(env('STRIPE_SECRET'));
$session = Session::retrieve($request->get('session_id'));
$customer = Customer::retrieve($session->customer);
$user = User::where('stripe_id', $customer->id)
->where('email', $customer->email)
->first();
$pm = $user->paymentMethods()->first()->toArray();
if ($user && $session->payment_status == 'paid')
{
$request->user()->newSubscription(
'default', env('STRIPE_PRICE_ID')
)->create($pm['id']);
return redirect('dashboard');
}
}
@martyyy …and for any one landing from Google (or any other search engine), don’t do the above, as it’s still possible for a payment to be taken and the user to close the browser before the above route is invoked.
The Stripe docs is pretty clear on what the best way to fulfil an order is after a payment—a webhook: https://stripe.com/docs/payments/checkout/fulfill-orders#fulfill
Set up a webhook to fulfil orders after a payment succeeds. Webhooks are the most reliable way to handle business-critical events.
Webhooks are fired by a server and don’t require a user to be present, so are a lot more reliable than waiting for a customer to be redirected to a specific URL to fire some order creation code.
And if you’re using Cashier, then it will come with a webhook handler out of the box that automatically creates subscriptions in your database when a subscription is created on Stripe’s side.
If you’re going to bump a thread after a year, at least post sound advice.
Please or to participate in this conversation.