0

I'm trying to get Cashier to work with Livewire and I'm running into an error where I have to press the submit button twice to get the paymentmethod .

In this video: https://www.loom.com/share/cb27ce3ae8ab46fab27e68afc7900eb4

I'm trying to dd(); the following.

dd($this->paymentmethod, $this->cardHolderName, $this->plan->title, $this->plan->stripe_id);

Everything is returned on the first submission except to the $this->paymentmethod I have to submit the form a 2nd time for $this->paymentmethod to return in the dd();

Note I do not refresh the page on the 2nd form submission.


Here is the relevant code:

Livewire View:

<div>

<h3 class="text-lg leading-6 font-medium text-gray-200">
  Plan {{ $plan->title }} Checkout | ${{ $plan->priceFormat }}
</h3>
          
<form wire:submit.prevent="submitSubscription">
                    @if(Session::has('success'))
                        It submitted!
                    @endif
                    <div id="error-wrapper"></div>
             
<label for="cardHolderName">Name on the card</label>

<input wire:model.lazy="cardHolderName" type="text" id="card-holder-name"/>
       
                        @if(!$cardHolderName)
                            @error('cardHolderName')
                            <p>{{ $message}}</p>
                            @enderror
                        @endif

<label for="card" class="block text-sm font-medium leading-5 text-gray-200">Card Details</label>
                  
  <div wire:ignore id="card-element"/></div>
                  
       
<button id="card-button" type="submit" wire:target="submitSubscription" data-secret="{{ $intent->client_secret }}">Submit</button>

</div>


@push('stripe')
        <script src="https://js.stripe.com/v3/"></script>
    @endpush
    @section('in-page-scripts')
        <script>
            const stripe = Stripe('{{ config('cashier.key') }}');
            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)
                }
            });
        </script>
    @endsection

Livewire Controller:

<?php

namespace App\Http\Livewire\Subscription;

use App\Plan;
use Livewire\Component;

class PlansCheckout extends Component {

    public $plan;
    public $error;
    public $cardHolderName;
    public $paymentmethod;

    public function mount(Plan $plan) {
        $this->plan = $plan;
    }

    public function render() {
        return view('livewire.subscription.plans-checkout', [
            'intent' => auth()->user()->createSetupIntent(),
        ]);
    }

    public function submitSubscription() {
        $data = $this->validate([
            'cardHolderName' => 'required',
        ]);

        dd($this->paymentmethod, $this->cardHolderName, $this->plan->title, $this->plan->stripe_id);

        $this->reset();
    }
}

Any help on how to fix this would be appreciated.

4

2 回答 2

1

I think your flow is wrong. The $this->paymentmethod will not be displayed in your dd() because the ajax made by the stripe still was not finished when the submitSubscription is called (so the @this.set don't change the public variable paymentmethod yet, this happens only when you click a second time).

Remove the wire:target="submitSubscription" from the button and add the followed script instead of your @this.set()

@this.call('setPayment', setupIntent.payment_method)
                }
            });

So in your livewire component:

public function setPayment($paymentmethod){
    $this->paymentmethod = $paymentmethod;
    $this->submitSubscription();
}

This code waits for stripe ajax to be finished, setting the payment data in your livewire component to then submit the subscription.

于 2020-07-20T13:58:48.970 回答
0

Once successfully token returned, use @this.call('add_or_update_payment_method') function in the last.

<div>

<h3 class="text-lg leading-6 font-medium text-gray-200">
  Plan {{ $plan->title }} Checkout | ${{ $plan->priceFormat }}
</h3>
          
<form wire:submit.prevent="submitSubscription">
                    @if(Session::has('success'))
                        It submitted!
                    @endif
                    <div id="error-wrapper"></div>
             
<label for="cardHolderName">Name on the card</label>

<input wire:model.lazy="cardHolderName" type="text" id="card-holder-name"/>
       
                        @if(!$cardHolderName)
                            @error('cardHolderName')
                            <p>{{ $message}}</p>
                            @enderror
                        @endif

<label for="card" class="block text-sm font-medium leading-5 text-gray-200">Card Details</label>
                  
  <div wire:ignore id="card-element"/></div>
                  
       
<button id="card-button" type="submit" wire:target="submitSubscription" data-secret="{{ $intent->client_secret }}">Submit</button>

</div>


@push('stripe')
        <script src="https://js.stripe.com/v3/"></script>
    @endpush
    @section('in-page-scripts')
        <script>
            const stripe = Stripe('{{ config('cashier.key') }}');
            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('add_or_update_payment_method')
                }
            });
        </script>
    @endsection
public $paymentmethod;
public function updatedPaymentmethod()
    {
        if (filled(auth()->user()->stripe_id)) {
            $this->add_or_update_payment_method();
        } else {
            // notify user error
        }
    }
   
    public function add_or_update_payment_method()
    {
        try {
            if (!auth()->user()->hasPaymentMethod()) {
                auth()->user()->createOrGetStripeCustomer();
                auth()->user()->addPaymentMethod($this->paymentmethod);
                auth()->user()->updateDefaultPaymentMethod($this->paymentmethod);
            } else {
                auth()->user()->updateDefaultPaymentMethod($this->paymentmethod);
            }
            return true;
        } catch (\Exception $e) {
            return false;
            // error handling
        }
    }```
于 2020-10-30T15:52:33.060 回答