set order status as paid after wallet payment

This commit is contained in:
2025-12-18 19:14:09 +03:30
parent 0362540875
commit add2127998
5 changed files with 28 additions and 41 deletions
@@ -37,8 +37,7 @@ export class OrdersService {
private readonly logger = new Logger(OrdersService.name); private readonly logger = new Logger(OrdersService.name);
private static readonly STATUS_TRANSITIONS: Record<OrderStatus, readonly OrderStatus[]> = { private static readonly STATUS_TRANSITIONS: Record<OrderStatus, readonly OrderStatus[]> = {
[OrderStatus.NEW]: [OrderStatus.PENDING_PAYMENT, OrderStatus.CONFIRMED, OrderStatus.CANCELED], [OrderStatus.PENDING_PAYMENT]: [OrderStatus.PAID, OrderStatus.FAILED, OrderStatus.CANCELED, OrderStatus.CONFIRMED],
[OrderStatus.PENDING_PAYMENT]: [OrderStatus.PAID, OrderStatus.FAILED, OrderStatus.CANCELED],
[OrderStatus.PAID]: [OrderStatus.CONFIRMED, OrderStatus.REFUNDED], [OrderStatus.PAID]: [OrderStatus.CONFIRMED, OrderStatus.REFUNDED],
[OrderStatus.CONFIRMED]: [OrderStatus.PREPARING, OrderStatus.CANCELED], [OrderStatus.CONFIRMED]: [OrderStatus.PREPARING, OrderStatus.CANCELED],
[OrderStatus.PREPARING]: [OrderStatus.READY, OrderStatus.SHIPPED, OrderStatus.CANCELED], [OrderStatus.PREPARING]: [OrderStatus.READY, OrderStatus.SHIPPED, OrderStatus.CANCELED],
@@ -56,7 +55,7 @@ export class OrdersService {
private readonly orderRepository: OrderRepository, private readonly orderRepository: OrderRepository,
private readonly paymentsService: PaymentsService, private readonly paymentsService: PaymentsService,
private readonly inventoryService: InventoryService, private readonly inventoryService: InventoryService,
) { } ) {}
async checkout(userId: string, restaurantId: string) { async checkout(userId: string, restaurantId: string) {
const cart = await this.cartService.findOneOrFail(userId, restaurantId); const cart = await this.cartService.findOneOrFail(userId, restaurantId);
@@ -80,8 +79,7 @@ export class OrdersService {
totalItems: cart.totalItems || 0, totalItems: cart.totalItems || 0,
description: cart.description, description: cart.description,
tableNumber: cart.tableNumber, tableNumber: cart.tableNumber,
status: OrderStatus.NEW, status: OrderStatus.PENDING_PAYMENT,
paymentStatus: PaymentStatusEnum.Pending,
}); });
em.persist(order); em.persist(order);
@@ -35,7 +35,6 @@ export class OrderRepository extends EntityRepository<Order> {
page = 1, page = 1,
limit = 10, limit = 10,
status, status,
paymentStatus,
search, search,
startDate, startDate,
endDate, endDate,
@@ -57,11 +56,6 @@ export class OrderRepository extends EntityRepository<Order> {
where.user = { id: userId }; where.user = { id: userId };
} }
// Filter by payment status
if (paymentStatus) {
where.paymentStatus = paymentStatus;
}
// Filter by date range // Filter by date range
if (startDate || endDate) { if (startDate || endDate) {
where.createdAt = {}; where.createdAt = {};
@@ -52,7 +52,11 @@ export class ZarinpalGateway implements IPaymentGateway {
}; };
try { try {
const res = await axios.post<IZarinpalPaymentResponse>(this.zarinpalRequestUrl, zarinpalRequest, this.axiosConfig); const res = await axios.post<IZarinpalPaymentResponse>(
this.zarinpalRequestUrl,
zarinpalRequest,
this.axiosConfig,
);
const { data, errors } = res.data ?? {}; const { data, errors } = res.data ?? {};
const code = data?.code; const code = data?.code;
const message = data?.message; const message = data?.message;
@@ -112,7 +116,11 @@ export class ZarinpalGateway implements IPaymentGateway {
authority: transactionId, authority: transactionId,
}; };
const res = await axios.post<IZarinpalVerifyResponse>(this.zarinpalVerifyUrl, zarinpalVerifyRequest, this.axiosConfig); const res = await axios.post<IZarinpalVerifyResponse>(
this.zarinpalVerifyUrl,
zarinpalVerifyRequest,
this.axiosConfig,
);
// Check if response data exists // Check if response data exists
if (!res.data || !res.data.data) { if (!res.data || !res.data.data) {
@@ -128,7 +136,7 @@ export class ZarinpalGateway implements IPaymentGateway {
return { return {
success: code === 100, success: code === 100,
referenceId: ref_id ? ref_id.toString() : undefined, referenceId: ref_id ? ref_id.toString() : '',
cardPan: card_pan, cardPan: card_pan,
raw: res.data, raw: res.data,
}; };
@@ -145,5 +153,4 @@ export class ZarinpalGateway implements IPaymentGateway {
getPaymentUrl(authority: string): string { getPaymentUrl(authority: string): string {
return `${this.zarinpalPaymentBaseUrl}/pg/StartPay/${authority}`; return `${this.zarinpalPaymentBaseUrl}/pg/StartPay/${authority}`;
} }
} }
+1 -1
View File
@@ -23,7 +23,7 @@ export interface IPaymentVerifyParams {
export interface IPaymentVerifyData { export interface IPaymentVerifyData {
success: boolean; success: boolean;
referenceId?: string; referenceId: string;
cardPan?: string; cardPan?: string;
raw: Record<string, any>; raw: Record<string, any>;
} }
@@ -18,15 +18,13 @@ export class PaymentsService {
private readonly em: EntityManager, private readonly em: EntityManager,
private readonly gatewayManager: GatewayManager, private readonly gatewayManager: GatewayManager,
private readonly inventoryService: InventoryService, private readonly inventoryService: InventoryService,
) { } ) {}
async payOrder( async payOrder(orderId: string): Promise<{ paymentUrl: string | null }> {
orderId: string,
): Promise<{ paymentUrl: string | null }> {
const ctx = await this.loadAndValidateOrder(orderId); const ctx = await this.loadAndValidateOrder(orderId);
// Idempotency: avoid creating/charging again for already-paid orders // Idempotency: avoid creating/charging again for already-paid orders
if (ctx.order.paymentStatus === PaymentStatusEnum.Paid) { if (ctx.order.status === OrderStatus.PAID) {
return { paymentUrl: null }; return { paymentUrl: null };
} }
@@ -47,9 +45,7 @@ export class PaymentsService {
} }
} }
private async loadAndValidateOrder( private async loadAndValidateOrder(orderId: string): Promise<OrderPaymentContext> {
orderId: string,
): Promise<OrderPaymentContext> {
const order = await this.em.findOne( const order = await this.em.findOne(
Order, Order,
{ id: orderId }, { id: orderId },
@@ -98,7 +94,7 @@ export class PaymentsService {
const order = await em.findOne(Order, { id: ctx.order.id }, { populate: ['user', 'restaurant'] }); const order = await em.findOne(Order, { id: ctx.order.id }, { populate: ['user', 'restaurant'] });
if (!order) throw new NotFoundException('Order not found'); if (!order) throw new NotFoundException('Order not found');
if (order.paymentStatus === PaymentStatusEnum.Paid) { if (order.status === OrderStatus.PAID) {
return; return;
} }
@@ -126,17 +122,14 @@ export class PaymentsService {
payment.status = PaymentStatusEnum.Paid; payment.status = PaymentStatusEnum.Paid;
payment.paidAt = new Date(); payment.paidAt = new Date();
order.paymentStatus = PaymentStatusEnum.Paid; order.status = OrderStatus.PAID;
em.persist([wallet, payment, order]); em.persist([wallet, payment, order]);
await em.flush(); await em.flush();
}); });
} }
private async handleOnlinePayment(ctx: OrderPaymentContext): Promise<{ paymentUrl: string }> {
private async handleOnlinePayment(
ctx: OrderPaymentContext,
): Promise<{ paymentUrl: string }> {
const gateway = this.gatewayManager.get(ctx.gateway!); const gateway = this.gatewayManager.get(ctx.gateway!);
const payment = await this.getOrCreateLatestPendingPayment(ctx.order.id, { const payment = await this.getOrCreateLatestPendingPayment(ctx.order.id, {
@@ -168,11 +161,7 @@ export class PaymentsService {
}; };
} }
async verifyPayment(transactionId: string, orderId: string): Promise<Payment> {
async verifyPayment(
transactionId: string,
orderId: string,
): Promise<Payment> {
return this.em.transactional(async em => { return this.em.transactional(async em => {
const payment = await em.findOne( const payment = await em.findOne(
Payment, Payment,
@@ -207,7 +196,7 @@ export class PaymentsService {
this.failPayment(payment); this.failPayment(payment);
return payment; return payment;
} }
this.markPaid(payment); this.markPaid(payment, result.referenceId);
this.confirmOrder(payment.order); this.confirmOrder(payment.order);
await em.flush(); await em.flush();
@@ -223,17 +212,16 @@ export class PaymentsService {
); );
} }
private markPaid(payment: Payment) { private markPaid(payment: Payment, referenceId: string) {
payment.status = PaymentStatusEnum.Paid; payment.status = PaymentStatusEnum.Paid;
payment.paidAt = new Date(); payment.paidAt = new Date();
payment.referenceId = payment.referenceId; payment.referenceId = referenceId;
payment.order.paymentStatus = PaymentStatusEnum.Paid;
} }
private failPayment(payment: Payment) { private failPayment(payment: Payment) {
payment.status = PaymentStatusEnum.Failed; payment.status = PaymentStatusEnum.Failed;
payment.failedAt = new Date(); payment.failedAt = new Date();
payment.order.paymentStatus = PaymentStatusEnum.Failed; payment.order.status = OrderStatus.FAILED;
} }
private confirmOrder(order: Order) { private confirmOrder(order: Order) {