Do you like this bundle? Leave a ★ or run composer global require symfony/thanks && composer thanks
to say thank you to all libraries you use in your current project, this included!
To integrate Stripe in the frontend of your application you have to basically follow the flow described by Stripe itself.
This bundle doesn't provide much front-end integration tools as the ones provided by Stripe are really strong and as the the front-end integration is really tied to your concrete implementation.
Are you using React? Or Bootstrap? Or are you using other technologies?
Here we report just a recap of the fundamental steps required to integrate Stripe in your frontend. For all other information, refer to the Stripe JS's documentation.
This is what we have to do:
- Import
Stripe.js
in ALL your pages; - Create the form to be included;
Stripe.js
permits your application to collect sensitive data from your customers in a secure way and send them to
Stripe's servers to tokenize and then use them later.
Passing n Internet sensitive data such as the credit card information is really dangerous. For this reason Stripe provides this script that takes care of all the security aspects of handling customers' information and payment credentials.
The only thing required by us, is using their strong tools to communicate with the Stripe's servers.
Stripe.js
does exactly this: opens a communication channel between our app and Stripe's servers, handling all the
informaiton flow in a secure way.
The Stripe.js
script has to be included in all pages of the app as it is able to understand the users' behavior and
intercept fraudolent behaviors by analyzing each action done by the user on our pages.
Including it in all your pages makes possible to spread the full potential of Stripe's Radar, the anti-fraud system powered by the machine learning that analyzes and processes hundreds of signals about thousand of thousands credit card processings.
So, the first thing we have to do is include this script in all pages of our app (without minifying nor combining it with other javascripts!):
{# app/base.html.twig #}
<!DOCTYPE html>
<html lang="{{ locale }}">
<head>
<title>{% block title %}{% endblock %}</title>
<meta name="description" content="{% block metaDescription %}{% endblock %}" />
{# This must be in the header as it is used by the credit card form that is loaded before all other Javascripts #}
<script type="text/javascript" src="https://js.stripe.com/v3/"></script>
...
</head>
<body>
...
</body>
</html>
To make your customer able to send you his credit card information, you need to provide him with a credit card form.
In the Stripe's documentation you can find some basic information about how to do this.
Stripe Bundle ships a form type for this and creates some Twig global variables useful to initialize Stripe.js.
The form type is really simple: it is composed of only one field: card_token
.
Before you ask why, let us explain a bit about how Stripe's credit cards processing works (you can see it in action in the link above).
In abstract, the flow is this:
- Your customer provides his credit card details on a form generated using the
Stripe.js
(v3) script; - The customer enters his credit card details in the form and submit it (more about submissionvery soon);
- Stripe processes these data and returns you a card token that is a unique identifier of the card on the Stripe's systems;
- You use the token to charge the customer.
This is the very abstract process. In each step there is a lot to know. And we are going to know it!
To make it a bit more concrete, lets see how StripeBundle implements it.
THIS IS THE STRIPE FLOW FOLLOWED BY THE SERENDIPITY HQ STRIPE BUNDLE:
- We create a form type, let's call it
PremiumType
, with the fields we like (they can be what we like: a set of features to select or what you like); - To this form type we add the Stripe Bundle
CreditCardStripeTokenType
that will contain the token returned by Stripe (yes, Stripe will return your app a token - be patient :) ); - On our page, we will render our form
PremiumType
; - We'll add to the form a button to submit the entire form;
A bit theoretical, isn't it? Let's make it practical! :)
Following is a piece of code from the form type we use on TrustBack.Me to make the merchant able to choose which features he likes to add to his store profile:
class PremiumType extends AbstractType
{
/**
* {@inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('ads', CheckboxType::class, ['required' => false])
->add('seo', CheckboxType::class, ['required' => false])
->add('social', CheckboxType::class, ['required' => false]);
}
}
We have created a method that serves the form:
public function getPlansForm(Store $store)
{
// First: Create your form as you like
$form = $this->formFactory->createBuilder(FormType::class, [
'action' => $this->router->generate('store_subscription', ['id' => $store->getId()]),
'method' => 'POST',
])
->add('plan', PremiumType::class, [
'data_class' => PremiumFeaturesEmbeddable::class,
'data' => $store->getPremium()
]);
if (null === $store->getBranchOf()->getStripeCustomer() || 0 === $store->getBranchOf()->getStripeCustomer()->getCards()->count())
// Then add the CreditCardStripeTokenType that will save the token returned by the Stripe API
$form->add('credit_card', CreditCardStripeTokenType::class);
return $form->getForm();
}
Without taking care, for the moment, of the if (null === $store->...
, note how we added to the form the field credit_card
setting it as a CreditCardStripeTokenType
type.
This will make we able to render the hidden field that will contain our card token returned by Stripe (be patient, we will speak about this in a moment! :) ).
And now it's time to render our form on the frontend:
{# ATTENTION 1: NOTE WE GIVE THE FORM AN ID #}
{{ form_start(form, {'attr': {'id': subscription_form_id, 'autocomplete': 'on'}}) }}
{{ form_widget(form.plan.ads) }}
{{ form_widget(form.plan.seo) }}
{{ form_widget(form.plan.social) }}
{# ATTENTION 2: NOTE WE RENDER THE HIDDEN FIELD WE ADDED AT STEP 2.3 #}
{{ form_widget(form.credit_card.card_token) }}
{# ATTENTION 3: WE ADD THE CLASS `charge` to disable the button once clicked #}
<div class="form-group">
<input type="submit" value="{% trans %}subscription.update{% endtrans %}" class="btn btn-success pull-right charge" />
</div>
{{ form_end(form) }}
{# ATTENTION 4: We initialize Stripe.js #}
<script type="text/javascript">
// Specifying the publishable key and the api version we want to use
const stripe = Stripe('{{ stripe_publishable_key }}', {'apiVersion': '{{ stripe_api_version }}' });
</script>
KEEP ATTENTION NOW: note these things:
- We gave the form an
id
; - We render the HIDDEN field
card_token
we added at step 2.3: it is only a container and is not meant to be filled by the User (more on this later), so we don't show it on the page; - We add a class
charge
to the button to submit the form so we will be able to disable it once the form is submitted; - We initialize
Stripe.js
passingstripe_publishable_key
andstripe_api_version
. Both are made available in Twig bySHQStripeBundle
that automatically configures them as global Twig variables.stripe_publishable_key
has the value you configured in the env variableSTRIPE_PUB_KEY
;stripe_api_version
has the value ofSerendipityHQ\Bundle\StripeBundle\SHQStripeBundle::SUPPORTED_STRIPE_API
: this is the version of the Stripe API that this bundle currently supports. This way you can upgrade the API used by your account without taking care of this bundle being broke or become incompatible.
In this section the real "magic" happens.
To get the credit card information, you have to first create the fields using the Stripe.js
(v3) library and then submit them to the Stripe's servers.
So we don't use the Symfony Form Component, but the Stripe.js script itself: this is because the script is always loaded from a secure server and the data transmitted are always tunneled through an HTTPS connection.
Once sent to the Stripe's servers, Stripe processes the information and returns to your page a token that represents the data you provided.
The token is received via Javascript and stored in your form via Javascript, then the form is submitted to your server where you handle it using PHP (and StripeBundle).
The field that will store the token is the one we added at step 2.3: the one created adding CreditCardStripeTokenType
.
Then you use this token to create the user on the Stripe's servers and associate the payment information to the just created user.
So we need the Javascript code to use to submit the credit card info and get back the generated token.
The Javascript code we have to use is exactly the the same described in the Stripe's documentation: you have to only take care of the binding between the Stripe's form and the Symfony's form.
To do this you have to modify the code just a little bit.
This is the code in the Submit the token and the rest of your form to your server
section of the Quick Start Stripe's documentation there is this code:
// Orignal code provided by Stripe (MUST BE ADAPTED)
function stripeTokenHandler(token) {
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();
}
Your code MUSt be a bit different:
- You have to not create the hidden input field as your form already has it (
CreditCardStripeTokenType
) - You have to select the already existent credit card field
- You have to update it with the token returned by Stripe
So, the javascript code will become this:
// An example of code you SHOULD use in your app
function stripeTokenHandler(token) {
// Select the form: the id is equal to the value you provided with the twig variable `{'attr': {'id': subscription_form_id ...`
var form = document.getElementById('payment-form');
// Now select the already rendered (as an hidden field!) credit card token form field
var hiddenInput = document.getElementById('form_credit_card_card_token');
// Here we update the hidden token field with the token returned by Stripe
hiddenInput.setAttribute('value', token.id);
// Submit the form
form.submit();
}
Now load your page and all should work well: the credit card form should be created and once submitted it should return back the token.
Try to fill the form with some test cards and submit it: what does it happen?
When you hit the the submit button, you should expect a really short delay before the form is submitted.
This delay is caused by the communication between you app and the Stripe's server.
This communication happens via the Stripe.js
library that we included in step 1.
When you hit the submit button, the submit event is intercepted by JavaScript.
Then the data of the form are sent to the Stripe's servers (only data about credit cards are sent to Stripe!) on a secure connection (SSL/TLS encrypted, also if your app hasn't SSL enabled).
The Stripe's servers save the card, tokenize it and return your app a token representation of the card.
The javascript on your page set this token as value of the card_token
field (remember? We added this field in our form in step 2.3) and then sends the entire form to your server.
As the form that collects the credit cards data doesn't have names, these data will never be submitted to your server, so you don't have to take care of their security: all the dirty stuff is done by the Stripe's servers.
The entire procedure is described in full details on the Stripe's documentation.
Now that we have a form ready to be used, and that we have a tokenized representation of the credit card, we only remains to write the code for the backend.
{% if company.stripeCustomer.cards.count > 1 %}
<div style="display: block;" role="button" data-toggle="collapse" href="#past-cards" aria-expanded="false">
<span class="glyphicon glyphicon-chevron-down"></span>{% trans %}company.account.billing.past_cards_are{% endtrans %}
<div class="collapse out" id="past-cards">
<p><small>{% trans %}company.account.billing.past_cards_are.disclaimer{% endtrans %}</small></p>
<ul>
{% for card in company.stripeCustomer.cards %}
{% if card.id != company.stripeCustomer.defaultSource %}
<li>{{ card.brand }}: xxxx-xxxx-xxxx-{{ card.last4 }} ({{ card.expMonth }}/{{ card.expYear }})</li>
{% endif %}
{% endfor %}
</ul>
</div>
</div>
{% endif %}
In the disclaimer something like:
Di queste carte non abbiamo più nessun dato utile per addebitarle: conserviamo per referenza solo le ultime 4 cifre e la data di scadenza.
Do you like this bundle?
LEAVE A ★
or run
composer global require symfony/thanks && composer thanks
to say thank you to all libraries you use in your current project, this included!
(Go back to index) | Next step: Integrate the back-end