Our next step is to set up the payment service.
So inside Solution Explorer.
Now back to our API functionality.
I'm just going to right click the API and say new folder.
And I'm going to call it services.
And inside the services folder let's just clean things up inside here I'm going to create a new file.
And I'm going to create a class called Payments Service.
And we're typically do this or put something inside a services folder where we're using a third party
service that doesn't rely on our database or Entity Framework or anything like that.
And let's configure this.
So we're going to need to inject inside here our configuration.
So I'm going to use the I configuration interface and call it config.
So that we've got access to the configuration keys that we've just created inside our payment service
class I'm going to create a method a public async task.
And this is going to return a payment intent that we get from the stripe package that we installed.
And for some reason I've got using system and using stripe there.
We'll need the using stripe.
I'm not sure why it's dimmed out, but it is necessary for what we're doing here.
And I'll use the method name of create or update payment intent.
And we'll pass in to this method as an argument, the basket and call it basket, as we're going to
need to see what's inside there in order to work out how much their order is going to be so that we
can notify stripe.
This is how much the customer is intending to pay.
Now we've got the create or update payment intent, because we're not going to stick our user to the
current basket until they've actually paid for it.
So they will have the opportunity to go back into our store, add or remove items.
And when they come back into the checkout, they'll once again hit the endpoint that uses this service
to update the payment intent.
That's the idea here.
Inside this, we're going to use another setting from the stripe package.
And this is going to be for our stripe configuration and the API key.
And we're going to set this to config.
And in square brackets we need to access the stripe settings colon secrets key.
And it's one of those pesky strings.
And once again your typo warning is here.
Please double check that this matches what you've used inside your app settings, whether you've used
Appsettings.json or app settings development.
Jason, please double check that string.
The easiest way for this not to work is to have a typo in there.
Then we're going to create a service.
So I'm going to say var service equals new payment intent service.
Something else that we get from stripe.
And then we'll create an intent.
So we'll use var intent equals new payments intent.
Again something we get from stripe.
And then we can work out our subtotal based on what is contained inside the basket.
So I'll specify var subtotal equals basket dot items dot sum.
And then we can work out how much our basket is worth.
So I'll use x goes two x dot quantity multiplied by x dot product dot price.
And that will give us our subtotal.
And then we can work out our delivery fee based on the subtotal.
Because we know if their order is over $100, then it's going to be free.
Otherwise it's going to be $5.
So the delivery fee will be equal to the subtotal.
And we'll check to see if it's greater than 10,000, which means $100.
And if that is the case, we'll set it to be zero.
Otherwise, it's going to be 500.
And then we can check to see if we already have a payment ID set.
Now what we'll need to do next actually is we'll need to go back to our basket entity.
And we're going to add a couple of things inside here related to payments.
So inside the properties for the basket let's add on a prop that's going to be a string and make it
optional.
And this is going to be a client secret that we'll store inside this basket.
And this is what the client will use to communicate directly with stripe without involvement of our
API.
But we need to get the client secret when we create the payment intent.
And once we have the payment intent, stripe will send us back the payment intent.
And that includes the client secret.
So that we can get this back to the client.
We'll include it with our basket.
Even though it says client secret, it doesn't matter that we're sending it back to the client.
It is supposed to be given to the client to communicate with stripe.
And also we'll have a prop that's a string again optional.
And this can be for the payment intent ID.
And the idea of this one being inside our basket as well is if they do update their payment intent,
or they do update their shopping cart or basket and then they come back to the checkout, then we're
going to need to update the payment intent.
Let stripe know of the new amount based on the changes they've made to the basket, and that will update
the payment intent in stripe so that when the customer does go to pay, everything matches.
Because if there is a mismatch between the amount that we said they were intending to pay and then something's
changed, then the payment won't work.
So we have made a change to an entity, which does mean we're going to need to create a new migration,
but we'll come on to that soon.
So now we have that available, we can continue with our payment service.
And we're going to check to see if the string is null or empty for the basket dot payment intent ID
if it is empty, that means it's a new payment intent.
So we'll create the configuration for that.
And we need to create some options.
So I'll say var options equals new payment intent creates options.
And we'll open curly brackets.
And then we'll specify an amount.
And that's going to be equal to the subtotal plus the delivery fee.
We'll also need to specify the currency which we'll set to USD in this case and the payment method types
we will specify in square brackets.
And then quotes is going to be card.
That's the payment type that we are going to accept for our application.
And then we can use intents equals and then use await service and create async to create that payment
intent and we need to pass it the options.
And what this returns to us is a payment intent from stripe.
We'll also add the else condition.
So below that curly brackets we'll add else open curly brackets again and create some options for if
we're updating a payment intent.
So I'll use options here again.
And this will be new payment intent update options.
And all we need to do here is double check the amount and set the amount based on what they've updated
it to.
So we'll use amount equals subtotal plus delivery fee inside here.
And we don't need to set anything else because the currency and payment method types well they're not
going to change.
And then we can use await service and update async.
And we'll pass in the basket and the payment intent ID and the options for this method.
and below the else statement we can return the intent.
And that's the code for the service complete.
And because it is a service, then we're going to add this to our program class so that we can use it
inside our controller and inject it inside our controller.
So we'll open up the program class next.
And inside here inside where we're defining our services.
And because it's a service we can put it wherever we want.
I'll just add it below the middleware because I feel that's the cleanest tidiest place to add it, not
because I have to put it here.
And then I'll specify builder services.
And this one we're going to use add scoped.
And remember the scoped is scoped to the HTTP request.
So when the HTTP request is received and it's going to the payments controller that we don't have yet,
the payments controller will have this payment service injected into it.
And when the payment controller is created by the framework, then it also creates a new instance of
our payment service that we're adding inside here.
And because we've updated our basket entity, that means we also need a new migration.
So I'm just going to open up the terminal.
Go back to the tab where I'm running the API.
I'm just going to stop it and clear this window and say dotnet new or dotnet f migrations ads and just
say basket entity updated and press return.
And this should work without issue and it has done so.
Let's go and check the new migration.
Just make sure we see what we expect to see.
Please always do this.
Please don't assume that just because it looks like it worked, it has worked.
And what we'd expect to see in here is the two columns being added, the client secret and the payment
intent ID.
So that looks good.
And the next thing we'll take a look at is adding a payments controller that makes use of this service.