Charge It (The Stripe PHP SDK)

Video not working?

It looks like your browser may not support the H264 codec. If you're using Linux, try a different browser or try installing the gstreamer0.10-ffmpeg gstreamer0.10-plugins-good packages.

Thanks! This saves us from needing to use Flash or encode videos in multiple formats. And that let's us get back to making more videos :). But as always, please feel free to message us.

Stripe has a nice, RESTful API, and you're going to spend a lot of time talking with it. Google for "Stripe API docs" to find this amazing page. You can set this as your new homepage: it describes every endpoint: how to create charges, customers and a lot of other things we're going to talk about.

But first, make sure that you select PHP on the top right. Thanks to this, the docs will show you code snippets in PHP. And those code snippets will use Stripe's PHP SDK library. Google for that and open its Github Page.

First, let's get this guy installed. Copy the composer require line, move over to your terminal, open a new tab and paste that:

composer require stripe/stripe-php

While we're waiting for Jordi to finish, let's keep going.

Using the Token to Create a Charge

To actually charge a user, we need to... well, create a Stripe charge. In the Stripe API, click "Charges" on the left and find Create a Charge.

Hey! It wrote the code for us. Copy the code block on the right. Now, go back to OrderController and first, create a new $token variable and set it to the stripeToken POST parameter. Now, paste that code:

... lines 1 - 30
public function checkoutAction(Request $request)
{
... lines 33 - 34
if ($request->isMethod('POST')) {
$token = $request->request->get('stripeToken');
\Stripe\Stripe::setApiKey("XXX_PRIVATEKEY_XXX");
\Stripe\Charge::create(array(
"amount" => $this->get('shopping_cart')->getTotal() * 100,
"currency" => "usd",
"source" => $token,
"description" => "First test charge!"
));
... lines 45 - 49
}
... lines 51 - 56
}
}

Let's go check on Composer. It's just finishing - perfect! My editor now sees all these new Stripe classes.

See that API key?

... lines 1 - 10
class OrderController extends BaseController
{
... lines 13 - 30
public function checkoutAction(Request $request)
{
... lines 33 - 34
if ($request->isMethod('POST')) {
... lines 36 - 37
\Stripe\Stripe::setApiKey("XXX_PRIVATEKEY_XXX");
... lines 39 - 49
}
... lines 51 - 56
}
}

Once again, this is a real key from our account in the test environment. This time, it's the secret key. The public key is the one in our template:

... lines 1 - 3
{% block body %}
<div class="nav-space-checkout">
<div class="container">
<div class="row">
... lines 8 - 34
<div class="col-xs-12 col-sm-6">
<form action="" method="POST">
<script
... line 38
data-key="pk_test_HxZzNHy8LImKK9LDtgMDRBwd"
... lines 40 - 45
</script>
</form>
</div>
</div>
</div>
</div>
{% endblock %}

Update charge details with our real information. To get the total, I'll fetch a service I created for the project called shopping_cart, call getTotal() on this, and then multiply it by 100:

... lines 1 - 10
class OrderController extends BaseController
{
... lines 13 - 30
public function checkoutAction(Request $request)
{
... lines 33 - 34
if ($request->isMethod('POST')) {
... lines 36 - 38
\Stripe\Charge::create(array(
"amount" => $this->get('shopping_cart')->getTotal() * 100,
... lines 41 - 43
));
... lines 45 - 49
}
... lines 51 - 56
}
}

For source, replace this fake token with the submitted $token variable:

... lines 1 - 10
class OrderController extends BaseController
{
... lines 13 - 30
public function checkoutAction(Request $request)
{
... lines 33 - 34
if ($request->isMethod('POST')) {
$token = $request->request->get('stripeToken');
... lines 37 - 38
\Stripe\Charge::create(array(
... lines 40 - 41
"source" => $token,
... line 43
));
... lines 45 - 49
}
... lines 51 - 56
}
}

The token basically represents the credit card that was just sent. We're saying: Use this card as the source for this charge. And then, put whatever you want for description, like "First test charge":

... lines 1 - 10
class OrderController extends BaseController
{
... lines 13 - 30
public function checkoutAction(Request $request)
{
... lines 33 - 34
if ($request->isMethod('POST')) {
... lines 36 - 38
\Stripe\Charge::create(array(
... lines 40 - 42
"description" => "First test charge!"
));
... lines 45 - 49
}
... lines 51 - 56
}
}

When this code runs, it will make an API request to Stripe. If that's successful, the user will be charged. If something goes wrong, Stripe will throw an Exception. More on that later.

Cleaning up after Checkout

But before we try it, we need to finish up a few application-specific things. For example, after check out, we need to empty the shopping cart. The products are great, but the customer probably doesn't want to buy them twice in a row:

... lines 1 - 10
class OrderController extends BaseController
{
... lines 13 - 30
public function checkoutAction(Request $request)
{
... lines 33 - 34
if ($request->isMethod('POST')) {
... lines 36 - 38
\Stripe\Charge::create(array(
... lines 40 - 43
));
$this->get('shopping_cart')->emptyCart();
... lines 47 - 49
}
... lines 51 - 56
}
}

Next, I want to show a success message to the user. To do that in Symfony, call $this->addFlash('success', 'Order Complete! Yay!'):

... lines 1 - 10
class OrderController extends BaseController
{
... lines 13 - 30
public function checkoutAction(Request $request)
{
... lines 33 - 34
if ($request->isMethod('POST')) {
... lines 36 - 45
$this->get('shopping_cart')->emptyCart();
$this->addFlash('success', 'Order Complete! Yay!');
return $this->redirectToRoute('homepage');
}
... lines 51 - 56
}
}

And finally, you should definitely redirect the page somewhere. I'll use redirectToRoute() to send the user to the homepage.

That is it. Now for the real moment of truth. Hit enter to reload our page without submitting, put in the fake credit card, any date, any CVC, and...

Tip

If you get some sort of API or connection, you may need to upgrade some TLS security settings.

Hey! Okay. No errors. That should mean it worked. How can we know? Go check out the Stripe Dashboard. This time, click "Payments". And there's our payment for $62. You can even see all the information that was used.

Congratulations guys! You just added a checkout to your site in 15 minutes. Now let's make this thing rock-solid.

Leave a comment!

This tutorial uses an older version of Symfony of the stripe-php SDK. The majority of the concepts are still valid, though there *are* differences. We've done our best to add notes & comments that describe these changes.

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": ">=5.5.9, <7.4",
        "symfony/symfony": "3.1.*", // v3.1.10
        "doctrine/orm": "^2.5", // v2.7.2
        "doctrine/doctrine-bundle": "^1.6", // 1.6.3
        "doctrine/doctrine-cache-bundle": "^1.2", // 1.3.0
        "symfony/swiftmailer-bundle": "^2.3", // v2.3.11
        "symfony/monolog-bundle": "^2.8", // 2.11.1
        "symfony/polyfill-apcu": "^1.0", // v1.2.0
        "sensio/distribution-bundle": "^5.0", // v5.0.22
        "sensio/framework-extra-bundle": "^3.0.2", // v3.0.16
        "incenteev/composer-parameter-handler": "^2.0", // v2.1.2
        "friendsofsymfony/user-bundle": "~2.0.1", // v2.0.1
        "stof/doctrine-extensions-bundle": "^1.2", // v1.2.2
        "stripe/stripe-php": "^3.15", // v3.23.0
        "doctrine/doctrine-migrations-bundle": "^1.1", // 1.1.1
        "twig/twig": "^1.24.1" // v1.35.2
    },
    "require-dev": {
        "sensio/generator-bundle": "^3.0", // v3.0.7
        "symfony/phpunit-bridge": "^3.0", // v3.1.2
        "hautelook/alice-bundle": "^1.3", // v1.3.1
        "doctrine/data-fixtures": "^1.2" // v1.2.1
    }
}