Using Stripe.js

Now that you've got your API keys setup in Charge, and we can talk to Stripe, the next step is to setup a basic payment form.

To start with we're just going to start with the most basic payment form.

TLDR; The full basic payment form is included in the demo templates as part of the download. See it in action here

Just to make sure it's all working, we'll setup a form with a fixed, one-time payment amount, with the minimum of card inputs. Once you've got that working you can extend and expand.

The Payment Page Overview #

Our payment page needs to have a few things. Specifically :

  1. The payment form must have an id of charge-form, a method of post, and an action input with a value of charge/charge. Optionally you can include the stripe public key on the form via a data-stripeKey attribute.
  2. A hidden input containing the encoded information about the payment request we're making
  3. A customerEmail input for the customer's receipt email
  4. The card inputs with appropriate data-stripe attributes
  5. The payment form javascript
  6. Error state handling

1. The Payment Form #

Our form needs to have a few parts. It should look something like this:

<form id="charge-form" method="post" data-publicKey="{{ craft.charge.publicKey }}">
  <input type="hidden" name="action" value="charge/charge"/>
  ..
</form>

2. The Payment Options input #

Charge uses an encrypted hidden input that's included as part of the form post to pass all the details about the payment request to our controller. It's very simple to setup in our form:

<form id="charge-form" method="post" data-publicKey="{{ craft.charge.publicKey }}">
  <input type="hidden" name="action" value="charge/charge"/>
  {% set options = {
      'planAmount' : 99.99
   } %}
  {{ craft.charge.setPaymentOptions(options) }}

  ..
</form> 

Here we're just setting the planAmount to be 99.99 (in our default currency). This will mean Charge will create a one-time payment of $99.99 when the payment request is submitted.

The {{ craft.charge.setPaymentOptions(options) }} simply encrypts this simple bit of information, and includes it as a hidden input on the form. Regardless of how complicated your payment setups might get later, this basic concept stays the same - set the payment details in an array, and use setPaymentOptions to encrypt and include the details on the payment form.


3. Add the Customer's input fields to the form #

For our most basic of payment forms, we just want to include a customerEmail input. This is key, and is the minimum Stripe requires.

If your user is logged in, you might want to pre-populate this for them too. We'll add this to the form like this:

<form id="charge-form" method="post" data-publicKey="{{ craft.charge.publicKey }}">
  <input type="hidden" name="action" value="charge/charge"/>
  {% set options = {
      'planAmount' : 99.99
   } %}
  {{ craft.charge.setPaymentOptions(options) }}

  <label for="customerEmail">Receipt Email</label>
  <input type="text" name="customerEmail" value="{{ currentUser ? currentUser.email }}" id="customerEmail"/>

  ..
</form> 

What about returning customers? Under the hood Charge will create and manage customer accounts for all your payment requests. If the user is logged in while a payment attempt is made, we'll try to reuse any existing customer accounts for them. For guests we'll always use a new customer account on Stripe.


4. The Card inputs, with data-stripe attributes #

Stripe uses javascript tokenisation of payment cards to keep things secure. Your server never sees the submitted card details, and the raw card details never leave the payment page, they're tokenised using your public key right on the page before any processing takes place.

To make this happen, we need to add a few extra attributes to the card inputs, and exclude the name attributes on the card inputs.

At minimum, we just need a card number, expiry month and year, and cvc.

Charge includes some additional helpers to enable neat card input formatting, automatically handle the expiry inputs as a single input, validate the card format and more, all without you doing any extra work. Our card inputs just need to look like this:

<form id="charge-form" method="post" data-publicKey="{{ craft.charge.publicKey }}">
  <input type="hidden" name="action" value="charge/charge"/>
  {% set options = {
      'planAmount' : 99.99
   } %}
  {{ craft.charge.setPaymentOptions(options) }}

  <label for="customerEmail">Receipt Email</label>
  <input type="text" name="customerEmail" value="{{ currentUser ? currentUser.email }}" id="customerEmail"/>

  <label for="cardNumber">Card Number</label>
  <input type="text" data-stripe="number" placeholder="•••• •••• •••• ••••" id="cardNumber"/>

  <label for="cardExpiry">Card Expiry</label>
  <input type="text" data-stripe="exp" placeholder="mm / yy" id="cardExpiry"/>

  <label for="cardCvc">Card CVC</label>
  <input type="text" data-stripe="cvc" placeholder="123" id="cardCvc"/>

  ..
</form> 

Exclude name, include data-stripe It's critical that you include the data-stripe attributes on the card inputs, and exclude the name attribute. The tokenisation js keys against the data attributes, and by excluding the name inputs, we can be sure the actual values will never submit to the server, regardless of any browser issues.


5. The payment form javascript #

The whole form is reliant on the payment javascript. We bundle a ready to go helper javascript library as part of Charge. Included in that library is a full set of input helpers, along with our specific form handling.

The payment form needs 3 javascript libraries, and one init block:

  • latest jquery (from local or your favourite cdn)
  • stripe_v2.js
  • jquery.charge.js
  • the on-page init

Including everything together will look something like this:

You've got two options to define your Stripe public key We recommend adding it as a data attribute on the charge form, so your js can be kept really clean. Alternatively you can define it explicitly via Stripe.setPublishableKey('{{ craft.charge.getPublicKey() }}'); on your page's js.

<script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
<script src="{{ resourceUrl('charge/js/stripe_v2.min.js') }}"></script>
<script src="{{ resourceUrl('charge/js/jquery.charge.js') }}"></script>
<script>
    (function () {
        $(this).charge();
    })();
</script>

Need to customise the javascript behaviour? The included jquery.charge.js covers most of the basic requirements, but it can't handle every custom situation. You're free to use your own javascript, or customise jquery.charge.js by creating your own copy. Just be careful not to overwrite the version bundled with charge to make sure you don't loose your changes in a later update.


6. Error Handling and form repopulation #

Finally we have to anticipate the cases where a payment might fail, or the customer enters invalid data on the form.

There's actually 2 separate classes of error that might occur, and we need to expose the information about both. First we have errors that occur during the stripe tokenisation of the payment method. These are added by the javascript before the page submits. For that, we just need to add an empty container with an id of payment-errors. Like so:

<div id="payment-errors"></div>

In addition, we can have errors relating to the actual payment handling. Customer data errors, validation failures and the like. For that, we need to use twig to show the errors. If the page does return with errors, we'll also have a charge object we can use to repopulate the form values with data.

What about repopulating card data? We don't get the full card data back because, by design, we never received it on the server. Instead we can use the card_token in it's place. In addition to the card token, we have information about the card we can use to help the customer

When we've added all the error handling, repopulation and card token data, our form will look something like this:

<form id="charge-form" method="post" data-stripeKey="{{ craft.charge.publicKey }}">
  <input type="hidden" name="action" value="charge/charge"/>
  {% set options = {
      'planAmount' : 99.99
   } %}
  {{ craft.charge.setPaymentOptions(options) }}

  <div id="payment-errors"></div>

  <label for="customerEmail">Receipt Email</label>
  <input type="text" name="customerEmail" value="{{ charge is defined ? charge.customerEmail : currentUser ? currentUser.email }}" id="customerEmail"/>
  {% if charge is defined %}{% for error in charge.getErrors('customerEmail') %}{{ error }}{% endfor %}{% endif %}

  {% if charge is defined and charge.cardToken %}
    {# We already have the card details #}
    Pay with : {{ charge.cardType }} - {{ charge.cardLast4 }}

    <input type="hidden" name="cardToken" value="{{ charge.cardToken }}" data-stripe="token"/>
    <input type="hidden" name="cardLast4" value="{{ charge.cardLast4 }}"/>
    <input type="hidden" name="cardType" value="{{ charge.cardType }}"/>
    <input type="hidden" name="cardName" value="{{ charge.cardName }}"/>
    <input type="hidden" name="cardExpMonth" value="{{ charge.cardExpMonth }}"/>
    <input type="hidden" name="cardExpMonth" value="{{ charge.cardExpMonth }}"/>
    <input type="hidden" name="cardExpYear" value="{{ charge.cardExpYear }}"/>

  {% else %}
    <label for="cardNumber">Card Number</label>
    <input type="text" data-stripe="number" placeholder="•••• •••• •••• ••••" id="cardNumber"/>

    <label for="cardExpiry">Card Expiry</label>
    <input type="text" data-stripe="exp" placeholder="mm / yy" id="cardExpiry"/>

    <label for="cardCvc">Card CVC</label>
    <input type="text" data-stripe="cvc" placeholder="123" id="cardCvc"/>
  {% endif %}

   <input type="submit" value="Pay with Stripe"/>
</form> 

<script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
<script src="{{ resourceUrl('charge/js/stripe_v2.min.js') }}"></script>
<script src="{{ resourceUrl('charge/js/jquery.charge.js') }}"></script>
<script>
    (function () {
        Stripe.setPublishableKey('{{ craft.charge.getPublicKey() }}');
        $(this).charge();
    })();
</script>

That's all there is to it.

To sum up what that covers -

  1. We have a payment form, with an id of charge-form, that performs a post request, with a hidden action input to charge/charge.
  2. We have the stripe public key attached to the charge form in a data attribute called data-stripeKey.
  3. We have our encoded payment information included on the form, in this case a set planAmount of $99.99
  4. We have a customerEmail input, along with error output and repopulation
  5. We have card inputs, all with data-stripe attributes, along with handling for returning forms
  6. We have our payment javascript all setup, and including the needed js libraries.

You can see a fully working version of exactly this at demos.squarebit.co.uk/charge/basic, and the fully working template is included in the plugin download, in the examples folder.

What's Next? #

This only covers the most basic setup, but is a good introduction to the core concepts. Once you have the payment form setup and working, most of the behavioural changes are all controlled by changing the paymentOptions array.

Dive deeper into the options for all that, along with triggering additional success, recurring and failure actions, how to setup recurring payments, multiple plan options that the user can change, extra options for payments including saved cards and more. All the information is detailed in these docs.