wallet payment
This commit is contained in:
@@ -280,9 +280,14 @@ export class OrdersService {
|
|||||||
if (order.status !== OrderStatus.Pending) {
|
if (order.status !== OrderStatus.Pending) {
|
||||||
throw new BadRequestException('Order must be pending before accepting');
|
throw new BadRequestException('Order must be pending before accepting');
|
||||||
}
|
}
|
||||||
|
// Wallet payments should be paid before accepting (like Online)
|
||||||
if (order.paymentMethod?.method === PaymentMethodEnum.Online && order.paymentStatus !== PaymentStatusEnum.Paid) {
|
if (order.paymentMethod?.method === PaymentMethodEnum.Online && order.paymentStatus !== PaymentStatusEnum.Paid) {
|
||||||
throw new BadRequestException('Order must be paid online before accepting');
|
throw new BadRequestException('Order must be paid online before accepting');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Cash, CardOnDelivery, and Wallet can be accepted even if payment status is pending
|
||||||
|
// (they're paid on delivery or already deducted)
|
||||||
|
|
||||||
order.status = OrderStatus.Confirmed;
|
order.status = OrderStatus.Confirmed;
|
||||||
await this.em.persistAndFlush(order);
|
await this.em.persistAndFlush(order);
|
||||||
return order;
|
return order;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { EntityManager, RequiredEntityData } from '@mikro-orm/core';
|
|||||||
import { Order } from '../../orders/entities/order.entity';
|
import { Order } from '../../orders/entities/order.entity';
|
||||||
import { PaymentMethod } from '../entities/payment-method.entity';
|
import { PaymentMethod } from '../entities/payment-method.entity';
|
||||||
import { CreatePaymentDto } from '../dto/create-payment.dto';
|
import { CreatePaymentDto } from '../dto/create-payment.dto';
|
||||||
|
import { User } from '../../users/entities/user.entity';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class PaymentsService {
|
export class PaymentsService {
|
||||||
@@ -57,7 +58,20 @@ export class PaymentsService {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
async startPayment(orderId: string): Promise<{ paymentUrl: string | null }> {
|
async startPayment(orderId: string): Promise<{ paymentUrl: string | null }> {
|
||||||
const { amount, method, restaurantDomain, gateway, merchantId } = await this.validateOrder(orderId);
|
const { amount, method, restaurantDomain, gateway, merchantId, user } = await this.validateOrder(orderId);
|
||||||
|
|
||||||
|
// Handle Wallet payment immediately
|
||||||
|
if (method === PaymentMethodEnum.Wallet) {
|
||||||
|
// Check wallet balance
|
||||||
|
if (user.wallet < amount) {
|
||||||
|
throw new BadRequestException('Insufficient wallet balance');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deduct from wallet and create payment record
|
||||||
|
const payment = await this.processWalletPayment(orderId, amount, user);
|
||||||
|
|
||||||
|
return { paymentUrl: null }; // No redirect needed for wallet
|
||||||
|
}
|
||||||
|
|
||||||
const { authority } = await this.createPayment(restaurantDomain, {
|
const { authority } = await this.createPayment(restaurantDomain, {
|
||||||
amount,
|
amount,
|
||||||
@@ -73,7 +87,7 @@ export class PaymentsService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async validateOrder(orderId: string) {
|
private async validateOrder(orderId: string) {
|
||||||
const order = await this.em.findOne(Order, { id: orderId });
|
const order = await this.em.findOne(Order, { id: orderId }, { populate: ['user', 'paymentMethod'] });
|
||||||
if (!order) {
|
if (!order) {
|
||||||
throw new NotFoundException('Order not found');
|
throw new NotFoundException('Order not found');
|
||||||
}
|
}
|
||||||
@@ -94,11 +108,12 @@ export class PaymentsService {
|
|||||||
throw new BadRequestException('Merchant ID is required for online payments');
|
throw new BadRequestException('Merchant ID is required for online payments');
|
||||||
}
|
}
|
||||||
const merchantId = paymentMethod.merchantId;
|
const merchantId = paymentMethod.merchantId;
|
||||||
|
const user = order.user;
|
||||||
// Create payment record and save authority
|
// Create payment record and save authority
|
||||||
const restaurantDomain = paymentMethod.restaurant.domain;
|
const restaurantDomain = paymentMethod.restaurant.domain;
|
||||||
const gateway = paymentMethod.gateway;
|
const gateway = paymentMethod.gateway;
|
||||||
const method = paymentMethod.method;
|
const method = paymentMethod.method;
|
||||||
return { amount, method, restaurantDomain, gateway, merchantId };
|
return { amount, method, restaurantDomain, gateway, merchantId, user };
|
||||||
}
|
}
|
||||||
|
|
||||||
async createPayment(domain: string, dto: CreatePaymentDto) {
|
async createPayment(domain: string, dto: CreatePaymentDto) {
|
||||||
@@ -125,6 +140,47 @@ export class PaymentsService {
|
|||||||
return { authority, payment };
|
return { authority, payment };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async processWalletPayment(orderId: string, amount: number, user: User): Promise<Payment> {
|
||||||
|
return this.em.transactional(async em => {
|
||||||
|
// Reload user to get latest wallet balance
|
||||||
|
const freshUser = await em.findOne(User, { id: user.id });
|
||||||
|
if (!freshUser) {
|
||||||
|
throw new NotFoundException('User not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Double-check balance
|
||||||
|
if (freshUser.wallet < amount) {
|
||||||
|
throw new BadRequestException('Insufficient wallet balance');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deduct from wallet
|
||||||
|
freshUser.wallet -= amount;
|
||||||
|
em.persist(freshUser);
|
||||||
|
|
||||||
|
// Create payment record
|
||||||
|
const payment = em.create(Payment, {
|
||||||
|
amount,
|
||||||
|
authority: null,
|
||||||
|
order: em.getReference(Order, orderId),
|
||||||
|
gateway: null,
|
||||||
|
status: PaymentStatusEnum.Paid,
|
||||||
|
paidAt: new Date(),
|
||||||
|
} as RequiredEntityData<Payment>);
|
||||||
|
|
||||||
|
em.persist(payment);
|
||||||
|
|
||||||
|
// Update order payment status
|
||||||
|
const order = await em.findOne(Order, { id: orderId });
|
||||||
|
if (order) {
|
||||||
|
order.paymentStatus = PaymentStatusEnum.Paid;
|
||||||
|
em.persist(order);
|
||||||
|
}
|
||||||
|
|
||||||
|
await em.flush();
|
||||||
|
return payment;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async verifyPayment(authority: string, orderId: string): Promise<Payment> {
|
async verifyPayment(authority: string, orderId: string): Promise<Payment> {
|
||||||
// Find payment by authority and orderId
|
// Find payment by authority and orderId
|
||||||
const payment = await this.em.findOne(
|
const payment = await this.em.findOne(
|
||||||
@@ -151,6 +207,14 @@ export class PaymentsService {
|
|||||||
throw new NotFoundException('Payment method not found');
|
throw new NotFoundException('Payment method not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For Wallet payments, they're already paid during startPayment
|
||||||
|
if (paymentMethod.method === PaymentMethodEnum.Wallet) {
|
||||||
|
if (payment.status === PaymentStatusEnum.Paid) {
|
||||||
|
return payment;
|
||||||
|
}
|
||||||
|
throw new BadRequestException('Wallet payment was not processed correctly');
|
||||||
|
}
|
||||||
|
|
||||||
// For non-online payments, mark as paid directly
|
// For non-online payments, mark as paid directly
|
||||||
// if (paymentMethod.method !== PaymentMethodEnum.Online) {
|
// if (paymentMethod.method !== PaymentMethodEnum.Online) {
|
||||||
// payment.status = PaymentStatusEnum.Paid;
|
// payment.status = PaymentStatusEnum.Paid;
|
||||||
|
|||||||
@@ -36,4 +36,12 @@ export const paymentMethodsData: PaymentMethodData[] = [
|
|||||||
gateway: PaymentGatewayEnum.ZarinPal,
|
gateway: PaymentGatewayEnum.ZarinPal,
|
||||||
merchantId: 'b6f55bd0-6eae-4045-aeb8-07d084fa8f73',
|
merchantId: 'b6f55bd0-6eae-4045-aeb8-07d084fa8f73',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: 'کیف پول',
|
||||||
|
method: PaymentMethodEnum.Wallet,
|
||||||
|
description: 'پرداخت از کیف پول',
|
||||||
|
enabled: true,
|
||||||
|
order: 4,
|
||||||
|
gateway: null,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
Reference in New Issue
Block a user