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

adam_anything's avatar

How to correctly implement Cashier, Stripe and 3D Secure

Hello all

I'm currently trying to learn how to implement Cashier, Stripe with 3D secure for subscriptions. After reading through the docs and some googling I have it 'working' and I guess there's two questions.

  1. Is it implemented 'correctly' to be more specific the parts i'm unsure about are the initial sending of the AJAX request and how to deal with redirecting after a payment failed or not.

  2. Should 3D secure and stripe ask for confirmation twice, once on initial submit and then again on the cashier.payment page after reading about, iv found some people had an issue with the SubscriptionBuilder class's off_session being set to false but mine is not.

My cashier version is 12.3 on Laravel 8.10.0

So to achieve the above I have so far done the following (stripped the code down so its more easily read)

1 -> Setup Intent

$intent = $user->createSetupIntent();

In my controller which is then passed to the view

2 -> Attach to form elements button

<button
    name="card-button"
    class=""
    id="card-button"
    data-secret="{{ $intent->client_secret }}"
>
    Subscribe Now
</button>
</button>

The view then gets the client secret attached to the buttons data attribute

3 -> Add stripe.js to page

<script src="https://js.stripe.com/v3/"></script>

4 -> Verify card with stripe and get a payment identifier

const { setupIntent, error } = await stripe.confirmCardSetup(
    clientSecret, {
        payment_method: {
            card: cardElement,
        }
    }
);

5 -> Pass payment method identifier to Laravel

axios.post('/subscribe', {
    payment_method: setupIntent.payment_method,
    plan: plan.value
})
    .then(function (response) {

        if (response.data !== 'undefined') {
            window.location.href = response.data;
        }

    })
    .catch(function (error) {
        setError(error.message);
    });

6 -> Add as a new payment method

    public function create(Request $request) {

        $url = '/dashboard';

        $request->user()->updateDefaultPaymentMethod($request->payment_method);

        try {

            $request->user()->newSubscription('default', $request->plan)->add();

        } catch (IncompletePayment $exception) {

            $url = route(
                'cashier.payment',
                [$exception->payment->id, 'redirect' => route('dashboard')]
            );

            return response($url, 200);
        }

        return response($url, 200);

    }

The above when handling an incomplete payment/3D secure I'm not sure that is being done correctly here.

This seems to work but I'd like to just check with people who know what they are doing that I have the correct idea here, hopefully this is not a silly question.

Thanks for any replies or suggestions.

Adam

0 likes
2 replies
adam_anything's avatar

Just incase someone comes across this, I opted for attaching the stripe token to the form as a hidden input on submit and then posting it to the endpoint, it may not be the correct way of doing this but prefer it to my previous attempt of changing the window.location.href to the response of the ajax.

function handleSubmit(token){
    let hiddenTokenInput = document.createElement('input');
    hiddenTokenInput.setAttribute('type', 'hidden');
    hiddenTokenInput.setAttribute('name', 'stripe_token');
    hiddenTokenInput.setAttribute('value', token);
    form.appendChild(hiddenTokenInput);

    form.submit();
}

and then posting to the endpoint, which either continues or redirects to Laravels built in way of handling the 3d secure.

        try {

            $request->user()->newSubscription('default', $request->plan)->add();

        } catch (IncompletePayment $exception) {

            return redirect()->route(
                'cashier.payment',
                [$exception->payment->id, 'redirect' => route('dashboard')]
            );

        }

        return  redirect()->route('dashboard');

Please or to participate in this conversation.