Okay, so we're ready to start creating our new microservice for processing payments for our reservations.
We are going to use the Stripe API to actually bill customers that book reservations using provided
payment details.
We will sign up for Stripe and actually make HTTP calls to the API in this microservice, which will
be calling from the reservation service when a new reservation is created.
So let's go ahead and get started.
Back in the terminal we're going to use the nest CLI as usual to to generate a new app by calling nest
generate app.
And we'll call this payments.
And you can see our newly created payments application was added to our app here and we have the default
controller out of the box here as well as the basic payments module that sets up the payments controller.
So the first thing we're going to do, as we did in our reservations and auth services, we're going
to go ahead and copy over the Docker file from the Reservations app and we'll place it inside of the
newly created payments app.
And the only change we'll make here is in our start up command.
To start the app, we will go into the Dist apps, slash payments folder.
All the rest of the steps will stay the same now.
So now we'll go back in the Docker compose file in the root of our app and we'll just copy over the
existing definition for the auth service, paste it in here and rename this to the payments service.
The contacts is still going to be in the route, except the Docker file now is going to live in the
payments directory and we're going to change the start up command here to NPM run dev payments.
We'll change the file path here to go to slash payments.
And then lastly, we're going to go ahead and expose Port 3003 for the payments microservice, which
will be a TCP microservice.
So we need to specify a TCP port.
We know 3000 is already taken by reservations and an auth.
We have 3001 and 3002 already taken.
So we'll go ahead and map local port 3003 to the TCP port on this microservice 3003 now in the payments
folder, we'll go ahead and create a m file here where we specify a port of 3003.
Now back in the terminal, I'll go ahead and Docker compose up again here where you can see we're going
ahead and starting to build the new payments Docker file.
So after the Docker file is finished building, you can see in our terminal here that our payment service
has started up successfully and the rest of the services now start up being starting up as well.
So now we can go back into the payments directory and we'll open up the Main.ts first where we will
turn this into a microservice, a standalone microservice.
So we'll delete the existing app const here and create a new one where we call Nest await nest factory
dot, create microservice.
And the first argument here will be the payments module as well as the actual options for this microservice.
Firstly, we're going to specify the transport here, which will be transport dot TCP because this is
a TCP microservice.
And then just like we did for the auth microservice, I want to specify the port here out of the config
service which pulls it from the dot m file for the service.
So let's set up our config module in the payments module.
Now we'll go back into the auth module, I'll copy the config module set up here, move it to the payments
module now.
And so we'll go ahead and import the config module from nestjs config as well as importing star as joy
from joy here where we get the schema validation, we're setting the config module to globally here
being available globally and the schema itself is going to specify just the port as we know it.
So we'll go ahead and delete the rest of the entries here and change TCP port to be just port here,
which is a required number.
So now if we follow this approach of creating the microservice, we can't pull the config service out
to get the port environment variable.
So we'll have to follow the same pattern we used for the auth service here where we created the app
and then called get config service.
So let's go ahead and delete the existing app again and we'll call wait nest factory dot create where
we just pass in the payments module.
Then we'll get access to the config service directly by calling app dot get and then passing in the
config service from nestjs config.
And lastly, we'll call app-connect microservice where we'll specify the options here.
The transport again will be transport dot TCP and then we'll have the options object here where we'll
specify.
By the host that we know will be on 0000 to bind to all available IP addresses on the machine and then
the port will come off of config service dot get and we will pull the port off of the config service
like so finally, instead of calling app dot, listen here, we'll change this and call app dot start
all microservices to start up this TCP microservice.
If you look in the terminal, you should see now that the payments logger says that the microservice
successfully started and we'll also go back in the payment module and make sure we import the logger
module from our app slash common directory up top here so that now our logs are nicely formatted with
the correlation ID and other context information from the peanut logger.
And then also don't forget in our main.ts we need to actually call app dot use logger and we pass an
app dot get and pass in the logger from Nestjs Pino here to make sure that our payments app will use
the peanut logger.
And now we should see the payment service using the peanut logger here.
Okay.
So our next step is to actually set up a stripe account so that we can get some API keys and actually
interact with their API and bill our users in our application.
So make sure you head over to Stripe.com and sign up for a new account.
After you sign up for a new account, you'll be able to create a new account within this dashboard here.
I've called Mine here Sleeper and on Sleeper, you can see right now we're in test mode, which will
allow us to make test charges and test this account in a sandbox environment.
And we also have keys down here.
So we have a publishable key as well as a secret key, which we will be using to interact with our API
on our back end.
So after we have our account set up, we are going to go ahead and install the NodeJS Stripe API client
library which will allow us to easily interact with the Stripe API.
So I'll go ahead and do a NPM install stripe here to go ahead and install Stripe and then we'll go ahead
and start our app back up here.
So now inside of our payments folder here in our payments service, let's go ahead and instantiate our
Stripe client by setting up a new private read only variable here that's going to be equal to a new
stripe here.
And we're going to go ahead and import Stripe from Stripe in our imports up top and by default we can
see we need to supply our API key.
So let's go ahead and in our dot env, we're going to set a new environment variable here called Stripe
secret key.
And we are going to pull this secret key off of our stripe dashboard.
So you can go ahead and reveal this secret key here, copy it and paste it in.
So make sure you use your own secret key here because this one will be deactivated.
So now that we have a stripe secret key, let's go into our payments module and firstly make sure that
we're validating that we actually have the Stripe secret key now, which is going to be joy dot string
dot required.
And now in our payment service, let's go ahead.
In our constructor, we'll get access to the private read only config service of type config service
and now we'll move our variable here which we actually need to call Stripe and we'll set this up top
and set it to new stripe.
And now we can use this dot config service dot get and pass in the stripe secret key as our secret key
here.
Parameter here is going to be an options object where we have to specify the API version and this is
going to be the latest version on this current installation.
So it's 2022, 1115 and so now we have our Stripe client fully configured.
If we get some environment validation error here in your back end, make sure you just restart it so
it picks up the new stripe secret key.
Okay, so we're now ready to actually create a charge on a customer's credit card with our Stripe API.
So let's go ahead and in the payment service we'll get rid of the get hello route here and we'll also
go ahead and remove this from the payments controller.
Let's go ahead and remove this route here.
And now we'll go ahead and actually create a new async method called Create charge.
And before we fill out the parameter.
Matters here.
Let's actually look at what we're going to be doing here.
So we're going to be using the payment intents API on Stripe, which is the most modern way to create
a new charge for a customer's credit card.
This is a way we can securely transmit credit card information over the wire and create a one time charge.
So to start, we need to create a new payment method.
So let's go ahead and do that by having a new variable here called payment method.
And we're going to go ahead and call await this dot stripe dot payment methods, and then we're going
to go ahead and create a new payment method here.
So we're going to specify the type here of type card.
And then the next object here is going to be the actual card itself.
And inside of the card object here, we can go ahead and look at the expected card type from the stripe
types here.
So let's go ahead and look at what this will look like.
So you can see all the information here that we're going to need for a credit card, the CVC expiration
expiration year and the number of the card.
So let's go ahead and specify this as the first parameter for our Create charge method.
It's going to be of type card, and this type is going to be the same type of the card here.
It's going to be stripe payment method, create params and we can just go ahead and actually copy this
here and then paste it in here as our parameter stripe payment method.
Create params dot card one.
So we'll go ahead and provide this card as the card property in our payment method here and make sure
we correctly spell payment method.
Okay.
So now that we have the card or the payment method, we're going to go ahead and actually create the
payment intent.
So let's go ahead and create a new payment intent variable that will set equal to await this dot stripe
dot payment intents and we'll call create here where we will pass in the payment method.
And this is where we pass in our payment method.id here so that it will reference this payment method
or this card and charge it appropriately.
So next, we need to supply the amount that we want to charge this particular customer.
And to do that we'll take a new amount parameter here of type number.
And now that we have the amount, we'll supply it here and we multiply this amount times 100 because
by default this will be the smallest unit of currency.
So 100 cents to charge $1.
So it will multiply every supplied value by 100 to get the dollar amount.
Next, we need to supply confirm here and set this to true.
And what this will do is it will immediately confirm the payment intent so that we charge the user right
away.
And then lastly, we're going to have a payment method types option here where we specify the payment
method, type in this case card.
And now I'm also going to supply the currency here.
I'm going to use USD for the currency.
You can feel free to use whatever currency you'd like.
So now that we've successfully charged the user, let's go ahead and simply return the payment intent
back to the caller now.
So now we have everything we need to set up a payment intent and actually bill a user's credit card.
So we know we want to expose this payment service API to our other microservices, particularly our
reservation service.
So as we've done before in our service, in order to expose this microservice functionality, we'll
have a new async route in our payments controller here that we'll go ahead and call create charge.
And this will of course be decorated with the message pattern decorator.
Here we will specify the message pattern of create charge.
Next, we're going to go ahead and extract the payload here, which in this case is what's being passed
to this message pattern.
So we'll go ahead and call this the data and we can actually supply a dedicated type here.
I'll go ahead and create a new DTO folder and inside of the DTO folder, I'll go ahead and have a new
create charged DTO dots and we'll go ahead and export this class of create charge.
DTO now we know the two parameters that the create charge DTO will take and we'll actually take this
from our create charge method where we have these two properties and we'll import Stripe here from Stripe
and we'll just add these two properties to our class here.
So now I'll also go back to the Create charge method and in here we can now just pull the card and the
amount directly off of this type object here of type.
Create charge DTO just to make this a bit cleaner.
And then in the payments controller here, we know that the data will be of type create charge.
DTO and I will return this dot payment service dot create charge and I'll go ahead and pass that data
directly into that method.