Compare commits

..

7 Commits

Author SHA1 Message Date
Kallesh B S
905c2263e5 Amount mismatch error 2025-09-12 17:37:58 +05:30
Kallesh B S
c1f03cdf43 28-08-2025 API changes, return specific value in applicationName from login API, payment data persist changes 2025-08-28 17:16:55 +05:30
Kallesh B S
3616b57a73 create login bugs 2025-08-26 17:02:03 +05:30
Kallesh B S
a2c2ed2a68 Added the Payment Data Persistence API 2025-08-25 16:14:25 +05:30
Kallesh B S
45d57cee8d initiate-payment api modified for dynamic amount from UI 2025-08-22 15:16:02 +05:30
Kallesh B S
0e59a652b0 19-08-2025 api modifications 2025-08-19 17:27:14 +05:30
Kallesh B S
75a537aab4 pay 2 2025-08-18 20:34:44 +05:30
24 changed files with 673 additions and 45 deletions

View File

@ -1,5 +1,9 @@
GET http://192.168.1.96:3006
###
GET http://localhost:3000/oracle/GetRegions/1
###
GET http://localhost:3000/oracle/SearchHolder/1

View File

@ -6,11 +6,12 @@ import { OriginCheckMiddleware } from './middleware/OriginCheck.middleware';
import { ReqBodyKeysToUppercaseMiddleware } from './middleware/reqBodyKeysToUppercase.middleware';
import { ConfigModule } from '@nestjs/config';
import { MailModule } from './mail/mail.module';
import { PaypalModule } from './paypal/paypal.module';
@Module({
imports: [
ConfigModule.forRoot({ isGlobal: true }),
AuthModule, DbModule, OracleModule, MailModule
AuthModule, DbModule, OracleModule, MailModule, PaypalModule
],
controllers: [],
providers: [],

View File

@ -1,7 +1,7 @@
import { Body, Controller, Get, HttpCode, Post, Put, Req, Res, UseGuards, UseInterceptors } from '@nestjs/common';
import { AuthService } from './auth.service';
import { ApiTags } from '@nestjs/swagger';
import { AuthLoginDTO, SendMailDTO } from './auth.dto';
import { AuthLoginDTO, AuthLoginOnlyDTO, SendMailDTO } from './auth.dto';
import { Request, Response } from 'express';
import { LogoutInterceptor } from 'src/interceptors/logout.interceptor';
import { RegisterGuard } from 'src/guards/register.guard';
@ -18,9 +18,9 @@ export class AuthController {
@Post('login')
@HttpCode(200)
async loginClient(@Body() body: AuthLoginDTO, @Res({ passthrough: true }) res: Response, @Req() req: Request) {
async loginClient(@Body() body: AuthLoginOnlyDTO, @Res({ passthrough: true }) res: Response, @Req() req: Request) {
let k: any = await this.authService.loginUser(body.P_EMAILADDR.toLowerCase(), body.P_PASSWORD, req);
let k: any = await this.authService.loginUser(body.P_EMAILADDR.toLowerCase(), body.P_PASSWORD, body.P_APPLICATIONNAME, req);
if (k.access_token) {
res.cookie('access_token', k.access_token, {

View File

@ -1,6 +1,7 @@
import { ApiProperty } from '@nestjs/swagger';
import { ApiProperty, IntersectionType } from '@nestjs/swagger';
import { Transform, Type } from 'class-transformer';
import { IsEmail, IsEnum, IsObject, IsOptional, IsString, ValidateNested } from 'class-validator';
import { APPLICATIONNAME_DTO } from 'src/dto/property.dto';
export class AuthLoginDTO {
@ApiProperty({ required: true })
@ -12,6 +13,8 @@ export class AuthLoginDTO {
P_PASSWORD: string;
}
export class AuthLoginOnlyDTO extends IntersectionType(AuthLoginDTO, APPLICATIONNAME_DTO) { }
export enum MailTypeDTO {
REGISTER_CLIENT = "REGISTER_CLIENT",
REGISTER_SP = "REGISTER_SP",

View File

@ -245,7 +245,7 @@ export class AuthService {
return verified;
}
async loginUser(username: string, password: string, req: Request): Promise<any> {
async loginUser(username: string, password: string, applicationName: string, req: Request): Promise<any> {
const access_token = req.cookies?.access_token;
const refresh_token = req.cookies?.refresh_token;
@ -276,6 +276,16 @@ export class AuthService {
}
}
const isValidCombo =
(applicationName === 'policy' && ROLE === 'ua') ||
(applicationName === 'service-provider' && ROLE === 'sa') ||
(applicationName === 'client' && ROLE === 'ca');
if (!isValidCombo) {
throw new UnauthorizedException("Authentication failed")
// console.log(!isValidCombo);
}
// if (ROLE === 'ua' && req.headers.origin !== 'https://policy.alphaomegainfosys.com') {
// throw new BadRequestException("Invalid username or password")
// }
@ -299,7 +309,7 @@ export class AuthService {
let tokens = await this.getTokenFromRefreshToken(req);
const decoded = jwt.decode(access_token, { complete: true });
const email: any = (decoded && typeof decoded === 'object') ? (decoded as any).payload?.email : undefined;
return { ...tokens, email }
return { ...tokens, email, ApplicationName: ROLE }
}
@ -311,6 +321,9 @@ export class AuthService {
return k;
} catch (error) {
if (error instanceof BadRequestException || error instanceof UnauthorizedException) {
throw error
}
throw new BadRequestException('Invalid username or password');
}
}

View File

@ -39,6 +39,61 @@ export class CARNETNO_DTO {
P_CARNETNO: string;
}
export class ORDERID_DTO {
@ApiProperty({ required: true, type: String, nullable: true })
@IsString()
@IsDefined({ message: 'Property P_ORDERID is required' })
P_ORDERID: string | null;
}
export class PRICE_DTO {
@ApiProperty({ required: true })
// @Max(999999999, {
// message: 'Property P_PRICE must not exceed 999999999',
// })
@Min(0.01, { message: 'Property P_PRICE must be greater than zero' })
// @IsInt({ message: 'Property P_PRICE allows only whole numbers' })
@IsNumber({}, { message: 'Property P_PRICE must be a number' })
@Transform(({ value }) => Number(value))
@IsDefined({ message: 'Property P_PRICE is required' })
P_PRICE: number;
}
export class PAYMENTAMOUNT_DTO {
@ApiProperty({ required: true })
// @Max(999999999, {
// message: 'Property P_PRICE must not exceed 999999999',
// })
@Min(0.01, { message: 'Property P_PAYMENTAMOUNT must be greater than zero' })
// @IsInt({ message: 'Property P_PRICE allows only whole numbers' })
@IsNumber({}, { message: 'Property P_PAYMENTAMOUNT must be a number' })
@Transform(({ value }) => Number(value))
@IsDefined({ message: 'Property P_PAYMENTAMOUNT is required' })
P_PAYMENTAMOUNT: number;
}
export class PAYMENTSTATUS_DTO {
@ApiProperty({ required: true })
@IsString({ message: 'Property P_STATUS must be a string' })
@IsDefined({ message: 'Property P_STATUS is required' })
P_STATUS: string;
}
export class PAYMENTERROR_DTO {
@ApiProperty({ required: true, type: String, nullable: true })
@IsString({ message: 'Property P_PAYMENTERROR must be a string' })
@IsDefined({ message: 'Property P_PAYMENTERROR is required' })
P_PAYMENTERROR: string | null = null;
}
export class DESCRIPTION_DTO {
@ApiProperty({ required: true })
@IsString()
@IsDefined({ message: 'Property P_DESCRIPTION is required' })
P_DESCRIPTION: string;
}
export class HEADERID_DTO {
@ApiProperty({ required: true })
@Max(999999999, {
@ -60,17 +115,17 @@ export class APPLICATIONNAME_DTO {
}
export class GOODS_PORT_DTO {
@ApiProperty({ required: true })
@ApiProperty({ required: true, type: String, nullable: true })
@IsString({ message: 'Property P_GOODSPORT must be a string' })
@IsDefined({ message: 'Property P_GOODSPORT is required' })
P_GOODSPORT: string;
P_GOODSPORT: string | null = null;
}
export class GOODS_COUNTRY_DTO {
@ApiProperty({ required: true })
@ApiProperty({ required: true, type: String, nullable: true })
@IsString({ message: 'Property P_GOODSCOUNTRY must be a string' })
@IsDefined({ message: 'Property P_GOODSCOUNTRY is required' })
P_GOODSCOUNTRY: string;
P_GOODSCOUNTRY: string | null = null;
}
export class REASON_CODE_DTO {
@ -90,7 +145,7 @@ export class EXTENSION_PERIOD_DTO {
@IsNumber({}, { message: 'Property P_EXTENSIONPERIOD must be a number' })
@Transform(({ value }) => Number(value))
@IsDefined({ message: 'Property P_EXTENSIONPERIOD is required' })
P_EXTENSIONPERIOD: number;
P_EXTENSIONPERIOD: number = 0;
}
export class ORDERTYPE_DTO {
@ -323,7 +378,7 @@ export class USSETS_DTO {
@Transform(({ value }) => Number(value))
@IsNumber({}, { message: 'Property P_USSETS must be a number' })
@IsDefined({ message: 'Property P_USSETS is required' })
P_USSETS: number;
P_USSETS: number = 0;
}
export enum VOT {

View File

@ -3,8 +3,11 @@ import { IntersectionType, PartialType } from "@nestjs/swagger";
import {
APPLICATIONNAME_DTO, AUTHREP_DTO, AUTO_FLAG_DTO, CARNETNO_DTO, COMMERCIAL_SAMPLE_FLAG_DTO,
COUNTRYTABLE_DTO, CUSTCOURIERNO_DTO, DELIVERYMETHOD_DTO, DELIVERYTYPE_DTO,
DESCRIPTION_DTO,
EXIBITIONS_FAIR_FLAG_DTO, EXTENSION_PERIOD_DTO, FORMOFSECURITY_DTO, GLTABLE_DTO, GLTABLE_ITEMNO_OPTIONAL_DTO, GOODS_COUNTRY_DTO, GOODS_PORT_DTO, HEADERID_DTO,
HORSE_FLAG_DTO, INSPROTECTION_DTO, ITEMNO_DTO, LDIPROTECTION_DTO, ORDERTYPE_DTO, PAYMENTMETHOD_DTO,
HORSE_FLAG_DTO, INSPROTECTION_DTO, ITEMNO_DTO, LDIPROTECTION_DTO, ORDERID_DTO, ORDERTYPE_DTO, PAYMENTAMOUNT_DTO, PAYMENTERROR_DTO, PAYMENTMETHOD_DTO,
PAYMENTSTATUS_DTO,
PRICE_DTO,
PRINTGL_DTO,
PROF_EQUIPMENT_FLAG_DTO, REASON_CODE_DTO, REFNO_DTO, SHIPADDRID_DTO, SHIPCONTACTID_DTO, SHIPNAME_DTO, SHIPTOTYPE_DTO, USSETS_DTO
} from "./carnet-application-property.dto";
@ -60,14 +63,19 @@ export class SaveExtensionApplicationDTO extends (IntersectionType(
USERID_DTO,
SPID_DTO,
HEADERID_DTO,
GOODS_PORT_DTO,
GOODS_COUNTRY_DTO,
PartialType(GOODS_PORT_DTO),
PartialType(GOODS_COUNTRY_DTO),
REASON_CODE_DTO,
EXTENSION_PERIOD_DTO
PartialType(EXTENSION_PERIOD_DTO)
)) { }
export class PrintCarnetDTO extends (IntersectionType(SPID_DTO, HEADERID_DTO, PRINTGL_DTO)) { }
export class PrintGLDTO extends (IntersectionType(SPID_DTO, HEADERID_DTO)) { }
export class CapturePaymentDTO extends (IntersectionType(ORDERID_DTO, APPLICATIONNAME_DTO, PRICE_DTO, USERID_DTO)) { }
export class InitiatePaymentDTO extends (IntersectionType(APPLICATIONNAME_DTO, PRICE_DTO, DESCRIPTION_DTO, USERID_DTO)) { }
export class GetOrderDetailsDTO extends (IntersectionType(ORDERID_DTO)) { }
export class GetPaymentHistoryDTO extends (IntersectionType(APPLICATIONNAME_DTO, USERID_DTO)) { }
export class SaveHistoryDTO extends (IntersectionType(APPLICATIONNAME_DTO, ORDERID_DTO, PAYMENTAMOUNT_DTO, PAYMENTSTATUS_DTO, USERID_DTO, PartialType(PAYMENTERROR_DTO))) { }
export class CreateApplicationDTO extends IntersectionType(
SPID_DTO, CLIENTID_DTO, LOCATIONID_DTO, USERID_DTO, APPLICATIONNAME_DTO,
@ -97,7 +105,7 @@ export class DeleteGenerallistItemsDTO extends IntersectionType(
) { }
export class AddCountriesDTO extends IntersectionType(
HEADERID_DTO, USSETS_DTO, COUNTRYTABLE_DTO, USERID_DTO
HEADERID_DTO, PartialType(USSETS_DTO), COUNTRYTABLE_DTO, USERID_DTO
) { }
export class UpdateShippingDetailsDTO extends IntersectionType(

View File

@ -60,6 +60,13 @@ export class CLIENTNAME_DTO {
P_CLIENTNAME: string;
}
export class INDUSTRY_TYPE_DTO {
@ApiProperty({ required: true })
@IsString({ message: 'Property P_INDUSTRYTYPE must be a string' })
@IsDefined({ message: 'Property P_INDUSTRYTYPE is required' })
P_INDUSTRYTYPE: string;
}
export class REVENUELOCATION_DTO {
@ApiProperty({ required: true })
@Length(0, 2, {

View File

@ -9,7 +9,7 @@ import {
import {
CLIENT_CONTACTID_DTO, CLIENTID_DTO, CLIENTLOCADDRESSTABLE_DTO, CLIENTLOCATIONID_DTO,
CLIENTNAME_DTO, LOCATIONNAME_DTO, NAMEOF_DTO, PREPARERNAME_DTO, REVENUELOCATION_DTO, STATUS_DTO
CLIENTNAME_DTO, INDUSTRY_TYPE_DTO, LOCATIONNAME_DTO, NAMEOF_DTO, PREPARERNAME_DTO, REVENUELOCATION_DTO, STATUS_DTO
} from "./manage-clients-property.dto";
import {
@ -34,7 +34,8 @@ export class CreateClientDataDTO extends IntersectionType(
PartialType(COUNTRY_DTO),
ISSUING_REGION_DTO,
REVENUELOCATION_DTO,
USERID_DTO
USERID_DTO,
INDUSTRY_TYPE_DTO
) { }
@ -49,7 +50,8 @@ export class UpdateClientDTO extends IntersectionType(
ZIP_DTO,
COUNTRY_DTO,
REVENUELOCATION_DTO,
USERID_DTO
USERID_DTO,
INDUSTRY_TYPE_DTO
) { }
export class UpdateClientContactsDTO extends IntersectionType(

View File

@ -1,6 +1,13 @@
import { ApiProperty } from "@nestjs/swagger";
import { IsNumber, IsString } from "class-validator";
export class PCT_VALUE_DTO {
@ApiProperty({ required: true })
@IsString()
P_PCT_VALUE: string;
}
export class EFFDATE_DTO {
@ApiProperty({ required: true })
@IsString()

View File

@ -6,7 +6,7 @@ import { HOLDERTYPE_DTO, USCIBMEMBERFLAG_DTO } from "../manage-holders/manage-ho
import {
BASICFEESETUPID_DTO, BONDRATESETUPID_DTO, CARGORATESETUPID_DTO, CFFEESETUPID_DTO,
COMMRATE_DTO, CSFEESETUPID_DTO, CUSTOMERTYPE_DTO, EFFDATE_DTO, EFFEESETUPID_DTO, ENDSETS_DTO,
ENDTIME_DTO, FEECOMMID_DTO, FEES_DTO, RATE_DTO, SPCLCOMMODITY_DTO, SPCLCOUNTRY_DTO,
ENDTIME_DTO, FEECOMMID_DTO, FEES_DTO, PCT_VALUE_DTO, RATE_DTO, SPCLCOMMODITY_DTO, SPCLCOUNTRY_DTO,
STARTSETS_DTO, STARTTIME_DTO, TIMEZONE_DTO
} from "./manage-fee-property.dto";
@ -36,7 +36,8 @@ export class CreateBondRateDTO extends IntersectionType(
SPCLCOUNTRY_DTO,
EFFDATE_DTO,
RATE_DTO,
USERID_DTO
USERID_DTO,
PCT_VALUE_DTO
) { }
export class CreateCargoRateDTO extends IntersectionType(
@ -100,7 +101,8 @@ export class UpdateBondRateDTO extends IntersectionType(
BONDRATESETUPID_DTO,
RATE_DTO,
EFFDATE_DTO,
USERID_DTO
USERID_DTO,
PCT_VALUE_DTO
) { }
export class UpdateCargoRateDTO extends IntersectionType(

View File

@ -1,7 +1,7 @@
import { IntersectionType } from '@nestjs/swagger';
import { REGION_CODE_DTO, REGIONID_DTO } from './region-property.dto';
import { NAME_DTO } from '../../property.dto';
import { NAME_DTO, SPID_DTO } from '../../property.dto';
export class InsertRegionsDto extends IntersectionType(REGION_CODE_DTO, NAME_DTO) { }
export class InsertRegionsDto extends IntersectionType(REGION_CODE_DTO, NAME_DTO, SPID_DTO) { }
export class UpdateRegionDto extends IntersectionType(REGIONID_DTO, NAME_DTO) { }

View File

@ -0,0 +1,19 @@
import { HttpException, HttpStatus } from '@nestjs/common';
export class NotFoundException extends HttpException {
constructor(
message = 'Not Found',
// errorCode = 'INTERNAL_ERROR',
// data: any = null,
) {
super(
{
statusCode: HttpStatus.NOT_FOUND ,
message,
// errorCode,
// data,
},
HttpStatus.NOT_FOUND,
);
}
}

View File

@ -7,7 +7,8 @@ import {
AddCountriesDTO,
AddGenerallistItemsDTO,
CA_UpdateHolderDTO,
CarnetProcessingCenterDTO, CarnetProcessingCenterDTO2, CopyCarnetDTO, CreateApplicationDTO, DeleteGenerallistItemsDTO, EditGenerallistItemsDTO, GetCarnetControlCenterDTO, GetExtendedSectionDTO, PrintCarnetDTO, PrintGLDTO, SaveCarnetApplicationDTO, SaveExtensionApplicationDTO, TransmitApplicationtoProcessDTO,
CapturePaymentDTO,
CarnetProcessingCenterDTO, CarnetProcessingCenterDTO2, CopyCarnetDTO, CreateApplicationDTO, DeleteGenerallistItemsDTO, EditGenerallistItemsDTO, GetCarnetControlCenterDTO, GetExtendedSectionDTO, GetOrderDetailsDTO, GetPaymentHistoryDTO, InitiatePaymentDTO, PrintCarnetDTO, PrintGLDTO, SaveCarnetApplicationDTO, SaveExtensionApplicationDTO, SaveHistoryDTO, TransmitApplicationtoProcessDTO,
UpdateExpGoodsAuthRepDTO,
UpdateShippingDetailsDTO
} from 'src/dto/property.dto';
@ -271,4 +272,33 @@ export class CarnetApplicationController {
}
}
//[payment]
@Post('InitiatePayment')
async InitiatePayment(@Body() body: InitiatePaymentDTO) {
return this.carnetApplicationService.InitiatePayment(body);
}
@Post('CompletePayment')
@HttpCode(200)
async CapturePayment(@Body() body: CapturePaymentDTO) {
return this.carnetApplicationService.CapturePayment(body);
}
// @Get('OrderDetails/:P_ORDERID')
async OrderDetails(@Param() body: GetOrderDetailsDTO) {
return this.carnetApplicationService.OrderDetails(body);
}
@Get('GetPaymentHistory/:P_APPLICATIONNAME/:P_USERID')
async GetPaymentHistory(@Param() body: GetPaymentHistoryDTO) {
return this.carnetApplicationService.GetPaymentHistory(body);
}
@Post('SaveHistory')
@HttpCode(200)
async SaveHistory(@Body() body: SaveHistoryDTO) {
return this.carnetApplicationService.SaveHistory(body);
}
}

View File

@ -20,10 +20,18 @@ import {
CopyCarnetDTO,
GetExtendedSectionDTO,
SaveExtensionApplicationDTO,
CarnetProcessingCenterDTO2
CarnetProcessingCenterDTO2,
CapturePaymentDTO,
InitiatePaymentDTO,
GetOrderDetailsDTO,
GetPaymentHistoryDTO,
SaveHistoryDTO
} from 'src/dto/property.dto';
import { OracleService } from '../oracle.service';
import { BadRequestException } from 'src/exceptions/badRequest.exception';
import axios, { AxiosError, AxiosRequestConfig } from 'axios';
import { PaypalService } from 'src/paypal/paypal.service';
import { NotFoundException } from 'src/exceptions/notFound.exception';
@Injectable()
export class CarnetApplicationService {
@ -32,7 +40,8 @@ export class CarnetApplicationService {
constructor(
private readonly oracleDBService: OracleDBService,
private readonly oracleService: OracleService
private readonly oracleService: OracleService,
private readonly payPalService: PaypalService
) { }
// [ CARNETAPPLICATION_PKG ]
@ -960,7 +969,7 @@ export class CarnetApplicationService {
}
// return { statusCode: 200, message: "Extended successfully", ...fres[0] };
return fres;
return fres.length > 0 ? fres[0] : [];
} catch (error) {
handleError(error, CarnetApplicationService.name)
} finally {
@ -1582,4 +1591,376 @@ export class CarnetApplicationService {
}
}
// [payment]
async formatAmount(amount) {
if (typeof amount !== 'number' || isNaN(amount)) {
throw new Error('Amount must be a valid number');
}
if (amount < 0) {
throw new BadRequestException('Amount must be non-negative');
}
return amount.toFixed(2); // Always returns a string with 2 decimal places
}
async InitiatePayment(body: InitiatePaymentDTO) {
// console.log(body);
const quantity = 1;
let orderID: string | null = null
const formattedAmount = await this.formatAmount(body.P_PRICE * quantity);
try {
const accessToken = await this.payPalService.generateAccessToken();
// return {
// id: "6CU709471L678832M",
// href: "https://www.sandbox.paypal.com/checkoutnow?token=6CU709471L678832M"
// }
const response = await axios({
url: process.env.PAYPAL_BASE_URL + '/v2/checkout/orders',
method: 'post',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer ' + accessToken
},
data: JSON.stringify({
intent: 'CAPTURE',
purchase_units: [
{
items: [
{
name: 'Carnet',
description: body.P_DESCRIPTION,
quantity: quantity,
unit_amount: {
currency_code: 'USD',
value: formattedAmount
}
}
],
amount: {
currency_code: 'USD',
value: formattedAmount,
breakdown: {
item_total: {
currency_code: 'USD',
value: formattedAmount
}
}
}
}
],
application_context: {
return_url: process.env.BASE_URL + '/complete-order',
cancel_url: process.env.BASE_URL + '/cancel-order',
shipping_preference: 'NO_SHIPPING',
user_action: 'PAY_NOW',
brand_name: 'test sample',
disable_funding: "card"
}
})
});
orderID = response?.data?.id;
// console.log('PayPal create order response headers:', response.headers);
// console.log('PayPal create order response data:', response.data);
const save_result = await this.SaveHistory(
{
P_APPLICATIONNAME: body.P_APPLICATIONNAME,
P_ORDERID: response?.data?.id,
P_PAYMENTAMOUNT: body.P_PRICE,
P_STATUS: "CREATED",
P_USERID: body.P_USERID
// "P_PAYMENTERROR": {}
}
);
if (save_result.statusCode === 200) {
return {
id: response?.data?.id, href: response?.data?.links?.find(obj => obj.rel === 'approve')?.href
};
}
else {
throw new InternalServerException();
}
// return { id: response.data.id };
} catch (error) {
console.error('Error creating PayPal order:', error.response?.data || error?.message || error);
await this.SaveHistory({
P_APPLICATIONNAME: body.P_APPLICATIONNAME,
P_ORDERID: orderID,
P_PAYMENTAMOUNT: body.P_PRICE,
P_STATUS: "FAILED",
P_USERID: body.P_USERID,
P_PAYMENTERROR: orderID ? error?.message : `Error while creating ORDERID : ${error?.message}`
});
throw new InternalServerException();
}
}
async CapturePayment(body: CapturePaymentDTO) {
try {
const approve_result = await this.OrderDetails({ P_ORDERID: body.P_ORDERID });
const formattedAmount = await this.formatAmount(body.P_PRICE);
if (approve_result.data.id === body.P_ORDERID
&& approve_result.data.status === "APPROVED"
&& approve_result.data.purchase_units[0]?.amount?.value === formattedAmount + ""
) {
await this.SaveHistory({
P_APPLICATIONNAME: body.P_APPLICATIONNAME,
P_ORDERID: body.P_ORDERID,
P_PAYMENTAMOUNT: body.P_PRICE,
P_STATUS: "APPROVED",
P_USERID: body.P_USERID
// P_PAYMENTERROR: `Error while capturing Payment : ${error?.message}`
});
const accessToken = await this.payPalService.generateAccessToken();
const response = await axios({
url: `${process.env.PAYPAL_BASE_URL}/v2/checkout/orders/${body.P_ORDERID}/capture`,
method: 'post',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${accessToken}`,
},
});
const capture = response?.data?.purchase_units?.[0]?.payments?.captures?.[0];
// console.log(JSON.stringify(response?.data));
await this.SaveHistory({
P_APPLICATIONNAME: body.P_APPLICATIONNAME,
P_ORDERID: body.P_ORDERID,
P_PAYMENTAMOUNT: body.P_PRICE,
P_STATUS: "COMPLETED",
P_USERID: body.P_USERID
// P_PAYMENTERROR: `Error while capturing Payment : ${error?.message}`
});
return {
statusCode: 200,
message: "Payment successful",
amount: `${capture?.amount?.value} ${capture?.amount?.currency_code}`,
};
}
else {
await this.SaveHistory({
P_APPLICATIONNAME: body.P_APPLICATIONNAME,
P_ORDERID: body.P_ORDERID,
P_PAYMENTAMOUNT: body.P_PRICE,
P_STATUS: "FAILED",
P_USERID: body.P_USERID,
P_PAYMENTERROR: `Error while validating user approval`
});
throw new InternalServerException("failed to save approved status");
}
} catch (error: any) {
const status = error.response?.status || 500;
const message = error.response?.data?.message || "Payment failed due to an unexpected error.";
// Optional: log full error for debugging
console.error("PayPal Capture Error:", {
status,
message,
details: error.response?.data,
});
if (status === 404) {
await this.SaveHistory({
P_APPLICATIONNAME: body.P_APPLICATIONNAME,
P_ORDERID: body.P_ORDERID,
P_PAYMENTAMOUNT: body.P_PRICE,
P_STATUS: "FAILED",
P_USERID: body.P_USERID,
P_PAYMENTERROR: `ORDERID NOT FOUND : ${error?.message}`
});
throw new NotFoundException(error.response?.data?.message || error?.message || "Resource Not Found")
}
await this.SaveHistory({
P_APPLICATIONNAME: body.P_APPLICATIONNAME,
P_ORDERID: body.P_ORDERID,
P_PAYMENTAMOUNT: body.P_PRICE,
P_STATUS: "FAILED",
P_USERID: body.P_USERID,
P_PAYMENTERROR: `Error while capturing Payment : ${error?.message}`
});
throw new InternalServerException();
}
}
async OrderDetails(body: GetOrderDetailsDTO) {
try {
// Max length of Order ID: 36 characters (UUID)
if (!body.P_ORDERID || body.P_ORDERID.length > 36) {
throw new BadRequestException("Invalid Order ID");
}
const accessToken = await this.payPalService.generateAccessToken();
const response = await axios({
url: `${process.env.PAYPAL_BASE_URL}/v2/checkout/orders/${body.P_ORDERID}`,
method: 'get',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${accessToken}`,
},
});
// console.log("PayPal Order Details:", JSON.stringify(response.data));
return {
statusCode: 200,
message: "Order details retrieved successfully",
data: response.data,
};
} catch (error: any) {
const isAxiosError = error.isAxiosError;
if (isAxiosError) {
const axiosErrorCode = error.code;
switch (axiosErrorCode) {
case 'ECONNABORTED':
case 'ETIMEDOUT':
console.error("PayPal request timed out. Please try again later.")
throw new InternalServerException("Service is currently unreachable. Please try again later.");
case 'ENOTFOUND':
case 'ECONNREFUSED':
console.error("PayPal service is currently unavailable. Please try again later.")
throw new InternalServerException("Service is currently unreachable. Please try again later.");
}
}
const status = error.response?.status || 500;
const message = error.response?.data?.message || "Failed to fetch order details.";
console.error("PayPal OrderDetails Error:", {
status,
message,
details: error.response?.data,
});
if (status === 404) {
throw new NotFoundException(message);
}
throw new InternalServerException("A Error Occured while Processing your Request");
}
}
async GetPaymentHistory(body: GetPaymentHistoryDTO) {
let connection;
try {
connection = await this.oracleDBService.getConnection();
const result = await connection.execute(
`BEGIN
Pay_GetPaymentHistory(
:P_APPLICATIONNAME, :P_USERID, :P_CURSOR
);
END;`,
{
P_APPLICATIONNAME: { val: body.P_APPLICATIONNAME, type: oracledb.DB_TYPE_NVARCHAR },
P_USERID: { val: body.P_USERID, type: oracledb.DB_TYPE_NVARCHAR },
P_CURSOR: { dir: oracledb.BIND_OUT, type: oracledb.CURSOR },
// P_CURSOR: { type: oracledb.CURSOR, dir: oracledb.BIND_OUT }
},
{ outFormat: oracledb.OUT_FORMAT_OBJECT }
);
await connection.commit();
const outBinds = result.outBinds;
if (!outBinds?.P_CURSOR) {
this.logger.error('One or more expected cursors are missing from stored procedure output.');
throw new InternalServerException("Incomplete data received from the database.");
}
const fres: any = await fetchCursor(outBinds.P_CURSOR, CarnetApplicationService.name);
if (fres.length > 0 && fres[0].ERRORMESG) {
this.logger.warn(fres[0].ERRORMESG);
throw new BadRequestException(fres[0].ERRORMESG)
}
return fres.length > 0 ? fres[0] : {}
} catch (error) {
handleError(error, CarnetApplicationService.name)
} finally {
await closeOracleDbConnection(connection, CarnetApplicationService.name)
}
}
async SaveHistory(body: SaveHistoryDTO) {
let connection;
try {
connection = await this.oracleDBService.getConnection();
const result = await connection.execute(
`BEGIN
Pay_SaveHistory(
:P_APPLICATIONNAME, :P_ORDERID, :P_PAYMENTAMOUNT, :P_STATUS, :P_USERID, :P_PAYMENTERROR, :P_CURSOR
);
END;`,
{
P_APPLICATIONNAME: { val: body.P_APPLICATIONNAME, type: oracledb.DB_TYPE_NVARCHAR },
P_ORDERID: { val: body.P_ORDERID, type: oracledb.DB_TYPE_NVARCHAR },
P_PAYMENTAMOUNT: { val: body.P_PAYMENTAMOUNT, type: oracledb.DB_TYPE_NUMBER },
P_STATUS: { val: body.P_STATUS, type: oracledb.DB_TYPE_NVARCHAR },
P_USERID: { val: body.P_USERID, type: oracledb.DB_TYPE_NVARCHAR },
P_PAYMENTERROR: { val: body.P_PAYMENTERROR, type: oracledb.DB_TYPE_NVARCHAR },
P_CURSOR: { dir: oracledb.BIND_OUT, type: oracledb.CURSOR },
// P_CURSOR: { type: oracledb.CURSOR, dir: oracledb.BIND_OUT }
},
{ outFormat: oracledb.OUT_FORMAT_OBJECT }
);
await connection.commit();
const outBinds = result.outBinds;
if (!outBinds?.P_CURSOR) {
this.logger.error('One or more expected cursors are missing from stored procedure output.');
throw new InternalServerException("Incomplete data received from the database.");
}
const fres: any = await fetchCursor(outBinds.P_CURSOR, CarnetApplicationService.name);
if (fres.length > 0 && fres[0].ERRORMESG) {
this.logger.warn(fres[0].ERRORMESG);
throw new BadRequestException(fres[0].ERRORMESG)
}
return { statusCode: 200, message: "Saved Successfully", ...fres[0] };
} catch (error) {
handleError(error, CarnetApplicationService.name)
} finally {
await closeOracleDbConnection(connection, CarnetApplicationService.name)
}
}
}

View File

@ -39,6 +39,7 @@ export class ManageClientsService {
P_ISSUINGREGION: null,
P_REVENUELOCATION: null,
P_USERID: null,
P_INDUSTRYTYPE: null
};
const reqBody = JSON.parse(JSON.stringify(body));
@ -58,6 +59,7 @@ export class ManageClientsService {
:P_SPID, :P_CLIENTNAME, :P_LOOKUPCODE, :P_ADDRESS1,
:P_ADDRESS2, :P_CITY, :P_STATE, :P_ZIP,
:P_COUNTRY, :P_ISSUINGREGION, :P_REVENUELOCATION, :P_USERID,
:P_INDUSTRYTYPE,
:P_CLIENTCURSOR
);
END;`,
@ -74,6 +76,7 @@ export class ManageClientsService {
P_ISSUINGREGION: { val: finalBody.P_ISSUINGREGION, type: oracledb.DB_TYPE_NVARCHAR },
P_REVENUELOCATION: { val: finalBody.P_REVENUELOCATION, type: oracledb.DB_TYPE_NVARCHAR },
P_USERID: { val: finalBody.P_USERID, type: oracledb.DB_TYPE_NVARCHAR },
P_INDUSTRYTYPE: { val: finalBody.P_INDUSTRYTYPE, type: oracledb.DB_TYPE_NVARCHAR },
P_CLIENTCURSOR: { type: oracledb.CURSOR, dir: oracledb.BIND_OUT }
},
{
@ -118,6 +121,7 @@ export class ManageClientsService {
P_COUNTRY: null,
P_REVENUELOCATION: null,
P_USERID: null,
P_INDUSTRYTYPE: null
};
const reqBody = JSON.parse(JSON.stringify(body));
@ -135,7 +139,8 @@ export class ManageClientsService {
MANAGEPREPARER_PKG.UpdateClient(
:P_SPID, :P_CLIENTID, :P_PREPARERNAME, :P_ADDRESS1,
:P_ADDRESS2, :P_CITY, :P_STATE, :P_ZIP,
:P_COUNTRY, :P_REVENUELOCATION, :P_USERID, :P_CURSOR
:P_COUNTRY, :P_REVENUELOCATION, :P_USERID, :P_INDUSTRYTYPE,
:P_CURSOR
);
END;`,
{
@ -150,6 +155,7 @@ export class ManageClientsService {
P_COUNTRY: { val: finalBody.P_COUNTRY, type: oracledb.DB_TYPE_NVARCHAR },
P_REVENUELOCATION: { val: finalBody.P_REVENUELOCATION, type: oracledb.DB_TYPE_NVARCHAR },
P_USERID: { val: finalBody.P_USERID, type: oracledb.DB_TYPE_NVARCHAR },
P_INDUSTRYTYPE: { val: finalBody.P_INDUSTRYTYPE, type: oracledb.DB_TYPE_NVARCHAR },
P_CURSOR: { type: oracledb.CURSOR, dir: oracledb.BIND_OUT }
},
{

View File

@ -195,6 +195,7 @@ export class ManageFeeService {
MANAGEFEE_SETUP_PKG.CREATEBONDRATE(
:P_SPID, :P_HOLDERTYPE, :P_USCIBMEMBERFLAG, :P_SPCLCOMMODITY,
:P_SPCLCOUNTRY, :P_EFFDATE, :P_RATE, :P_USERID,
:P_PCT_VALUE,
:P_CURSOR
);
END;`,
@ -207,6 +208,7 @@ export class ManageFeeService {
P_EFFDATE: { val: body.P_EFFDATE, type: oracledb.DB_TYPE_VARCHAR },
P_RATE: { val: body.P_RATE, type: oracledb.DB_TYPE_NUMBER },
P_USERID: { val: body.P_USERID, type: oracledb.DB_TYPE_VARCHAR },
P_PCT_VALUE: { val: body.P_PCT_VALUE, type: oracledb.DB_TYPE_NVARCHAR },
P_CURSOR: { type: oracledb.CURSOR, dir: oracledb.BIND_OUT }
},
{
@ -238,7 +240,7 @@ export class ManageFeeService {
const result = await connection.execute(
`BEGIN
MANAGEFEE_SETUP_PKG.UPDATEBONDRATE(
:P_BONDRATESETUPID, :P_RATE, :P_EFFDATE, :P_USERID,
:P_BONDRATESETUPID, :P_RATE, :P_EFFDATE, :P_USERID, :P_PCT_VALUE,
:P_CURSOR
);
END;`,
@ -247,6 +249,7 @@ export class ManageFeeService {
P_RATE: { val: body.P_RATE, type: oracledb.DB_TYPE_NUMBER },
P_EFFDATE: { val: body.P_EFFDATE, type: oracledb.DB_TYPE_NVARCHAR },
P_USERID: { val: body.P_USERID, type: oracledb.DB_TYPE_NVARCHAR },
P_PCT_VALUE: { val: body.P_PCT_VALUE, type: oracledb.DB_TYPE_NVARCHAR },
P_CURSOR: { type: oracledb.CURSOR, dir: oracledb.BIND_OUT }
},
{

View File

@ -10,11 +10,13 @@ import { UserMaintenanceModule } from './user-maintenance/user-maintenance.modul
import { CarnetApplicationModule } from './carnet-application/carnet-application.module';
import { OracleService } from './oracle.service';
import { AuthModule } from 'src/auth/auth.module';
import { PaypalModule } from 'src/paypal/paypal.module';
@Global()
@Module({
imports: [
DbModule,
PaypalModule,
CarnetApplicationModule,
UserMaintenanceModule,
HomePageModule,

View File

@ -1,11 +1,11 @@
import { RegionService } from './region.service';
import { Get, Post, Body, Controller, Patch, UseGuards } from '@nestjs/common';
import { Get, Post, Body, Controller, Patch, UseGuards, Param } from '@nestjs/common';
import { ApiTags } from '@nestjs/swagger';
import { Roles } from 'src/decorators/roles.decorator';
import { InsertRegionsDto, UpdateRegionDto } from 'src/dto/property.dto';
import { InsertRegionsDto, SPID_DTO, UpdateRegionDto } from 'src/dto/property.dto';
import { JwtAuthGuard } from 'src/guards/jwt-auth.guard';
import { RolesGuard } from 'src/guards/roles.guard';
@ -26,9 +26,9 @@ export class RegionController {
return this.regionService.updateRegions(body);
}
@Get('/GetRegions')
getRegions() {
return this.regionService.getRegions();
@Get('/GetRegions/:P_SPID')
getRegions(@Param() param: SPID_DTO) {
return this.regionService.getRegions(param);
}
}

View File

@ -5,7 +5,7 @@ import { BadRequestException } from 'src/exceptions/badRequest.exception';
import { InternalServerException } from 'src/exceptions/internalServerError.exception';
import { closeOracleDbConnection, fetchCursor, handleError } from 'src/utils/helper';
import { InsertRegionsDto, UpdateRegionDto } from 'src/dto/property.dto';
import { InsertRegionsDto, SPID_DTO, UpdateRegionDto } from 'src/dto/property.dto';
@Injectable()
export class RegionService {
@ -21,11 +21,12 @@ export class RegionService {
const result = await connection.execute(
`BEGIN
USCIB_Managed_Pkg.InsertNewRegion(:P_REGION,:P_NAME,:P_CURSOR);
USCIB_Managed_Pkg.InsertNewRegion(:P_REGION, :P_NAME, :P_SPID, :P_CURSOR);
END;`,
{
P_REGION: { val: body.P_REGION, type: oracledb.DB_TYPE_VARCHAR },
P_NAME: { val: body.P_NAME, type: oracledb.DB_TYPE_VARCHAR },
P_SPID: { val: body.P_SPID, type: oracledb.DB_TYPE_NUMBER },
P_CURSOR: { type: oracledb.CURSOR, dir: oracledb.BIND_OUT },
},
{ outFormat: oracledb.OUT_FORMAT_OBJECT }
@ -95,16 +96,17 @@ export class RegionService {
}
}
async getRegions() {
async getRegions(body: SPID_DTO) {
let connection;
try {
connection = await this.oracleDBService.getConnection();
const result = await connection.execute(
`BEGIN
USCIB_Managed_Pkg.GetRegions(:P_CURSOR);
USCIB_Managed_Pkg.GetRegions(:P_SPID, :P_CURSOR);
END;`,
{
P_SPID: { val: body.P_SPID, type: oracledb.DB_TYPE_NUMBER },
P_CURSOR: { type: oracledb.CURSOR, dir: oracledb.BIND_OUT }
},
{ outFormat: oracledb.OUT_FORMAT_OBJECT }

View File

@ -395,7 +395,7 @@ export class UserMaintenanceService {
const mailRes = await this.authService.sendMail({ P_TO: body.P_USERID, P_MAIL_TYPE: MailTypeDTO.REGISTER_CLIENT })
if (mailRes.statusCode !== 200) {
return new InternalServerException();
throw new InternalServerException();
}
return { statusCode: 201, message: "Client registration initiated successfully", ...fres[0] };

View File

@ -0,0 +1,4 @@
import { Controller } from '@nestjs/common';
@Controller('paypal')
export class PaypalController {}

View File

@ -0,0 +1,10 @@
import { Global, Module } from '@nestjs/common';
import { PaypalController } from './paypal.controller';
import { PaypalService } from './paypal.service';
@Global()
@Module({
providers: [PaypalService],
exports: [PaypalService]
})
export class PaypalModule { }

View File

@ -0,0 +1,69 @@
// paypal.service.ts
import { Injectable, Logger } from '@nestjs/common';
import axios from 'axios';
import { InternalServerException } from 'src/exceptions/internalServerError.exception';
@Injectable()
export class PaypalService {
private accessToken: string | null = null;
private tokenExpiresAt: number = 0; // Unix timestamp in milliseconds
private readonly clientId = process.env.PAYPAL_CLIENT_ID;
private readonly clientSecret = process.env.PAYPAL_CLIENT_SECRET;
private readonly baseUrl = process.env.PAYPAL_BASE_URL; // or live URL
private readonly logger = new Logger(PaypalService.name);
async generateAccessToken(): Promise<any> {
// return process.env.PAYPAL_AT
const now = Date.now();
// Check if token is still valid
if (this.accessToken && now < this.tokenExpiresAt - 60_000) {
return this.accessToken;
}
// Fetch new token
const auth = Buffer.from(`${this.clientId}:${this.clientSecret}`).toString('base64');
try {
const tokenUrl = process.env.PAYPAL_BASE_URL + '/v1/oauth2/token';
const params = new URLSearchParams();
params.append('grant_type', 'client_credentials');
const response = await axios.post(
tokenUrl,
params.toString(), // or use `params` directly
{
auth: {
username: process.env.PAYPAL_CLIENT_ID!,
password: process.env.PAYPAL_SECRET!,
},
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
}
);
const { access_token, expires_in } = response.data;
this.accessToken = access_token;
this.tokenExpiresAt = now + expires_in * 1000; // convert seconds to ms
// this.logger.warn(`Fetched new PayPal access token, expires in ${access_token} seconds`);
// console.log(access_token);
if (this.accessToken) {
return this.accessToken;
}
throw new InternalServerException("Error while getting paypal access token")
} catch (error) {
this.logger.error('Failed to fetch PayPal access token', error);
throw new InternalServerException('PayPal token fetch failed');
}
}
}