mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-09-08 00:45:41 +00:00
feat: ( telebirr ) implement provider, signing, and webhook handler
This commit is contained in:
@@ -1,13 +1,15 @@
|
||||
import { Injectable, NotFoundException, BadRequestException } from '@nestjs/common';
|
||||
import { Injectable, Logger, NotFoundException, BadRequestException } from '@nestjs/common';
|
||||
import { PrismaService } from '../../common/prisma.service';
|
||||
import { SeatsService } from '../seats/seats.service';
|
||||
import { TicketsService } from '../tickets/tickets.service';
|
||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||
import { PaymentIntentStatus } from '@prisma/client';
|
||||
import { InitiatePaymentDto, RefundDto, AddPaymentMethodDto } from './payments.dto';
|
||||
import { telebirrAdapter, cbeBirrAdapter, eBirrAdapter, cardAdapter } from './payments.adapters';
|
||||
|
||||
@Injectable()
|
||||
export class PaymentsService {
|
||||
private readonly logger = new Logger(PaymentsService.name);
|
||||
constructor(
|
||||
private prisma: PrismaService,
|
||||
private seatsService: SeatsService,
|
||||
@@ -69,6 +71,76 @@ export class PaymentsService {
|
||||
|
||||
getPaymentMethods(userId: string) { return this.prisma.paymentMethod.findMany({ where: { userId }, orderBy: { isDefault: 'desc' } }); }
|
||||
|
||||
async finalizePaymentSuccess(input: {
|
||||
intentId: string;
|
||||
providerTxnId?: string;
|
||||
paidAt?: Date;
|
||||
}): Promise<{ alreadyFinalized: boolean }> {
|
||||
const intent = await this.prisma.paymentIntent.findUnique({
|
||||
where: { id: input.intentId },
|
||||
});
|
||||
if (!intent) throw new NotFoundException('PaymentIntent not found');
|
||||
if (intent.status === PaymentIntentStatus.SUCCEEDED) {
|
||||
return { alreadyFinalized: true };
|
||||
}
|
||||
if (intent.status === PaymentIntentStatus.CANCELLED) {
|
||||
throw new BadRequestException('PaymentIntent is cancelled; cannot finalize');
|
||||
}
|
||||
|
||||
const booking = await this.prisma.booking.findUnique({
|
||||
where: { id: intent.bookingId },
|
||||
include: { seats: true },
|
||||
});
|
||||
if (!booking) throw new NotFoundException('Booking not found');
|
||||
|
||||
const paidAt = input.paidAt ?? new Date();
|
||||
await this.prisma.$transaction(async (tx) => {
|
||||
await tx.paymentIntent.update({
|
||||
where: { id: intent.id },
|
||||
data: {
|
||||
status: PaymentIntentStatus.SUCCEEDED,
|
||||
providerTxnId: input.providerTxnId ?? intent.providerTxnId ?? undefined,
|
||||
paidAt,
|
||||
},
|
||||
});
|
||||
await tx.booking.update({
|
||||
where: { id: booking.id },
|
||||
data: { status: 'CONFIRMED' },
|
||||
});
|
||||
});
|
||||
|
||||
await this.seatsService.confirmSeats(booking.seats.map((s) => s.seatId));
|
||||
await this.ticketsService.generate(booking.id);
|
||||
await this.awardLoyaltyPoints(booking.passengerId, booking.totalMinor, booking.id);
|
||||
this.eventEmitter.emit('payment.succeeded', { booking });
|
||||
return { alreadyFinalized: false };
|
||||
}
|
||||
|
||||
async markPaymentFailed(input: {
|
||||
intentId: string;
|
||||
failureCode?: string;
|
||||
failureMessage?: string;
|
||||
}): Promise<void> {
|
||||
const intent = await this.prisma.paymentIntent.findUnique({
|
||||
where: { id: input.intentId },
|
||||
});
|
||||
if (!intent) throw new NotFoundException('PaymentIntent not found');
|
||||
if (
|
||||
intent.status === PaymentIntentStatus.SUCCEEDED ||
|
||||
intent.status === PaymentIntentStatus.CANCELLED
|
||||
) {
|
||||
return;
|
||||
}
|
||||
await this.prisma.paymentIntent.update({
|
||||
where: { id: intent.id },
|
||||
data: {
|
||||
status: PaymentIntentStatus.FAILED,
|
||||
failureCode: input.failureCode,
|
||||
failureMessage: input.failureMessage,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
private async awardLoyaltyPoints(passengerId: string, amountMinor: number, bookingId: string) {
|
||||
const points = Math.floor(amountMinor / 100);
|
||||
const account = await this.prisma.loyaltyAccount.findUnique({ where: { passengerId } });
|
||||
|
||||
Reference in New Issue
Block a user