Okay.
Now let's take a look at populating these two methods.
We need to handle what happens when the intent failed.
Now we're creating the order before we know if the payment has succeeded or the payment did not succeed.
And when we create an order, we do things like reduce the quantity in stock.
And we also need to think about updating our order status to say that the payment failed as well.
So inside the handle payment failed method, we'll first need to get hold of our order.
So we'll specify var order equals await and context dot orders.
And we're going to need to include our items because we need to reverse effectively what the consequences
of creating an order did such as reducing stock quantity.
So we're going to include the order items.
And then we need to get hold of the order based on the payment intent ID.
So we're going to use first or default async and we'll use x goes to x dot payment intent ID that's
equal to the intent.id.
And we want to make sure we have this order before we go any further.
So what we can use is the double question mark.
So if this returns null then we execute the code to the right of this.
And we're going to throw a new exception and just say order not found as we do not want to proceed any
further if we do not have the order, because we need to affect what's going on inside our database
here.
So on the basis that we do have the order, then we're going to loop over each item in the order items,
get hold of the product from our database, and we're going to add back in the quantity of that item
in stock.
So we'll use a foreach loop and we'll get the snippet from that.
And we'll say var item in order dot order items.
And then we need to get hold of the product.
So we'll save our product item equals await and context dot products.
And then we're going to use the find async method and pass in the item the item ordered dot product
id.
And once again we need to make sure we have this product.
So we'll use the double question mark again.
And if we do not have the products then we're going to throw a new exception and say problem updating
order stock.
And below this we'll just specify a product item dot quantity and stock.
And we're adding this back in.
So it's going to be plus equals item dot quantity.
And then below the for each loop we can set the order status.
So we'll specify order auto order status equals order status and say payment failed.
And we'll need to save these changes into our database.
So we'll specify a weight context and save changes.
Async.
So the idea being that inside our method here after we call the await handle payment intent failed,
we still return okay to stripe.
Because stripe has done its job.
It sent us the payment intent.
And we've updated our database based on the failure of that payment.
So even though the payment failed, we're still returning.
Okay.
To stripe on this.
And let's update the other method inside here.
The payment intent succeeded.
So once again inside this method we will still need to go out and get our order.
So let's just copy the code we used here inside the handle.
Payment intent failed and we'll just paste it inside this one.
And then we can specify the order order status.
And we can set it to order status dot payment received which is true.
The payment has been received.
But and there is a but here because let's give you a scenario which one of the students from the previous
version of this course brought up.
Supposing I went to my catalog inside here and I added something to the carts, and then I went to the
baskets.
And then if I click checkout, then hot reloads.
So I have to go back and restart the API server, make sure there's no errors inside here.
I'm just going to go to the API tab and just control R and just control R to restart the API server.
And then I'll be able to explain with more clarity about the scenario I'm about to talk about once it
does actually restart.
Okay.
So let's go back to the browser.
I'll go back to the basket, I'll click checkout again and then we get taken to our checkout.
Now at this point the payment intent has been created.
But does that mean I cannot be sneaky?
And right click the catalog and open up my application in a new tab.
Now I've got access to the catalog and I can start adding more things to the cards.
In this browser tab I've got two items.
In this browser tab, I've got one item of $200 and the payment intent is $200.
And I could check out and I could pay $200.
But what I have here is two items in this basket.
That's $350, and that's what's going to be saved on our basket on our API server.
So there'd be a mismatch between what the user pays versus what's contained inside our order.
So we're going to need to checklist effectively because we can't rely on our users to be well-behaved
and not do something sneaky like this.
And there's a few different approaches to this.
We could lock the basket on checkout.
We could add a property to our basket and say check out started at this time.
And then we could give the user, let's say, 15 minutes to complete the checkout and not allow any
new items to be added to that basket whilst the checkout is effectively locked.
That would be one approach, and that would be a better user experience for those sneaky users that
are trying to beat our system.
The way that we'll approach it is we'll check the amount of the order and compare it to the payment
intent.
If there's a mismatch, then we're going to have a status of payment mismatch and it won't be a successful
order.
And then we could stop the order at that point.
Now the user would still have paid and the payment would have been accepted.
So that means we'd need to issue a refund, etc. via stripe.
But we're really talking about edge cases here.
We're talking about a user that's being sneaky on purpose, that enters a checkout and then opens the
application in a new tab to be sneaky.
So I'm not too worried about that user that's being sneaky for this demonstration on this training course,
I just really wanted to point out that there is the possibility, the way that we're processing the
payments and when we're creating one thing after another, there's potential for misbehaviour to occur.
And this is one of the misbehaviors that could occur that we can catch by checking the amounts of the
payment intent versus the amount of the order.
So that's what we'll do inside here.
And we'll check to see if the order dot get total is not equal to the intent dot amount.
And if that is the case then we're going to set the order.
In fact let's do this inside curly brackets.
Just to be completely clear, we're going to set the order order status to be equal to.
And in fact let's create another status in our order status enum.
And we'll also have a payment mismatch.
To clarify that the order doesn't match the payment intent, and we'll set that to order status of payment
mismatch.
And then we'll have an else condition.
Which goes below the if statement and open curly brackets here.
And then we'll set the order to payment received for this one.
So at this stage as well we'll take the opportunity to remove the basket from our database.
At the same time there's no more need to have that basket stored in our database at this stage.
So we'll get a hold of our baskets and we'll set this equal to await and context baskets first or default
async.
And we'll use our payment intent ID to get hold of this.
So x dot payment intent ID that's equal to the intent.id.
And let's just move this down.
And then we'll check to make sure the basket is not equal to null.
And if that is the case, we'll say context dot baskets dot remove and pass in the baskets.
And then below this we can save our changes.
So we'll just use await context and save changes async.
And we're not returning anything from this that's being handled in the method above.
So that's our intent succeeded and intent failed handled.
And there's another small adjustment we need to make.
Because if the payment does fail, then we still want our user to be able to update the order at a future
point.
So we'll need to go back to our orders controller so that we can accommodate an order being updated,
as well as an order being created.
So we'll go to our orders controller.
And inside here at the moment we're only accommodating a new order.
So after we've created the subtotal and delivery fee.
We will then interrogate our database to see if we do have an order in our database.
So above the var order I'll say var order equals await context dot orders.
And we'll include the order items.
And we'll specify first or default async.
And we'll use the payment intent ID.
Equal to the basket dot payment intent ID.
And then we can check to see if we have this order.
So.
So just above where I've got var order that's currently in an error I'll say if the order is equal to
null then we will be creating a new order.
So I'll cut this code and paste it inside the if statement.
And we don't need the var because we've got the order declared above.
And then we can create the new order, and we'll also move up our context, add orders into the if statement
as well, and below the if statement we'll have the else statements.
So that means we're effectively updating an existing order.
And they could have changed the order items.
So we'll specify the order order items is equal to the items.
Otherwise everything's going to stay the same inside here.
And I've got the basket remove code inside here.
But we're handling this in our stripe webhook which is a better place to do this.
And we're also deleting the cookie from the client side as well.
So we don't need either of these two lines of code inside here anymore as they're just duplicating things
that are already happening anyway.
And that's our webhook code created now.
So our next step is to take a look and see how we can use this webhook with stripe.
And we'll take a look at that next.