From 5bb18afa11fe9462e2dd60aac6683a45d788408d Mon Sep 17 00:00:00 2001 From: morteza-mortezai Date: Tue, 23 Dec 2025 10:16:45 +0330 Subject: [PATCH] online order notif after success --- .../orders/listeners/order.listeners.ts | 7 ++ src/modules/payments/events/payment.events.ts | 9 ++ .../payments/listeners/payment.listeners.ts | 104 +++++++++++++++++- .../payments/services/payments.service.ts | 14 ++- 4 files changed, 127 insertions(+), 7 deletions(-) diff --git a/src/modules/orders/listeners/order.listeners.ts b/src/modules/orders/listeners/order.listeners.ts index e654b7d..4298611 100644 --- a/src/modules/orders/listeners/order.listeners.ts +++ b/src/modules/orders/listeners/order.listeners.ts @@ -8,6 +8,7 @@ import { NotificationService } from 'src/modules/notifications/services/notifica import { ConfigService } from '@nestjs/config'; import { OrderRepository } from '../repositories/order.repository'; import { OrderStatus } from '../interface/order.interface'; +import { PaymentMethodEnum } from 'src/modules/payments/interface/payment'; @Injectable() export class OrderListeners { @@ -44,11 +45,17 @@ export class OrderListeners { this.logger.log( `Order created event received: ${event.orderId} for restaurant: ${event.restaurantId} and order number: ${event.orderNumber}`, ); + + const order = await this.OrderRepository.findOne(event.orderId); + if (order?.paymentMethod.method === PaymentMethodEnum.Online) { + return; + } // get admnin os restuaraant that have order permissuins const admins = await this.adminService.findAdminsWithPermission(event.restaurantId, Permission.MANAGE_ORDERS); const recipients = admins.map(admin => ({ adminId: admin.id, })); + await this.notificationService.sendNotification({ restaurantId: event.restaurantId, message: { diff --git a/src/modules/payments/events/payment.events.ts b/src/modules/payments/events/payment.events.ts index c43e9c5..4759143 100644 --- a/src/modules/payments/events/payment.events.ts +++ b/src/modules/payments/events/payment.events.ts @@ -11,3 +11,12 @@ export class paymentSucceedEvent { } + +export class onlinePaymentSucceedEvent { + constructor( + public readonly paymentId: string, + public readonly restaurantId: string, + public readonly orderNumber: string, + public readonly total: number, + ) { } +} \ No newline at end of file diff --git a/src/modules/payments/listeners/payment.listeners.ts b/src/modules/payments/listeners/payment.listeners.ts index 2ffa29e..9181130 100644 --- a/src/modules/payments/listeners/payment.listeners.ts +++ b/src/modules/payments/listeners/payment.listeners.ts @@ -1,23 +1,25 @@ import { Injectable, Logger } from '@nestjs/common'; import { OnEvent } from '@nestjs/event-emitter'; import { AdminRepository } from 'src/modules/admin/repositories/admin.repository'; -import { paymentSucceedEvent } from '../events/payment.events'; +import { onlinePaymentSucceedEvent, paymentSucceedEvent } from '../events/payment.events'; import { Permission } from 'src/common/enums/permission.enum'; import { NotifTitleEnum } from 'src/modules/notifications/interfaces/notification.interface'; import { NotificationService } from 'src/modules/notifications/services/notification.service'; import { ConfigService } from '@nestjs/config'; +import { PaymentMethodEnum } from '../interface/payment'; +import { OrdersService } from 'src/modules/orders/providers/orders.service'; @Injectable() export class PaymentListeners { private readonly logger = new Logger(PaymentListeners.name); - private paymentSuccessSmsTemplateId: string; + private orderCreatedSmsTemplateId: string; constructor( private readonly adminService: AdminRepository, private readonly notificationService: NotificationService, private readonly configService: ConfigService, - // private readonly orderSer + private readonly orderService: OrdersService, ) { - this.paymentSuccessSmsTemplateId = this.configService.get('SMS_PATTERN_PAYMENT_SUCCESS') ?? '123'; + this.orderCreatedSmsTemplateId = this.configService.get('SMS_PATTERN_ORDER_CREATED') ?? '123'; } private getPaymentMethodFarsi(method: string): string { @@ -47,10 +49,10 @@ export class PaymentListeners { title: NotifTitleEnum.PAYMENT_SUCCESS, content: `پرداخت سفارش شماره ${event.orderNumber} با روش ${this.getPaymentMethodFarsi(event.paymentMethod)} و مبلغ ${event.total} تومان با موفقیت انجام شد`, sms: { - templateId: this.paymentSuccessSmsTemplateId, + templateId: this.orderCreatedSmsTemplateId, parameters: { orderNumber: event.orderNumber, - + }, }, pushNotif: { @@ -76,4 +78,94 @@ export class PaymentListeners { } } + @OnEvent(onlinePaymentSucceedEvent.name) + async handleOnlinePaymentSucceed(event: onlinePaymentSucceedEvent) { + try { + + this.logger.log( + `Online payment succeed event received: ${event.paymentId} for restaurant: ${event.restaurantId} and order number: ${event.orderNumber}`, + ); + const admins = await this.adminService.findAdminsWithPermission(event.restaurantId, Permission.MANAGE_ORDERS); + const recipients = admins.map(admin => ({ + adminId: admin.id, + })); + + + await this.notificationService.sendNotification({ + restaurantId: event.restaurantId, + message: { + title: NotifTitleEnum.ORDER_CREATED, + content: `سفارش شماره ${event.orderNumber} با مبلغ ${event.total} تومان با موفقیت ایجاد شد`, + sms: { + templateId: this.orderCreatedSmsTemplateId, + parameters: { + orderNumber: event.orderNumber, + total: event.total.toString(), + }, + }, + pushNotif: { + title: `سفارش جدید`, + content: `سفارش شماره ${event.orderNumber} با مبلغ ${event.total} تومان با موفقیت ایجاد شد`, + icon: `/`, + action: { + type: NotifTitleEnum.ORDER_CREATED, + url: `/`, + }, + }, + }, + recipients, + metadata: { + priority: 1, + }, + }); + + const order = await this.orderService.findOne(event.paymentId, event.restaurantId); + if (!order) { + this.logger.error( + `Order not found: ${event.paymentId} for restaurant: ${event.restaurantId}`, + ); + return; + } + if (order.paymentMethod.method === PaymentMethodEnum.Online) { + return; + } + const userRecipients = [ + { + userId: order.user.id, + }, + ]; + await this.notificationService.sendNotification({ + restaurantId: event.restaurantId, + message: { + title: NotifTitleEnum.PAYMENT_SUCCESS, + content: `پرداخت سفارش شماره ${order.orderNumber} با روش ${this.getPaymentMethodFarsi(order.paymentMethod.method)} و مبلغ ${order.total} تومان با موفقیت انجام شد`, + sms: { + templateId: this.orderCreatedSmsTemplateId, + parameters: { + orderNumber: String(order.orderNumber), + total: order.total.toString(), + }, + }, + pushNotif: { + title: `پرداخت موفق`, + content: `پرداخت سفارش شماره ${order.orderNumber} با روش ${this.getPaymentMethodFarsi(order.paymentMethod.method)} و مبلغ ${order.total} تومان با موفقیت انجام شد`, + icon: `/`, + action: { + type: NotifTitleEnum.PAYMENT_SUCCESS, + url: `/`, + }, + }, + }, + recipients: userRecipients, + metadata: { + priority: 1, + }, + }); + } catch (error) { + this.logger.error( + `Failed to send notification for online payment succeed event: ${event.restaurantId}`, + error instanceof Error ? error.stack : String(error), + ); + } + } } diff --git a/src/modules/payments/services/payments.service.ts b/src/modules/payments/services/payments.service.ts index 0d9af49..940d8be 100644 --- a/src/modules/payments/services/payments.service.ts +++ b/src/modules/payments/services/payments.service.ts @@ -10,6 +10,8 @@ import { OrderPaymentContext } from '../interface/payment'; import { OrderStatus } from 'src/modules/orders/interface/order.interface'; import { ChartPeriodEnum, PaymentChartDto } from '../dto/payment-chart.dto'; import { PaymentMessage, OrderMessage } from 'src/common/enums/message.enum'; +import { EventEmitter2 } from '@nestjs/event-emitter'; +import { onlinePaymentSucceedEvent, paymentSucceedEvent } from '../events/payment.events'; @Injectable() export class PaymentsService { @@ -18,6 +20,7 @@ export class PaymentsService { constructor( private readonly em: EntityManager, private readonly gatewayManager: GatewayManager, + private readonly eventEmitter: EventEmitter2, ) { } async payOrder(orderId: string): Promise<{ paymentUrl: string | null }> { @@ -127,6 +130,10 @@ export class PaymentsService { em.persist([wallet, payment, order]); await em.flush(); }); + this.eventEmitter.emit( + paymentSucceedEvent.name, + new paymentSucceedEvent(ctx.order.id, ctx.order.restaurant.id, String(ctx.order?.orderNumber) || '', ctx.method, ctx.amount), + ); } private async handleOnlinePayment(ctx: OrderPaymentContext): Promise<{ paymentUrl: string }> { @@ -162,7 +169,7 @@ export class PaymentsService { } async verifyPayment(transactionId: string, orderId: string): Promise { - return this.em.transactional(async em => { + const payment =await this.em.transactional(async em => { const payment = await em.findOne( Payment, { transactionId, order: { id: orderId } }, @@ -202,6 +209,11 @@ export class PaymentsService { await em.flush(); return payment; }); + this.eventEmitter.emit( + onlinePaymentSucceedEvent.name , + new onlinePaymentSucceedEvent(payment.id, payment.order.restaurant.id, String(payment.order?.orderNumber) || '', payment.amount), + ); + return payment; } findAllByRestaurantId(restId: string, userId: string) {