Introduction:
In this tutorial, we will continue our journey of building a microservice-based reservation system with payment integration using NestJS. We've previously covered setting up the communication between the reservation and payment services. In this post, we will focus on billing the user and implementing validation.
Billing the User
Before we create a reservation in our database, we want to bill the user using the newly created CreateChargeDTO and our payments service.
update your reservations.service.ts
with the following content:
import { Inject, Injectable } from '@nestjs/common';
import { ReservationsRepository } from './reservations.repository';
import { CreateReservationDto } from './dto/create-reservation.dto';
import { UpdateReservationDto } from './dto/update-reservation.dto';
import { PAYMENTS_SERVICE } from '@app/common';
import { ClientProxy } from '@nestjs/microservices';
import { map } from 'rxjs';
@Injectable()
export class ReservationsService {
constructor(
private readonly reservationsRepository: ReservationsRepository,
@Inject(PAYMENTS_SERVICE) private readonly payment_service: ClientProxy,
) {}
async create(createReservationDto: CreateReservationDto, userId: string) {
return this.payment_service
.send('create_charge', createReservationDto.charge)
.pipe(
map(async () => {
return this.reservationsRepository.create({
...createReservationDto,
timestamp: new Date(),
userId,
});
}),
);
}
async findAll() {
return this.reservationsRepository.find({});
}
async findOne(_id: string) {
return this.reservationsRepository.findOne({ _id });
}
async update(_id: string, updateReservationDto: UpdateReservationDto) {
return this.reservationsRepository.findOneAndUpdate(
{ _id },
{ $set: updateReservationDto },
);
}
async remove(_id: string) {
return this.reservationsRepository.findOneAndDelete({ _id });
}
}
Dynamic Invoice IDs
Instead of hardcoding the invoice ID, let's extract it from the Stripe response, to ensure that it correlates the reservation with the Stripe charge. This dynamic approach ensures accurate invoice tracking.
Update the Create Reservation DTO: (create-reservation.dto.ts
)
import { CreateChargeDto } from '@app/common';
import { Type } from 'class-transformer';
import { IsDate, IsDefined, IsNotEmpty, ValidateNested } from 'class-validator';
export class CreateReservationDto {
@IsDate()
@Type(() => Date)
startDate: Date;
@IsDate()
@Type(() => Date)
endDate: Date;
@Type(() => CreateChargeDto)
@IsDefined()
@IsNotEmpty()
@ValidateNested()
charge: CreateChargeDto;
}
We removed the user-supplied invoice ID field and
eliminate unused fields place ID.
Update the Reservation Schema:(reservation.entity.ts
)
import { AbstractDocument } from '@app/common';
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
@Schema({ versionKey: false })
export class ReservationDocument extends AbstractDocument {
@Prop({ type: Date, default: Date.now })
timestamp: Date;
@Prop({ type: Date, required: true })
startDate: Date;
@Prop({ type: Date, required: true })
endDate: Date;
@Prop({ type: String, required: true })
userId: string;
@Prop({ type: String, required: true })
invoiceId: string;
}
export const ReservationSchema =
SchemaFactory.createForClass(ReservationDocument);
We removed the place ID field since it is not being used and retained the invoice ID field to store the dynamically extracted ID.
Update your reservations.service.ts
with the following content:
import { Inject, Injectable } from '@nestjs/common';
import { ReservationsRepository } from './reservations.repository';
import { CreateReservationDto } from './dto/create-reservation.dto';
import { UpdateReservationDto } from './dto/update-reservation.dto';
import { PAYMENTS_SERVICE } from '@app/common';
import { ClientProxy } from '@nestjs/microservices';
import { map } from 'rxjs';
@Injectable()
export class ReservationsService {
constructor(
private readonly reservationsRepository: ReservationsRepository,
@Inject(PAYMENTS_SERVICE) private readonly payment_service: ClientProxy,
) {}
async create(createReservationDto: CreateReservationDto, userId: string) {
return this.payment_service
.send('create_charge', createReservationDto.charge)
.pipe(
map(async (res) => {
return this.reservationsRepository.create({
...createReservationDto,
timestamp: new Date(),
invoiceId: res.id,
userId,
});
}),
);
}
async findAll() {
return this.reservationsRepository.find({});
}
async findOne(_id: string) {
return this.reservationsRepository.findOne({ _id });
}
async update(_id: string, updateReservationDto: UpdateReservationDto) {
return this.reservationsRepository.findOneAndUpdate(
{ _id },
{ $set: updateReservationDto },
);
}
async remove(_id: string) {
return this.reservationsRepository.findOneAndDelete({ _id });
}
}
- When creating a reservation, we send a request to Stripe to create a payment intent.
- In the response from Stripe, we extract the payment intent ID, which serves as the invoice ID.
- We use this extracted invoice ID to correlate the reservation with the Stripe charge.
Conclusion:
In this tutorial, we've extended our microservice-based reservation system with payment integration, focusing on billing the user and implementing robust validation. We've also shown how to handle unauthorized requests and dynamically extract invoice IDs for precise tracking. Furthermore, by using default test cards, we ensure secure payment processing. This comprehensive guide will help you build a robust and secure reservation system with payment capabilities using NestJS. Happy coding!
Top comments (0)