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

boyjarv's avatar

payment fail invalid payment method

I am trying to subscribe a user on my application using stripe I keep getting:

"message": "The payment attempt failed because of an invalid payment method.",
    "exception": "Laravel\Cashier\Exceptions\IncompletePayment",
    "file": "/Users/johnbiddulph/Documents/SITES/jb-admin-api/vendor/laravel/cashier/src/Exceptions/IncompletePayment.php",
    "line": 42,
    "trace": [
        {
            "file": "/Users/johnbiddulph/Documents/SITES/jb-admin-api/vendor/laravel/cashier/src/Payment.php",
            "line": 162,
            "function": "paymentMethodRequired",
            "class": "Laravel\Cashier\Exceptions\IncompletePayment",
            "type": "::"
        },
        {
....  and so on.....

Here is my code:

<template>
  <div id="register">
    <TopNavigation />
    <div class="w-full p-6 flex justify-center items-center">
      <div class="w-full max-w-xs">
        <div class="bg-gray-600 p-8 shadow rounded mb-6">
          <h1 class="mb-6 text-lg text-gray-100 font-thing">
            Let's get rocking
          </h1>
          <div class="mb-4">
            <SelectInput
              label="Client"
              :labelColor="false"
              :options="transformedOptions"
              placeholder="Please select"
              v-model:input="clientId"
              :error="errors.client_id ? errors.client_id[0] : ''"
            />
          </div>  
          <div class="mb-4">
            <TextInput
              label="First Name"
              :labelColor="false"
              placeholder="John"
              v-model:input="firstName"
              inputType="text"
              :error="errors.first_name ? errors.first_name[0] : ''"
            />
          </div>
          <div class="mb-4">
            <TextInput
              label="Last Name"
              :labelColor="false"
              placeholder="John"
              v-model:input="lastName"
              inputType="text"
              :error="errors.last_name ? errors.last_name[0] : ''"
            />
          </div>
          <div class="mb-4">
            <TextInput
              label="Email"
              :labelColor="false"
              placeholder="[email protected]"
              v-model:input="email"
              inputType="text"
              :error="errors.email ? errors.email[0] : ''"
            />
          </div>
          <div class="mb-4">
            <TextInput
              label="Password"
              :labelColor="false"
              placeholder="john123456"
              v-model:input="password"
              inputType="password"
              :error="errors.password ? errors.password[0] : ''"
            />
          </div>
          <div class="mb-4">
            <TextInput
              label="Confirm Password"
              :labelColor="false"
              placeholder="john123456"
              v-model:input="confirmPassword"
              inputType="password"
            />
          </div>
          
          <label>
            Card Details
            <!-- <div id="card-element" ref="cardElement"></div> -->
            <div id="card-number-element"></div>
            <div id="card-expiry-element"></div>
            <div id="card-cvc-element"></div>
          </label>
                    
          <button
            class="block w-full bg-green-500 text-white rounded-sm py-3 text-sm tracking-wide"
            type="submit"
            @click="register"
          >
            Register
          </button>
        </div>

        <p class="text-center text-md text-gray-900">
          Already have an account?...
          <router-link
            class="text-blue-500 no-underline hover:underline"
            to="/login"
          >
            Login
          </router-link>
        </p>
      </div>
    </div>
  </div>
</template>

<script setup>
import { loadStripe } from '@stripe/stripe-js';
import { ref, onMounted, computed } from "vue";
import { useUserStore } from '../store/UserStore';
import { useClientStore } from '../store/ClientStore';
import { useProfileStore } from '../store/ProfileStore';
import { usePostStore } from '../store/PostStore';
import { useRouter } from 'vue-router';
import TextInput from "@/components/global/TextInput.vue";
import SelectInput from "@/components/global/SelectInput.vue";
import axios from "axios";
import TopNavigation from "@/components/structure/TopNavigation.vue";

const userStore = useUserStore();
const clientStore = useClientStore();
const router = useRouter();
const profileStore = useProfileStore();
const postStore = usePostStore();

let errors = ref([]);
let clientId = ref(null);
let firstName = ref(null);
let lastName = ref(null);
let email = ref(null);
let password = ref(null);
let confirmPassword = ref(null);
let clients = ref([]);
let stripe = ref(null); // Declare stripe as a reactive property

let cardNumberElement = ref(null);
let cardExpiryElement = ref(null);
let cardCvcElement = ref(null);



let transformedOptions = computed(() => {
  return clients.value.map(client => ({
    value: client.id,
    label: client.name
  }));
});

onMounted(async () => {
  await clientStore.fetchClients();
  clients.value = clientStore.clients;
  stripe.value = await initializeStripe(); // Assign the initialized stripe value
  const elements = stripe.value.elements(); // Get the Stripe elements instance
  cardNumberElement.value = elements.create('cardNumber');
  cardExpiryElement.value = elements.create('cardExpiry');
  cardCvcElement.value = elements.create('cardCvc');
  cardNumberElement.value.mount('#card-number-element');
  cardExpiryElement.value.mount('#card-expiry-element');
  cardCvcElement.value.mount('#card-cvc-element');
});


const initializeStripe = async () => {
  const stripe = await loadStripe('pk_test_51LGnwdH8B3O205oeNawhEVgb4g7R3iFEQjPk25ScCUvq0jYgv46VQCDH2tpDnJW08WYdjYzXNvDZYLB2oDUjavWd0044IrAwxH'); // Replace with your Stripe publishable key
  //const stripe = await loadStripe('pk_live_51LGnwdH8B3O205oeOdsDQZ10MwxqRwREqVLTHXBTKXddrsAV8KDBDCzHP7YW2jv4N4LdXYxEYon8guwdqWka92q900K5AijE1d');
  return stripe;
}

const register = async () => {
  errors.value = [];

  try {
    let res = await axios.post('api/register', {
      client_id: clientId.value,
      first_name: firstName.value,
      last_name: lastName.value,
      email: email.value,
      password: password.value,
      password_confirmation: confirmPassword.value
    });
    axios.defaults.headers.common['Authorization'] = 'Bearer ' + res.data.token;
    console.log('Response Data: ', res);
    const user = res.data.user;
    userStore.setUserDetails(res);
    console.log('UserStore: ', userStore);
    await profileStore.fetchProfileById(userStore.id);
    await postStore.fetchPostsByUserId(userStore.id);
    const stripeInstance = await initializeStripe();
    console.log('stripeInstance: ', stripeInstance)
    // Make an API request to your backend to create a customer and subscription
    const subscriptionResponse = await axios.post('api/create-subscription', {
      user,
      customerEmail: email.value,
      customerName: firstName.value + ' ' + lastName.value,
      priceId: 'price_1NSmEQH8B3O205oeHPngASZm', // Replace with your actual Stripe price ID
      cardNumber: cardNumberElement.value,
      cardExpiry: cardExpiryElement.value,
      cardCvc: cardCvcElement.value,
    });
    console.log('subscriptionResponse: ', subscriptionResponse)
    // Handle the subscription response as needed
    const subscriptionId = subscriptionResponse.data.subscriptionId;
    // ...
    console.log('subscriptionId: ', subscriptionId)
    router.push('/account/profile/' + userStore.id);
    } catch (error) {
      if (error.response && error.response.data && error.response.data.errors) {
      errors.value = error.response.data.errors;
    } else {
      errors.value = ['An error occurred. Please try again.'];
    }
  }
};

</script>

0 likes
34 replies
boyjarv's avatar
<div id="card-number-element"></div>
            <div id="card-expiry-element"></div>
            <div id="card-cvc-element"></div>
1 like
boyjarv's avatar

@vincent15000 Thanks, I'm getting so confused!

I believe the problem to be in my register method here:

const register = async () => {
  errors.value = [];

  try {
    let res = await axios.post('api/register', {
      client_id: clientId.value,
      first_name: firstName.value,
      last_name: lastName.value,
      email: email.value,
      password: password.value,
      password_confirmation: confirmPassword.value,
    });
    axios.defaults.headers.common['Authorization'] = 'Bearer ' + res.data.token;
    console.log('Response Data: ', res);
    const user = res.data.user;
    userStore.setUserDetails(res);
    console.log('UserStore: ', userStore);
    await profileStore.fetchProfileById(userStore.id);
    await postStore.fetchPostsByUserId(userStore.id);
    const stripeInstance = await initializeStripe();
    console.log('stripeInstance: ', stripeInstance)
    // Make an API request to your backend to create a customer and subscription
    const subscriptionResponse = await axios.post('api/create-subscription', {
      user,
      customerEmail: email.value,
      customerName: firstName.value + ' ' + lastName.value,
      priceId: 'price_1NSmEQH8B3O205oeHPngASZm', // Replace with your actual Stripe price ID
      paymentMethod: cardElement.value, // Pass the cardElement reference here
    }).then(function(result) {
    // Handle result.error or result.setupIntent
    console.log('result: ', result);
    });
    console.log('subscriptionResponse: ', subscriptionResponse)
    // Handle the subscription response as needed
    const subscriptionId = subscriptionResponse.data.subscriptionId;
    // ...
    console.log('subscriptionId: ', subscriptionId)
    router.push('/account/profile/' + userStore.id);
  } catch (error) {
    if (error.response && error.response.data && error.response.data.errors) {
    errors.value = error.response.data.errors;
  } else {
    errors.value = ['An error occurred. Please try again.'];
  }
  }
};

if anyone can help, I would be happy to pay some money Thanks

1 like
boyjarv's avatar

@vincent15000 hi, aren't I already doing this here:

public function create(Request $request)
    {
        $user = User::findOrFail($request->user['id']); // Assuming the authenticated user is available
        
        $user->newSubscription('default', $request->priceId)
            ->create($request->paymentMethodId, [
                'email' => $request->customerEmail,
                'name' => $request->customerName,
                // Additional data fields can be set here
            ]);

        // Optionally, you can retrieve the subscription ID and return it in the response
        $subscriptionId = $user->subscription('default')->id;

        return response()->json([
            'subscriptionId' => $subscriptionId,
        ]);
    }
boyjarv's avatar

I am still getting:

"message": "The payment attempt failed because of an invalid payment method.",
    "exception": "Laravel\Cashier\Exceptions\IncompletePayment",

here is my updated code with handlePaymentMethodSetup():

<script setup>
import { loadStripe } from '@stripe/stripe-js';
import { ref, onMounted, computed } from "vue";
import { useUserStore } from '../store/UserStore';
import { useClientStore } from '../store/ClientStore';
import { useProfileStore } from '../store/ProfileStore';
import { usePostStore } from '../store/PostStore';
import { useRouter } from 'vue-router';
import TextInput from "@/components/global/TextInput.vue";
import SelectInput from "@/components/global/SelectInput.vue";
import axios from "axios";
import TopNavigation from "@/components/structure/TopNavigation.vue";

const userStore = useUserStore();
const clientStore = useClientStore();
const router = useRouter();
const profileStore = useProfileStore();
const postStore = usePostStore();

let errors = ref([]);
let clientId = ref(null);
let firstName = ref(null);
let lastName = ref(null);
let email = ref(null);
let password = ref(null);
let confirmPassword = ref(null);
let clients = ref([]);
let stripe = ref(null); // Declare stripe as a reactive property
let cardElement = ref(null);


let transformedOptions = computed(() => {
  return clients.value.map(client => ({
    value: client.id,
    label: client.name
  }));
});

onMounted(async () => {
  await clientStore.fetchClients();
  clients.value = clientStore.clients;

  stripe.value = await initializeStripe(); // Assign the initialized stripe value

  const elements = stripe.value.elements(); // Get the Stripe elements instance

  // Create the card element and mount it to the DOM
  const card = elements.create('card');
  card.mount('#card-element');

  cardElement.value = card; // Store the card element reference in the reactive property
  
});


const initializeStripe = async () => {
  const stripe = await loadStripe('pk_test_xxx'); // Replace with your Stripe publishable key
  //const stripe = await loadStripe('pk_live_xxx');
  return stripe;
}

const handlePaymentMethodSetup = async () => {
  const stripe = await initializeStripe();
  const cardHolderName = firstName.value + ' ' + lastName.value; // Moved inside the function

  try {
    // Use your server to create a SetupIntent and get the clientSecret
    const clientSecret = 'sk_test_xxx'; // Replace this with the actual clientSecret received from your server.

    const { setupIntent, error } = await stripe.confirmCardSetup(
      clientSecret,
      {
        payment_method: {
          card: cardElement.value, // Use cardElement.value here
          billing_details: { name: cardHolderName }
        }
      }
    );

    if (error) {
      console.error('Error:', error.message);
      // Display "error.message" to the user...
    } else {
      console.log('SetupIntent:', setupIntent);
      // The card has been verified successfully...
    }
  } catch (err) {
    console.error('Error:', err.message);
    // Handle any other errors that might occur during the payment method setup
  }
};


const register = async () => {
  errors.value = [];

  try {
    await handlePaymentMethodSetup(); // Await the promise returned by handlePaymentMethodSetup
    let res = await axios.post('api/register', {
      client_id: clientId.value,
      first_name: firstName.value,
      last_name: lastName.value,
      email: email.value,
      password: password.value,
      password_confirmation: confirmPassword.value,
    });
    axios.defaults.headers.common['Authorization'] = 'Bearer ' + res.data.token;
    console.log('Response Data: ', res);
    const user = res.data.user;
    userStore.setUserDetails(res);
    console.log('UserStore: ', userStore);
    await profileStore.fetchProfileById(userStore.id);
    await postStore.fetchPostsByUserId(userStore.id);
    const stripeInstance = await initializeStripe();
    console.log('stripeInstance: ', stripeInstance)
    // Make an API request to your backend to create a customer and subscription
    const subscriptionResponse = await axios.post('api/create-subscription', {
      user,
      customerEmail: email.value,
      customerName: firstName.value + ' ' + lastName.value,
      priceId: 'price_1NSmEQH8B3O205oeHPngASZm', // Replace with your actual Stripe price ID
      paymentMethod: cardElement.value, // Pass the cardElement reference here
    }).then(function(result) {
    // Handle result.error or result.setupIntent
    console.log('result: ', result);
    });
    console.log('subscriptionResponse: ', subscriptionResponse)
    // Handle the subscription response as needed
    const subscriptionId = subscriptionResponse.data.subscriptionId;
    // ...
    console.log('subscriptionId: ', subscriptionId)
    router.push('/account/profile/' + userStore.id);
  } catch (error) {
    if (error.response && error.response.data && error.response.data.errors) {
    errors.value = error.response.data.errors;
  } else {
    errors.value = ['An error occurred. Please try again.'];
  }
  }
};

</script>

1 like
vincent15000's avatar

Why would the payment method be the card element value ?

paymentMethod: cardElement.value // ???

The payment method is a code beginning by pm_ and automatically generated by Stripe when you create a new payment method for a customer.

Step by step, you could have it work.

In the view where your customer will type his payment details, you need to pass the intent secret variable. This view can for example be the view where you display the price the customer has to pay and the form to type the payment details (name, credit card number, ...).

https://laravel.com/docs/10.x/billing#storing-payment-methods

// content of the controller method to display the view to type the payment details
return view('update-payment-method', [
    'intent' => $user->createSetupIntent()
]);

In this view, you need to have the stripe JS script.

// form for the customer to type his payment details
<form method="POST" action="{{ route('subscribe.post') }}" id="payment-form">
    @csrf

	<input id="card-holder-name" type="text">
	 
	<!-- Stripe Elements Placeholder -->
	<div id="card-element"></div>
	 
	<button id="card-button" data-secret="{{ $intent->client_secret }}">
	    Update Payment Method
	</button>
</form>

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

<script>
    const stripe = Stripe('your-stripe-public-key');
 
    const elements = stripe.elements();
    const cardElement = elements.create('card');
 
    cardElement.mount('#card-element');
</script>
 
// script to confirm the card details
<script>
	const cardHolderName = document.getElementById('card-holder-name');
	const cardButton = document.getElementById('card-button');
	const clientSecret = cardButton.dataset.secret;
	 
	cardButton.addEventListener('click', async (e) => {
		e.preventDefault();

	    const { setupIntent, error } = await stripe.confirmCardSetup(
	        clientSecret, {
	            payment_method: {
	                card: cardElement,
	                billing_details: { name: cardHolderName.value }
	            }
	        }
	    );
	 
	    if (error) {
	        console.log(error.message);
	    } else {
	        stripeSubscriptionHandler(setupIntent.payment_method);
	    }
	});

	// function to submit the form and pass the payment method to the controller and 
    function stripeSubscriptionHandler(paymentMethod) {
        let form = document.getElementById('payment-form');
        let hiddenInput = document.createElement('input');
        hiddenInput.setAttribute('type', 'hidden');
        hiddenInput.setAttribute('name', 'paymentMethod');
        hiddenInput.setAttribute('value', paymentMethod);
        form.appendChild(hiddenInput);
        form.submit();
    }
</script>

Then in the controller to subscribe.

auth()->user()->newSubscription(
    'default', 'price_sdjfhgkjlghfdg' // here is the price you have created directly on stripe
)->create($request->paymentMethod);

return redirect()->route('subscribe');

I just tested this code a few minutes ago and it works fine.

PS Your code is very confusing because it contains a lot of lines. You should factorize it to let it easier to read.

vincent15000's avatar

@boyjarv And once the payment method is registered, instead of creating a new one, for example for future payments, you can retrieve the default payment method of the customer.

In that case all the JS scripts are not required and you have only to retrieve the payment method of the customer to use it.

auth()->user()->newSubscription(
    'default', 'price_sdjfhgkjlghfdg'
)->create(auth()->user()->defaultPaymentMethod()->id);

return redirect()->route('subscribe');

If the customer has registered several payment methods you can retrieve all his payment methods and let him choose which he wants to use.

boyjarv's avatar

argh I'm so confused, I'm getting this now: "message": "Object of type App\Models\User is not callable", "exception": "Error",

here is my backend subscription controller:

<?php

namespace App\Http\Controllers\API;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Models\User;
use Laravel\Cashier\Cashier;

class SubscriptionController extends Controller
{
    public function create(Request $request)
    {
        $user = User::findOrFail($request->user['id']); // Assuming the authenticated user is available
        
        // $user->newSubscription('default', $request->priceId)
        //     ->create($request->paymentMethodId, [
        //         'email' => $request->customerEmail,
        //         'name' => $request->customerName,
        //         // Additional data fields can be set here
        //     ]);

            $user()->newSubscription(
                'default', 'price_1NXpY1Jd6VREmFDKf3BLAS7R'
            )->create($user()->defaultPaymentMethod()->id);

        // Optionally, you can retrieve the subscription ID and return it in the response
        $subscriptionId = $user->subscription('default')->id;

        return response()->json([
            'subscriptionId' => $subscriptionId,
        ]);
    }
}

here is my vue front end tidied:

<template>
  <div id="register">
    <TopNavigation />
    <div class="w-full p-6 flex justify-center items-center">
      <div class="w-full max-w-xs">
        <div class="bg-gray-600 p-8 shadow rounded mb-6">
          <h1 class="mb-6 text-lg text-gray-100 font-thing">
            Let's get rocking
          </h1>
          <form @submit.prevent="handleSubmit" id="payment-form">
          <div class="mb-4">
            <SelectInput
              label="Client"
              :labelColor="false"
              :options="transformedOptions"
              placeholder="Please select"
              v-model:input="clientId"
              :error="errors.client_id ? errors.client_id[0] : ''"
            />
          </div>  
          <div class="mb-4">
            <TextInput
              label="First Name"
              :labelColor="false"
              placeholder="John"
              v-model:input="firstName"
              inputType="text"
              :error="errors.first_name ? errors.first_name[0] : ''"
            />
          </div>
          <div class="mb-4">
            <TextInput
              label="Last Name"
              :labelColor="false"
              placeholder="John"
              v-model:input="lastName"
              inputType="text"
              :error="errors.last_name ? errors.last_name[0] : ''"
            />
          </div>
          <div class="mb-4">
            <TextInput
              label="Email"
              :labelColor="false"
              placeholder="[email protected]"
              v-model:input="email"
              inputType="text"
              :error="errors.email ? errors.email[0] : ''"
            />
          </div>
          <div class="mb-4">
            <TextInput
              label="Password"
              :labelColor="false"
              placeholder="john123456"
              v-model:input="password"
              inputType="password"
              :error="errors.password ? errors.password[0] : ''"
            />
          </div>
          <div class="mb-4">
            <TextInput
              label="Confirm Password"
              :labelColor="false"
              placeholder="john123456"
              v-model:input="confirmPassword"
              inputType="password"
            />
          </div>
          
          <label>
            Card Number
            <div id="card-element" ref="cardElement"></div>
          </label>
                   
          <button 
            class="block w-full bg-green-500 text-white rounded-sm py-3 text-sm tracking-wide"
            type="submit"
            id="card-button"
            >
            {{ buttonLabel }}
          </button>
          </form>
        </div>

        <p class="text-center text-md text-gray-900">
          Already have an account?...
          <router-link
            class="text-blue-500 no-underline hover:underline"
            to="/login"
          >
            Login
          </router-link>
        </p>
      </div>
    </div>
  </div>
</template>

<script setup>
import { loadStripe } from '@stripe/stripe-js';
import { ref, onMounted, computed } from "vue";
import { useUserStore } from '../store/UserStore';
import { useClientStore } from '../store/ClientStore';
import { useProfileStore } from '../store/ProfileStore';
import { usePostStore } from '../store/PostStore';
import { useRouter } from 'vue-router';
import TextInput from "@/components/global/TextInput.vue";
import SelectInput from "@/components/global/SelectInput.vue";
import axios from "axios";
import TopNavigation from "@/components/structure/TopNavigation.vue";

const userStore = useUserStore();
const clientStore = useClientStore();
const router = useRouter();
const profileStore = useProfileStore();
const postStore = usePostStore();

let errors = ref([]);
let clientId = ref(null);
let firstName = ref(null);
let lastName = ref(null);
let email = ref(null);
let password = ref(null);
let confirmPassword = ref(null);
let clients = ref([]);
let stripe = ref(null); // Declare stripe as a reactive property
let cardElement = ref(null);


let transformedOptions = computed(() => {
  return clients.value.map(client => ({
    value: client.id,
    label: client.name
  }));
});

const clientSecret = 'sk_test_xxxx'; // Replace with your client secret

let buttonLabel = ref('Register');

onMounted(async () => {
  await clientStore.fetchClients();
  clients.value = clientStore.clients;

  stripe.value = await initializeStripe(); // Assign the initialized stripe value

  const elements = stripe.value.elements(); // Get the Stripe elements instance

  // Create the card element and mount it to the DOM
  const card = elements.create('card');
  card.mount('#card-element');

  cardElement.value = card; // Store the card element reference in the reactive property
  
});


const initializeStripe = async () => {
  const stripe = await loadStripe('pk_test_xxx'); // Replace with your Stripe publishable key
  //const stripe = await loadStripe('pk_live_xxx');
  return stripe;
}

const handlePayment = async () => {
  try {
    const { setupIntent, error } = await initializeStripe.confirmCardSetup(clientSecret, {
      payment_method: {
        card: cardElement,
        billing_details: { name: firstName.value + ' ' + lastName.value }
      }
    });

    if (error) {
      console.error(error.message);
    } else {
      stripeSubscriptionHandler(setupIntent.payment_method);
    }
  } catch (err) {
    console.error(err.message);
  }
};

const handleSubmit = async () => {
  buttonLabel.value = 'Processing...'; // Show "Processing..." during form submission
  await handlePayment(); // Wait for the handlePayment to complete
  buttonLabel.value = 'Register'; // Reset button label to "Register" after completion
  register(); // Call the register function to handle form submission
};

const stripeSubscriptionHandler = (paymentMethod) => {
  const form = document.getElementById('payment-form');
  const hiddenInput = document.createElement('input');
  hiddenInput.setAttribute('type', 'hidden');
  hiddenInput.setAttribute('name', 'paymentMethod');
  hiddenInput.setAttribute('value', paymentMethod);
  form.appendChild(hiddenInput);
  form.submit();
};

const register = async () => {
  errors.value = [];

  try {
    let res = await axios.post('api/register', {
      client_id: clientId.value,
      first_name: firstName.value,
      last_name: lastName.value,
      email: email.value,
      password: password.value,
      password_confirmation: confirmPassword.value,
    });
    axios.defaults.headers.common['Authorization'] = 'Bearer ' + res.data.token;

    const user = res.data.user;
    userStore.setUserDetails(res);
    await profileStore.fetchProfileById(userStore.id);
    await postStore.fetchPostsByUserId(userStore.id);
    
    // Make an API request to your backend to create a customer and subscription
    const subscriptionResponse = await axios.post('api/create-subscription', {
      user,
      customerEmail: email.value,
      customerName: firstName.value + ' ' + lastName.value,
      priceId: 'price_1NSmEQH8B3O205oeHPngASZm', // Replace with your actual Stripe price ID
      paymentMethod: cardElement.value, // Pass the cardElement reference here
    });
    console.log('subscriptionResponse: ', subscriptionResponse)
    // Handle the subscription response as needed
   
    const subscriptionId = subscriptionResponse.data.subscriptionId;
    console.log('subscriptionId: ', subscriptionId)
    
    router.push('/account/profile/' + userStore.id);
  } catch (error) {
    if (error.response && error.response.data && error.response.data.errors) {
    errors.value = error.response.data.errors;
  } else {
    errors.value = ['An error occurred. Please try again.'];
  }
  }
};

</script>

boyjarv's avatar

Please someone help?! I'm pulling my hair out here... Thank you @vincent15000 but I looked at your code and tried to implement it into my project and was unsuccessful - I can pay someone to help me with this

1 like
boyjarv's avatar

@vincent15000 Thank you, it's a personal project, to show case my skills... this really is the final piece hence making it live.. although I will be adding to my project.

The idea is that when someone registers, they pay a subscription fee and can then upload unlimiited artwork, I'll have a look at: https://laracasts.com/series/billing-with-laravel-cashier but feel my code may be too messy now

Thanks

1 like
boyjarv's avatar

@vincent15000 so I have been at it all night again following carefully the instructions except my project is in Vue not blade, I am currently trying to get the Client secret out of the form

here is the top part of my form:

<form @submit.prevent="handleSubmit" id="payment-form" ref="paymentForm" :data-secret="clientSecret">

here is my handlePayment method:

const handlePayment = async () => {
  const stripe = await initializeStripe();
  const elements = stripe.elements();
  try {
    // Create the card element and mount it to the DOM
    // Get the form element reference using $refs
    const form = ref(paymentForm);
console.log('Form: ', form);
    // Access the 'data-secret' attribute using getAttribute
    const clientSecret = form.value.getAttribute('data-secret');
    const card = elements.create('card');
    card.mount('#card-element');
    

    const { setupIntent } = await stripe.confirmCardSetup(clientSecret, {
      payment_method: {
        card,
        billing_details: {
          name: firstName.value + ' ' + lastName.value,
          // You can add additional billing details here if needed
        },
      },
    });
    paymentMethodId.value = setupIntent.payment_method;
    console.log('setup intent: ' + setupIntent)
  } catch (err) {
    console.error(err.message);
  }
};

so I'm stuck on this line it doesn't know what clientSecret is, I can't paste in the string directly:

const { setupIntent } = await stripe.confirmCardSetup(clientSecret, {

its taken me about 2 hours to get up to 14:30 on this video: https://laracasts.com/series/billing-with-laravel-cashier/episodes/1

vincent15000's avatar

@boyjarv It seems that you are putting the value into datasets.

<div id="mydiv" data-mysuperdata="mydata"></div>

If this is the case, you can retrieve the datas like this.

var element = document.getElementById('mydiv');
var data = element.dataset.mysuperdata;

You have tried this : form.dataset.secret and this will work only if the data-secret attribute is inside the form tag.

Can you share the piece of HTML code where you are using the data-secret attribute please ?

boyjarv's avatar

@vincent15000

Thanks, I already shared this bit above:

<form @submit.prevent="handleSubmit" id="payment-form" ref="paymentForm" :data-secret="clientSecret">

I'm getting: RegisterView.vue:230 Cannot read properties of undefined (reading 'secret')

1 like
vincent15000's avatar

@boyjarv Does this work ?

var form = document.getElementById('payment-form');
var data = form.dataset.secret;
console.log(data);
boyjarv's avatar

@vincent15000 no, I get:

Invalid value for stripe.confirmCardSetup intent secret: value should be a client secret of the form ${id}secret${secret}. You specified: sk_test_51LGnwdH8B3O205oeVPUinr3kxVokSe3Bxx.......

1 like
vincent15000's avatar

@boyjarv The client secret is different from the stripe public key, they are not the same.

The stripe public key is to call the API.

And the client secret is to check if the card is correctly setup.

You're going to fast, you have to check everything step by step before going further.

1 like
martinbean's avatar

You're going to fast, you have to check everything step by step before going further.

💯

1 like
boyjarv's avatar

ok so what am I doing wrong? I am following along slowly and the video states to get the Client_secret?!

1 like
vincent15000's avatar

@boyjarv I don't know what you are doing wrong. Sure something is wrong, but the only way to find what is to write the code step by step and after each step check the content of the different variables you are handling to be sure that they contain the right values.

These steps are based on my complete example in a previous comment.

For each step, you need to add only a piece of code, not all the code at once, and each time check if it works before adding the next piece of code.

Step 1 : create a setup intent => check that the setup intent is successfully created and contains the client_secret property, this can be done with a dd($intent) inside the controller

Step 2 : write the form used to subscribe => check if the form displays correctly and check the source code of the page to check if the client secret is put in the data-secret attribute of the button

Step 3 : add the js.stripe.com/v3 script => check if there is no error in the console

Step 4 : add the script to handle the form (the script where you have to use the stripe public key) => check if there is no error in the console

Step 5 : add the script to confirm the card setup, here you have a condition directly in the script to check if there is an error => add a console.log in each part (error => console.log(error.message) / no error => console.log(setupIntent)) and check in the console if all works fine, you should see the setupIntent object with the payment_method property

Step 6 : add the paymentMethod value to the form by handling the DOM => check if the payment_method is added into the form

Step 7 : submit the form => from the controller, check with dd() what values are in the request, you should have the payment_method (and other values)

Step 8 : in the controller, add the needed code to create the subcription => check in the database and on your stripe dashboard if the subscription is created

1 like
boyjarv's avatar

Thanks @vincent15000 but I ended up paying a freelancer to get this sorted for me.

Felt like I was going round in circles. Even following video tutorials?!

The only think I’m stuck on now is the US card input is asking for ZIP code. I need Uk postcode field

1 like

Please or to participate in this conversation.