diff --git a/migrations/20250108080238-create-payment-table.js b/migrations/20250108080238-create-payment-table.js new file mode 100644 index 0000000..ce8f59c --- /dev/null +++ b/migrations/20250108080238-create-payment-table.js @@ -0,0 +1,56 @@ +'use strict'; + +module.exports = { + up: async (queryInterface, Sequelize) => { + await queryInterface.dropTable("Payments", { cascade: true }); + await queryInterface.createTable('Payments', { + id: { + type: Sequelize.INTEGER, + autoIncrement: true, + primaryKey: true, + allowNull: false, + }, + userId: { + type: Sequelize.INTEGER, + allowNull: false, + references: { + model: 'Users', + key: 'id', + }, + onDelete: 'CASCADE', + }, + walletId: { + type: Sequelize.INTEGER, + allowNull: false, + references: { + model: 'Wallets', + key: 'id', + }, + onDelete: 'CASCADE', + }, + paymentAmount: { + type: Sequelize.INTEGER, + allowNull: false, + }, + status: { + type: Sequelize.ENUM('completed', 'failed'), + allowNull: false, + defaultValue: 'failed', + }, + paymentDate: { + 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('Payments'); + }, +}; diff --git a/src/invoice/invoice.controller.ts b/src/invoice/invoice.controller.ts index cecb688..fb675db 100644 --- a/src/invoice/invoice.controller.ts +++ b/src/invoice/invoice.controller.ts @@ -4,8 +4,8 @@ import { InvoiceService } from "./invoice.service"; @Controller("invoice") export class InvoiceController { constructor(private readonly invoiceService: InvoiceService) {} - // @Get(":userId") - // async getInvoices(@Param("userId") userId: number): Promise { - // return this.invoiceService.getInvoicesByUser(userId); - // } + @Get(":userId") + async getInvoices(@Param("userId") userId: number): Promise { + return this.invoiceService.getInvoiceByUser(userId); + } } diff --git a/src/payment/entities/payment.entity.ts b/src/payment/entities/payment.entity.ts new file mode 100644 index 0000000..4535b29 --- /dev/null +++ b/src/payment/entities/payment.entity.ts @@ -0,0 +1,41 @@ +import { BelongsTo, Column, DataType, ForeignKey, Model, Table } from "sequelize-typescript"; +import { User } from "src/users/entities/user.entity"; +import { Wallet } from "src/wallet/entities/wallet.entity"; + +@Table +export class Payment extends Model { + @ForeignKey(() => User) + @Column + userId: number; + + @BelongsTo(() => User, { onDelete: "CASCADE" }) + user: User; + + @ForeignKey(() => Wallet) + @Column + walletId: number; + + @BelongsTo(() => Wallet, { onDelete: "CASCADE" }) + wallet: Wallet; + + @Column({ + type: DataType.INTEGER, + allowNull: false, + }) + paymentAmount: number; + + + @Column({ + type: DataType.ENUM( "completed", "failed",), + allowNull: false, + defaultValue: "failed", + }) + status: string; + + @Column({ + type: DataType.DATE, + allowNull: false, + defaultValue: DataType.NOW, + }) + paymentDate: Date; +} diff --git a/src/payment/payment.controller.ts b/src/payment/payment.controller.ts index 941866e..ebef011 100644 --- a/src/payment/payment.controller.ts +++ b/src/payment/payment.controller.ts @@ -1,22 +1,24 @@ import { Controller, Post, Body, Param, Get, Query } from "@nestjs/common"; import { PaymentService } from "./payment.service"; -import { CartService } from "../cart/cart.service"; import { InvoiceService } from "../invoice/invoice.service"; import { WalletService } from "src/wallet/wallet.service"; +import { console } from "inspector"; +import { InjectModel } from "@nestjs/sequelize"; +import { Payment } from "./entities/payment.entity"; @Controller("payment") export class PaymentController { constructor( - private readonly wallet: WalletService, + @InjectModel(Payment) private readonly paymentModel: typeof Payment, + private readonly walletService: WalletService, private readonly paymentService: PaymentService, - private readonly cartService: CartService, private readonly invoiceService: InvoiceService, ) {} @Post("request/:userId") async requestPayment(@Param("userId") userId: number): Promise<{ url: string }> { const invoice = await this.invoiceService.getInvoiceByUser(userId); - const totalAmount = Math.round(invoice.totalPaymentAmount); + const totalAmount = invoice.totalPaymentAmount; const callbackUrl = `http://localhost:3000/payment/verify?userId=${userId}`; const paymentUrl = await this.paymentService.requestPayment(totalAmount, "Purchase products", callbackUrl); @@ -34,15 +36,28 @@ export class PaymentController { if (!userId) { throw new Error("User ID is required."); } - + const invoice = await this.invoiceService.getInvoiceByUser(userId); + const totalAmount = invoice.totalPaymentAmount; + const wallet = this.walletService.getBalance(userId); try { - const invoice = await this.invoiceService.getInvoiceByUser(userId); - const totalAmount = Math.round(invoice.totalPaymentAmount); const refId = await this.paymentService.verifyPayment(Authority, totalAmount); - await this.wallet.addBalance(userId, totalAmount); + await this.walletService.addBalance(userId, totalAmount); + const wallet = this.walletService.getBalance(userId); + await this.paymentModel.create({ + userId, + walletId: (await wallet).walletId, + paymentAmount: totalAmount, + status: "completed", + }); return { message: "Payment successful", refId }; } catch (error) { console.log(error); + await this.paymentModel.create({ + userId, + walletId: (await wallet).walletId, + paymentAmount: totalAmount, + status: "failed", + }); throw new Error(`Error during payment verification: ${error.message}`); } } diff --git a/src/payment/payment.module.ts b/src/payment/payment.module.ts index 8bc39e0..d90f0c9 100644 --- a/src/payment/payment.module.ts +++ b/src/payment/payment.module.ts @@ -5,9 +5,11 @@ import { InvoiceService } from 'src/invoice/invoice.service'; import { CartModule } from 'src/cart/cart.module'; import { WalletModule } from 'src/wallet/wallet.module'; import { InvoiceModule } from 'src/invoice/invoice.module'; +import { Payment } from './entities/payment.entity'; +import { SequelizeModule } from '@nestjs/sequelize'; @Module({ - imports:[CartModule,WalletModule,InvoiceModule], + imports:[SequelizeModule.forFeature([Payment]),CartModule,WalletModule,InvoiceModule], controllers: [PaymentController], providers: [PaymentService], }) diff --git a/src/payment/payment.service.ts b/src/payment/payment.service.ts index b4d2ce1..0ce823e 100644 --- a/src/payment/payment.service.ts +++ b/src/payment/payment.service.ts @@ -1,4 +1,6 @@ import { Injectable, InternalServerErrorException } from '@nestjs/common'; +import { InjectModel } from '@nestjs/sequelize'; +import { Payment } from './entities/payment.entity'; const ZarinpalCheckout = require('zarinpal-checkout'); @@ -7,6 +9,7 @@ export class PaymentService { private zarinpal; constructor( + ) { this.zarinpal = this.initializeZarinpal(); } @@ -28,6 +31,7 @@ export class PaymentService { if (result.status === 100) { return result.url; } else { + throw new Error(`Payment request failed with status: ${result.status}`); } } catch (error) { diff --git a/src/wallet/wallet.controller.ts b/src/wallet/wallet.controller.ts index 99e002b..f660740 100644 --- a/src/wallet/wallet.controller.ts +++ b/src/wallet/wallet.controller.ts @@ -1,16 +1,12 @@ import { Controller, Get, Post, Body, Patch, Param, Delete } from "@nestjs/common"; import { WalletService } from "./wallet.service"; -import { AddBalanceResponse } from "./add-balance-response.interface"; @Controller("wallet") export class WalletController { constructor(private readonly walletService: WalletService) {} @Get(":userId") - async getBalance(@Param("userId") userId: number): Promise { + async getBalance(@Param("userId") userId: number) { return this.walletService.getBalance(userId); } - @Post(":userId/add") - async addBalance(@Param("userId") userId: number, @Body("amount") amount: number): Promise { - return this.walletService.addBalance(userId, amount); - } + } diff --git a/src/wallet/wallet.service.ts b/src/wallet/wallet.service.ts index 231ac6c..5415885 100644 --- a/src/wallet/wallet.service.ts +++ b/src/wallet/wallet.service.ts @@ -8,10 +8,16 @@ import { AddBalanceResponse } from "./add-balance-response.interface"; export class WalletService { constructor(@InjectModel(Wallet) private walletModel: typeof Wallet) {} - async getBalance(userId: number): Promise { + async getBalance(userId: number){ const wallet = await this.walletModel.findOne({ where: { userId } }); - return wallet ? wallet.balance : 0; + + if (!wallet) { + throw new HttpException("Wallet not found", HttpStatus.NOT_FOUND); + } + + return { walletId:wallet.id, userId:wallet.userId ,balance: wallet.balance }; } + async addBalance(userId: number, amount: number): Promise { try { const wallet = await this.walletModel.findOne({ where: { userId } });