Render Stripe Elements in Livewire Component On Multistep Form

1.6k views Asked by At

I created a multi-step form using Laravel Livewire, and I cannot figure out how to get Stripe Elements to render/initiate on the 3rd step of my form, or any step besides the 1st step.

I understand it's not working because the Stripe JS is not initiating when I reach step 3 due to no page reload. I just don't understand how to initiate the Stripe JS when I'm on step 3 without reloading the page.

Here is a live working example of the issue I'm having (You can mess with the code here): https://laravelplayground.com/#/snippets/2f89e808-d9e1-404a-bd65-3dad6168d26e

Any help or a point in the right direction would be extremely helpful.

Livewire Component Class:


use Livewire\Component;

class LivewireComponent extends Component
{

   public $pet_name;
   public $pet_type;
   public $cardHolderName;
   public $step;
    
    
    private $stepActions = [
        'submit1',
        'submit2',
        'submit3'
    ];
    
    public function mount()
    {
        $this->step = 0;
    }

    public function decreaseStep() {
        $this->step--;
    }

    public function submit() {
        $action = $this->stepActions[$this->step];
        $this->$action();
    }

    public function submit1() {
        $this->validate([
            'pet_name' => 'required|min:1',
        ]);
        $this->step++;
    }

    public function submit2() {
        $this->validate([
            'pet_type' => 'required',
        ]);
        $this->step++;
    }

    public function submit3() {
        dd($this->pet_name, $this->pet_type);
    }
  
    public function render()
    {
        return view('livewire-component');
    }
}

Livewire Component: (I striped all the classes out so it's more readable)

<div>

    <form wire:submit.prevent="submit">
    {{--STEP 1--}}
    @if($step == 0)
    <div>
        <label for="pet_name">Pet Name</label>
        <input id="pet_name" wire:model.lazy="pet_name">
    </div>

{{--ADD STRIPE ELEMENTS WORKS HERE ON 1ST STEP BUT NOT ON 3rd STEP--}}
{{-- @include('stripe-card-component') --}}
{{--/ADD STRIPE ELEMENTS/--}}
    </div>
    @endif


{{--STEP 2--}}
    @if($step == 1)
    <div>
        <label for="pet_type">Pet Type | Cat or Dog</label>
        <input id="pet_type" wire:model.lazy="pet_type">
    </div>
    @endif


{{--STEP 3--}}
    @if($step == 2)
    <div>
     <p>This is where my issue is. I cannot figure out how to get Stripe Elements to InitIate on Steps other than the first step.</p>

{{--ADD STRIPE ELEMENTS DOES NOT WORK HERE ON 3RD STEP--}}

     @include('stripe-card-component')

{{--/ADD STRIPE ELEMENTS DOES NOT WORK HERE ON 3RD STEP/--}}
    </div>
    @endif


//Buttons For Previous and Next Go Here

    </form>
</div>

Stripe-Card Blade Component:

<div>
    <label for="cardHolderName">Name on the card</label>
    <div>
        <input wire:model.lazy="cardHolderName" type="text" id="card-holder-name"/>
    </div>

    <label for="card">Card Details</label>
    <div>
        <div wire:ignore id="card-element"/>
    </div>
</div>


//Goes to the header
@push('stripe')
<script src="https://js.stripe.com/v3/"></script>
@endpush

//Goes before end body tag
@push('stripe-js')

    <script>
        const stripe = Stripe('pk_RXwtgk4Z5VR82S94vtwmam6P8qMXQ');
        const elements = stripe.elements();
        const cardElement = elements.create('card');
        cardElement.mount('#card-element');
        const cardHolderName = document.getElementById('card-holder-name');
        const cardButton = document.getElementById('card-button');
        const clientSecret = cardButton.dataset.secret;

        cardButton.addEventListener('click', async (e) => {
            const {setupIntent, error} = await stripe.confirmCardSetup(
                clientSecret, {
                    payment_method: {
                        card: cardElement,
                        billing_details: {name: cardHolderName.value}
                    }
                }
            );
            if (error) {
                let errorWrapper = document.getElementById('error-wrapper')
                errorWrapper.textContent = error.error
                console.info(error)
            } else {
                //   console.info(setupIntent.payment_method)
            @this.set('paymentMethod', setupIntent.payment_method)
            @this.call('submit')
            }
        });

    </script>
@endpush
1

There are 1 answers

0
Cal Payne On

You can dispatch a browser event from Livewire on your last step.

$this->dispatchBrowserEvent('lastStep');

With JS you can catch this event and then execute your Stripe JS code.

window.addEventListener('lastStep', event => {

});

This will make your Stripe JS code execute without reloading the page.