Fix price issues in models and enhance payment system

master
nicekid1 2 months ago
parent d0cb571c40
commit 871fda173b
  1. 17
      migrations/20250104112403-create-product.js
  2. 41
      migrations/20250105062732-create-invoice.js
  3. 8
      migrations/20250105063054-create-cart.js
  4. 43
      migrations/20250108065213-create-wallet.js
  5. 4
      src/cart/dto/add-to-cart.dto.ts
  6. 2
      src/cart/entities/cart.entity.ts
  7. 2
      src/invoice/entities/invoice.entity.ts
  8. 4
      src/invoice/invoice.service.ts
  9. 28
      src/payment/payment.controller.ts
  10. 3
      src/payment/payment.module.ts
  11. 1
      src/payment/payment.service.ts
  12. 7
      src/products/dto/create-product.dto.ts
  13. 6
      src/products/dto/update-product.dto.ts
  14. 2
      src/products/entities/product.entity.ts
  15. 2
      src/wallet/entities/wallet.entity.ts
  16. 2
      src/wallet/wallet.service.ts

@ -1,15 +1,14 @@
"use strict"; 'use strict';
module.exports = { module.exports = {
up: async (queryInterface, Sequelize) => { up: async (queryInterface, Sequelize) => {
await queryInterface.dropTable("Products", { cascade: true }); await queryInterface.dropTable('Products', { cascade: true });
await queryInterface.createTable('Products', {
await queryInterface.createTable("Products", {
id: { id: {
type: Sequelize.INTEGER, type: Sequelize.INTEGER,
allowNull: false,
autoIncrement: true, autoIncrement: true,
primaryKey: true, primaryKey: true,
allowNull: false,
}, },
name: { name: {
type: Sequelize.STRING, type: Sequelize.STRING,
@ -20,7 +19,7 @@ module.exports = {
allowNull: false, allowNull: false,
}, },
price: { price: {
type: Sequelize.DECIMAL(10, 2), type: Sequelize.INTEGER,
allowNull: false, allowNull: false,
}, },
imageUrl: { imageUrl: {
@ -51,17 +50,17 @@ module.exports = {
createdAt: { createdAt: {
type: Sequelize.DATE, type: Sequelize.DATE,
allowNull: false, allowNull: false,
defaultValue: Sequelize.NOW, defaultValue: Sequelize.fn('NOW'),
}, },
updatedAt: { updatedAt: {
type: Sequelize.DATE, type: Sequelize.DATE,
allowNull: false, allowNull: false,
defaultValue: Sequelize.NOW, defaultValue: Sequelize.fn('NOW'),
}, },
}); });
}, },
down: async (queryInterface, Sequelize) => { down: async (queryInterface, Sequelize) => {
await queryInterface.dropTable("Products"); await queryInterface.dropTable('Products');
}, },
}; };

@ -1,16 +1,8 @@
"use strict"; 'use strict';
module.exports = { module.exports = {
up: async (queryInterface, Sequelize) => { up: async (queryInterface, Sequelize) => {
const tableExists = await queryInterface await queryInterface.createTable('Invoices', {
.describeTable("Invoices")
.then(() => true)
.catch(() => false);
if (tableExists) {
await queryInterface.dropTable("Invoices", { cascade: true });
}
await queryInterface.createTable("Invoices", {
id: { id: {
type: Sequelize.INTEGER, type: Sequelize.INTEGER,
autoIncrement: true, autoIncrement: true,
@ -21,34 +13,45 @@ module.exports = {
type: Sequelize.INTEGER, type: Sequelize.INTEGER,
allowNull: false, allowNull: false,
references: { references: {
model: "Users", model: 'Users',
key: "id", key: 'id',
}, },
onDelete: "CASCADE", onDelete: 'CASCADE',
}, },
totalPaymentAmount: { totalPaymentAmount: {
type: Sequelize.FLOAT, type: Sequelize.INTEGER,
allowNull: false, allowNull: false,
}, },
status: { status: {
type: Sequelize.ENUM("pending", "paid"), type: Sequelize.ENUM('pending', 'paid'),
allowNull: false, allowNull: false,
defaultValue: "pending", defaultValue: 'pending',
}, },
createdAt: { createdAt: {
type: Sequelize.DATE, type: Sequelize.DATE,
allowNull: false, allowNull: false,
defaultValue: Sequelize.fn("NOW"), defaultValue: Sequelize.fn('NOW'),
}, },
updatedAt: { updatedAt: {
type: Sequelize.DATE, type: Sequelize.DATE,
allowNull: false, allowNull: false,
defaultValue: Sequelize.fn("NOW"), defaultValue: Sequelize.fn('NOW'),
},
});
await queryInterface.addConstraint('Invoices', {
fields: ['userId'],
type: 'foreign key',
name: 'fk_user_id',
references: {
table: 'Users',
field: 'id',
}, },
onDelete: 'CASCADE',
}); });
}, },
down: async (queryInterface, Sequelize) => { down: async (queryInterface, Sequelize) => {
await queryInterface.dropTable("Invoices", { cascade: true }); await queryInterface.dropTable('Invoices');
}, },
}; };

@ -21,7 +21,7 @@ module.exports = {
}, },
productId: { productId: {
type: Sequelize.INTEGER, type: Sequelize.INTEGER,
allowNull: true, allowNull: false,
references: { references: {
model: 'Products', model: 'Products',
key: 'id', key: 'id',
@ -30,9 +30,9 @@ module.exports = {
}, },
invoiceId: { invoiceId: {
type: Sequelize.INTEGER, type: Sequelize.INTEGER,
allowNull: true, allowNull: false,
references: { references: {
model: 'Invoices', model: 'Invoices', // نام جدول Invoices
key: 'id', key: 'id',
}, },
onDelete: 'CASCADE', onDelete: 'CASCADE',
@ -42,7 +42,7 @@ module.exports = {
allowNull: true, allowNull: true,
}, },
productPrice: { productPrice: {
type: Sequelize.DECIMAL(10, 2), type: Sequelize.INTEGER,
allowNull: true, allowNull: true,
}, },
status: { status: {

@ -0,0 +1,43 @@
'use strict';
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.dropTable("Wallets", { cascade: true });
await queryInterface.createTable('Wallets', {
id: {
type: Sequelize.INTEGER,
autoIncrement: true,
primaryKey: true,
allowNull: false,
},
userId: {
type: Sequelize.INTEGER,
allowNull: false,
references: {
model: 'Users',
key: 'id',
},
onDelete: 'CASCADE',
},
balance: {
type: Sequelize.INTEGER,
allowNull: false,
defaultValue: 0,
},
createdAt: {
type: Sequelize.DATE,
allowNull: false,
defaultValue: Sequelize.fn('NOW'),
},
updatedAt: {
type: Sequelize.DATE,
allowNull: false,
defaultValue: Sequelize.fn('NOW'),
},
});
},
down: async (queryInterface, Sequelize) => {
await queryInterface.dropTable('Wallets');
},
};

@ -1,8 +1,8 @@
// add-to-cart.dto.ts // add-to-cart.dto.ts
import { IsInt, IsNotEmpty, IsNumber, min, Min } from 'class-validator'; import { isInt, IsInt, IsNotEmpty, IsNumber, min, Min } from 'class-validator';
export class AddToCartDto { export class AddToCartDto {
@IsNumber() @IsInt()
@IsNotEmpty() @IsNotEmpty()
productId: number; productId: number;

@ -33,7 +33,7 @@ export class Cart extends Model<Cart> {
quantity: number; quantity: number;
@Column({ @Column({
type: DataType.DECIMAL(10, 2), type: DataType.INTEGER,
allowNull: true, allowNull: true,
}) })
productPrice: number; productPrice: number;

@ -23,7 +23,7 @@ export class Invoice extends Model<Invoice> {
carts: Cart[]; carts: Cart[];
@Column({ @Column({
type: DataType.FLOAT, type: DataType.INTEGER,
allowNull: false, allowNull: false,
}) })
totalPaymentAmount: number; totalPaymentAmount: number;

@ -3,7 +3,6 @@ import { InjectModel } from "@nestjs/sequelize";
import { Invoice } from "./entities/invoice.entity"; import { Invoice } from "./entities/invoice.entity";
import { CartService } from "src/cart/cart.service"; import { CartService } from "src/cart/cart.service";
import { User } from "src/users/entities/user.entity"; import { User } from "src/users/entities/user.entity";
import { where } from "sequelize";
@Injectable() @Injectable()
export class InvoiceService { export class InvoiceService {
@ -44,8 +43,7 @@ export class InvoiceService {
await invoice.save(); await invoice.save();
} }
async getInvoiceByUser(userId: number): Promise<Invoice> {
async getInvoiceByUserAndCart(userId: number): Promise<Invoice> {
try { try {
if (!userId ) { if (!userId ) {
throw new HttpException("User ID are required.", HttpStatus.BAD_REQUEST); throw new HttpException("User ID are required.", HttpStatus.BAD_REQUEST);

@ -10,19 +10,14 @@ export class PaymentController {
private readonly wallet: WalletService, private readonly wallet: WalletService,
private readonly paymentService: PaymentService, private readonly paymentService: PaymentService,
private readonly cartService: CartService, private readonly cartService: CartService,
private readonly invoiceService: InvoiceService,
) {} ) {}
@Post("request/:userId") @Post("request/:userId")
async requestPayment(@Param("userId") userId: number): Promise<{ url: string }> { async requestPayment(@Param("userId") userId: number): Promise<{ url: string }> {
const userCartItems = await this.cartService.getUserCart(userId); const invoice = await this.invoiceService.getInvoiceByUser(userId);
const totalAmount = Math.round(invoice.totalPaymentAmount);
if (!userCartItems || userCartItems.cartItems.length === 0) { const callbackUrl = `http://localhost:3000/payment/verify?userId=${userId}`;
throw new Error("Cart is empty!");
}
const totalAmount = userCartItems.totalPrice;
const callbackUrl = `http://localhost:3000/payment/verify?userId=${userId}`;
const paymentUrl = await this.paymentService.requestPayment(totalAmount, "Purchase products", callbackUrl); const paymentUrl = await this.paymentService.requestPayment(totalAmount, "Purchase products", callbackUrl);
return { url: paymentUrl }; return { url: paymentUrl };
@ -41,19 +36,14 @@ export class PaymentController {
} }
try { try {
const userCartItems = await this.cartService.getUserCart(userId); const invoice = await this.invoiceService.getInvoiceByUser(userId);
if (!userCartItems || userCartItems.cartItems.length === 0) { const totalAmount = Math.round(invoice.totalPaymentAmount);
throw new Error("Cart is empty!");
}
const totalAmount = userCartItems.totalPrice;
const refId = await this.paymentService.verifyPayment(Authority, totalAmount); const refId = await this.paymentService.verifyPayment(Authority, totalAmount);
await this.wallet.addBalance(userId,totalAmount) await this.wallet.addBalance(userId, totalAmount);
return { message: "Payment successful", refId}; return { message: "Payment successful", refId };
} catch (error) { } catch (error) {
console.log(error) console.log(error);
throw new Error(`Error during payment verification: ${error.message}`); throw new Error(`Error during payment verification: ${error.message}`);
} }
} }
} }

@ -4,9 +4,10 @@ import { PaymentController } from './payment.controller';
import { InvoiceService } from 'src/invoice/invoice.service'; import { InvoiceService } from 'src/invoice/invoice.service';
import { CartModule } from 'src/cart/cart.module'; import { CartModule } from 'src/cart/cart.module';
import { WalletModule } from 'src/wallet/wallet.module'; import { WalletModule } from 'src/wallet/wallet.module';
import { InvoiceModule } from 'src/invoice/invoice.module';
@Module({ @Module({
imports:[CartModule,WalletModule], imports:[CartModule,WalletModule,InvoiceModule],
controllers: [PaymentController], controllers: [PaymentController],
providers: [PaymentService], providers: [PaymentService],
}) })

@ -42,7 +42,6 @@ export class PaymentService {
Amount: amount, Amount: amount,
Authority: authority, Authority: authority,
}); });
if (result.status === 100) { if (result.status === 100) {
return result.RefID; return result.RefID;
} else { } else {

@ -1,4 +1,4 @@
import { IsString, IsNumber, IsOptional, IsNotEmpty, IsArray } from 'class-validator'; import { IsString, IsNumber, IsOptional, IsNotEmpty, IsArray, IsInt } from 'class-validator';
export class CreateProductDto { export class CreateProductDto {
@IsString() @IsString()
@ -9,7 +9,7 @@ export class CreateProductDto {
@IsNotEmpty() @IsNotEmpty()
description: string; description: string;
@IsNumber() @IsInt()
@IsNotEmpty() @IsNotEmpty()
price: number; price: number;
@ -23,8 +23,7 @@ export class CreateProductDto {
tags?: string[]; tags?: string[];
@IsOptional() @IsOptional()
@IsNumber() @IsInt()
@IsNotEmpty()
quantity?: number; quantity?: number;
@IsOptional() @IsOptional()

@ -1,4 +1,4 @@
import { IsString, IsNumber, IsOptional, IsArray } from 'class-validator'; import { IsString, IsNumber, IsOptional, IsArray, IsInt, isInt } from 'class-validator';
export class UpdateProductDto { export class UpdateProductDto {
@IsOptional() @IsOptional()
@ -10,7 +10,7 @@ export class UpdateProductDto {
description?: string; description?: string;
@IsOptional() @IsOptional()
@IsNumber() @IsInt()
price?: number; price?: number;
@IsOptional() @IsOptional()
@ -23,7 +23,7 @@ export class UpdateProductDto {
tags?: string[]; tags?: string[];
@IsOptional() @IsOptional()
@IsNumber() @IsInt()
quantity?: number; quantity?: number;
@IsOptional() @IsOptional()

@ -15,7 +15,7 @@ export class Product extends Model<Product> {
description: string; description: string;
@Column({ @Column({
type: DataType.DECIMAL(10, 2), type: DataType.INTEGER,
allowNull: false, allowNull: false,
}) })
price: number; price: number;

@ -11,7 +11,7 @@ export class Wallet extends Model<Wallet> {
user: User; user: User;
@Column({ @Column({
type: DataType.DECIMAL(10, 2), type: DataType.INTEGER,
allowNull: false, allowNull: false,
defaultValue: 0, defaultValue: 0,
}) })

@ -17,7 +17,7 @@ export class WalletService {
const wallet = await this.walletModel.findOne({ where: { userId } }); const wallet = await this.walletModel.findOne({ where: { userId } });
if (wallet) { if (wallet) {
wallet.balance += Number(amount); wallet.balance += amount;
await wallet.save(); await wallet.save();
return { message: "Balance updated successfully.", balance: wallet.balance }; return { message: "Balance updated successfully.", balance: wallet.balance };
} else { } else {

Loading…
Cancel
Save