This episode is for members only

Sign up to access "Build an E-Commerce Platform" right now.

Get started
Already a member? Sign in to continue
Playing
72. Creating and updating a PaymentIntent

Episodes

0%
Your progress
  • Total: 8h 42m
  • Played: 0m
  • Remaining: 8h 42m
Join or sign in to track your progress
01. Introduction and demo
9m 42s
0%
02. Installing Laravel, Breeze and Livewire
4m 47s
0%
03. Creating categories
9m 4s
0%
04. Recursively displaying categories
8m 21s
0%
05. Product model and migration
2m 50s
0%
06. Showing a product
7m 50s
0%
07. Product variation setup
10m 26s
0%
08. Creating the product selector
10m 34s
0%
09. Loading child variation dropdowns
4m 28s
0%
10. Faking adding the final variation
9m 33s
0%
11. Setting up product stock
4m 34s
0%
12. Calculating variation stock levels
9m 1s
0%
13. Adding product images with MediaLibrary
8m 50s
0%
14. Creating the product gallery
7m 28s
0%
15. Providing a fallback image
2m 56s
0%
16. Adding media to product variations
3m 37s
0%
17. Creating the cart model
3m 37s
0%
18. Registering the cart service
6m 34s
0%
19. Creating a cart session
11m 4s
0%
20. Showing the cart in the navigation
9m 19s
0%
21. Caching the cart instance
3m 2s
0%
22. Adding items to the cart
14m 1s
0%
23. Creating the notification component
8m 5s
0%
24. Showing the user's cart
6m 50s
0%
25. Outputting cart items
4m 50s
0%
26. Showing variation specifics
8m 8s
0%
27. Updating item quantity
8m 2s
0%
28. Removing an item from the cart
6m 1s
0%
29. Calculating the cart summary
8m 7s
0%
30. Showing the category products page
5m 1s
0%
31. Indexing products in Meilisearch
8m 32s
0%
32. Hooking up products to categories
4m 15s
0%
33. Building the product browser
13m 32s
0%
34. Showing child categories
1m 51s
0%
35. Indexing product variations for filtering
8m
0%
36. Outputting variations for filtering
12m 52s
0%
37. Hooking up product filters with Livewire
7m 48s
0%
38. Filtering products
12m 24s
0%
39. Filtering by price
9m 50s
0%
40. Adding global navigation search
7m 50s
0%
41. Handling products that are not live
3m 22s
0%
42. Price range category fix
1m 18s
0%
43. Scaffolding the checkout page
8m 6s
0%
44. Listing shipping options
9m 3s
0%
45. Calculating the cart totals
2m 52s
0%
46. Validating the account form
9m 10s
0%
47. Validating the shipping form
5m 47s
0%
48. Saving the shipping address
8m 4s
0%
49. Selecting a saved shipping address
6m 39s
0%
50. Fix shipping address error for non authenticated users
1m 7s
0%
51. Redirecting if the cart is empty
2m 42s
0%
52. Checking for quantity changes
7m 11s
0%
53. Syncing if quantities have changed
11m 59s
0%
54. Flashing a message when quantities have changed
5m 48s
0%
55. Setting up for orders
5m 17s
0%
56. Creating an order
13m 24s
0%
57. Attaching variations to order
6m 13s
0%
58. Reducing stock after ordering
2m 56s
0%
59. Meilisearch filter query fix
1m 33s
0%
60. Showing the order confirmation page
7m 39s
0%
61. Attaching orders for registering guest users
5m 17s
0%
62. Scaffolding the orders page
5m 30s
0%
63. Filling in order variation details
5m 24s
0%
64. Returning the order status
4m 39s
0%
65. Detecting order status changes
10m 49s
0%
66. Sending the order status change email
5m 12s
0%
67. Sending an order confirmation email
2m 47s
0%
68. Handling deleted cart records
4m 44s
0%
69. Transferring the guest cart
2m 44s
0%
70. Creating a presenter for the order status
4m 31s
0%
71. Setting up Stripe
3m 43s
0%
72. Creating and updating a PaymentIntent
16m 21s
0%
73. The Stripe card form
3m 35s
0%
74. Validating before payment
5m 34s
0%
75. Submitting a payment
6m 40s
0%
76. Checking for a successful payment
5m 47s
0%
77. Handling Stripe client errors
3m 11s
0%
78. Entangling Stripe customer data
2m 18s
0%

Transcript

00:00
Let's now deal with creating and updating a Stripe payment intent. Now, if you've not worked with Stripe before or if you've used Stripe within another package
00:10
and haven't seen payment intent directly, this might be a little bit confusing at first. But let's go through this and figure out exactly what we need to do inside of our checkout to get this working. OK, so first of all, there's lots of information on the payment intent API, of course, over on the Stripe documentation. So I'd recommend you read through that just to get a feeling for
00:34
this as well. But let's go ahead and just explain exactly what we need to do within the flow of our checkout. So when we go ahead and add a product to a car, so let's go ahead and choose one of these as we've done a load of times before. When we hit our checkout page, the first thing that we're going to need to do is create a payment intent. Now, this is like an object that tells
00:59
Stripe the amount that we want to charge, the type of charge this is going to be and a host of other information. You can make these as complex or as simple as you like. What we will then do is later update that payment intent with the amount if this changes. So at the moment we're on $90. If, for example, I go ahead and add in a different shipping option, this bumps up to $110. And of
01:23
course, if I were to add another product to this, it would increase the price as well. Or if I removed an item from the car, it would also decrease the price. So it would update the payment intent amount. Now, what we then do is we pass the payment intent client secret through to our Stripe card form. What that's going to allow us to then do is process the payment on the client side.
01:48
Stripe will then update the payment intent to a successful status or a succeeded status. And then what we can do is once we post this through to our server, we can check that payment intent either directly or via a webhook to see if that payment was successful. Now, the good reason for the payment intent API is just so you have a single source of truth for your payments.
02:12
If, for example, the user clicked Confirm Order and Pay and the payment was made, that payment intent would be set to successful. When we try and process this again, it will not charge the user twice because that payment intent has already been set successful. The charge has already been made. So we'll see this object as we go through the next few parts. But let's focus now on creating
02:37
out a very simple payment intent. Now, we need to be careful here because we don't want to keep creating lots and lots of different payment intents. We kind of just want one payment intent object which serves through the entire lifecycle of the user adding items to their cart and going ahead and checking out. But we can play around with this here by just creating a few dummy ones
02:57
out just to see what they look like. So let's first of all go ahead and look at what this looks like. I'm going to create a variable here called payment intent. And we're going to go ahead and pluck out our Stripe service from our container. We're then going to go ahead and say payment intents. And let's get rid of that dollar and create. There's a bunch of options we can pass
03:17
through to here. We're just going to keep this really, really simple. So let's go ahead and choose the amount. We already know this because we have a total property. So we can go ahead and make sure we cast this to an integer and grab that total property out. The other really important thing is the currency. So Stripe knows how to charge this. This is going to be USD for us.
03:38
And of course, if you wanted to configure this to update later, you can do. We also have this setup future usage property, which for us, we don't have any kind of recurring billing. This is just on session. You have other options here, for example, recurring billing where the user is going to be charged in the future. But of course, in our case, we're just charging the user once
04:01
and that's done. Now, we also have the ability to pass in metadata as well. So for example, if you wanted to reference in your payment intent, the cart ID or the cart UUID, you could do this as well. That would be really useful if you are setting up a webhook to then create the order. That's not what we're going to be doing, but this is an option. So I'm going to go ahead and get rid
04:23
of the metadata for now. And this is pretty much all we need. So we're going to go ahead and just die dump on this payment intent just to see what this looks like. Let's give this cart a refresh here. And you can see we've now got payment intent object with all of this information, some of which we can set, some of which comes directly from Stripe. Now, if we scroll down here,
04:43
you can see that we've got this status here, requires source. Now, basically what that means is we're waiting for a payment to be made. We're waiting for a payment source to be provided. So we can go ahead and mark this as successful. So we've got a payment intent ID here, which is really, really important because if we refresh this page, what that's effectively done is created
05:05
another payment intent. Now, that's not really going to matter too much. You could technically just create a payment intent every time the user hits this page, always passing in the correct amount for this. But we kind of want to just keep the single payment intent so we can track this across the flow of our checkout. So what we need to work out how to do is set this payment intent
05:28
ID somewhere in our cart. And by that, I mean over in our cart session and then go ahead and retrieve it the next time the user goes over to the checkout because, of course, the user could land on checkout but go back, add another item, and then come back in again. So that's what we're going to focus on here and checking if the cart has a payment intent. Now, for that, we're going
05:50
to need to add perhaps a column to our database table. You can even store this in a session if you wanted to. But since we're using a kind of session model hybrid for our car, it makes sense to add it over on the table. So let's go ahead and make a migration here. And let's just say add payment intent ID to carts table. And let's go ahead and open that up.
06:14
And let's go down and add this in here. It's just going to be a string. So let's just say string and payment intent ID. Really importantly, this needs to be nullable because it's not always going to be there. And, of course, we can fill in the down migration and just drop that column if we go down. So let's go ahead and say php artisan
06:37
migrate. And we're done. Great. Okay. So now what we need to do is get the ability to, over in our cart, grab this payment intent ID and also check if we have a payment intent ID as well. So let's do these two things maybe just down the bottom here just so we don't lose where we are. So let's do this just underneath here. So we need a has payment intent method. And we also probably want
07:07
a get payment intent ID method as well. Now, this does kind of tie us down to a Stripe implementation directly within our cart. If you're using another payment provider, it's going to be a little bit easier than this because most payment providers don't give you the ability to set up payment intents. Okay. So to get the payment intent ID, of course, we want to
07:31
go ahead and use our current instance, which gives us that cart model, and just say payment intent ID. That's pretty much all we need to do. Now, for has payment intent ID, all we really need to do is check if it is not null because, of course, it's nullable. So we're just going to go ahead and pass in the method that we've already created, get payment intent ID. That's pretty much it.
07:53
Okay. So now let's come back to our render method over on checkout. Now, be aware that this is going to be run every single time we make any kind of change to our checkout page. So, of course, we don't want to have to keep running this to create a new payment intent every single time. So what we're going to do is, first of all, up here, check if the cart has a payment intent ID or has payment
08:18
intent. Let's just double check what we call this. Yeah, has payment intent. So if the cart does have a payment intent, we want to go ahead and get the payment intent and potentially update the payment intent with the new amount. Now, otherwise, we want to go ahead and create a payment intent, which we're already doing down here. And then we want to go ahead and set the payment intent ID
08:43
directly within our cart. Now, I think before we go any further, it might make sense to move this out to a method because we don't want to clutter the render method with too much of this. So let's go ahead and create out a method in here called get payment intent. And let's go ahead and make sure we accept in our cart in here, which we'll have to pass in manually.
09:06
And let's just paste everything we've done in here already. This will also return the payment intent as well. So we will always have that available. And we can go ahead and pass this down to our view so we can extract out that client secret that we spoke about earlier. So let's go ahead and say get payment intent. And let's pass through our cart instance in there because that requires that.
09:27
And that's pretty much it. So we want to return this payment intent once we've created it. Otherwise, we want to get it, update it with a new amount, and then return that payment intent in here as well. So we've created it, but let's go ahead and look at getting it. Now, for this to work and to be able to get a payment intent by its ID, we need to have stored this ID over on the cart.
09:52
So over in our cart model, let's go ahead and create out an update payment intent ID or update payment intent if we wanted to. And then we want to accept into here a payment intent ID. In fact, let's call that update payment intent ID because it makes a little bit more sense. Now for this, we just want to go ahead and grab our instance and update it. And we want to pass
10:18
in the payment intent ID. And of course, that's going to be the payment intent ID that we get in. So for this to work, let's go over to our actual cart model and just make sure we update this with our fillable fields. And that's, of course, going to be the payment intent ID. Okay, so now that we've got this method in our cart, over in our checkout, we can go here and say cart update payment
10:44
method or payment intent ID. And from that payment intent that we create, go ahead and set the ID. So what I'm going to do now is just comment this out. And let's just see what happens when we go ahead and run this. Now, remember, we're calling this from down here and also passing the payment intent in. Let's give that a refresh and see what happens. Okay, so if we come over to our cart table
11:04
now, you can see that that payment intent ID has been stored. That means that we're only creating one and we can retrieve this the next time around to update it if we need to. So let's go up to the top and let's uncomment this. And let's just die dump here on has payment intent ID, just so we know that that's working. And there we go. So we know that we've got a payment intent ID now,
11:27
and we can look this up within Stripe. So we're going to do the same thing and create a similar variable. And that's going to be from Stripe payment intents once again. But this time, we're going to go ahead and retrieve that instead. So let's go ahead and retrieve this by the payment intent ID. So let's say cart get payment intent ID. And let's die dump on this just to make sure
11:51
we're getting the same one back. Okay, let's give that a refresh. And there we go. Sure enough, we're retrieving the payment intent ID or the payment intent via its ID in exactly the same way as we saw before. So let's leave it at that for now and go ahead and return that payment intent, dump out the client secret so we know what we're working with. And then we'll come back to update
12:14
this in just a moment. So if I give this checkout refresh, you will notice more of a delay now because we're hitting the Stripe API. So it's not the end of the world, it's not too slow. But this is probably the easiest way that we can get this implemented. Okay, so over in checkout.blade.php, we're going to go ahead and just dump out if we just head over to our views and live wire,
12:38
just where the Stripe card form is the payment intent client secret. So let's say payment intent and client secret. That's what we want to pass through to our card form. So when we do submit this, Stripe knows which payment intent to use to make a charge and then go ahead and update this. So that's the payment intent client secret that we're going to use to pass into the card form
13:00
when we get to that in the next episode. Okay, so let's just experiment with this. At the moment, we've got a total of $90. If I go and add another product to the car, or I go over to my car and increase the quantity here, of course that updates this and we can see this over in the checkout area. But let's just look at that payment intent that we've already got and see what this gives
13:20
us. So let's just come up here and die dump on that payment intent and give that a refresh. And let's look at the amount here. It's still at $90 or 9,000 cents. So that's a problem because when we pass this through to our card form, the user will only be charged this amount. So we need to make sure that this is always up to date. And it really depends on what kind of
13:43
information you have. But in our case, the total is really the only thing that we need to update here. So we only want to update this if the payment intent status is not successful. If it's already succeeded, it's not going to work. There are a couple of other cases where this wouldn't work as well. But let's just go ahead and do a quick check for payment intent status. And we'll
14:03
check if this is succeeded. And if it is succeeded, or rather if it's not succeeded, then we want to go ahead and update this. So to update a payment intent, we just want to do pretty much the same as we've done here, but call update. And we want to pass through the new total, which is just exactly the same as what we did before to create this. And that is just this and total. So that's
14:31
pretty much all we need to do. Okay, now let's go back and give this a refresh. And let's have a look. We need to actually pass the payment intent ID in here. So we know what we're updating. So let's say cart get payment intent ID. That's the first argument. The second argument is the properties that you need to update. Let's give that a refresh. And there we go. Okay, so we've
14:53
got an unknown parameter total. That is just because this needs to be amount under stripe, as we saw here. Let's give that another refresh. And there we go. Okay, so now if we go ahead and die dump the payment intent here, that will have been updated with our new total. And there we go for 1800. And of course, if we were to go ahead and update the shipping amount,
15:17
the total takes that into account. So let's just try this one more time. By switching over to the $20 shipping, that's now $200. Let's once again bring in that die dump. And that will have been updated with the full amount. Great. Okay, so hopefully that makes sense. We create a payment intent, which we use to charge the user on the client. We update the cart's payment intent ID,
15:42
so we can retrieve it later here. And if it does need updating, e.g., if it's not succeeded, we're going to go ahead and make a request here to update that payment intent. So we've always got the right amount to charge. Now, at the moment, this is always called. We could probably have some other condition in here where we don't necessarily need to update it if the price
16:03
hasn't changed, but we can always get to that later. The most important thing is getting this to work and getting it to work properly. Then we can always tweak it later. Okay, so we've got our payment intent and we've got the secret here, which we can use to charge the card. Let's go ahead and set up our Stripe payment form first.
78 episodes8 hrs 42 mins

Overview

Build a robust e-commerce platform with a Laravel and Livewire. Features products with unlimited and flexible variations, a product browser with filters and price range slider, global product search, guest checkout, shipping and payment implementation, order status tracking and more.

Alex Garrett-Smith
Alex Garrett-Smith
Hey, I'm the founder of Codecourse!

Comments

No comments, yet. Be the first to leave a comment.