credircard payment
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
import { Migration } from '@mikro-orm/migrations';
|
||||
|
||||
export class Migration20260615130000_remove_payment_details_from_orders extends Migration {
|
||||
|
||||
override async up(): Promise<void> {
|
||||
this.addSql(`alter table "orders" drop column if exists "payment_description";`);
|
||||
this.addSql(`alter table "orders" drop column if exists "payment_attachments";`);
|
||||
}
|
||||
|
||||
override async down(): Promise<void> {
|
||||
this.addSql(`alter table "orders" add column "payment_description" text null;`);
|
||||
this.addSql(`alter table "orders" add column "payment_attachments" jsonb null;`);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -694,6 +694,7 @@ export const enum OrderMessage {
|
||||
PAYMENT_METHOD_NOT_ENABLED = 'روش پرداخت برای این رستوران فعال نیست',
|
||||
FOOD_NOT_FOUND = 'غذا یافت نشد',
|
||||
FOOD_NOT_BELONGS_TO_RESTAURANT = 'غذا به این رستوران تعلق ندارد',
|
||||
CREDIT_CARD_PAYMENT_DETAILS_REQUIRED = 'حداقل یکی از توضیحات پرداخت یا پیوست رسید کارت به کارت الزامی است',
|
||||
}
|
||||
|
||||
export const enum CartMessage {
|
||||
@@ -726,6 +727,7 @@ export const enum CartMessage {
|
||||
FOOD_ONLY_AVAILABLE_DURING_MEAL_TIMES = 'غذا [foodTitle] فقط در ساعات وعدههای غذایی (صبحانه، ناهار، عصرانه یا شام) در دسترس است. زمان فعلی خارج از ساعات وعدههای غذایی است',
|
||||
FOOD_ONLY_AVAILABLE_FOR_MEAL_TYPES = 'غذا [foodTitle] فقط برای [allowedMealTypes] در دسترس است. زمان فعلی [mealType] است که با این غذا سازگار نیست',
|
||||
FOOD_ONLY_AVAILABLE_ON_DAYS = 'غذا [foodTitle] فقط در روزهای [allowedDays] در دسترس است. امروز [currentDay] است که این غذا در دسترس نیست',
|
||||
CREDIT_CARD_PAYMENT_DETAILS_REQUIRED = 'حداقل یکی از توضیحات پرداخت یا پیوست رسید کارت به کارت الزامی است',
|
||||
}
|
||||
|
||||
export const enum PaymentMessage {
|
||||
|
||||
@@ -39,7 +39,7 @@ export class SetAllCartParmsDto {
|
||||
carAddress?: SetCarDeliveryDto;
|
||||
|
||||
@ApiProperty({
|
||||
description: 'Payment receipt attachments (URLs)',
|
||||
description: 'Payment receipt attachments (URLs). At least one of paymentDesc or attachments is required for credit card.',
|
||||
required: false,
|
||||
type: [String],
|
||||
example: ['https://example.com/receipt.jpg'],
|
||||
@@ -50,7 +50,7 @@ export class SetAllCartParmsDto {
|
||||
attachments?: string[];
|
||||
|
||||
@ApiProperty({
|
||||
description: 'Payment description or note',
|
||||
description: 'Payment description or note. At least one of paymentDesc or attachments is required for credit card.',
|
||||
required: false,
|
||||
example: 'Transfer from account ending 1234',
|
||||
})
|
||||
|
||||
@@ -5,6 +5,7 @@ import { Restaurant } from '../../restaurants/entities/restaurant.entity';
|
||||
import { UserAddress } from '../../users/entities/user-address.entity';
|
||||
import { User } from '../../users/entities/user.entity';
|
||||
import { PaymentMethod } from '../../payments/entities/payment-method.entity';
|
||||
import { PaymentMethodEnum } from '../../payments/interface/payment';
|
||||
import { Delivery } from '../../delivery/entities/delivery.entity';
|
||||
import { DeliveryMethodEnum } from '../../delivery/interface/delivery';
|
||||
import { PointTransaction } from '../../users/entities/point-transaction.entity';
|
||||
@@ -216,6 +217,23 @@ export class CartValidationService {
|
||||
return paymentMethod;
|
||||
}
|
||||
|
||||
assertCreditCardPaymentDetails(
|
||||
paymentMethod: PaymentMethod,
|
||||
paymentDesc?: string | null,
|
||||
attachments?: string[] | null,
|
||||
): void {
|
||||
if (paymentMethod.method !== PaymentMethodEnum.CreditCard) {
|
||||
return;
|
||||
}
|
||||
|
||||
const hasDescription = Boolean(paymentDesc?.trim());
|
||||
const hasAttachments = Boolean(attachments?.length);
|
||||
|
||||
if (!hasDescription && !hasAttachments) {
|
||||
throw new BadRequestException(CartMessage.CREDIT_CARD_PAYMENT_DETAILS_REQUIRED);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert wallet has enough balance
|
||||
*/
|
||||
|
||||
@@ -115,17 +115,17 @@ export class CartService {
|
||||
|
||||
cart.paymentMethodId = paymentMethodId;
|
||||
|
||||
// Set description (if provided)
|
||||
if (description !== undefined) {
|
||||
cart.description = description;
|
||||
}
|
||||
|
||||
if (attachments !== undefined) {
|
||||
cart.attachments = attachments;
|
||||
}
|
||||
|
||||
if (paymentDesc !== undefined) {
|
||||
cart.paymentDesc = paymentDesc;
|
||||
if (paymentMethod.method === PaymentMethodEnum.CreditCard) {
|
||||
this.validationService.assertCreditCardPaymentDetails(paymentMethod, paymentDesc, attachments);
|
||||
cart.paymentDesc = paymentDesc?.trim() || undefined;
|
||||
cart.attachments = attachments?.length ? attachments : undefined;
|
||||
} else {
|
||||
delete cart.paymentDesc;
|
||||
delete cart.attachments;
|
||||
}
|
||||
|
||||
// Final validation: if effective delivery method is courier, ensure all foods have pickupServe
|
||||
|
||||
@@ -94,4 +94,22 @@ export class AdminCreateOrderDto {
|
||||
})
|
||||
@IsOptional()
|
||||
carAddress?: OrderCarAddress;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: 'Payment description. At least one of paymentDesc or paymentAttachments is required for credit card.',
|
||||
example: 'Transfer from account ending 1234',
|
||||
})
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
paymentDesc?: string;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: 'Payment receipt attachments (URLs). At least one of paymentDesc or paymentAttachments is required for credit card.',
|
||||
type: [String],
|
||||
example: ['https://example.com/receipt.jpg'],
|
||||
})
|
||||
@IsOptional()
|
||||
@IsArray()
|
||||
@IsString({ each: true })
|
||||
paymentAttachments?: string[];
|
||||
}
|
||||
|
||||
@@ -64,6 +64,7 @@ export class OrdersService {
|
||||
async checkout(userId: string, restaurantId: string) {
|
||||
const cart = await this.cartService.findOneOrFail(userId, restaurantId);
|
||||
const validated = await this.validateCartForOrder(userId, restaurantId, cart);
|
||||
console.log(cart);
|
||||
|
||||
const order = await this.em.transactional(async em => {
|
||||
const order = em.create(Order, {
|
||||
@@ -115,8 +116,7 @@ export class OrdersService {
|
||||
status: PaymentStatusEnum.Pending,
|
||||
method: order.paymentMethod.method,
|
||||
gateway: order.paymentMethod.gateway ?? null,
|
||||
...(cart.attachments?.length ? { attachments: cart.attachments } : {}),
|
||||
...(cart.paymentDesc ? { description: cart.paymentDesc } : {}),
|
||||
...this.buildCreditCardPaymentFields(validated.paymentMethod, cart.paymentDesc, cart.attachments),
|
||||
});
|
||||
|
||||
em.persist(payment);
|
||||
@@ -164,6 +164,7 @@ export class OrdersService {
|
||||
|
||||
const paymentMethod = await this.getPaymentMethodOrFail(dto.paymentMethodId, restaurantId);
|
||||
this.assertPaymentMethodEnabled(paymentMethod);
|
||||
this.assertCreditCardPaymentDetails(paymentMethod, dto.paymentDesc, dto.paymentAttachments);
|
||||
|
||||
this.assertAdminDeliveryRequirements(dto, delivery);
|
||||
|
||||
@@ -215,6 +216,7 @@ export class OrdersService {
|
||||
status: PaymentStatusEnum.Pending,
|
||||
method: order.paymentMethod.method,
|
||||
gateway: order.paymentMethod.gateway ?? null,
|
||||
...this.buildCreditCardPaymentFields(paymentMethod, dto.paymentDesc, dto.paymentAttachments),
|
||||
});
|
||||
em.persist(payment);
|
||||
|
||||
@@ -255,6 +257,7 @@ export class OrdersService {
|
||||
|
||||
const paymentMethod = await this.getPaymentMethodOrFail(cart.paymentMethodId!, restaurantId);
|
||||
this.assertPaymentMethodEnabled(paymentMethod);
|
||||
this.assertCreditCardPaymentDetails(paymentMethod, cart.paymentDesc, cart.attachments);
|
||||
|
||||
const orderItemsData = await this.buildOrderItemsData(cart, restaurantId);
|
||||
|
||||
@@ -479,6 +482,35 @@ export class OrdersService {
|
||||
}
|
||||
}
|
||||
|
||||
private buildCreditCardPaymentFields(
|
||||
paymentMethod: PaymentMethod,
|
||||
paymentDesc?: string | null,
|
||||
attachments?: string[] | null,
|
||||
): Pick<Payment, 'description' | 'attachments'> {
|
||||
|
||||
return {
|
||||
...(attachments?.length ? { attachments } : {}),
|
||||
...(paymentDesc?.trim() ? { description: paymentDesc.trim() } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
private assertCreditCardPaymentDetails(
|
||||
paymentMethod: PaymentMethod,
|
||||
paymentDesc?: string | null,
|
||||
attachments?: string[] | null,
|
||||
): void {
|
||||
if (paymentMethod.method !== PaymentMethodEnum.CreditCard) {
|
||||
return;
|
||||
}
|
||||
|
||||
const hasDescription = Boolean(paymentDesc?.trim());
|
||||
const hasAttachments = Boolean(attachments?.length);
|
||||
|
||||
if (!hasDescription && !hasAttachments) {
|
||||
throw new BadRequestException(OrderMessage.CREDIT_CARD_PAYMENT_DETAILS_REQUIRED);
|
||||
}
|
||||
}
|
||||
|
||||
private assertDeliveryMethodRequirements(cart: Cart, delivery: Delivery) {
|
||||
if (delivery.method === DeliveryMethodEnum.DineIn) {
|
||||
if (!cart.tableNumber || cart.tableNumber.trim() === '') {
|
||||
|
||||
Reference in New Issue
Block a user