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');
}
Stripe sends the payment_intent. succeeded event when payment is successful and the payment_intent. payment_failed event when payment isn't successful. When payment is unsuccessful, you can find more details by inspecting the PaymentIntent's last_payment_error property.
Incomplete paymentsIt's just people that didn't finish the checkout. Some merchants have been worried by errors that appear in the logs when we're communicating with Stripe. These errors are normal and are Stripe's way of responding to bad data like misspelled emails or when the card couldn't be charged.
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:
pk_test_xxx
.Lastly, I found a Stripe integration package for Laravel, might be worth trying: https://github.com/cartalyst/stripe-laravel
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With