178 lines
4.7 KiB
TypeScript
178 lines
4.7 KiB
TypeScript
import { Injectable } from '@nestjs/common';
|
|
import { Prisma, PrismaClient } from '@prisma/client';
|
|
import crypto from 'crypto';
|
|
import Razorpay from 'razorpay';
|
|
|
|
import ApiError from '../../../common/utils/helper/ApiError';
|
|
|
|
const razorpay = new Razorpay({
|
|
key_id: process.env.RAZORPAY_KEY_ID!,
|
|
key_secret: process.env.RAZORPAY_KEY_SECRET!,
|
|
});
|
|
|
|
@Injectable()
|
|
export class PaymentService {
|
|
constructor(private prisma: PrismaClient) {}
|
|
|
|
async createOrder(
|
|
userXid: number,
|
|
payload: {
|
|
amount: number;
|
|
currency?: string;
|
|
receipt?: string;
|
|
notes?: Record<string, string | number | boolean | null>;
|
|
itineraryHeaderXid?: number;
|
|
},
|
|
) {
|
|
if (!process.env.RAZORPAY_KEY_ID || !process.env.RAZORPAY_KEY_SECRET) {
|
|
throw new ApiError(500, 'Razorpay credentials are not configured.');
|
|
}
|
|
|
|
if (!Number.isFinite(payload.amount) || payload.amount <= 0) {
|
|
throw new ApiError(400, 'amount must be a positive number.');
|
|
}
|
|
|
|
if (
|
|
payload.itineraryHeaderXid !== undefined &&
|
|
payload.itineraryHeaderXid !== null
|
|
) {
|
|
const itineraryHeader = await this.prisma.itineraryHeader.findFirst({
|
|
where: {
|
|
id: payload.itineraryHeaderXid,
|
|
ownerXid: userXid,
|
|
isActive: true,
|
|
deletedAt: null,
|
|
},
|
|
select: {
|
|
id: true,
|
|
},
|
|
});
|
|
|
|
if (!itineraryHeader) {
|
|
throw new ApiError(
|
|
404,
|
|
'Itinerary not found for the logged-in user.',
|
|
);
|
|
}
|
|
}
|
|
|
|
const amountInPaise = Math.round(payload.amount * 100);
|
|
const receipt = payload.receipt ?? `receipt_${Date.now()}`;
|
|
const currency = payload.currency ?? 'INR';
|
|
|
|
const order = (await razorpay.orders.create({
|
|
amount: amountInPaise,
|
|
currency,
|
|
receipt,
|
|
notes: payload.notes ?? undefined,
|
|
} as any)) as any;
|
|
|
|
const paymentOrder = await this.prisma.paymentOrders.create({
|
|
data: {
|
|
userXid,
|
|
itineraryHeaderXid: payload.itineraryHeaderXid ?? null,
|
|
razorpayOrderId: order.id,
|
|
receipt: order.receipt ?? receipt,
|
|
amount: order.amount ?? amountInPaise,
|
|
currency: order.currency ?? currency,
|
|
paymentStatus: order.status ?? 'created',
|
|
notes: payload.notes
|
|
? (payload.notes as Prisma.InputJsonValue)
|
|
: null,
|
|
isActive: true,
|
|
},
|
|
});
|
|
|
|
return {
|
|
paymentOrderId: paymentOrder.id,
|
|
orderId: order.id,
|
|
amount: paymentOrder.amount,
|
|
currency: paymentOrder.currency,
|
|
receipt: paymentOrder.receipt,
|
|
status: paymentOrder.paymentStatus,
|
|
};
|
|
}
|
|
|
|
async verifyPayment(
|
|
userXid: number,
|
|
payload: {
|
|
paymentId: string;
|
|
orderId: string;
|
|
signature: string;
|
|
},
|
|
) {
|
|
if (!process.env.RAZORPAY_KEY_SECRET) {
|
|
throw new ApiError(500, 'Razorpay credentials are not configured.');
|
|
}
|
|
|
|
const paymentId = payload.paymentId?.trim();
|
|
const orderId = payload.orderId?.trim();
|
|
const signature = payload.signature?.trim();
|
|
|
|
if (!paymentId || !orderId || !signature) {
|
|
throw new ApiError(
|
|
400,
|
|
'paymentId, orderId and signature are required.',
|
|
);
|
|
}
|
|
|
|
const generatedSignature = crypto
|
|
.createHmac('sha256', process.env.RAZORPAY_KEY_SECRET)
|
|
.update(`${orderId}|${paymentId}`)
|
|
.digest('hex');
|
|
|
|
if (generatedSignature !== signature) {
|
|
throw new ApiError(400, 'Invalid signature.');
|
|
}
|
|
|
|
const paymentOrder = await this.prisma.paymentOrders.findFirst({
|
|
where: {
|
|
razorpayOrderId: orderId,
|
|
userXid,
|
|
isActive: true,
|
|
deletedAt: null,
|
|
},
|
|
});
|
|
|
|
if (!paymentOrder) {
|
|
throw new ApiError(404, 'Payment order not found.');
|
|
}
|
|
|
|
if (
|
|
paymentOrder.paymentStatus === 'paid' &&
|
|
paymentOrder.razorpayPaymentId === paymentId
|
|
) {
|
|
return {
|
|
paymentOrderId: paymentOrder.id,
|
|
orderId: paymentOrder.razorpayOrderId,
|
|
paymentId: paymentOrder.razorpayPaymentId,
|
|
status: paymentOrder.paymentStatus,
|
|
verifiedAt: paymentOrder.verifiedAt,
|
|
paidAt: paymentOrder.paidAt,
|
|
};
|
|
}
|
|
|
|
const updatedPaymentOrder = await this.prisma.paymentOrders.update({
|
|
where: {
|
|
id: paymentOrder.id,
|
|
},
|
|
data: {
|
|
razorpayPaymentId: paymentId,
|
|
razorpaySignature: signature,
|
|
paymentStatus: 'paid',
|
|
verifiedAt: new Date(),
|
|
paidAt: new Date(),
|
|
},
|
|
});
|
|
|
|
return {
|
|
paymentOrderId: updatedPaymentOrder.id,
|
|
orderId: updatedPaymentOrder.razorpayOrderId,
|
|
paymentId: updatedPaymentOrder.razorpayPaymentId,
|
|
status: updatedPaymentOrder.paymentStatus,
|
|
verifiedAt: updatedPaymentOrder.verifiedAt,
|
|
paidAt: updatedPaymentOrder.paidAt,
|
|
};
|
|
}
|
|
}
|