wallet payment
This commit is contained in:
@@ -280,9 +280,14 @@ export class OrdersService {
|
||||
if (order.status !== OrderStatus.Pending) {
|
||||
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) {
|
||||
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;
|
||||
await this.em.persistAndFlush(order);
|
||||
return order;
|
||||
|
||||
@@ -6,6 +6,7 @@ import { EntityManager, RequiredEntityData } from '@mikro-orm/core';
|
||||
import { Order } from '../../orders/entities/order.entity';
|
||||
import { PaymentMethod } from '../entities/payment-method.entity';
|
||||
import { CreatePaymentDto } from '../dto/create-payment.dto';
|
||||
import { User } from '../../users/entities/user.entity';
|
||||
|
||||
@Injectable()
|
||||
export class PaymentsService {
|
||||
@@ -57,7 +58,20 @@ export class PaymentsService {
|
||||
// }
|
||||
|
||||
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, {
|
||||
amount,
|
||||
@@ -73,7 +87,7 @@ export class PaymentsService {
|
||||
}
|
||||
|
||||
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) {
|
||||
throw new NotFoundException('Order not found');
|
||||
}
|
||||
@@ -94,11 +108,12 @@ export class PaymentsService {
|
||||
throw new BadRequestException('Merchant ID is required for online payments');
|
||||
}
|
||||
const merchantId = paymentMethod.merchantId;
|
||||
const user = order.user;
|
||||
// Create payment record and save authority
|
||||
const restaurantDomain = paymentMethod.restaurant.domain;
|
||||
const gateway = paymentMethod.gateway;
|
||||
const method = paymentMethod.method;
|
||||
return { amount, method, restaurantDomain, gateway, merchantId };
|
||||
return { amount, method, restaurantDomain, gateway, merchantId, user };
|
||||
}
|
||||
|
||||
async createPayment(domain: string, dto: CreatePaymentDto) {
|
||||
@@ -125,6 +140,47 @@ export class PaymentsService {
|
||||
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> {
|
||||
// Find payment by authority and orderId
|
||||
const payment = await this.em.findOne(
|
||||
@@ -151,6 +207,14 @@ export class PaymentsService {
|
||||
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
|
||||
// if (paymentMethod.method !== PaymentMethodEnum.Online) {
|
||||
// payment.status = PaymentStatusEnum.Paid;
|
||||
|
||||
@@ -36,4 +36,12 @@ export const paymentMethodsData: PaymentMethodData[] = [
|
||||
gateway: PaymentGatewayEnum.ZarinPal,
|
||||
merchantId: 'b6f55bd0-6eae-4045-aeb8-07d084fa8f73',
|
||||
},
|
||||
{
|
||||
title: 'کیف پول',
|
||||
method: PaymentMethodEnum.Wallet,
|
||||
description: 'پرداخت از کیف پول',
|
||||
enabled: true,
|
||||
order: 4,
|
||||
gateway: null,
|
||||
},
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user