Buy

The old, embedded form had a couple of nice formatting behaviors - like automatically adding a space between every 4 card numbers. Fortunately, Stripe has us covered once again here. Go back to the documentation and scroll down - they eventually reference something called jQuery.payment: a neat little JavaScript library for formatting checkout fields nicely.

It even provides validation, in case you want to make sure the numbers are sane before sending them off to Stripe.

I've already downloaded this library into the web/js directory, so all we need to do is include it on the page and point it at our form.

At the top, add a new script tag and set its src="js/jQuery.payment.min.js":

98 lines app/Resources/views/order/checkout.html.twig
... lines 1 - 3
{% block javascripts %}
... lines 5 - 6
<script type="text/javascript" src="https://js.stripe.com/v2/"></script>
<script src="{{ asset('js/jquery.payment.min.js') }}"></script>
... lines 9 - 57
{% endblock %}
... lines 59 - 98

The asset function is an optional helper function from Symfony - nothing magic going on there.

Then, down below... try to ignore the ugly indentation that I should have fixed earlier, and say $form.find(). We need to find the credit card number input. But don't worry! I planned ahead and gave it a special js-cc-number class. I also added js-cc-exp and js-cc-cvc:

68 lines app/Resources/views/order/_cardForm.html.twig
<form action="" method="POST" class="js-checkout-form checkout-form">
... lines 2 - 12
<div class="row">
<div class="col-xs-8 col-sm-6 col-sm-offset-2 form-group">
<div class="input-group">
... lines 16 - 18
<input data-stripe="number" type="text" autocomplete="off" class="form-control js-cc-number" id="card-number" required placeholder="Card Number"/>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-4 col-sm-3 col-sm-offset-2 form-group">
<div class="input-group">
... lines 27 - 29
<input data-stripe="exp" type="text" size="4" autocomplete="off" class="form-control js-cc-exp" id="card-expiration" required="required" placeholder="mm/yy"/>
</div>
</div>
<div class="col-xs-4 col-sm-3 form-group">
<div class="input-group">
... lines 35 - 37
<input data-stripe="cvc" type="text" size="4" autocomplete="off" class="form-control js-cc-cvc" id="card-cvc" required="required" placeholder="CVC"/>
</div>
</div>
</div>
... lines 42 - 66
</form>

Fill in .js-cc-number and then call .payment('formatCardNumber'):

98 lines app/Resources/views/order/checkout.html.twig
... lines 1 - 3
{% block javascripts %}
... lines 5 - 9
<script type="text/javascript">
... lines 11 - 12
$(function () {
var $form = $('.js-checkout-form');
$form.find('.js-cc-number').payment('formatCardNumber');
... lines 17 - 28
});
... lines 30 - 56
</script>
{% endblock %}
... lines 59 - 98

Repeat this two more times for js-cc-exp with formatCardExpiry and formatCardCVC. Don't forget to update that class name too:

98 lines app/Resources/views/order/checkout.html.twig
... lines 1 - 15
$form.find('.js-cc-number').payment('formatCardNumber');
$form.find('.js-cc-exp').payment('formatCardExpiry');
$form.find('.js-cc-cvc').payment('formatCardCVC');
... lines 19 - 98

Try it out! So sweet! The card field gets pretty auto-spacing and even more importantly, the library adds the slash automatically for the expiration field. It also limits the CVC field to a maximum of 4 numbers.

So custom forms are a little bit more work. But they fundamentally work the same.

Before we finish, there's one big hole left in our setup: failing gracefully when someone's card is declined.

Leave a comment!