diff --git a/src/dto/carnet-application/carnet-application-property.dto.ts b/src/dto/carnet-application/carnet-application-property.dto.ts index 9b9045b..c66f1c6 100644 --- a/src/dto/carnet-application/carnet-application-property.dto.ts +++ b/src/dto/carnet-application/carnet-application-property.dto.ts @@ -51,7 +51,7 @@ export class PRICE_DTO { // @Max(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' }) @IsNumber({}, { message: 'Property P_PRICE must be a number' }) @Transform(({ value }) => Number(value)) @@ -59,6 +59,33 @@ export class PRICE_DTO { 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 { @ApiProperty({ required: true }) @IsString() diff --git a/src/dto/carnet-application/carnet-application.dto.ts b/src/dto/carnet-application/carnet-application.dto.ts index 5b3fc34..594ab32 100644 --- a/src/dto/carnet-application/carnet-application.dto.ts +++ b/src/dto/carnet-application/carnet-application.dto.ts @@ -5,7 +5,8 @@ import { 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, 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, PRINTGL_DTO, PROF_EQUIPMENT_FLAG_DTO, REASON_CODE_DTO, REFNO_DTO, SHIPADDRID_DTO, SHIPCONTACTID_DTO, SHIPNAME_DTO, SHIPTOTYPE_DTO, USSETS_DTO @@ -71,7 +72,10 @@ export class SaveExtensionApplicationDTO extends (IntersectionType( 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)) { } -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( SPID_DTO, CLIENTID_DTO, LOCATIONID_DTO, USERID_DTO, APPLICATIONNAME_DTO, diff --git a/src/oracle/carnet-application/carnet-application.controller.ts b/src/oracle/carnet-application/carnet-application.controller.ts index 0b74a32..47f918c 100644 --- a/src/oracle/carnet-application/carnet-application.controller.ts +++ b/src/oracle/carnet-application/carnet-application.controller.ts @@ -8,7 +8,7 @@ import { AddGenerallistItemsDTO, CA_UpdateHolderDTO, 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, UpdateShippingDetailsDTO } from 'src/dto/property.dto'; @@ -275,16 +275,30 @@ export class CarnetApplicationController { //[payment] @Post('InitiatePayment') - @UseGuards() async InitiatePayment(@Body() body: InitiatePaymentDTO) { return this.carnetApplicationService.InitiatePayment(body); } @Post('CompletePayment') @HttpCode(200) - @UseGuards() 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); + } + } diff --git a/src/oracle/carnet-application/carnet-application.service.ts b/src/oracle/carnet-application/carnet-application.service.ts index 439e17e..de8b0f2 100644 --- a/src/oracle/carnet-application/carnet-application.service.ts +++ b/src/oracle/carnet-application/carnet-application.service.ts @@ -22,7 +22,10 @@ import { SaveExtensionApplicationDTO, CarnetProcessingCenterDTO2, CapturePaymentDTO, - InitiatePaymentDTO + InitiatePaymentDTO, + GetOrderDetailsDTO, + GetPaymentHistoryDTO, + SaveHistoryDTO } from 'src/dto/property.dto'; import { OracleService } from '../oracle.service'; import { BadRequestException } from 'src/exceptions/badRequest.exception'; @@ -1605,6 +1608,7 @@ export class CarnetApplicationService { async InitiatePayment(body: InitiatePaymentDTO) { + // console.log(body); const quantity = 1; @@ -1613,7 +1617,10 @@ export class CarnetApplicationService { try { 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({ 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]; + // console.log(JSON.stringify(response?.data)); return { 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) + } + } }