gstreamer0.10-ffmpeg
gstreamer0.10-plugins-good
packages.
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.
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 represents the credit card that was just sent. So 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.
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.
And 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.
Hey djibril,
Your token does look like a valid Stripe token. If you have this error, probably the token was regenerated somehow. Do you hardcode this token manually as a string? Or do you retrieve a dynamic token from Request object and pass it with a variable as we show in screencast?
Cheers!
when i process the payment it tries to redirect me to /your-server-side-code
This is very strange because my controller specifies to redirect me to my home page "return $this->redirectToRoute('user_home_show'); "
Is that stripe trying to auto-redirect me somewhere?
Hey Blueblazer172 ,
Do you have a similar issue? If you describe the problem a bit more - we'll try to help you.
Cheers!
again I solved it myself :P
I had to remove the action="/your-server-side-code" parameter in
<form method="POST">
in the checkout.html.twig file
that easy xD
Awesome screencast so far! :) Thanks a lot! :)
Just an extra step: Make sure you upgrade nss, curl and openssl, or else Stripe will reject our request with message saying that we're trying to setup non-TLS1.2 communication. According to their blog (source: https://stripe.com/blog/upg... ), the new users starting from July 1st will have to use TLS 1.2. Prior to upgrade (and error) I had:
curl 7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.16.2.3 Basic ECC zlib/1.2.3 libidn/1.18 libssh2/1.4.2
Protocols: tftp ftp telnet dict ldap ldaps http file https ftps scp sftp
Features: GSS-Negotiate IDN IPv6 Largefile NTLM SSL libz
Cheers! :)
Ah, thanks for the note Jovan - very helpful! I hadn't seen this - but I'm sure others will have this exact same error :).
Cheers!
Yo azeem!
It's not a "shopping cart project", but instead, a "ShoppingCart" class (and shoppping_cart service) that I pre-configured in our project before starting the tutorial :). If you check out the start code, you'll find the class in src/AppBundle/Store/ShoppingCart.php, and the service is configured in services.yml. I didn't want to take the time to setup a shopping cart, so I did it before the tutorial so we could use it.
Does that help? Let me know!
Cheers!
Hey Dany,
As you can see from logs, your form is submitting to the wrong page. Instead of submitting to the "/charge" page you should submit form to the "/checkout", because in this screencast the form handling in CheckoutAction()
.
Cheers!
// 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
"composer/package-versions-deprecated": "^1.11" // 1.11.99
},
"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
}
}
Hello i have a error with my token:
No such token: tok_1D0HYdGNRPEKuC6IlRFsOREX