12. Payment Processing with Stripe API
Published: 2023-11-07 03:28:11

## 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](https://stripe.com/), 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.

1. We created a payment intent with the payment method and charge amount.
2. We set the confirm the payment intent to true to charge the user immediately.
3. 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.