Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

shez1983's avatar
Level 23

cashier/checkout - api -

So i am allowing user to buy produ (adhoc). and wanting to use stripe hosted checkouts (https://laravel.com/docs/11.x/billing#single-charge-checkouts) I am using an api with a separate frontend

Here is the flow:

  • user clicks on a button in frontend site (eg site.com/product/123)
  • f/e notifies b/e which returns a checkout link
  • f/e redirects to that
  • user pays
  •  -> stripe redirects them to?
    
  •      -> either frontend with param or a new paage?
    
  •      -> or the API which redirects them to f/e?
    

I am confused on the last two steps, how do i tell api/db that user bought the product so i can record it?

0 likes
3 replies
jamesbuch79's avatar

Stripe webhooks are critical for reliable payment processing - they ensure you don't miss any payment updates even if the customer closes their browser during the checkout process.

Before we get to that, suppose site.com/product/123 returns the chargeCheckout configured on your billable model. It expects an amount, product name and optional quantity, and may have some extra stuff in there.

I think what you are say you are doing is redirecting to your /charge-checkout with the required and optional parameters if present, which would then create a checkout session, and send the customer to Stripe's checkout page.

After this, there is a default URL to notify you of a payment, or a default cancellation redirect which I think is just / at your domain. You can override with success_url and cancel_url.

So the steps are:

Create your single charge checkout, with $billableModel->checkoutCharge(). This redirects to Stripe's payment page.

Stripe handles things.

Stripe sends customer back to you, to your success or cancellation URL.

A webhook is called with a payload, some kind of event with data which you may handle.

For instance:

$checkout = $user->checkoutCharge(3600, 'coffee mug', 3, [
            'success_url' => 'https://store.com/payment/success',
            'cancel_url' => 'https://store.com/payment/cancelled',
]);

// Do something with $checkout (which is Checkout class) if you need to

Cashier automatically handles common webhook events from Stripe, but the simple case is that you get a redirect to your success and cancelled urls. Cashier is very much focused on other events like subscriptions and the like.

If you want to explicitly handle something, you can:

class WebhookController extends \Laravel\Cashier\Http\Controllers\WebhookController
{
    protected function handleCheckoutSessionCompleted($payload)
    {
        // Your custom logic here
    }
}

Cashier has some magic to handle events by the name of your handle... methods in a class that extends the WebhookController.

If you need to do something more than what Cashier provides, you can do it like this, and completely override the webhook handler.

public function handleWebhook(Request $request)
{
    $payload = $request->all();
    
    switch ($payload['type']) {
        case 'checkout.session.completed':
            // Handle successful checkout
            break;
        case 'checkout.session.expired':
            // Handle expired checkout
            break;
    }
}

Find out what the payload looks like from Stripe's documentation on checkout events.

// Using Cashier's extended controller:
Route::post(
    'stripe/webhook',
    [MyWebhookController::class, 'handleWebhook']
)->name('cashier.webhook');

// Or your completely custom controller, which is StripeController with a method handleWebhook:
Route::post(
    'stripe/webhook',
    [StripeController::class, 'handleWebhook']
);

Look in config/cashier.php for some configuration options. But it mostly depends on whether you just want either a redirect to a success or cancellation page, or you want that and a custom handled event from Stripe, in which case you must meddle with the webhook handler or define your own.

So the flow is this:

Create checkout session with:

// return this, or do something with it and return, Cashier redirects to Stripe checkout

$billableModel->checkoutCharge(12345, 'Item', 1, [
   'success_url' => 'https://something',
   'cancel_url' => 'https://something', ... may be other meta data here if you want to research that
   ]);

Stripe checkout handles the next part, generates a checkout.session.completed if it was successful, otherwise may be a checkout.session.expired, and then the customer is redirected back to your defined success or cancelled URL.

To get anything specific, you need to handle the webhook.

  • When customers are ready to complete their purchase, your application creates a new Checkout Session.
  • The Checkout Session provides a URL that redirects customers to a Stripe-hosted payment page.
  • Customers enter their payment details on the payment page and complete the transaction.
  • After the transaction, a webhook fulfils the order using the checkout.session.completed event.

To be clear "webhook fulfills the order using the checkout.session.completed event" means you are sent a big JSON blob to process with your webhook handler. It will tell you what happened in there. You can find the event details here: https://docs.stripe.com/api/checkout/sessions/object

If payment_status is 'paid' and status is 'complete' then you can fulfill the order.

shez1983's avatar
Level 23

sorry i have not had a chance.. i know what i need to do from the B/E side.. its just from F/E (which is a vue js hosted site using laravel API)..

so users go to products page (there is no cart,  people buy single product )
- send checkout link in the jSON so F/E knows the URL for checkout.
	- the PROBLEM here is when you create the URL, you need to give it a success/failure URL.. 
		- do I give my vue JS site URL (of the product they are buying, and append with ?=success so Vue js knows what happened...) 
- have a checkout button 
-redirects to Stripe checkout
- user pays.. 
- redirects to? 

i guess my issue is API knowing about the VUE JS site, to create success/failure URLS.. the other method would be to NOT use STRIPE checkout and build my own 'system' but i thought It might be easier to offload all the work to stripe.

Please or to participate in this conversation.