set order status as paid after wallet payment
This commit is contained in:
@@ -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}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,9 +196,9 @@ 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();
|
||||||
return payment;
|
return payment;
|
||||||
});
|
});
|
||||||
@@ -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) {
|
||||||
|
|||||||
Reference in New Issue
Block a user