laravel / cashier-stripe

Laravel Cashier provides an expressive, fluent interface to Stripe's subscription billing services.
https://laravel.com/docs/billing
MIT License
2.37k stars 667 forks source link

New subscription: The payment attempt failed because additional action is required before it can be completed. #1651

Closed TheGlenn88 closed 6 months ago

TheGlenn88 commented 6 months ago

Cashier Stripe Version

15.2.1

Laravel Version

10.43.0

PHP Version

8.1.27

Database Driver & Version

Postrgres 14

Description

Hey,

I seem to be having a problem that was also solved a long time ago where creating a newSubscription fails because Stripe is requesting 3D Secure again, even though this should be an off_session payment and the SetupIntent was created using off_session and 3D Secure was completed successfully.

Is there something I'm doing wrong here, or is this a bug in Cashier?

<?php

namespace App\Http\Controllers;

use App\Http\Requests\LandingSubmitRequest;
use App\Models\LandingSubmission;
use App\Models\User;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Redirect;
use Inertia\Inertia;
use Inertia\Response;

class PaymentController extends Controller
{
    /**
     * Display the Payment page.
     */
    public function show(Request $request): Response
    {
        /** @var User $user */
        $user = auth('supervisor')->user();
        $clientSecret = $user->createSetupIntent()->client_secret;

        return Inertia::render(
            'Payment/Index',
            [
                'client_secret' => $clientSecret,
                'stripe_pk' => env('STRIPE_KEY')
            ]
        );
    }

    public function submit(Request $request): RedirectResponse
    {
        /** @var User $user */
        $user = auth('supervisor')->user();

        $paymentMethod = data_get($request, 'setupIntent.payment_method');

        if ($paymentMethod) {
            $user->createOrGetStripeCustomer();
            $user->updateDefaultPaymentMethod($paymentMethod);
            $user->newSubscription('default', 'price_1Oi0rWLlrJQvxIhfqs8sW3Ga')->create($paymentMethod);
        }

        return redirect('supervisor/dashboard');
    }
}
<script setup>
import {ref} from 'vue'
import GuestLayout from "@/Layouts/GuestLayout.vue";
import {Head, router} from "@inertiajs/vue3";
import StepsNav from "@/Components/Signup/StepsNav.vue";
import {loadStripe} from '@stripe/stripe-js'
import { onMounted } from 'vue';

const props = defineProps({
    client_secret: {
        type: String,
        required: true,
    },
    stripe_pk: {
        type: String,
        required: true,
    }
});

const stripePromise = loadStripe(props.stripe_pk);
const cardElement = ref(null);
const card = ref(null);

onMounted(async () => {
    const stripe = await stripePromise;
    const elements = stripe.elements();
    card.value = elements.create('card');
    card.value.mount(cardElement.value);
});

async function pay() {
    const stripe = await stripePromise;

    const { setupIntent, error } = await stripe.confirmCardSetup(props.client_secret, {
        payment_method: {
            card: card.value,
            billing_details: {
                name: 'Joe Bloggs',
            },
        },
    });

    if (error) {
        console.error(error.message);
    } else {
        console.log('SetupIntent confirmed:', setupIntent);
        router.post(route('payment.submit'), {setupIntent: setupIntent});
    }
}

</script>

<template>
    <GuestLayout>
        <Head title="Payment" />
        <StepsNav selected-user-type="supervisor" :current-step="3"/>
        <div ref="cardElement"></div>
        <button @click="pay" :data-secret="client_secret">Pay</button>
    </GuestLayout>
</template>

Thanks Mike

Steps To Reproduce

make a payment using card 4000002500003155 or 4000003800000446 using the code example, the challenge will happen twice.

driesvints commented 6 months ago

Heya please follow the docs for failed payment handling: https://laravel.com/docs/10.x/billing#handling-failed-payments

If you have question why this happens it's best to contact Stripe support.