This commit is contained in:
Kallesh B S 2025-08-18 20:34:44 +05:30
parent a61623b0aa
commit 75a537aab4
10 changed files with 256 additions and 5 deletions

View File

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

View File

@ -39,6 +39,13 @@ export class CARNETNO_DTO {
P_CARNETNO: string; P_CARNETNO: string;
} }
export class ORDERID_DTO {
@ApiProperty({ required: true })
@IsString()
@IsDefined({ message: 'Property P_ORDERID is required' })
P_ORDERID: string;
}
export class HEADERID_DTO { export class HEADERID_DTO {
@ApiProperty({ required: true }) @ApiProperty({ required: true })
@Max(999999999, { @Max(999999999, {

View File

@ -4,7 +4,7 @@ import {
APPLICATIONNAME_DTO, AUTHREP_DTO, AUTO_FLAG_DTO, CARNETNO_DTO, COMMERCIAL_SAMPLE_FLAG_DTO, APPLICATIONNAME_DTO, AUTHREP_DTO, AUTO_FLAG_DTO, CARNETNO_DTO, COMMERCIAL_SAMPLE_FLAG_DTO,
COUNTRYTABLE_DTO, CUSTCOURIERNO_DTO, DELIVERYMETHOD_DTO, DELIVERYTYPE_DTO, COUNTRYTABLE_DTO, CUSTCOURIERNO_DTO, DELIVERYMETHOD_DTO, DELIVERYTYPE_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, ORDERTYPE_DTO, PAYMENTMETHOD_DTO, HORSE_FLAG_DTO, INSPROTECTION_DTO, ITEMNO_DTO, LDIPROTECTION_DTO, ORDERID_DTO, ORDERTYPE_DTO, PAYMENTMETHOD_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
} from "./carnet-application-property.dto"; } from "./carnet-application-property.dto";
@ -68,6 +68,7 @@ export class SaveExtensionApplicationDTO extends (IntersectionType(
export class PrintCarnetDTO extends (IntersectionType(SPID_DTO, HEADERID_DTO, PRINTGL_DTO)) { } export class PrintCarnetDTO extends (IntersectionType(SPID_DTO, HEADERID_DTO, PRINTGL_DTO)) { }
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 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

@ -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,6 +7,7 @@ import {
AddCountriesDTO, AddCountriesDTO,
AddGenerallistItemsDTO, AddGenerallistItemsDTO,
CA_UpdateHolderDTO, CA_UpdateHolderDTO,
CapturePaymentDTO,
CarnetProcessingCenterDTO, CarnetProcessingCenterDTO2, CopyCarnetDTO, CreateApplicationDTO, DeleteGenerallistItemsDTO, EditGenerallistItemsDTO, GetCarnetControlCenterDTO, GetExtendedSectionDTO, PrintCarnetDTO, PrintGLDTO, SaveCarnetApplicationDTO, SaveExtensionApplicationDTO, TransmitApplicationtoProcessDTO, CarnetProcessingCenterDTO, CarnetProcessingCenterDTO2, CopyCarnetDTO, CreateApplicationDTO, DeleteGenerallistItemsDTO, EditGenerallistItemsDTO, GetCarnetControlCenterDTO, GetExtendedSectionDTO, PrintCarnetDTO, PrintGLDTO, SaveCarnetApplicationDTO, SaveExtensionApplicationDTO, TransmitApplicationtoProcessDTO,
UpdateExpGoodsAuthRepDTO, UpdateExpGoodsAuthRepDTO,
UpdateShippingDetailsDTO UpdateShippingDetailsDTO
@ -271,4 +272,19 @@ export class CarnetApplicationController {
} }
} }
//[payment]
@Post('InitiatePayment')
@UseGuards()
async InitiatePayment() {
return this.carnetApplicationService.InitiatePayment();
}
@Post('CompletePayment')
@HttpCode(200)
@UseGuards()
async CapturePayment(@Body() body: CapturePaymentDTO) {
return this.carnetApplicationService.CapturePayment(body);
}
} }

View File

@ -20,10 +20,14 @@ import {
CopyCarnetDTO, CopyCarnetDTO,
GetExtendedSectionDTO, GetExtendedSectionDTO,
SaveExtensionApplicationDTO, SaveExtensionApplicationDTO,
CarnetProcessingCenterDTO2 CarnetProcessingCenterDTO2,
CapturePaymentDTO
} 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';
import axios, { AxiosError, AxiosRequestConfig } from 'axios';
import { PaypalService } from 'src/paypal/paypal.service';
import { NotFoundException } from 'src/exceptions/notFound.exception';
@Injectable() @Injectable()
export class CarnetApplicationService { export class CarnetApplicationService {
@ -32,7 +36,8 @@ export class CarnetApplicationService {
constructor( constructor(
private readonly oracleDBService: OracleDBService, private readonly oracleDBService: OracleDBService,
private readonly oracleService: OracleService private readonly oracleService: OracleService,
private readonly payPalService: PaypalService
) { } ) { }
// [ CARNETAPPLICATION_PKG ] // [ CARNETAPPLICATION_PKG ]
@ -1582,4 +1587,121 @@ export class CarnetApplicationService {
} }
} }
// [payment]
async InitiatePayment() {
try {
const accessToken = await this.payPalService.generateAccessToken();
// return { accessToken, orderid: "1abc" }
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: 'carnet',
quantity: 1,
unit_amount: {
currency_code: 'USD',
value: '100.00'
}
}
],
amount: {
currency_code: 'USD',
value: '100.00',
breakdown: {
item_total: {
currency_code: 'USD',
value: '100.00'
}
}
}
}
],
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"
}
})
});
// console.log('PayPal create order response headers:', response.headers);
// console.log('PayPal create order response data:', response.data);
return {
id: response.data.id, href: response?.data?.links?.find(obj => obj.rel === 'approve')?.href
};
// return { id: response.data.id };
} catch (error) {
console.error('Error creating PayPal order:', error.response?.data || error.message || error);
// Return a structured error object or throw to be handled by caller
throw new InternalServerException();
// return {
// error: true,
// message: 'Failed to create PayPal order',
// details: error.response?.data || error.message || error
// };
}
}
async CapturePayment(body: CapturePaymentDTO) {
try {
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];
return {
statusCode: 200,
message: "Payment successful",
amount: `${capture?.amount?.value} ${capture?.amount?.currency_code}`,
};
} 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) {
throw new NotFoundException(error.response?.data?.message || error?.message || "Resource Not Found")
}
throw new InternalServerException();
}
}
} }

View File

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

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');
}
}
}