WEBVTT

00:00.000 --> 00:00.240
Okay.

00:00.270 --> 00:06.510
Our next task is to create a controller that's going to contain an endpoint so that our clients can

00:06.510 --> 00:13.020
effectively create a payment intent, and that will be returned with the basket to the client.

00:13.020 --> 00:16.830
So inside the controllers folder let's create a new file.

00:16.860 --> 00:21.150
It's going to be a class and we'll call it payments controller.

00:21.570 --> 00:27.150
And inside here we'll derive from the base API controller.

00:27.180 --> 00:30.390
And we're going to inject two things in here.

00:30.390 --> 00:39.960
We're going to have our payment service and call it payment service and our store context and call it

00:39.960 --> 00:41.160
context.

00:42.120 --> 00:48.090
And inside here we'll have an HTTP post for our endpoint.

00:49.140 --> 00:55.590
And we'll make it public async task of action result.

00:55.590 --> 01:05.210
And we're going to return a basket DTO from this and we'll call it create or updates payment intent

01:05.210 --> 01:06.980
as the name of the method.

01:07.580 --> 01:14.540
And because we're returning a basket DTO, let's go and check that and make sure that we add the new

01:14.540 --> 01:15.860
properties that we have here.

01:15.860 --> 01:22.430
So we're going to have a prop that's a string that's a client secrets.

01:22.820 --> 01:28.670
And we'll make that optional because of course we don't have it when they first start adding things

01:28.670 --> 01:29.570
to their baskets.

01:29.570 --> 01:36.530
And we'll also have the string for the payment intent ID as well.

01:36.530 --> 01:41.180
And I'm just going to go to the basket and make sure that is what I've called both of those properties.

01:41.180 --> 01:42.950
And that is indeed the case.

01:42.980 --> 01:43.730
Okay.

01:44.210 --> 01:47.330
So back to our payments controller.

01:48.260 --> 01:50.750
And first of all we'll get the basket.

01:50.750 --> 01:56.750
So we'll say var basket equals await and context dot baskets.

01:57.770 --> 02:01.700
And we need to get the basket with the items.

02:01.700 --> 02:05.280
We're going to need something similar to what we've used before.

02:05.310 --> 02:10.320
And if we go to our basket controller, let's take a look at what we did to go and get the basket.

02:10.320 --> 02:12.810
We've got this retrieve basket method.

02:12.810 --> 02:18.900
And if we go and take a look at this, do we have an opportunity to make this reusable.

02:18.900 --> 02:23.820
So it's not just usable inside our basket controller, but we can use it elsewhere as well.

02:23.850 --> 02:24.720
Of course we do.

02:24.750 --> 02:29.970
And do we have an extension method for anything related to our basket?

02:29.970 --> 02:30.810
At the moment we do.

02:30.840 --> 02:32.730
We've got a basket extensions class.

02:32.730 --> 02:34.440
So let's make use of this.

02:34.890 --> 02:42.390
I'll go to our basket controller and let's copy this code where we're returning this.

02:42.390 --> 02:47.670
And we could create an extension method on the iqueryable of basket for instance, that includes this

02:47.670 --> 02:50.610
code to make it easier for us to go and get the basket.

02:50.610 --> 02:51.930
So I'll go to the basket.

02:51.930 --> 02:56.730
I'll just copy this into my clipboard and go to the basket extensions.

02:56.730 --> 03:01.230
And let's create below this extension method.

03:01.260 --> 03:10.440
Let's create another extension method and we'll use public static async task that returns a basket.

03:11.220 --> 03:15.060
And we'll call it get basket with items.

03:15.390 --> 03:20.190
And we'll use this extension on the item variable of basket.

03:20.190 --> 03:26.520
So we'll use item variable of type basket and specify query.

03:26.520 --> 03:34.740
And we'll also pass in the string and make this string optional for the basket ID and open curly brackets

03:34.740 --> 03:35.040
in there.

03:35.040 --> 03:38.940
Let's just bring this down because I've gone off the edge of the screen there.

03:39.930 --> 03:44.190
And inside here I'll just paste in what I have inside my clipboard.

03:44.190 --> 03:51.180
But instead of awaiting context dot baskets, we'll just use return await query.

03:51.180 --> 03:57.240
And then let's use the quick fix on the include so that we can bring in using Microsoft Entity Framework

03:57.240 --> 03:57.990
Core.

03:58.560 --> 04:06.270
And instead of the request cookies basket ID, we'll just use the basket ID that we're passing in as

04:06.270 --> 04:07.260
a parameter.

04:07.350 --> 04:13.590
Now we're getting a warning here because we've said that we are returning a basket, not basket or null.

04:13.590 --> 04:15.240
We're not making this optional.

04:15.240 --> 04:17.640
We only want this query to return the basket.

04:17.640 --> 04:19.890
We don't want it to return anything else.

04:19.890 --> 04:29.010
So we will to the right of the closing parenthesis here we'll add double question mark.

04:29.010 --> 04:37.290
So if this query is going to return null then we'll execute what's to the right of the double question

04:37.290 --> 04:37.620
mark.

04:37.620 --> 04:45.930
And in this case we're just throw a new exception and just say cannot get baskets as the message inside

04:45.930 --> 04:46.500
here.

04:46.530 --> 04:49.140
Now exceptions continue to be exceptional.

04:49.140 --> 04:52.140
So this is fine to use inside here as it is.

04:52.170 --> 04:58.950
And it guarantees in our code that when we make this request, we ensure that if that request is successful,

04:58.950 --> 05:01.110
then we definitely are working with a basket.

05:01.110 --> 05:04.890
So we don't need to be defensive in our code where we're using it.

05:04.890 --> 05:12.660
So let's go back to our payments controller, and now we have access to our new get basket with items

05:12.660 --> 05:13.110
method.

05:13.110 --> 05:22.020
And inside here we can pass in the request dot cookies and the basket ID.

05:22.020 --> 05:27.330
And if we hover over the baskets then technically it still could be null.

05:27.480 --> 05:29.700
Okay, so we will be defensive.

05:29.730 --> 05:36.420
My prediction that we didn't need to be defensive was a bit short lived, because with this method,

05:36.420 --> 05:40.740
it's still suggesting that it could be undefined.

05:40.740 --> 05:43.290
And that's because of the string I think here.

05:43.290 --> 05:45.300
Regardless, we'll just be defensive here.

05:45.330 --> 05:55.080
We'll check to see if the basket is equal to null, and if it is, then we'll just return a bad request

05:56.790 --> 05:59.880
and say problem with the basket.

05:59.880 --> 06:07.990
I'll be vague inside there, and I wouldn't expect to see that anyway based on how this endpoint is

06:07.990 --> 06:08.920
going to be used.

06:08.920 --> 06:13.480
But for our compiler, we need to be defensive so that we don't get these nasty warnings inside our

06:13.480 --> 06:14.320
code.

06:14.350 --> 06:16.870
So next we're going to create our new intent.

06:16.870 --> 06:19.090
So we'll save our intent equals await.

06:19.090 --> 06:21.370
And we'll use our payment service.

06:21.370 --> 06:24.400
And we'll use the create or update payment intent.

06:24.400 --> 06:27.910
And we'll pass through our basket to that method.

06:28.810 --> 06:30.460
We'll check to see if we have the intent.

06:30.460 --> 06:36.070
Because if we hover over the intent then that will also potentially be null.

06:36.070 --> 06:37.960
We can see that question mark there.

06:37.960 --> 06:41.710
So again we'll check to see if the intent is null here.

06:41.770 --> 06:51.220
And if it is we're going to return a bad request and just say problem creating payment intent.

06:53.710 --> 06:56.770
And then we can update our basket with the new information.

06:56.770 --> 06:58.840
So the basket payment intent ID.

06:58.840 --> 07:04.150
And because we're using this for create or update then this might not be null.

07:04.150 --> 07:06.460
So we can use another operator.

07:06.460 --> 07:09.790
Here we'll use a double question mark which represents null.

07:09.790 --> 07:15.610
If it is null, then we're going to use equals after this, which is going to assign it to, in this

07:15.610 --> 07:17.470
case the intents id.

07:17.890 --> 07:21.160
And then we'll do the same for the basket clientsecret.

07:21.190 --> 07:23.590
Once again we'll check to see if it's null.

07:23.830 --> 07:30.370
If it is, then we're going to assign this to the intents dot clientsecret that we get from stripe.

07:31.030 --> 07:40.150
Following this we can then specify var results equals await and use our context and save changes async

07:40.150 --> 07:41.770
because we're updating our basket.

07:41.770 --> 07:46.150
And we'll check to see if the number of changes is greater than zero.

07:46.240 --> 07:51.670
If the not results then that means there was nothing saved into our database.

07:51.670 --> 08:02.680
So we'll again we'll return a bad request at this point and say in the message problem updating baskets

08:02.680 --> 08:05.020
with intents.

08:05.260 --> 08:11.170
And these error messages are really more for us as it's going to tell us which stage it failed if it

08:11.170 --> 08:12.460
did fail at all.

08:12.610 --> 08:16.960
And then we can return the basket.

08:17.170 --> 08:23.260
And we've got a two DTO method we can use here to return our basket as well, so we can take advantage

08:23.260 --> 08:24.550
of that.

08:25.390 --> 08:29.230
Now, I didn't update the basket controller with our extension method.

08:29.260 --> 08:34.780
I'll leave that to you if you wish to do that, because we don't need this retrieve basket method in

08:34.780 --> 08:34.960
here.

08:34.990 --> 08:37.570
Now we can use the extension method.

08:37.570 --> 08:40.180
But to save a bit of time, I'm not going to demonstrate that.

08:40.180 --> 08:41.740
I'll just leave that up to you.

08:41.740 --> 08:46.900
If you do want to update your basket controller to use that as well, it should work just fine.

08:46.930 --> 08:51.220
And one more thing I want to check before we test this functionality back in our payments controller.

08:51.250 --> 08:52.600
Did we make this authorised?

08:52.600 --> 08:53.200
We did not.

08:53.200 --> 08:56.500
So I'm just going to make this an authorised endpoint.

08:56.500 --> 09:01.300
So the user would need to be authenticated before they can create this payment intent.

09:01.300 --> 09:04.870
But where we're going to use this is inside our checkout.

09:04.870 --> 09:08.950
So we're not letting the user get to the checkout before they're authenticated.

09:08.950 --> 09:13.460
So we'll just make this endpoint authenticated as well.

09:14.420 --> 09:16.640
So now we're in a position where we can go and test this.

09:16.640 --> 09:21.380
So I'll just go back to the terminal and I'll just execute Netwatch to restart the API.

09:21.410 --> 09:24.860
Because I did stop it when I created a new migration.

09:24.860 --> 09:27.860
So we'll make sure this starts correctly.

09:27.860 --> 09:29.510
And that looks good.

09:29.510 --> 09:33.920
So we'll head back to postman to test this specific functionality.

09:34.280 --> 09:36.680
So let's just double check.

09:36.710 --> 09:44.810
Actually if I just click on the login and click send and log in as Bob at test.com, click send and

09:44.810 --> 09:49.010
just double check the cookies and see if we've got a basket cookie inside here I do not.

09:49.040 --> 09:52.640
I only have the authentication cookie.

09:52.670 --> 09:54.920
In fact, I do have a basket cookie.

09:54.950 --> 09:56.390
Where did that come from?

09:56.390 --> 09:56.810
No idea.

09:56.810 --> 10:03.500
But I'm going to delete this basket ID because I want to make sure I'm creating a clean basket, in

10:03.500 --> 10:08.390
this case because I have dropped my database previously, I've got no idea if that's a valid basket.

10:08.390 --> 10:13.580
And I'm going to click on Get Basket to see what I get back from the API In this next request, I'll

10:13.580 --> 10:19.640
click send and I get the 204 no content, which effectively means I do not have a basket.

10:19.640 --> 10:26.030
So I'll add an item to the basket which will have the effect of creating a basket in our API.

10:26.060 --> 10:27.020
So I'll click this one.

10:27.020 --> 10:30.560
Just adding a product with the id of two and a quantity of one.

10:30.560 --> 10:33.680
I'll click send and this gets created.

10:33.680 --> 10:38.750
I now have a basket with a basket ID and I should have the cookie for this.

10:38.780 --> 10:44.870
Although postman being weird seems to need me to click that button twice so that I can see the basket

10:44.870 --> 10:48.530
ID which should match what I got returned in that request.

10:48.530 --> 10:54.230
So starts 070, and if I take a look at this then that looks great.

10:54.260 --> 10:55.040
Okay.

10:55.760 --> 11:01.040
And if I get the basket and click send, that should confirm that everything is working as it should.

11:01.040 --> 11:04.730
So now we get to take a look at creating our payment ID.

11:04.760 --> 11:07.550
Now this part is going to go out to stripe.

11:07.550 --> 11:10.250
And we don't send anything in the body of this request.

11:10.250 --> 11:11.810
I don't have anything there.

11:11.900 --> 11:16.400
And we are going to send up with the authorization.

11:16.400 --> 11:17.870
I'll just use inherit for parent.

11:17.870 --> 11:22.400
It should send up the cookie with the request anyway because I have it inside postman.

11:22.970 --> 11:26.330
And we'll click send on this.

11:26.330 --> 11:28.910
So let's click send.

11:29.090 --> 11:32.480
And it looks like we've got a 200 okay.

11:33.290 --> 11:38.810
And annoyingly we've got no client secret or payment intent ID inside here.

11:38.810 --> 11:45.560
I've just remembered what I didn't do, which is update our extension method in the basket extensions.

11:45.560 --> 11:55.220
We didn't update this to also return the client secrets and the payment intent ID from our basket,

11:55.220 --> 11:58.010
which we should have when we execute this method.

11:58.010 --> 12:03.830
So just inside here I'm going to specify the client secrets and say basket dot client secret and also

12:03.830 --> 12:08.390
the payment intent ID and say basket dot payment intent ID.

12:09.920 --> 12:18.110
Now can we update our payment intent ID And even though we didn't have this information inside our baskets.

12:18.770 --> 12:23.930
Well, if we think about how we're getting our basket on the back end, all we need is our basket ID

12:24.380 --> 12:28.400
and it's in our basket in the database that should have this populated.

12:28.400 --> 12:32.840
And if we just double check that theory, we'll open up the solution explorer.

12:32.870 --> 12:37.610
Let's just click on store DB and see what we have inside our basket.

12:38.030 --> 12:39.830
And let's click on this.

12:39.830 --> 12:41.480
And this is the basket we're working with.

12:41.510 --> 12:43.100
We start 070.

12:43.100 --> 12:49.400
And I can see that I've got the client secret and the payment intent ID populated inside there.

12:49.400 --> 12:52.730
What that means is I should be able to update my basket.

12:52.730 --> 12:59.120
And one thing before we do do that, let's go and take a look at the stripe dashboard inside the transactions.

12:59.120 --> 13:07.370
What we should see is that we do have a payment intent created $150 with the payment intent ID, and

13:07.370 --> 13:09.590
that's the one that would have just been created there.

13:09.590 --> 13:13.490
So our first transaction is incomplete because it hasn't been paid for yet.

13:13.490 --> 13:17.880
But this is a payment intent that's now recorded inside stripe.

13:17.880 --> 13:25.050
So what we should be able to do is update our basket and update our payment intent based on the fact

13:25.050 --> 13:29.670
that we have this payment intent ID in our basket as well.

13:29.670 --> 13:33.780
So let's go take a look and I'll add another item to the basket.

13:33.780 --> 13:36.000
In fact I'll just increase the quantity.

13:36.000 --> 13:37.830
So I'll send this request again.

13:37.860 --> 13:38.730
Click send.

13:38.760 --> 13:47.640
Now we've got two quantity of this product which should meet or should have a price now of $300.

13:47.640 --> 13:56.190
So if I create a payment intent and click send then I've got a problem updating basket with intent.

13:56.220 --> 14:02.280
Okay I think this is about the point where I'm prepared to say, maybe it's time we took a look in the

14:02.280 --> 14:08.250
debugger and see what's going on as we go throughout the process of creating this intent, and it will

14:08.250 --> 14:10.860
help us solve this problem if there is one.

14:10.860 --> 14:15.390
And it's not just related to hot reload, but whatever, we'll take a look through the debugger in the

14:15.390 --> 14:17.460
next lesson at what's going on here.
