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

mikefolsom's avatar

Submitting a Stripe Checkout form with Vue, hidden elements are null

I'm attempting a pretty simple Stripe Checkout flow with Vue.

I have a form serving as the root element, with three buttons, each of which will select a particular subscription plan. Things seem to be working fine until the form is actually submitted. The hidden elements stripe_token and stripe_email come through with null values, even though they are being updated via v-model directives. In other words, if I stop short of submitting the form, they have values populated from the Stripe token, as evidenced by inspecting the DOM and by Vue Dev Tools. But once the form is submitted, those values seem to be wiped out.

The blade file:

@extends('layouts.master')

@section('content')
<div class="container">
    {!! Form::open([
        'url' => '/subscriptions',
        'id' => 'checkoutForm',
    ]) !!}
        @foreach ($plans as $plan)
        <div>
            {{ $plan->name }} {{ $plan->description }}
            <button v-on:click.prevent="subscribe({{ $plan->id }})">Select</button>
        </div>
        @endforeach
        <input type="hidden" name="selected_plan" v-model="selectedPlanId">
        <input type="hidden" name="stripe_email" v-model="stripeEmail">
        <input type="hidden" name="stripe_token" v-model="stripeToken">
    {!! Form::close() !!}
</div>
@endsection

@push('scripts.body')
<script>
    var plans = {!! $plans !!}; // JSON from the controller
</script>
<script src="https://checkout.stripe.com/checkout.js"></script>
<script src="/js/checkout.js"></script>
@endpush

checkout.js:

var vm = new Vue({

    el: "#checkoutForm",

    data: {
        plans: plans, // From a global JSON array
        selectedPlan: null, // Default value
        stripeEmail: '', // v-model for hidden form field
        stripeToken: ''  // v-model for hidden form field
    },

    computed: {
        selectedPlanId() {
            if (this.selectedPlan) {
                return this.selectedPlan.id;
            }
            return '';
        }
    },

    methods: {
        subscribe(planId) {
            let plan = this.findPlanById(planId);
            console.log(plan); // works
            this.selectedPlan = plan;
            // The following opens a Stripe checkout widget
            // with all the correct information.
            this.handler.open({
                name: plan.name,
                description: plan.description,
                amount: plan.price * 100 // stored as decimal
            });
        },

        findPlanById(id) {
            return this.plans.find(plan => plan.id == id);
        }
    },

    created() {
        this.handler = StripeCheckout.configure({
            key: window.Laravel.stripeKey,
            locale: 'auto',
            token: (token) => {
                console.log(token); // works as expected
                this.stripeToken = token.id; // Works in Vue Dev Tools
                this.stripeEmail = token.email; // Works in Vue Dev Tools
                // If we stop here, the hidden form elements are populated
                // with the correct values.
                this.$el.submit(); // Submits the form
                // When the form hits the server, the hidden fields have null values???
            }
        });
    }
})

And if I dd() the POST request:

array:4 [
  "_token" => "fOz6VEqW2tNVMhAlB5s7XG6g9kfaBTqcnNIGkZYk"
  "selected_plan" => "3"
  "stripe_email" => null
  "stripe_token" => null
]

It has to be something stupid I'm doing, but I'm not seeing it!

TIA

0 likes
1 reply
mikefolsom's avatar
mikefolsom
OP
Best Answer
Level 21

UPDATE: It works if I wrap the form submission in a setTimeout(). Apparently it needs a little time for the updated values to "take". Now moving on to creating a standalone component instead.

Please or to participate in this conversation.