简体   繁体   中英

Laravel stripe payment form isn't handled just posted

I'm using the default card element from Stripe which can be found here . The form renders and the validation stripe includes works & renders. However, I never get a stripeToken generated so the subscription fails due to;

This customer has no attached payment source

When I die dump my requests the stripeToken is NULL . I think this is because the stripe form handler doesn't work at all for me, the event listener they include doesn't ever fire.

Looks like the form is just posting like a normal form instead of the prevent default JS listener added by stripe.

<form action="{{ route('subscriptionCreate') }}" method="post" id="payment-form">
  @csrf
  <input type="hidden" name="plan" value="{{ $plan->id }}">
  <div class="form-row">
    <label for="card-element">
      Credit or debit card
    </label>
    <div id="card-element">
      <!-- A Stripe Element will be inserted here. -->
    </div>

    <div id="card-errors" role="alert"></div>
  </div>

  <button>Submit Payment</button>
</form>

The Javascript included from the elements example;

<script>

// Create a Stripe client.
var stripe = Stripe('###Removed###');

// Create an instance of Elements.
var elements = stripe.elements();

// Custom styling can be passed to options when creating an Element.
// (Note that this demo uses a wider set of styles than the guide below.)
var style = {
  base: {
    color: '#32325d',
    fontFamily: '"Helvetica Neue", Helvetica, sans-serif',
    fontSmoothing: 'antialiased',
    fontSize: '16px',
    '::placeholder': {
      color: '#aab7c4'
    }
  },
  invalid: {
    color: '#fa755a',
    iconColor: '#fa755a'
  }
};

// Create an instance of the card Element.
var card = elements.create('card', {style: style});

// Add an instance of the card Element into the `card-element` <div>.
card.mount('#card-element');

// Handle real-time validation errors from the card Element.
card.addEventListener('change', function(event) {
  var displayError = document.getElementById('card-errors');
  if (event.error) {
    displayError.textContent = event.error.message;
  } else {
    displayError.textContent = '';
  }
});

// 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);
    }
  });
});


// // Submit the form with the token ID.
function stripeTokenHandler(token) {
  // Insert the token ID into the form so it gets submitted to the server
  var form = document.getElementById('payment-form');
  var hiddenInput = document.createElement('input');
  hiddenInput.setAttribute('type', 'hidden');
  hiddenInput.setAttribute('name', 'stripeToken');
  hiddenInput.setAttribute('value', token.id);
  form.appendChild(hiddenInput);

  // Submit the form
  form.submit();
}
</script>

This is my controller, as I mentioned the $request->stripeToken doesn't exist I don't think it ever gets added to the form.

public function create(Request $request)
{

    $plan = Plan::findOrFail($request->get('plan'));

    $user = $request->user();

    $request->user()
        ->newSubscription($plan->name, $plan->stripe_plan)
        ->create($request->stripeToken,[
            'email' => $user->email
        ]);

    return redirect()->route('home')->with('success', 'Your plan subscribed successfully');
}

I've created https://jsfiddle.net/s8foxw9r/2/ that confirms the stripeTokenHandler is working (and the token is being generated correctly).

If you open the jsfiddle URL, then open the developer tools in your browser and look at the console output you will see a dump of the token object that is passed to stripeTokenHandler() .

When you hit Submit Payment the page POST s to https://postman-echo.com/post which will dump out your request. You'll notice the request looks something like this:

{
  "args": {},
  "data": "",
  "files": {},
  "form": {
    "stripeToken": "tok_1F07d72eZvKYlo2CqBprboVK"
  },
  "headers": {
    "x-forwarded-proto": "https",
    "host": "postman-echo.com",
    "content-length": "40",
    "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3",
    "accept-encoding": "gzip, deflate, br",
    "accept-language": "en-US,en;q=0.9",
    "cache-control": "no-cache",
    "content-type": "application/x-www-form-urlencoded",
    "cookie": "sails.sid=s%3AHyTHsNyIhRvFINR3EGiXw1Kf12oufx84.jd6rEiCaqHsrM8eOGN1x%2ByzU%2BMatjM4l5S1Ekxhxdyo",
    "origin": "https://fiddle.jshell.net",
    "pragma": "no-cache",
    "referer": "https://fiddle.jshell.net/",
    "upgrade-insecure-requests": "1",
    "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36",
    "x-forwarded-port": "443"
  },
  "json": {
    "stripeToken": "tok_1F07d72eZvKYlo2CqBprboVK"
  },
  "url": "https://postman-echo.com/post"
}

The important parts are headers.content-type and json attributes. This means the stripe.js lib POST ed the form via the application/x-www-form-urlencoded MIME type, and that the stripeToken was included.

This means your issue is one/both of the following:

  1. If you are not seeing a token generated in the browser, your stripe private key is probably incorrect. I'd start out with using your stripe test private key, it looks like pk_test_xxx .
  2. Your Laravel server is not setup correctly. Lots of potential issues, would need more info, and unfortunately I've never used Laravel.

Lastly, I found a Stripe integration package for Laravel, might be worth trying: https://github.com/cartalyst/stripe-laravel

I had the same issue before this is how it worked for me:

composer require stripe/stripe-php

then update the env file with your keys

STRIPE_KEY=xxxxxxxxxxxxxxxx
STRIPE_SECRET=xxxxxxxxxxxxxxx

then with your controller

use Stripe;
    public function create(Request $request)
    {
        $plan = Plan::findOrFail($request->get('plan'));
        $user = $request->user();
        $request->user()
        ->newSubscription($plan->name, $plan->stripe_plan);
       // getting the plan cost
        $amount = $plan->plan_amount;

        Stripe\Stripe::setApiKey(env('STRIPE_SECRET'));
        Stripe\Charge::create ([
                "amount" => ($amount * 100),
                "currency" => "usd",
                "source" => $request->stripeToken,
                "description" => "Test payment" 
        ]);
    return redirect()->route('home')->with('success', 'Your plan subscribed successfully');
     }

A default Laravel installation will include an app.js file. By simply removing or commenting out this file from being included in your header allows Stripe to generate the token using Stripe Elements.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM