Added the Payment Data Persistence API

This commit is contained in:
Kallesh B S 2025-08-25 16:14:25 +05:30
parent 45d57cee8d
commit a2c2ed2a68
4 changed files with 214 additions and 8 deletions

View File

@ -51,7 +51,7 @@ export class PRICE_DTO {
// @Max(999999999, { // @Max(999999999, {
// message: 'Property P_PRICE must not exceed 999999999', // message: 'Property P_PRICE must not exceed 999999999',
// }) // })
@Min(0.1, { message: 'Property P_PRICE must be greater than zero' }) @Min(0.01, { message: 'Property P_PRICE must be greater than zero' })
// @IsInt({ message: 'Property P_PRICE allows only whole numbers' }) // @IsInt({ message: 'Property P_PRICE allows only whole numbers' })
@IsNumber({}, { message: 'Property P_PRICE must be a number' }) @IsNumber({}, { message: 'Property P_PRICE must be a number' })
@Transform(({ value }) => Number(value)) @Transform(({ value }) => Number(value))
@ -59,6 +59,33 @@ export class PRICE_DTO {
P_PRICE: number; 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 })
@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 { export class DESCRIPTION_DTO {
@ApiProperty({ required: true }) @ApiProperty({ required: true })
@IsString() @IsString()

View File

@ -5,7 +5,8 @@ import {
COUNTRYTABLE_DTO, CUSTCOURIERNO_DTO, DELIVERYMETHOD_DTO, DELIVERYTYPE_DTO, COUNTRYTABLE_DTO, CUSTCOURIERNO_DTO, DELIVERYMETHOD_DTO, DELIVERYTYPE_DTO,
DESCRIPTION_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, 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, ORDERID_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, PRICE_DTO,
PRINTGL_DTO, PRINTGL_DTO,
PROF_EQUIPMENT_FLAG_DTO, REASON_CODE_DTO, REFNO_DTO, SHIPADDRID_DTO, SHIPCONTACTID_DTO, SHIPNAME_DTO, SHIPTOTYPE_DTO, USSETS_DTO PROF_EQUIPMENT_FLAG_DTO, REASON_CODE_DTO, REFNO_DTO, SHIPADDRID_DTO, SHIPCONTACTID_DTO, SHIPNAME_DTO, SHIPTOTYPE_DTO, USSETS_DTO
@ -72,6 +73,9 @@ export class PrintCarnetDTO extends (IntersectionType(SPID_DTO, HEADERID_DTO, PR
export class PrintGLDTO extends (IntersectionType(SPID_DTO, HEADERID_DTO)) { } export class PrintGLDTO extends (IntersectionType(SPID_DTO, HEADERID_DTO)) { }
export class CapturePaymentDTO extends (IntersectionType(ORDERID_DTO)) { } export class CapturePaymentDTO extends (IntersectionType(ORDERID_DTO)) { }
export class InitiatePaymentDTO extends (IntersectionType(PRICE_DTO, DESCRIPTION_DTO)) { } export class InitiatePaymentDTO extends (IntersectionType(PRICE_DTO, DESCRIPTION_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( export class CreateApplicationDTO extends IntersectionType(
SPID_DTO, CLIENTID_DTO, LOCATIONID_DTO, USERID_DTO, APPLICATIONNAME_DTO, SPID_DTO, CLIENTID_DTO, LOCATIONID_DTO, USERID_DTO, APPLICATIONNAME_DTO,

View File

@ -8,7 +8,7 @@ import {
AddGenerallistItemsDTO, AddGenerallistItemsDTO,
CA_UpdateHolderDTO, CA_UpdateHolderDTO,
CapturePaymentDTO, CapturePaymentDTO,
CarnetProcessingCenterDTO, CarnetProcessingCenterDTO2, CopyCarnetDTO, CreateApplicationDTO, DeleteGenerallistItemsDTO, EditGenerallistItemsDTO, GetCarnetControlCenterDTO, GetExtendedSectionDTO, InitiatePaymentDTO, PrintCarnetDTO, PrintGLDTO, SaveCarnetApplicationDTO, SaveExtensionApplicationDTO, TransmitApplicationtoProcessDTO, CarnetProcessingCenterDTO, CarnetProcessingCenterDTO2, CopyCarnetDTO, CreateApplicationDTO, DeleteGenerallistItemsDTO, EditGenerallistItemsDTO, GetCarnetControlCenterDTO, GetExtendedSectionDTO, GetOrderDetailsDTO, GetPaymentHistoryDTO, InitiatePaymentDTO, PrintCarnetDTO, PrintGLDTO, SaveCarnetApplicationDTO, SaveExtensionApplicationDTO, SaveHistoryDTO, TransmitApplicationtoProcessDTO,
UpdateExpGoodsAuthRepDTO, UpdateExpGoodsAuthRepDTO,
UpdateShippingDetailsDTO UpdateShippingDetailsDTO
} from 'src/dto/property.dto'; } from 'src/dto/property.dto';
@ -275,16 +275,30 @@ export class CarnetApplicationController {
//[payment] //[payment]
@Post('InitiatePayment') @Post('InitiatePayment')
@UseGuards()
async InitiatePayment(@Body() body: InitiatePaymentDTO) { async InitiatePayment(@Body() body: InitiatePaymentDTO) {
return this.carnetApplicationService.InitiatePayment(body); return this.carnetApplicationService.InitiatePayment(body);
} }
@Post('CompletePayment') @Post('CompletePayment')
@HttpCode(200) @HttpCode(200)
@UseGuards()
async CapturePayment(@Body() body: CapturePaymentDTO) { async CapturePayment(@Body() body: CapturePaymentDTO) {
return this.carnetApplicationService.CapturePayment(body); 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

@ -22,7 +22,10 @@ import {
SaveExtensionApplicationDTO, SaveExtensionApplicationDTO,
CarnetProcessingCenterDTO2, CarnetProcessingCenterDTO2,
CapturePaymentDTO, CapturePaymentDTO,
InitiatePaymentDTO InitiatePaymentDTO,
GetOrderDetailsDTO,
GetPaymentHistoryDTO,
SaveHistoryDTO
} from 'src/dto/property.dto'; } from 'src/dto/property.dto';
import { OracleService } from '../oracle.service'; import { OracleService } from '../oracle.service';
import { BadRequestException } from 'src/exceptions/badRequest.exception'; import { BadRequestException } from 'src/exceptions/badRequest.exception';
@ -1605,6 +1608,7 @@ export class CarnetApplicationService {
async InitiatePayment(body: InitiatePaymentDTO) { async InitiatePayment(body: InitiatePaymentDTO) {
// console.log(body);
const quantity = 1; const quantity = 1;
@ -1613,7 +1617,10 @@ export class CarnetApplicationService {
try { try {
const accessToken = await this.payPalService.generateAccessToken(); const accessToken = await this.payPalService.generateAccessToken();
// return { accessToken, orderid: "1abc" } // return {
// id: "6CU709471L678832M",
// href: "https://www.sandbox.paypal.com/checkoutnow?token=6CU709471L678832M"
// }
const response = await axios({ const response = await axios({
url: process.env.PAYPAL_BASE_URL + '/v2/checkout/orders', url: process.env.PAYPAL_BASE_URL + '/v2/checkout/orders',
@ -1697,6 +1704,7 @@ export class CarnetApplicationService {
}); });
const capture = response?.data?.purchase_units?.[0]?.payments?.captures?.[0]; const capture = response?.data?.purchase_units?.[0]?.payments?.captures?.[0];
// console.log(JSON.stringify(response?.data));
return { return {
statusCode: 200, statusCode: 200,
@ -1723,4 +1731,157 @@ export class CarnetApplicationService {
} }
} }
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)
}
}
} }