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

SquareNetMedia's avatar

Stripe Payments & Livewire 3

I am getting an error You cannot confirm this PaymentIntent because it's missing a payment method but the payments are being processed by stripe.

This is my code

    <!-- Email Address -->
    <div class="mb-4">
        <label for="email" class="text-sm font-medium text-gray-700 mb-2 block">Email Address</label>
        <input id="email" name="email" type="email"
            class="bg-white border border-gray-400 rounded shadow-inner h-12 p-2 text-gray-700 w-full" required>
    </div>

    <!-- Invoice Amount -->
    <div class="mb-4">
        <label for="invoice_amount" class="text-sm font-medium text-gray-700 mb-2 block">Invoice
            Amount</label>
        <input id="invoice_amount" name="invoice_amount" type="text" wire:model.defer="invoiceAmount"
            class="bg-white border border-gray-400 rounded shadow-inner h-12 p-2 text-gray-700 w-full" required
            placeholder="e.g., 100.05">
    </div>
    <div id="card-element"></div>
    <div id="card-errors" class="text-red-500 mt-2"></div>
    <button class="bg-blue-500 text-white rounded px-4 py-2 mt-3 w-full" id="pay">Pay
        Now</button>
</form>
<script>
    const stripe = Stripe('{{ config('services.stripe.key') }}');
    const elements = stripe.elements();

    // const component = @this;

    const cardElement = elements.create('card', {
        style: {
            base: {
                color: '#32325d',
                fontFamily: '"Helvetica Neue", Helvetica, sans-serif',
                fontSmoothing: 'antialiased',
                fontSize: '16px',
                '::placeholder': {
                    color: '#aab7c4'
                }
            },
            invalid: {
                color: '#fa755a',
                iconColor: '#fa755a'
            }
        },
        hidePostalCode: true
    });

    cardElement.mount('#card-element');

    const form = document.getElementById('payment-form');
    const submitButton = document.getElementById('pay');

    //When the form is submitted
    form.addEventListener('submit', async (e) => {
        e.preventDefault();
        submitButton.disabled = true;

        // Step 1: Create Payment Method
        const {
            paymentMethod,
            error
        } = await stripe.createPaymentMethod({
            type: 'card',
            card: cardElement,
            billing_details: {
                name: document.getElementById('name').value,
                email: document.getElementById('email').value,
            },
        });

        if (error) {
            // Display error on frontend
            const errorElement = document.getElementById('card-errors');
            errorElement.textContent = error.message;
            submitButton.disabled = false;
        } else {
            // Step 2: Update Livewire and let Livewire handle the rest
            // @this.set('paymentMethodId', paymentMethod.id);
            // @this.createAndConfirmPayment();
            @this.set('paymentMethodId', paymentMethod.id);
            @this.call('createAndConfirmPayment');
        }
    });
</script>

@endpush

And the code for livewire component

public $invoiceAmount; public $paymentMethodId;

public function createAndConfirmPayment()
{
    // Use the paymentMethodId property set from the frontend
    Stripe::setApiKey(config('services.stripe.secret'));

    $amountInCents = (int) ($this->invoiceAmount * 100); // Convert to pence

    // Create the Payment Intent and attach the payment method
    $paymentIntent = PaymentIntent::create([
        'amount' => $amountInCents,
        'currency' => 'gbp',
        'payment_method' => $this->paymentMethodId, // Attach the payment method
        'confirmation_method' => 'manual', // Manually confirm the payment
        'confirm' => true, //  Dont onfirm the payment immediately
        'return_url' => route('payment.success'), // The URL to redirect after payment
    ]);



    // Handle the success or failure of the payment as needed
    if ($paymentIntent->status === 'succeeded') {
        // Payment successful, do further processing
        // e.g., update order status, send confirmation, etc.
        return redirect()->route('payment.success');
    } else {
        // Handle other payment statuses (e.g., requires_action, requires_payment_method)
    }
}

public function render()
{
    return view('livewire.payment.payment-form');
}

not sure where I have gone wrong

0 likes
2 replies
LaryAI's avatar
Level 58

The error message "You cannot confirm this PaymentIntent because it's missing a payment method" suggests that the payment_method is not being correctly attached to the PaymentIntent. Let's go through the code and make sure everything is set up correctly.

Frontend (JavaScript)

  1. Ensure that the form has the correct id attribute (payment-form).
  2. Make sure the name input field is present in the form, as it is being referenced in the billing_details.

Backend (Livewire Component)

  1. Ensure that the paymentMethodId is being correctly set and passed to the createAndConfirmPayment method.
  2. Correct the confirm parameter in the PaymentIntent::create method to false if you want to confirm it manually later.

Updated Code

Frontend (HTML & JavaScript)

Backend (Livewire Component)

Explanation

  1. Frontend:

    • Added a name input field to capture the user's name.
    • Ensured the form has the correct id attribute (payment-form).
    • Updated the JavaScript to correctly handle the form submission and set the paymentMethodId.
  2. Backend:

    • Corrected the confirm parameter to false in the PaymentIntent::create method.
    • Added a manual confirmation step using $paymentIntent->confirm().

This should resolve the issue of the missing payment method and ensure the payment is processed correctly.

SquareNetMedia's avatar

Thanks but that doesn't seem to work, the payment medthod ID is being created, but there is a delay in livewire picking it up. When I DD on the PaymentMethodID, its NULL for 30 seconds before being updated. So not sure where the code is going wrong

Please or to participate in this conversation.