Introduction
In today's tutorial, we will guide you through creating a microservice for processing payments in your reservation system using the Stripe API. This microservice will handle the task of billing customers when they book reservations, and we'll take you through each step to set it up.
Let's get started!
Step 1: Creating a New NestJS App
To kick things off, we'll use the Nest CLI to generate a new app. Open your terminal and run the following command:
nest generate app payments
This command creates a new NestJS application named "payments" for handling payments in your reservation system. It includes a default controller and a basic payments module.
Step 2: Docker Configuration
Next, we'll set up Docker for this microservice. I will not explain this code because we already tackled it in our previous tutorials
touch apps/payments/Dockerfile
it will create a Dockerfile inside the payments
folder now populate it with this content:
# Use Node Alpine as the base image for development
FROM node:alpine as development
WORKDIR /usr/src/app
COPY package.json package-lock.json ./
RUN npm install -g && npm install
COPY . .
RUN npm run build
# Create a production image
FROM node:alpine as production
ARG NODE_ENV=production
ENV NODE_ENV=${NODE_ENV}
WORKDIR /usr/src/app
COPY package.json package-lock.json ./
RUN npm install --only=production
COPY --from=development /usr/src/app/dist ./dist
COPY --from=development /usr/src/app/package.json ./package.json
CMD [ "node", "dist/apps/payments/main" ]
What we did was just Copy the Docker file from your reservations app and place it in the payments app folder. In the Docker file, we changed the startup command to point to the dist/apps/payments folder.
Update docker-compose.yaml - Now, update your Docker Compose file to include the payments service. Make sure to specify a unique port (e.g., 3003) and expose it for the payments microservice.
Open your docker-compose file and update it with the following content to include payment service:
services:
reservations:
build:
context: .
dockerfile: apps/reservations/Dockerfile
target: development
command: npm run start:dev reservations
env_file:
- ./apps/reservations/.env
ports:
- '3000:3000'
volumes:
- .:/usr/src/app
auth:
build:
context: .
dockerfile: apps/auth/Dockerfile
target: development
command: npm run start:dev auth
env_file:
- apps/auth/.env
ports:
- '3001:3001'
volumes:
- .:/usr/src/app
payments:
build:
context: .
dockerfile: apps/payments/Dockerfile
target: development
command: npm run start:dev payments
env_file:
- apps/payments/.env
ports:
- '3003:3003'
volumes:
- .:/usr/src/app
mongo:
image: mongo
Step 3: Configuring the Microservice
In the payments folder, open the main.ts file and turn this app into a standalone microservice. Update it with the following content:
import { NestFactory } from '@nestjs/core';
import { PaymentsModule } from './payments.module';
import { Logger } from 'nestjs-pino';
import { Transport } from '@nestjs/microservices';
import { ConfigService } from '@nestjs/config';
async function bootstrap() {
const app = await NestFactory.create(PaymentsModule);
const configService = app.get(ConfigService);
app.connectMicroservice({
transport: Transport.TCP,
options: {
host: '0.0.0.0',
port: configService.get('PORT'),
},
});
app.useLogger(app.get(Logger));
await app.startAllMicroservices();
}
bootstrap();
We just configure the transport as "transport.TCP" and set the port using the config service. But since we are using a PORT environment variable which does not yet exist in the payments service we should now create a .env file and specify the PORT variables
touch apps/payments/.env
populate it with the following code:
PORT=3003
Then on your config module in the payments module (payments.module.ts
) set it up as we did for the auth service. This allows you to access and validate the PORT environment variable as follows:
import { Module } from '@nestjs/common';
import { PaymentsController } from './payments.controller';
import { PaymentsService } from './payments.service';
import * as Joi from 'joi';
import { ConfigModule } from '@nestjs/config';
import { LoggerModule } from '@app/common';
@Module({
imports: [
LoggerModule,
ConfigModule.forRoot({
isGlobal: true,
validationSchema: Joi.object({
PORT: Joi.number().required(),
STRIPE_SECRET_KEY: Joi.string().required(), //we will set up this on step 4
}),
}),
],
controllers: [PaymentsController],
providers: [PaymentsService],
})
export class PaymentsModule {}
Step 4: Setting up the Stripe API
Before you can start processing payments, you need to set up a Stripe account. Head to Stripe's website, sign up for an account, and create an account within your dashboard.
In your Stripe dashboard, you will find your publishable key
and secret key
. We will use the secret key
for API calls, so make sure to set it as an environment variable in your .env file.
update your payments .env
file and include your secret_key which you got from your stripe dashboard:
PORT=3003
STRIPE_SECRET_KEY=sk_test_51NZp96JyPkGHZlunZYLEUwARyNBuxHbJ3tVFat6XtJxSxLwhJgIxMdeC4wtt42WkrWCM3H20yRVeMkc9SW38Uprt00xPMCJIDR
Make sure to use your own secret key, the secret key I provided is deactivated
Next, install the Node.js Stripe API client library with the following command:
npm install stripe
Step 5: Initializing the Stripe Client
let's create a DTO (Data Transfer Object) first
mkdir apps/payments/src/dto
touch apps/payments/src/dto/create-charge.dto.ts
it will create a new directory named dto
inside your payments folder then inside dto
it will create a file named create-charge.dto.ts
populate the create-charge.dto.ts with this content:
import Stripe from 'stripe';
export class CreateChargeDto {
card: Stripe.PaymentMethodCreateParams.Card1;
amount: number;
}
We can now use this CreatechargeDto to our controller and service
Open your payments.service.ts
and populate it with the following content:
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import Stripe from 'stripe';
import { CreateChargeDto } from './dto/create-charge.dto';
@Injectable()
export class PaymentsService {
private readonly stripe = new Stripe(
this.configService.get('STRIPE_SECRET_KEY'),
{
apiVersion: '2023-10-16',
},
);
constructor(private readonly configService: ConfigService) {}
async createCharge({ amount }: CreateChargeDto) {
const paymentIntent = await this.stripe.paymentIntents.create({
amount: amount * 100,
automatic_payment_methods: {
enabled: true,
allow_redirects: 'never',
},
confirm: true,
payment_method: 'pm_card_visa',
currency: 'usd',
});
return paymentIntent;
}
}
In the payments service, we created a private read-only variable to initialize the Stripe client using your secret key from the config service. and we set up the API version as well.
We also create a payment charge. using the Payment Intents API provided by Stripe for secure payment processing. We created a new payment method and then charged the user.
- We created a payment intent with the payment method and charge amount.
- We set the confirm the payment intent to true to charge the user immediately.
- We add an async method called** createCharge** to handle payment charges. Define the parameters, such as the card details and the amount to charge.
Step 7: Exposing the Payment Service
To make the payment service accessible to other microservices, set up a new async route in the payments controller decorated with the @MessagePattern decorator. Extract the payload and define a dedicated DTO for the data being passed to the create charge method.
Open your payment.controller.ts then update its content with this code:
import { Controller, UsePipes, ValidationPipe } from '@nestjs/common';
import { PaymentsService } from './payments.service';
import { MessagePattern, Payload } from '@nestjs/microservices';
import { CreateChargeDto } from '@app/common';
@Controller()
export class PaymentsController {
constructor(private readonly paymentsService: PaymentsService) {}
@MessagePattern('create_charge')
@UsePipes(new ValidationPipe())
async createcharge(@Payload() data: CreateChargeDto) {
return this.paymentsService.createCharge(data);
}
}
We use NestJS's built-in validation pipes
to validate the incoming metadata on the payload.
Conclusion
Congratulations! You've successfully created a microservice for processing payments using the Stripe API. This service can be integrated into your reservation system to handle payment processing securely. With the right configuration and integration, your application is ready to bill customers for their reservations.
In the next lessons, we will integrate and test it with our reservations.
Top comments (0)