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

sun_down's avatar

Stripe elements + custom form fields

So I have been trying to get my Vue component to output a custom payment form based on the example the docs give. I want to be able to have a top row that allows you to pick either "5, 10, 20 or other___" with another row that has options on a dropdown for subscriptions. Then the last one would be the stripe card element where you would put the card info. I cannot get it to send me the information over to my method that handles the server logic. This is what I have so far.

<template>
<div class="wrapper">
<form action="/charge" method="post" id="payment-form">
<slot></slot>
  <div class="form-row">
        <input type="radio" id="one" value="5" v-model="picked">
        <label for="one">One</label>
        <br>
        <input type="radio" id="two" value="10" v-model="picked">
        <label for="two">Two</label>
        <input type="radio" id="three" value="20" v-model="picked">
        <label for="three">Three</label>
        <input type="radio" id="four" value="50" v-model="picked">
        <label for="four">Four</label>

        <select v-model="selected">
        <option disabled value="">Please select a Subscription</option>
        <option>5</option>
        <option>10</option>
        <option>20</option>
        </select>
    <label for="card-element">
      Credit or debit card
    </label>
    <div id="card-element">
      <!-- A Stripe Element will be inserted here. -->
    </div>

    <!-- Used to display form errors. -->
    <div id="card-errors" role="alert"></div>
  </div>

  <button v-on:click="purchase" class="btn btn-Stripe">Submit Payment</button>
</form>
</div>

</template>

<script>
let stripe = Stripe(`config('services.stripe.key')`),
    elements = stripe.elements(),
    card = undefined;

let style = {
  base: {
    color: '#32325d',
    fontFamily: '"Helvetica Neue", Helvetica, sans-serif',
    fontSmoothing: 'antialiased',
    fontSize: '16px',
    '::placeholder': {
      color: '#aab7c4'
    }
  },
  invalid: {
    color: '#fa755a',
    iconColor: '#fa755a'
  }
};
export default {
    data () {
        return {
            picked: false,
            selected: '',
            purchase: ''
        }
    },
    mounted () {
    card = elements.create('card', {style: style});
    card.mount("#card-element");

    card.addEventListener('change', function(event) {
    var displayError = document.getElementById('card-errors');
    if (event.error) {
        displayError.textContent = event.error.message;
    } else {
        displayError.textContent = '';
    }
    });
  },
    purchase () {
        // Handle form submission
    var form = document.getElementById('payment-form');
    form.addEventListener('submit', function(event) {
    event.preventDefault();

    stripe.createToken(card).then(function(result) {
        if (result.error) {
        // Inform the user if there was an error
        var errorElement = document.getElementById('card-errors');
        errorElement.textContent = result.error.message;
        } else {
        // Send the token to your server
        stripeTokenHandler(result.token);
        }
    });
});
  }
}
</script>

The view looks like this.

      <div class="row wc">
      <div id="app">
          <example-component>
            {{ csrf_field() }}
          </example-component>
      </div>
    </div>

My methods are.

    public function charge(Request $request)
    {
        try {
            Stripe::setApiKey(config('services.stripe.secret'));

            $customer = Customer::create([
                'email' => request('stripeEmail'),
                'source' => request('stripeToken')
            ]);

            Charge::create([
                'customer' => $customer->id,
                'amount' => 999,
                'currency' => 'usd'
            ]);

            return redirect('membership');
        } catch (\Exception $ex) {
            return $ex->getMessage();
        }
    }

    public function subscribe(Request $request)
    {
        try {
            Stripe::setApiKey(config('services.stripe.secret'));

            $user = User::find(1);
            $user->newSubscription('Site Membership', 'Bronze')->create($request->stripeToken);

            return redirect('membership');
        } catch (\Exception $ex) {
            return $ex->getMessage();
        }
    }

Now I have a working version where I used stripe checkout to do either one time charges or subscriptions, but I have to have two checkout forms that take you to two methods as you can see above. Works fine on all accounts but now I wanna migrate to only one form and possibly one method to handle all the logic with Vue. My question is, will that be possible? or will I need a way to distinguish if the user clicked on a one time payment option or the dropdown for subscription and pass it over to either method? I have not done this before and the way to handle this sort of logic is beyond me lol. Any help would be appreciated as The videos I have seen do not cover what I am looking for and I have tried to piece my own solution from different sources.

0 likes
0 replies

Please or to participate in this conversation.