gstreamer0.10-ffmpeg
gstreamer0.10-plugins-good
packages.
Click on the documentation link at the top, and then click Embedded Form. There are two ways to build a checkout-form: the easy & lazy way - via an embedded form that Stripe builds for you - or the harder way - with an HTML form that you build yourself. Our sheep investors want us to hoof-it and get this live, so let's do the easy way first - and switch to a custom form later.
To get the embedded form, copy the form code. Then, head to the app and open the
app/Resources/views/order/checkout.html.twig
file. This is the template for the
checkout page.
At the bottom, I already have a spot waiting for the checkout form. Paste the code there:
... 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 | |
src="https://checkout.stripe.com/checkout.js" class="stripe-button" | |
data-key="pk_test_HxZzNHy8LImKK9LDtgMDRBwd" | |
data-amount="999" | |
data-name="Dollar Shear Club" | |
data-description="Widget" | |
data-image="/img/documentation/checkout/marketplace.png" | |
data-locale="auto"> | |
</script> | |
</form> | |
</div> | |
</div> | |
</div> | |
</div> | |
{% endblock %} |
Oh! And as promised: this pk_test
value is the public key from our test environment.
Let me show you what I mean: in Stripe, open your "Account Settings" on the top and then select "API Keys". Each environment - Test and Live - have their own two keys: the secret key and the publishable or public key. Right now, we're using the public key for the test environment - so once we get this going, orders will show up there. After we deploy, we'll need to switch to the Live environment keys.
Oh, and, I think it's obvious - but these secret keys need to be kept secret. The last thing you should do is create a screencast and publish them to the web. Oh crap.
But anyways, without doing any more work, go back to the browser and refresh the page. Hello checkout button! And hello checkout form! Obviously, $9.99 isn't the right price, for these amazing sheep accessories.
To fix that, head back to the template. Everything about the form is controlled with
these HTML attributes. Obviously, the most important one is amount
. Set it to
{{ cart.total }}
- cart
is a variable I've passed into the template - then
the important part: * 100
:
... 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 | |
... lines 38 - 39 | |
data-amount="{{ cart.total * 100 }}" | |
... lines 41 - 45 | |
</script> | |
</form> | |
</div> | |
</div> | |
</div> | |
</div> | |
{% endblock %} |
Whenever you talk about amounts in Stripe, you use the smallest denomination
of the currency, so cents in USD. If you need to charge the user $5, then tell Stripe
to charge them an amount of 500
cents.
Then, fill in anything else that's important to you, for example, data-image
. I'll
set this to our logo:
... 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 | |
... lines 38 - 42 | |
data-image="{{ asset('images/logo.png') }}" | |
... lines 44 - 45 | |
</script> | |
</form> | |
</div> | |
</div> | |
</div> | |
</div> | |
{% endblock %} |
Refresh to reflect the new settings. The total should be $62, and it is! Because
we're using the test environment, Stripe gives us fake, test cards we can use to
checkout. I'll show you others later - but to checkout successfully, use
4242 4242 4242 4242
. You can use any valid future expiration and any CVC.
Ok, moment of truth: hit pay!
It worked! I think... Wait, what just happened? Well, a really important step just happened - a step that's core to how Stripe checkout works.
First, credit card information is never, ever sent to our servers... which is the greatest news I have ever heard from a security standpoint. I do not want to handle your CC number: this would greatly increase the security requirements on my server.
Instead, when you hit "Pay", this sends the credit card information to Stripe directly, via AJAX. If the card is valid, it sends back a token string, which represents that card. The Stripe JS puts that token into the form as an hidden input field and then submits the form like normal to our server. So the only thing that's sent to our server is this token. The customer has not been charged yet, but with a little more work - we can fetch that token in our code and ask Stripe to charge that credit card.
Let's go get that token on the server. Open up src/AppBundle/Controller/OrderController.php
and find checkoutAction()
:
... lines 1 - 9 | |
class OrderController extends BaseController | |
{ | |
... lines 12 - 25 | |
/** | |
* @Route("/checkout", name="order_checkout") | |
* @Security("is_granted('ROLE_USER')") | |
*/ | |
public function checkoutAction() | |
{ | |
$products = $this->get('shopping_cart')->getProducts(); | |
return $this->render('order/checkout.html.twig', array( | |
'products' => $products, | |
'cart' => $this->get('shopping_cart') | |
)); | |
} | |
} |
This controller renders the checkout page. And because the HTML form has action=""
:
... 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"> | |
... lines 37 - 46 | |
</form> | |
</div> | |
</div> | |
</div> | |
</div> | |
{% endblock %} |
When Stripe submits the form, it submits right back to this same URL and controller.
To fetch the token, add a Request
argument, and make sure you have the use
statement
on top:
... lines 1 - 8 | |
use Symfony\Component\HttpFoundation\Request; | |
class OrderController extends BaseController | |
{ | |
... lines 13 - 30 | |
public function checkoutAction(Request $request) | |
{ | |
... lines 33 - 45 | |
} | |
} |
Then, inside the method, say if ($request->isMethod('POST')
, then we know
the form was just submitted. If so, dump($request->get('stripeToken'))
:
... lines 1 - 10 | |
class OrderController extends BaseController | |
{ | |
... lines 13 - 30 | |
public function checkoutAction(Request $request) | |
{ | |
$products = $this->get('shopping_cart')->getProducts(); | |
if ($request->isMethod('POST')) { | |
$token = $request->request->get('stripeToken'); | |
dump($token); | |
} | |
... lines 40 - 45 | |
} | |
} |
If you read Stripe's documentation, that's the name
of the hidden input field.
Try it out! Refresh and fill in the info again: use your trusty fake credit card number,
some fake data and Pay. The form submits and the page refreshes. But thanks to the
dump()
function, hover over the target icon in the web debug toolbar. Perfect! We
were able to fetch the token.
In a second, we're going to send this back to Stripe and ask them to actually charge the credit card it represents. But before we do that, head back to the Stripe dashboard.
Stripe shows you a log of pretty much everything that happens. Click the Logs link: these are all the interactions we've had with Stripe's API, including a few from before I hit record on the screencast. Click the first one: this is the AJAX request that the JavaScript made to Stripe: it sent over the credit card information, and Stripe sent back the token. If I search for the token that was just dumped, it matches.
Ok, let's use that token to charge our customer.
Hey Viet Son Nguyen !
> thank you very much for your tutorial, you did a great job
Thanks! But I *am* sorry to say that this tutorial is starting to show its age! Several parts are quite old and things have changed :/.
For the embedded checkout form, I'm not sure they support this anymore. They do have a way where you can easily redirect a user to Stripe - https://stripe.com/docs/pay... - or you can build a custom form, the new way is to use Stripe elements - https://stripe.com/docs/str... - which are pretty cool because they give you some control, but take care of a lot of the work for you. I think my embedded code used in this tutorial may still work, but it's not the recommended way of doing it anymore.
Cheers!
can you tell me why this elements dont want to render on my site?
https://ibb.co/8gXrNsB
Is this becouse of xampp server?
Hey Simon,
Thank you for providing the screenshot, it really helps to understand it better!
It looks like those requests to JS files are blocked by something... probably by your browser? I see you're using Chrome, could you try to open it in Incognito mode? Or if still not work, try to open this in another browser, e.g. Firefox? Does it helps?
If still does not work - could you do a right click on a blocked file and open it in a new tab? Do you see its content? Or do you see any errors? Which errors?
P.S. It might be because of the server you use, i.e. XAMPP. I'd recommend you to try it in another server, e.g. Symfony or PHP built-in servers and see if those JS requests are still blocked.
I hope this helps to debug this!
Cheers!
Hey Simon,
Ah, yes, that plugin may do things like this too, good find! As a workaround, you probably could whitelist the website and it should be ok.
Cheers!
and by the way rendering was problem made by form-row. In symfony with bootstrap it was cross rendering with some other class. I had to remove class form-row from div and it worked.
Hey simon,
Glad you found the problem! And thanks for sharing your solution with others!
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,
thank you very much for your tutorial, you did a great job.
In this video, I try to connect stripe to my project (Symfony 5). But it have a lot of change in stripe...
I can't find the bellow in the stripe's document...
<script src="https://checkout.stripe.com..." class="stripe-button" data-key="pk_test_HxZzNHy8LImKK9LDtgMDRBwd" data-amount="999" data-name="Dollar Shear Club" data-description="Widget" data-image="/img/documentation/checkout/marketplace.png" data-locale="auto">
</script>