javascript - Laravel Cashier - prevent creating user if payment fail ← (PHP, Laravel, JavaScript)

I'm using Laravel 5.8 and Cashier to create subscribers using Stripe. I need to take payment before the user is created.

My code frontend:

<div class="form-group">
   <input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email" placeholder="Email Address">
   @error('email')
   <span class="invalid-feedback" role="alert">
   <strong>{{ $message }}</strong>
   </span>
   @enderror                
</div>
<div class="form-group row">
   <div class="col-sm-6 mb-3 mb-sm-0">
      <input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="new-password" placeholder="Password">
      @error('password')
      <span class="invalid-feedback" role="alert">
      <strong>{{ $message }}</strong>
      </span>
      @enderror                  
   </div>
   <div class="col-sm-6">
      <input id="password-confirm" type="password" class="form-control" name="password_confirmation" required autocomplete="new-password" placeholder="Confirm Password">
   </div>
</div>
<hr>
<div class="text-center">
   <h1 class="h4 text-gray-900 mb-4">Payment Details</h1>
</div>
<div class="flex flex-wrap mb-6">
   <label for="card-element" class="block text-gray-700 text-sm font-bold mb-2">
   Choose a subscription plan
   </label>
   <select id="plan" name="plan" class="form-control">
      <option value="price_1HU5qCJ0Bip59UAeKGtNPHra">Boost B2C & B2B - 29,99 eur a month (most popular)</option>
      <option value="price_1HU5qOJ0Bip59UAeSBeGhV7Q">Boost B2C - 14,99 eur a month</option>
   </select>
</div>
<div class="flex flex-wrap mb-6 mt-4">
   <label for="card-element" class="block text-gray-700 text-sm font-bold mb-2">
   Please enter your Credit Card detail below
   </label>
   <div id="card-element" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"></div>
   <div id="card-errors" class="text-red-400 text-bold mt-2 text-sm font-medium text-danger"></div>
</div>
<div class="col-md-12">
   <div class="form-group{{ $errors->has('g-recaptcha-response') ? ' has-error' : '' }}">
      <label class="col-md-4 control-label">Captcha</label>
      <div class="col-md-6 pull-center">
         {!! app('captcha')->display() !!}
         @if ($errors->has('g-recaptcha-response'))
         <span class="help-block">
         <strong>{{ $errors->first('g-recaptcha-response') }}</strong>
         </span>
         @endif
      </div>
   </div>
</div>
<button type="submit" id="card-button" class="mt-3 btn btn-primary shadow-sm inline-block align-right text-right select-none border font-bold whitespace-no-wrap py-2 px-4 rounded text-base leading-normal no-underline text-gray-100 bg-blue-500 hover:bg-blue-700 pull-right">
{{ __('Register') }}
</button>

also there is JS code:

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

    <script>
        const stripe = Stripe('pk_live_123456789');
        console.log(stripe);
        const elements = stripe.elements();
        const cardElement = elements.create('card');
        cardElement.mount('#card-element');
        const cardHolderName = document.getElementById('name');
        const cardButton = document.getElementById('card-button');
        const clientSecret = cardButton.dataset.secret;
        let validCard = false;
        const cardError = document.getElementById('card-errors');
        cardElement.addEventListener('change', function(event) {
            
            if (event.error) {
                validCard = false;
                cardError.textContent = event.error.message;
            } else {
                validCard = true;
                cardError.textContent = '';
            }
        });
        var form = document.getElementById('signup-form');
        form.addEventListener('submit', async (e) => {
            event.preventDefault();
            const { paymentMethod, error } = await stripe.createPaymentMethod(
                'card', cardElement, {
                    billing_details: { name: cardHolderName.value }
                }
            );
            if (error) {
                // Display "error.message" to the user...
                console.log(error);
                cardError.textContent = error.message;
            } else {

                // The card has been verified successfully...
                var hiddenInput = document.createElement('input');
                hiddenInput.setAttribute('type', 'hidden');
                hiddenInput.setAttribute('name', 'payment_method');
                hiddenInput.setAttribute('value', paymentMethod.id);
                form.appendChild(hiddenInput);
                // Submit the form
                form.submit();
            }
        });
    
    </script>

my RegisterController.php is:

 public function register(Request $request)
    {


        $this->validator($request->all())->validate();


        DB::beginTransaction();

        event(new Registered($user = $this->create($request->all())));

        try {

            $newSubscription = $user->newSubscription('main', $request->plan)->create($request->payment_method, ['email' => $user->email]);
        } catch ( Exception $exception ){
            DB::rollback();
            return redirect()->back()->with(['error_message' => $exception->getMessage()]);
        }


        DB::commit();

        $this->guard()->login($user);

        return $this->registered($request, $user)
                        ?: redirect($this->redirectPath());
    }


    protected function validator(array $data)
    {
        return Validator::make($data, [
            'name' => ['required', 'string', 'max:255'],
            'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
            'password' => ['required', 'string', 'min:8', 'confirmed'],
            'g-recaptcha-response' => ['required', 'captcha'],
        ]);
    }

    /**
     * Create a new user instance after a valid registration.
     *
     * @param  array  $data
     * @return \App\User
     */
    protected function create(array $data)
    {       
        
        return User::create([
            'name' => $data['name'],
            'email' => $data['email'],
            'hotel_name' => $data['hotel_name'],
            'address' => $data['address'],
            'country' => $data['country'],
            'city' => $data['city'],
            'zipcode' => $data['zipcode'],
            'position' => $data['position'],
            'website' => $data['website'],
            'phone' => $data['phone'],
            'password' => Hash::make($data['password']),
            'token' => str_random(5),
        ]);
    }

I have a problem with BOTS. There create a hundred new users each day. I want to prevent it.

My code creates a user when Stripe payment fails and the user got an error but in the database, the user is created. WHy? I want to prevent it. So if there is no successful payment user should NOT be created. Please help!

Answer



Solution:

Why not put the create user in the try/catch block?

 public function register(Request $request)
 {


     $this->validator($request->all())->validate();

     try {
            event(new Registered($user = $this->create($request->all())));

            $newSubscription = $user->newSubscription('main', $request->plan)->create($request->payment_method, ['email' => $user->email]);

            $this->guard()->login($user);

            return $this->registered($request, $user) ?: redirect($this->redirectPath());

        } catch ( Exception $exception ){

            return redirect()->back()->with(['error_message' => $exception->getMessage()]);

        }
    }

Source