Compare commits
10 Commits
9e665f8710
...
ee10b49cf8
Author | SHA1 | Date |
---|---|---|
|
ee10b49cf8 | 2 months ago |
|
548f161f7c | 2 months ago |
|
c5c334692d | 2 months ago |
|
4520299ef0 | 2 months ago |
|
98139f75bf | 2 months ago |
|
7da8f22cb2 | 2 months ago |
|
7fa178b293 | 2 months ago |
|
187811a048 | 2 months ago |
|
122bbacc0d | 2 months ago |
|
35521f8e0c | 2 months ago |
22 changed files with 443 additions and 176 deletions
@ -0,0 +1,54 @@ |
||||
"use strict"; |
||||
|
||||
module.exports = { |
||||
up: async (queryInterface, Sequelize) => { |
||||
const tableExists = await queryInterface |
||||
.describeTable("Invoices") |
||||
.then(() => true) |
||||
.catch(() => false); |
||||
|
||||
if (tableExists) { |
||||
await queryInterface.dropTable("Invoices", { cascade: true }); |
||||
} |
||||
await queryInterface.createTable("Invoices", { |
||||
id: { |
||||
type: Sequelize.INTEGER, |
||||
autoIncrement: true, |
||||
primaryKey: true, |
||||
allowNull: false, |
||||
}, |
||||
userId: { |
||||
type: Sequelize.INTEGER, |
||||
allowNull: false, |
||||
references: { |
||||
model: "Users", |
||||
key: "id", |
||||
}, |
||||
onDelete: "CASCADE", |
||||
}, |
||||
totalPaymentAmount: { |
||||
type: Sequelize.FLOAT, |
||||
allowNull: false, |
||||
}, |
||||
status: { |
||||
type: Sequelize.ENUM("pending", "paid"), |
||||
allowNull: false, |
||||
defaultValue: "pending", |
||||
}, |
||||
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("Invoices", { cascade: true }); |
||||
}, |
||||
}; |
@ -1,17 +1,11 @@ |
||||
import { Controller, Get, Post, Body, Patch, Param, Delete } from "@nestjs/common"; |
||||
import { InvoiceService } from "./invoice.service"; |
||||
import { Invoice } from "./entities/invoice.entity"; |
||||
|
||||
@Controller("invoice") |
||||
export class InvoiceController { |
||||
constructor(private readonly invoiceService: InvoiceService) {} |
||||
@Post("create") |
||||
async createInvoice(@Body() body: { userId: number; totalAmount: number }): Promise<Invoice> { |
||||
const { userId, totalAmount } = body; |
||||
return this.invoiceService.createInvoice(userId, totalAmount); |
||||
} |
||||
@Get(":userId") |
||||
async getInvoices(@Param("userId") userId: number): Promise<any> { |
||||
return this.invoiceService.getInvoicesByUser(userId); |
||||
} |
||||
// @Get(":userId")
|
||||
// async getInvoices(@Param("userId") userId: number): Promise<any> {
|
||||
// return this.invoiceService.getInvoicesByUser(userId);
|
||||
// }
|
||||
} |
||||
|
@ -1,12 +1,14 @@ |
||||
import { Module } from '@nestjs/common'; |
||||
import { InvoiceService } from './invoice.service'; |
||||
import { InvoiceController } from './invoice.controller'; |
||||
import { SequelizeModule } from '@nestjs/sequelize'; |
||||
import { Invoice } from './entities/invoice.entity'; |
||||
import { Module, forwardRef } from "@nestjs/common"; |
||||
import { SequelizeModule } from "@nestjs/sequelize"; |
||||
import { InvoiceController } from "./invoice.controller"; |
||||
import { InvoiceService } from "./invoice.service"; |
||||
import { Invoice } from "./entities/invoice.entity"; |
||||
import { CartModule } from "src/cart/cart.module"; |
||||
|
||||
@Module({ |
||||
imports : [SequelizeModule.forFeature([Invoice])], |
||||
imports: [SequelizeModule.forFeature([Invoice]), forwardRef(()=>CartModule)], |
||||
controllers: [InvoiceController], |
||||
providers: [InvoiceService], |
||||
exports: [InvoiceService], |
||||
}) |
||||
export class InvoiceModule {} |
||||
|
@ -1,52 +1,71 @@ |
||||
import { HttpException, HttpStatus, Injectable } from "@nestjs/common"; |
||||
import { forwardRef, HttpException, HttpStatus, Inject, Injectable } from "@nestjs/common"; |
||||
import { InjectModel } from "@nestjs/sequelize"; |
||||
import { Invoice } from "./entities/invoice.entity"; |
||||
import { CartService } from "src/cart/cart.service"; |
||||
import { User } from "src/users/entities/user.entity"; |
||||
import { where } from "sequelize"; |
||||
|
||||
@Injectable() |
||||
export class InvoiceService { |
||||
constructor(@InjectModel(Invoice) private readonly invoiceModel: typeof Invoice) {} |
||||
constructor( |
||||
@InjectModel(Invoice) private readonly invoiceModel: typeof Invoice, |
||||
@Inject(forwardRef(() => CartService)) |
||||
private cartService: CartService, |
||||
) {} |
||||
|
||||
async createInvoice(userId: number, totalAmount: number): Promise<Invoice> { |
||||
try { |
||||
if (!userId) { |
||||
throw new HttpException("User id not found!", HttpStatus.BAD_REQUEST); |
||||
} |
||||
|
||||
const newInvoice = await this.invoiceModel.create({ userId, totalAmount }); |
||||
return newInvoice; |
||||
} catch (error) { |
||||
|
||||
if (error instanceof HttpException) { |
||||
throw error; |
||||
} |
||||
|
||||
throw new HttpException("An error occurred while creating the invoice.", HttpStatus.INTERNAL_SERVER_ERROR); |
||||
async createInvoiceFromCart(userId: number): Promise<Invoice> { |
||||
const user = await User.findByPk(userId); |
||||
if (!user) { |
||||
throw new HttpException("User not found", HttpStatus.NOT_FOUND); |
||||
} |
||||
const invoice = await this.invoiceModel.create({ |
||||
userId, |
||||
totalPaymentAmount: 0, |
||||
}); |
||||
return invoice; |
||||
} |
||||
async updateTotalPayment(userId: number) { |
||||
const user = await User.findByPk(userId); |
||||
if (!user) { |
||||
throw new HttpException("User not found", HttpStatus.NOT_FOUND); |
||||
} |
||||
|
||||
const userCartItems = await this.cartService.getUserCart(userId); |
||||
if (!userCartItems || !userCartItems.cartItems || userCartItems.cartItems.length === 0) { |
||||
throw new HttpException("Cart is empty", HttpStatus.BAD_REQUEST); |
||||
} |
||||
|
||||
let invoice = await this.invoiceModel.findOne({ where: { userId, status: "pending" } }); |
||||
if (!invoice) { |
||||
throw new HttpException("Invoice not found", HttpStatus.NOT_FOUND); |
||||
} |
||||
|
||||
invoice.totalPaymentAmount = userCartItems.totalPrice; |
||||
await invoice.save(); |
||||
} |
||||
async getInvoicesByUser(userId: number): Promise<Invoice[]> { |
||||
|
||||
|
||||
async getInvoiceByUserAndCart(userId: number): Promise<Invoice> { |
||||
try { |
||||
if (!userId) { |
||||
throw new HttpException('User ID is required.', HttpStatus.BAD_REQUEST); |
||||
if (!userId ) { |
||||
throw new HttpException("User ID are required.", HttpStatus.BAD_REQUEST); |
||||
} |
||||
|
||||
const invoices = await this.invoiceModel.findAll({ |
||||
where: { userId }, |
||||
const invoice = await this.invoiceModel.findOne({ |
||||
where: { userId, status:'pending' }, |
||||
}); |
||||
|
||||
if (!invoices || invoices.length === 0) { |
||||
throw new HttpException('No invoices found for this user.', HttpStatus.NOT_FOUND); |
||||
if (!invoice) { |
||||
throw new HttpException("Invoice not found for this user and cart.", HttpStatus.NOT_FOUND); |
||||
} |
||||
|
||||
return invoices; |
||||
return invoice; |
||||
} catch (error) { |
||||
if (error instanceof HttpException) { |
||||
throw error; |
||||
} |
||||
throw new HttpException( |
||||
'An error occurred while retrieving invoices.', |
||||
HttpStatus.INTERNAL_SERVER_ERROR, |
||||
); |
||||
throw new HttpException("An error occurred while retrieving the invoice.", HttpStatus.INTERNAL_SERVER_ERROR); |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
@ -1,25 +1,59 @@ |
||||
import { Controller, Get, Query } from '@nestjs/common'; |
||||
import { PaymentService } from './payment.service'; |
||||
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"; |
||||
|
||||
@Controller('payment') |
||||
@Controller("payment") |
||||
export class PaymentController { |
||||
constructor(private readonly paymentService: PaymentService) {} |
||||
@Get('request') |
||||
async requestPayment(){ |
||||
const amount = 10000;
|
||||
const description = 'Test payment'; |
||||
const callbackUrl = 'http://localhost:3000/payment/verify'; |
||||
const paymentUrl = await this.paymentService.requestPayment(amount, description, callbackUrl); |
||||
return { paymentUrl }; |
||||
constructor( |
||||
private readonly wallet: WalletService, |
||||
private readonly paymentService: PaymentService, |
||||
private readonly cartService: CartService, |
||||
) {} |
||||
|
||||
@Post("request/:userId") |
||||
async requestPayment(@Param("userId") userId: number): Promise<{ url: string }> { |
||||
const userCartItems = await this.cartService.getUserCart(userId); |
||||
|
||||
if (!userCartItems || userCartItems.cartItems.length === 0) { |
||||
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); |
||||
|
||||
return { url: paymentUrl }; |
||||
} |
||||
@Get('verify') |
||||
async verifyPayment(@Query('Authority') authority: string, @Query('Status') status: string) { |
||||
if (status === 'OK') { |
||||
const amount = 10000;
|
||||
const refId = await this.paymentService.verifyPayment(authority, amount); |
||||
return { success: true, refId }; |
||||
} else { |
||||
return { success: false, message: 'Payment canceled' }; |
||||
|
||||
@Get("verify") |
||||
async verifyPayment(@Query() query: { Authority: string; Status: string; userId: number }): Promise<any> { |
||||
const { Authority, Status, userId } = query; |
||||
|
||||
if (Status !== "OK") { |
||||
throw new Error("Payment failed"); |
||||
} |
||||
|
||||
if (!userId) { |
||||
throw new Error("User ID is required."); |
||||
} |
||||
|
||||
try { |
||||
const userCartItems = await this.cartService.getUserCart(userId); |
||||
if (!userCartItems || userCartItems.cartItems.length === 0) { |
||||
throw new Error("Cart is empty!"); |
||||
} |
||||
const totalAmount = userCartItems.totalPrice; |
||||
|
||||
const refId = await this.paymentService.verifyPayment(Authority, totalAmount); |
||||
await this.wallet.addBalance(userId,totalAmount) |
||||
return { message: "Payment successful", refId}; |
||||
} catch (error) { |
||||
console.log(error) |
||||
throw new Error(`Error during payment verification: ${error.message}`); |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
@ -1,37 +1,55 @@ |
||||
import { Injectable } from '@nestjs/common'; |
||||
import { Injectable, InternalServerErrorException } from '@nestjs/common'; |
||||
|
||||
const ZarinpalCheckout = require('zarinpal-checkout'); |
||||
|
||||
@Injectable() |
||||
export class PaymentService { |
||||
private zarinpal; |
||||
|
||||
constructor() { |
||||
this.zarinpal = ZarinpalCheckout.create('00000000-0000-0000-0000-000000000000', true);
|
||||
constructor( |
||||
) { |
||||
this.zarinpal = this.initializeZarinpal(); |
||||
} |
||||
|
||||
private initializeZarinpal() { |
||||
const merchantId = '00000000-0000-0000-0000-000000000000'; // Merchant ID should be valid
|
||||
const sandboxMode = true;
|
||||
return ZarinpalCheckout.create(merchantId, sandboxMode); |
||||
} |
||||
|
||||
async requestPayment(amount: number, description: string, callbackUrl: string) { |
||||
const result = await this.zarinpal.PaymentRequest({ |
||||
Amount: amount, |
||||
CallbackURL: callbackUrl, |
||||
Description: description, |
||||
}); |
||||
|
||||
if (result.status === 100) { |
||||
return result.url; |
||||
} else { |
||||
throw new Error(`Error in payment request: ${result.status}`); |
||||
async requestPayment(amount: number, description: string, callbackUrl: string): Promise<string> { |
||||
try { |
||||
const result = await this.zarinpal.PaymentRequest({ |
||||
Amount: amount,
|
||||
CallbackURL: callbackUrl, |
||||
Description: description, |
||||
}); |
||||
|
||||
if (result.status === 100) { |
||||
return result.url; |
||||
} else { |
||||
throw new Error(`Payment request failed with status: ${result.status}`); |
||||
} |
||||
} catch (error) { |
||||
console.log('Error in PaymentRequest:', error); |
||||
throw new InternalServerErrorException(`Error in payment request: ${error.message}`); |
||||
} |
||||
} |
||||
async verifyPayment(authority: string, amount: number) { |
||||
const result = await this.zarinpal.PaymentVerification({ |
||||
Amount: amount, |
||||
Authority: authority, |
||||
}); |
||||
|
||||
if (result.status === 100) { |
||||
return result.RefID;
|
||||
} else { |
||||
throw new Error(`Payment verification failed: ${result.status}`); |
||||
|
||||
async verifyPayment(authority: string, amount: number): Promise<string> { |
||||
try { |
||||
const result = await this.zarinpal.PaymentVerification({ |
||||
Amount: amount, |
||||
Authority: authority, |
||||
}); |
||||
|
||||
if (result.status === 100) { |
||||
return result.RefID;
|
||||
} else { |
||||
throw new Error(`Payment verification failed with status: ${result.status}`); |
||||
} |
||||
} catch (error) { |
||||
throw new InternalServerErrorException(`Error in payment verification: ${error.message}`); |
||||
} |
||||
} |
||||
} |
||||
|
@ -1,19 +1,16 @@ |
||||
import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/common'; |
||||
import { WalletService } from './wallet.service'; |
||||
import { AddBalanceResponse } from './add-balance-response.interface'; |
||||
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') |
||||
@Controller("wallet") |
||||
export class WalletController { |
||||
constructor(private readonly walletService: WalletService) {} |
||||
@Get(':userId') |
||||
async getBalance(@Param('userId') userId: number): Promise<number> { |
||||
@Get(":userId") |
||||
async getBalance(@Param("userId") userId: number): Promise<number> { |
||||
return this.walletService.getBalance(userId); |
||||
} |
||||
@Post(':userId/add') |
||||
async addBalance( |
||||
@Param('userId') userId: number,
|
||||
@Body('amount') amount: number
|
||||
): Promise<AddBalanceResponse> { |
||||
@Post(":userId/add") |
||||
async addBalance(@Param("userId") userId: number, @Body("amount") amount: number): Promise<AddBalanceResponse> { |
||||
return this.walletService.addBalance(userId, amount); |
||||
} |
||||
} |
||||
|
Loading…
Reference in new issue