pay 2
This commit is contained in:
parent
a61623b0aa
commit
75a537aab4
@ -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: [],
|
||||
|
||||
@ -39,6 +39,13 @@ export class CARNETNO_DTO {
|
||||
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 {
|
||||
@ApiProperty({ required: true })
|
||||
@Max(999999999, {
|
||||
|
||||
@ -4,7 +4,7 @@ import {
|
||||
APPLICATIONNAME_DTO, AUTHREP_DTO, AUTO_FLAG_DTO, CARNETNO_DTO, COMMERCIAL_SAMPLE_FLAG_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,
|
||||
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,
|
||||
PROF_EQUIPMENT_FLAG_DTO, REASON_CODE_DTO, REFNO_DTO, SHIPADDRID_DTO, SHIPCONTACTID_DTO, SHIPNAME_DTO, SHIPTOTYPE_DTO, USSETS_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 PrintGLDTO extends (IntersectionType(SPID_DTO, HEADERID_DTO)) { }
|
||||
export class CapturePaymentDTO extends (IntersectionType(ORDERID_DTO)) { }
|
||||
|
||||
export class CreateApplicationDTO extends IntersectionType(
|
||||
SPID_DTO, CLIENTID_DTO, LOCATIONID_DTO, USERID_DTO, APPLICATIONNAME_DTO,
|
||||
|
||||
19
src/exceptions/notFound.exception.ts
Normal file
19
src/exceptions/notFound.exception.ts
Normal 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,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -7,6 +7,7 @@ import {
|
||||
AddCountriesDTO,
|
||||
AddGenerallistItemsDTO,
|
||||
CA_UpdateHolderDTO,
|
||||
CapturePaymentDTO,
|
||||
CarnetProcessingCenterDTO, CarnetProcessingCenterDTO2, CopyCarnetDTO, CreateApplicationDTO, DeleteGenerallistItemsDTO, EditGenerallistItemsDTO, GetCarnetControlCenterDTO, GetExtendedSectionDTO, PrintCarnetDTO, PrintGLDTO, SaveCarnetApplicationDTO, SaveExtensionApplicationDTO, TransmitApplicationtoProcessDTO,
|
||||
UpdateExpGoodsAuthRepDTO,
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -20,10 +20,14 @@ import {
|
||||
CopyCarnetDTO,
|
||||
GetExtendedSectionDTO,
|
||||
SaveExtensionApplicationDTO,
|
||||
CarnetProcessingCenterDTO2
|
||||
CarnetProcessingCenterDTO2,
|
||||
CapturePaymentDTO
|
||||
} 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 +36,8 @@ export class CarnetApplicationService {
|
||||
|
||||
constructor(
|
||||
private readonly oracleDBService: OracleDBService,
|
||||
private readonly oracleService: OracleService
|
||||
private readonly oracleService: OracleService,
|
||||
private readonly payPalService: PaypalService
|
||||
) { }
|
||||
|
||||
// [ 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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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,
|
||||
|
||||
4
src/paypal/paypal.controller.ts
Normal file
4
src/paypal/paypal.controller.ts
Normal file
@ -0,0 +1,4 @@
|
||||
import { Controller } from '@nestjs/common';
|
||||
|
||||
@Controller('paypal')
|
||||
export class PaypalController {}
|
||||
10
src/paypal/paypal.module.ts
Normal file
10
src/paypal/paypal.module.ts
Normal 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 { }
|
||||
69
src/paypal/paypal.service.ts
Normal file
69
src/paypal/paypal.service.ts
Normal 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');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user