commit f2f0ecef94b1b1c5c3f8939ab788cf73e05f8784 Author: unknown Date: Wed Aug 5 23:22:43 2026 +0530 first commit diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..3b41682 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +/mvnw text eol=lf +*.cmd text eol=crlf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..667aaef --- /dev/null +++ b/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..8dea6c2 --- /dev/null +++ b/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,3 @@ +wrapperVersion=3.3.4 +distributionType=only-script +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.12/apache-maven-3.9.12-bin.zip diff --git a/logs/application.log b/logs/application.log new file mode 100644 index 0000000..9414d53 --- /dev/null +++ b/logs/application.log @@ -0,0 +1,4 @@ +2026-08-05 23:00:21.624 INFO c.main.ipks.IpksCbiApplicationTests - Starting IpksCbiApplicationTests using Java 26.0.2 with PID 7628 (started by Bishwajeet in D:\Users\Bishwajeet\Desktop\bulk_creation_api) +2026-08-05 23:00:21.624 DEBUG c.main.ipks.IpksCbiApplicationTests - Running with Spring Boot v3.3.5, Spring v6.1.14 +2026-08-05 23:00:21.625 INFO c.main.ipks.IpksCbiApplicationTests - No active profile set, falling back to 1 default profile: "default" +2026-08-05 23:00:24.285 INFO c.main.ipks.IpksCbiApplicationTests - Started IpksCbiApplicationTests in 3.474 seconds (process running for 5.663) diff --git a/mvnw b/mvnw new file mode 100644 index 0000000..bd8896b --- /dev/null +++ b/mvnw @@ -0,0 +1,295 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Apache Maven Wrapper startup batch script, version 3.3.4 +# +# Optional ENV vars +# ----------------- +# JAVA_HOME - location of a JDK home dir, required when download maven via java source +# MVNW_REPOURL - repo url base for downloading maven distribution +# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output +# ---------------------------------------------------------------------------- + +set -euf +[ "${MVNW_VERBOSE-}" != debug ] || set -x + +# OS specific support. +native_path() { printf %s\\n "$1"; } +case "$(uname)" in +CYGWIN* | MINGW*) + [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")" + native_path() { cygpath --path --windows "$1"; } + ;; +esac + +# set JAVACMD and JAVACCMD +set_java_home() { + # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched + if [ -n "${JAVA_HOME-}" ]; then + if [ -x "$JAVA_HOME/jre/sh/java" ]; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACCMD="$JAVA_HOME/jre/sh/javac" + else + JAVACMD="$JAVA_HOME/bin/java" + JAVACCMD="$JAVA_HOME/bin/javac" + + if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then + echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2 + echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2 + return 1 + fi + fi + else + JAVACMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v java + )" || : + JAVACCMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v javac + )" || : + + if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then + echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2 + return 1 + fi + fi +} + +# hash string like Java String::hashCode +hash_string() { + str="${1:-}" h=0 + while [ -n "$str" ]; do + char="${str%"${str#?}"}" + h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296)) + str="${str#?}" + done + printf %x\\n $h +} + +verbose() { :; } +[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; } + +die() { + printf %s\\n "$1" >&2 + exit 1 +} + +trim() { + # MWRAPPER-139: + # Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds. + # Needed for removing poorly interpreted newline sequences when running in more + # exotic environments such as mingw bash on Windows. + printf "%s" "${1}" | tr -d '[:space:]' +} + +scriptDir="$(dirname "$0")" +scriptName="$(basename "$0")" + +# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties +while IFS="=" read -r key value; do + case "${key-}" in + distributionUrl) distributionUrl=$(trim "${value-}") ;; + distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;; + esac +done <"$scriptDir/.mvn/wrapper/maven-wrapper.properties" +[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" + +case "${distributionUrl##*/}" in +maven-mvnd-*bin.*) + MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ + case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in + *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;; + :Darwin*x86_64) distributionPlatform=darwin-amd64 ;; + :Darwin*arm64) distributionPlatform=darwin-aarch64 ;; + :Linux*x86_64*) distributionPlatform=linux-amd64 ;; + *) + echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2 + distributionPlatform=linux-amd64 + ;; + esac + distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip" + ;; +maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;; +*) MVN_CMD="mvn${scriptName#mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;; +esac + +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}" +distributionUrlName="${distributionUrl##*/}" +distributionUrlNameMain="${distributionUrlName%.*}" +distributionUrlNameMain="${distributionUrlNameMain%-bin}" +MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}" +MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")" + +exec_maven() { + unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || : + exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD" +} + +if [ -d "$MAVEN_HOME" ]; then + verbose "found existing MAVEN_HOME at $MAVEN_HOME" + exec_maven "$@" +fi + +case "${distributionUrl-}" in +*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;; +*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;; +esac + +# prepare tmp dir +if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then + clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; } + trap clean HUP INT TERM EXIT +else + die "cannot create temp dir" +fi + +mkdir -p -- "${MAVEN_HOME%/*}" + +# Download and Install Apache Maven +verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +verbose "Downloading from: $distributionUrl" +verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +# select .zip or .tar.gz +if ! command -v unzip >/dev/null; then + distributionUrl="${distributionUrl%.zip}.tar.gz" + distributionUrlName="${distributionUrl##*/}" +fi + +# verbose opt +__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR='' +[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v + +# normalize http auth +case "${MVNW_PASSWORD:+has-password}" in +'') MVNW_USERNAME='' MVNW_PASSWORD='' ;; +has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;; +esac + +if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then + verbose "Found wget ... using wget" + wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl" +elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then + verbose "Found curl ... using curl" + curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl" +elif set_java_home; then + verbose "Falling back to use Java to download" + javaSource="$TMP_DOWNLOAD_DIR/Downloader.java" + targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName" + cat >"$javaSource" <<-END + public class Downloader extends java.net.Authenticator + { + protected java.net.PasswordAuthentication getPasswordAuthentication() + { + return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() ); + } + public static void main( String[] args ) throws Exception + { + setDefault( new Downloader() ); + java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() ); + } + } + END + # For Cygwin/MinGW, switch paths to Windows format before running javac and java + verbose " - Compiling Downloader.java ..." + "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java" + verbose " - Running Downloader.java ..." + "$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")" +fi + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +if [ -n "${distributionSha256Sum-}" ]; then + distributionSha256Result=false + if [ "$MVN_CMD" = mvnd.sh ]; then + echo "Checksum validation is not supported for maven-mvnd." >&2 + echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + elif command -v sha256sum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c - >/dev/null 2>&1; then + distributionSha256Result=true + fi + elif command -v shasum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then + distributionSha256Result=true + fi + else + echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2 + echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + fi + if [ $distributionSha256Result = false ]; then + echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2 + echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2 + exit 1 + fi +fi + +# unzip and move +if command -v unzip >/dev/null; then + unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip" +else + tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar" +fi + +# Find the actual extracted directory name (handles snapshots where filename != directory name) +actualDistributionDir="" + +# First try the expected directory name (for regular distributions) +if [ -d "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" ]; then + if [ -f "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/bin/$MVN_CMD" ]; then + actualDistributionDir="$distributionUrlNameMain" + fi +fi + +# If not found, search for any directory with the Maven executable (for snapshots) +if [ -z "$actualDistributionDir" ]; then + # enable globbing to iterate over items + set +f + for dir in "$TMP_DOWNLOAD_DIR"/*; do + if [ -d "$dir" ]; then + if [ -f "$dir/bin/$MVN_CMD" ]; then + actualDistributionDir="$(basename "$dir")" + break + fi + fi + done + set -f +fi + +if [ -z "$actualDistributionDir" ]; then + verbose "Contents of $TMP_DOWNLOAD_DIR:" + verbose "$(ls -la "$TMP_DOWNLOAD_DIR")" + die "Could not find Maven distribution directory in extracted archive" +fi + +verbose "Found extracted Maven distribution directory: $actualDistributionDir" +printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$actualDistributionDir/mvnw.url" +mv -- "$TMP_DOWNLOAD_DIR/$actualDistributionDir" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME" + +clean || : +exec_maven "$@" diff --git a/mvnw.cmd b/mvnw.cmd new file mode 100644 index 0000000..92450f9 --- /dev/null +++ b/mvnw.cmd @@ -0,0 +1,189 @@ +<# : batch portion +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Apache Maven Wrapper startup batch script, version 3.3.4 +@REM +@REM Optional ENV vars +@REM MVNW_REPOURL - repo url base for downloading maven distribution +@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output +@REM ---------------------------------------------------------------------------- + +@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0) +@SET __MVNW_CMD__= +@SET __MVNW_ERROR__= +@SET __MVNW_PSMODULEP_SAVE=%PSModulePath% +@SET PSModulePath= +@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @( + IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B) +) +@SET PSModulePath=%__MVNW_PSMODULEP_SAVE% +@SET __MVNW_PSMODULEP_SAVE= +@SET __MVNW_ARG0_NAME__= +@SET MVNW_USERNAME= +@SET MVNW_PASSWORD= +@IF NOT "%__MVNW_CMD__%"=="" ("%__MVNW_CMD__%" %*) +@echo Cannot start maven from wrapper >&2 && exit /b 1 +@GOTO :EOF +: end batch / begin powershell #> + +$ErrorActionPreference = "Stop" +if ($env:MVNW_VERBOSE -eq "true") { + $VerbosePreference = "Continue" +} + +# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties +$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl +if (!$distributionUrl) { + Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" +} + +switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) { + "maven-mvnd-*" { + $USE_MVND = $true + $distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip" + $MVN_CMD = "mvnd.cmd" + break + } + default { + $USE_MVND = $false + $MVN_CMD = $script -replace '^mvnw','mvn' + break + } +} + +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +if ($env:MVNW_REPOURL) { + $MVNW_REPO_PATTERN = if ($USE_MVND -eq $False) { "/org/apache/maven/" } else { "/maven/mvnd/" } + $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace "^.*$MVNW_REPO_PATTERN",'')" +} +$distributionUrlName = $distributionUrl -replace '^.*/','' +$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$','' + +$MAVEN_M2_PATH = "$HOME/.m2" +if ($env:MAVEN_USER_HOME) { + $MAVEN_M2_PATH = "$env:MAVEN_USER_HOME" +} + +if (-not (Test-Path -Path $MAVEN_M2_PATH)) { + New-Item -Path $MAVEN_M2_PATH -ItemType Directory | Out-Null +} + +$MAVEN_WRAPPER_DISTS = $null +if ((Get-Item $MAVEN_M2_PATH).Target[0] -eq $null) { + $MAVEN_WRAPPER_DISTS = "$MAVEN_M2_PATH/wrapper/dists" +} else { + $MAVEN_WRAPPER_DISTS = (Get-Item $MAVEN_M2_PATH).Target[0] + "/wrapper/dists" +} + +$MAVEN_HOME_PARENT = "$MAVEN_WRAPPER_DISTS/$distributionUrlNameMain" +$MAVEN_HOME_NAME = ([System.Security.Cryptography.SHA256]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join '' +$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME" + +if (Test-Path -Path "$MAVEN_HOME" -PathType Container) { + Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME" + Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" + exit $? +} + +if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) { + Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl" +} + +# prepare tmp dir +$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile +$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir" +$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null +trap { + if ($TMP_DOWNLOAD_DIR.Exists) { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } + } +} + +New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null + +# Download and Install Apache Maven +Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +Write-Verbose "Downloading from: $distributionUrl" +Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +$webclient = New-Object System.Net.WebClient +if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) { + $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD) +} +[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 +$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum +if ($distributionSha256Sum) { + if ($USE_MVND) { + Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." + } + Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash + if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) { + Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property." + } +} + +# unzip and move +Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null + +# Find the actual extracted directory name (handles snapshots where filename != directory name) +$actualDistributionDir = "" + +# First try the expected directory name (for regular distributions) +$expectedPath = Join-Path "$TMP_DOWNLOAD_DIR" "$distributionUrlNameMain" +$expectedMvnPath = Join-Path "$expectedPath" "bin/$MVN_CMD" +if ((Test-Path -Path $expectedPath -PathType Container) -and (Test-Path -Path $expectedMvnPath -PathType Leaf)) { + $actualDistributionDir = $distributionUrlNameMain +} + +# If not found, search for any directory with the Maven executable (for snapshots) +if (!$actualDistributionDir) { + Get-ChildItem -Path "$TMP_DOWNLOAD_DIR" -Directory | ForEach-Object { + $testPath = Join-Path $_.FullName "bin/$MVN_CMD" + if (Test-Path -Path $testPath -PathType Leaf) { + $actualDistributionDir = $_.Name + } + } +} + +if (!$actualDistributionDir) { + Write-Error "Could not find Maven distribution directory in extracted archive" +} + +Write-Verbose "Found extracted Maven distribution directory: $actualDistributionDir" +Rename-Item -Path "$TMP_DOWNLOAD_DIR/$actualDistributionDir" -NewName $MAVEN_HOME_NAME | Out-Null +try { + Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null +} catch { + if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) { + Write-Error "fail to move MAVEN_HOME" + } +} finally { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } +} + +Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..78afe40 --- /dev/null +++ b/pom.xml @@ -0,0 +1,72 @@ + + + 4.0.0 + + + org.springframework.boot + spring-boot-starter-parent + 3.3.5 + + + + com.main + ipks + 0.0.1-SNAPSHOT + ipks + Account Opening Via API + + + 17 + + + + + + org.springframework.boot + spring-boot-starter-web + + + + + org.springframework.boot + spring-boot-starter-validation + + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + org.apache.maven.plugins + maven-compiler-plugin + 3.14.1 + + ${java.version} + ${java.version} + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/java/com/main/ipks/IpksCbsApplication.java b/src/main/java/com/main/ipks/IpksCbsApplication.java new file mode 100644 index 0000000..a9fcdd9 --- /dev/null +++ b/src/main/java/com/main/ipks/IpksCbsApplication.java @@ -0,0 +1,13 @@ +package com.main.ipks; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class IpksCbsApplication { + + public static void main(String[] args) { + SpringApplication.run(IpksCbsApplication.class, args); + } + +} diff --git a/src/main/java/com/main/ipks/api/GlobalExceptionHandler.java b/src/main/java/com/main/ipks/api/GlobalExceptionHandler.java new file mode 100644 index 0000000..ab4c6b2 --- /dev/null +++ b/src/main/java/com/main/ipks/api/GlobalExceptionHandler.java @@ -0,0 +1,73 @@ +package com.main.ipks.api; + +import com.main.ipks.service.errors.*; +import jakarta.servlet.http.HttpServletRequest; +import org.springframework.http.*; +import org.springframework.web.bind.annotation.*; + +import java.util.Map; + +@RestControllerAdvice +public class GlobalExceptionHandler { + + /** Token generation/unavailability -> 503 */ + @ExceptionHandler(TokenFetchException.class) + public ResponseEntity> handleToken(TokenFetchException ex, HttpServletRequest req) { + return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE) + .contentType(MediaType.APPLICATION_JSON) + .body(Map.of( + "status", "FAIL", + "message", ex.getMessage() != null ? ex.getMessage() : "Token service unavailable", + "response", null, + "error", 1 + )); + } + + /** Downstream returned HTTP 200 but body.status == FAIL -> return the same JSON as-is to client with 400 (or as you set). */ + @ExceptionHandler(DownstreamBusinessPayloadException.class) + public ResponseEntity handleBusinessPayload(DownstreamBusinessPayloadException ex) { + HttpStatus status = ex.getStatusToReturn() != null ? ex.getStatusToReturn() : HttpStatus.BAD_REQUEST; + return ResponseEntity.status(status) + .contentType(MediaType.APPLICATION_JSON) + .body(ex.getRawJson()); // raw JSON from C-EDGE, unchanged + } + + /** Empty/blank body from downstream -> 502 with standard FAIL JSON */ + @ExceptionHandler(DownstreamEmptyBodyException.class) + public ResponseEntity> handleEmptyBody(DownstreamEmptyBodyException ex) { + return ResponseEntity.status(HttpStatus.BAD_GATEWAY) + .contentType(MediaType.APPLICATION_JSON) + .body(Map.of( + "status", "FAIL", + "message", ex.getMessage() != null ? ex.getMessage() : "Empty response from downstream", + "response", null, + "error", 1 + )); + } + + /** Optional: transport/format/HTTP errors -> 502 (can also propagate raw body if you like) */ + @ExceptionHandler(DownstreamBadResponseException.class) + public ResponseEntity> handleBadGateway(DownstreamBadResponseException ex) { + return ResponseEntity.status(HttpStatus.BAD_GATEWAY) + .contentType(MediaType.APPLICATION_JSON) + .body(Map.of( + "status", "FAIL", + "message", ex.getMessage() != null ? ex.getMessage() : "Downstream call failed", + "response", null, + "error", 1 + )); + } + + /** Fallback */ + @ExceptionHandler(Exception.class) + public ResponseEntity> handleAny(Exception ex) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) + .contentType(MediaType.APPLICATION_JSON) + .body(Map.of( + "status", "FAIL", + "message", "Unexpected error", + "response", null, + "error", 1 + )); + } +} \ No newline at end of file diff --git a/src/main/java/com/main/ipks/config/RestTemplateConfig.java b/src/main/java/com/main/ipks/config/RestTemplateConfig.java new file mode 100644 index 0000000..a842776 --- /dev/null +++ b/src/main/java/com/main/ipks/config/RestTemplateConfig.java @@ -0,0 +1,19 @@ +package com.main.ipks.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; + +// import org.springframework.http.client.SimpleClientHttpRequestFactory; +// import javax.net.ssl.*; +// import java.security.SecureRandom; +// import java.security.cert.X509Certificate; + +@Configuration +public class RestTemplateConfig { + + @Bean + public RestTemplate restTemplate() { + return new RestTemplate(); + } +} \ No newline at end of file diff --git a/src/main/java/com/main/ipks/controller/IpksController.java b/src/main/java/com/main/ipks/controller/IpksController.java new file mode 100644 index 0000000..95f7717 --- /dev/null +++ b/src/main/java/com/main/ipks/controller/IpksController.java @@ -0,0 +1,46 @@ +package com.main.ipks.controller; + +import com.main.ipks.dto.amend.IpksAmendCustomerRequest; +import jakarta.validation.Valid; +import org.springframework.http.MediaType; + +import org.springframework.web.bind.annotation.RestController; +import com.main.ipks.service.CbsIntegrationService; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + + + + +@RestController +@RequestMapping("/ipks") +public class IpksController { + + private static final Logger log = LoggerFactory.getLogger(IpksController.class); + + private final CbsIntegrationService cbsIntegrationService; + + public IpksController(CbsIntegrationService cbsIntegrationService) { + this.cbsIntegrationService = cbsIntegrationService; + } + + @PostMapping( + value = "/amendcustomerinfo", + consumes = MediaType.APPLICATION_JSON_VALUE, + produces = MediaType.APPLICATION_JSON_VALUE + ) + public ResponseEntity amendCustomer(@Valid @RequestBody IpksAmendCustomerRequest request) { + log.info("IPKS Amend Customer request received for BANK-ID- {}", request.getBankId()); + String response = cbsIntegrationService.amendCustomer(request); + + return ResponseEntity.ok() + .contentType(MediaType.APPLICATION_JSON) + .body(response); + } + +} diff --git a/src/main/java/com/main/ipks/dto/TokenResponse.java b/src/main/java/com/main/ipks/dto/TokenResponse.java new file mode 100644 index 0000000..a5742e2 --- /dev/null +++ b/src/main/java/com/main/ipks/dto/TokenResponse.java @@ -0,0 +1,66 @@ +package com.main.ipks.dto; + + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + + +@JsonIgnoreProperties(ignoreUnknown = true) // ignore any extra fields from server +public class TokenResponse { + + @JsonProperty("status") + private String status; + + @JsonProperty("message") + private String message; + + + @JsonProperty("response") + private String response; + + @JsonProperty("error") + private int error; + + + public TokenResponse() {} + + + public TokenResponse(String status, String message, String response, int error) { + this.status = status; + this.message = message; + this.response = response; + this.error = error; + } + + // --- Getters & Setters --- + + public String getStatus() { + return status; + } + public void setStatus(String status) { + this.status = status; + } + + public String getMessage() { + return message; + } + public void setMessage(String message) { + this.message = message; + } + + public String getResponse() { + return response; + } + public void setResponse(String response) { + this.response = response; + } + + public int getError() { + return error; + } + public void setError(int error) { + this.error = error; + } + + +} diff --git a/src/main/java/com/main/ipks/dto/amend/ChangeDetailPromptRequest.java b/src/main/java/com/main/ipks/dto/amend/ChangeDetailPromptRequest.java new file mode 100644 index 0000000..4256f45 --- /dev/null +++ b/src/main/java/com/main/ipks/dto/amend/ChangeDetailPromptRequest.java @@ -0,0 +1,20 @@ +package com.main.ipks.dto.amend; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +@JsonInclude(JsonInclude.Include.ALWAYS) +public class ChangeDetailPromptRequest { + + @JsonProperty("AcctNum1") + private Long acctNum1; + + @JsonProperty("DfltInt1") + private int dfltInt1 = 1; + + public Long getAcctNum1() { return acctNum1; } + public void setAcctNum1(Long acctNum1) { this.acctNum1 = acctNum1; } + + public int getDfltInt1() { return dfltInt1; } + public void setDfltInt1(int dfltInt1) { this.dfltInt1 = dfltInt1; } +} diff --git a/src/main/java/com/main/ipks/dto/amend/CustomerData.java b/src/main/java/com/main/ipks/dto/amend/CustomerData.java new file mode 100644 index 0000000..d86297c --- /dev/null +++ b/src/main/java/com/main/ipks/dto/amend/CustomerData.java @@ -0,0 +1,602 @@ +package com.main.ipks.dto.amend; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +@JsonInclude(JsonInclude.Include.ALWAYS) +public class CustomerData { + + @JsonProperty("AcctNum1") + private String acctNum1; + + @JsonProperty("CustTyp1") + private String custTyp1; + + @JsonProperty("DfltInt1") + private String dfltInt1; + + @JsonProperty("Title1") + private String title1; + + @JsonProperty("DfltStr1") + private String dfltStr1; + + @JsonProperty("DfltStr2") + private String dfltStr2; + + @JsonProperty("DfltStr3") + private String dfltStr3; + + @JsonProperty("DfltStr4") + private String dfltStr4; + + @JsonProperty("DfltStr5") + private String dfltStr5; + + @JsonProperty("DfltStr6") + private String dfltStr6; + + @JsonProperty("DfltStr7") + private String dfltStr7; + + @JsonProperty("DfltStr8") + private String dfltStr8; + + @JsonProperty("CountryCode1") + private String countryCode1; + + @JsonProperty("DfltStr9") + private String dfltStr9; + + @JsonProperty("DfltStr10") + private String dfltStr10; + + @JsonProperty("DfltStr11") + private String dfltStr11; + + @JsonProperty("DfltStr12") + private String dfltStr12; + + @JsonProperty("Natlty1") + private String natlty1; + + @JsonProperty("Occup1") + private String occup1; + + @JsonProperty("LangCode1") + private String langCode1; + + @JsonProperty("DfltStr13") + private String dfltStr13; + + @JsonProperty("Dt1") + private String dt1; + + @JsonProperty("DfltStr14") + private String dfltStr14; + + @JsonProperty("IdTyp1") + private String idTyp1; + + @JsonProperty("DomRisk1") + private String domRisk1; + + @JsonProperty("BrdrRisk1") + private String brdrRisk1; + + @JsonProperty("DfltInt5") + private String dfltInt5; + + @JsonProperty("DfltInt4") + private String dfltInt4; + + @JsonProperty("SegCode1") + private String segCode1; + + @JsonProperty("VipCode2") + private String vipCode2; + + @JsonProperty("DfltInt12") + private String dfltInt12; + + @JsonProperty("DfltInt2") + private String dfltInt2; + + @JsonProperty("YesNo1") + private String yesNo1; + + @JsonProperty("YesNo2") + private String yesNo2; + + @JsonProperty("YesNo3") + private String yesNo3; + + @JsonProperty("YesNo4") + private String yesNo4; + + @JsonProperty("YesNo5") + private String yesNo5; + + @JsonProperty("IndCode1") + private String indCode1; + + @JsonProperty("CountryCode2") + private String countryCode2; + + @JsonProperty("DfltStr15") + private String dfltStr15; + + @JsonProperty("DfltStr25") + private String dfltStr25; + + @JsonProperty("DfltStr16") + private String dfltStr16; + + @JsonProperty("DfltStr17") + private String dfltStr17; + + @JsonProperty("DfltStr18") + private String dfltStr18; + + @JsonProperty("DfltStr19") + private String dfltStr19; + + @JsonProperty("DfltStr20") + private String dfltStr20; + + @JsonProperty("DfltStr21") + private String dfltStr21; + + @JsonProperty("DfltStr22") + private String dfltStr22; + + @JsonProperty("Dt2") + private String dt2; + + @JsonProperty("DfltStr23") + private String dfltStr23; + + @JsonProperty("DfltStr24") + private String dfltStr24; + + @JsonProperty("Dt3") + private String dt3; + + @JsonProperty("Dt4") + private String dt4; + + @JsonProperty("DfltStr26") + private String dfltStr26; + + @JsonProperty("DfltStr27") + private String dfltStr27; + + @JsonProperty("Dt5") + private String dt5; + + @JsonProperty("Dt6") + private String dt6; + + @JsonProperty("DfltStr28") + private String dfltStr28; + + @JsonProperty("Dt7") + private String dt7; + + @JsonProperty("Dt8") + private String dt8; + + @JsonProperty("DfltStr31") + private String dfltStr31; + + @JsonProperty("DfltStr32") + private String dfltStr32; + + @JsonProperty("DfltStr33") + private String dfltStr33; + + @JsonProperty("DfltStr29") + private String dfltStr29; + + @JsonProperty("DfltStr30") + private String dfltStr30; + + @JsonProperty("DfltStr34") + private String dfltStr34; + + @JsonProperty("DfltStr35") + private String dfltStr35; + + @JsonProperty("DfltStr36") + private String dfltStr36; + + @JsonProperty("DfltStr37") + private String dfltStr37; + + @JsonProperty("DfltStr38") + private String dfltStr38; + + @JsonProperty("YesNo6") + private String yesNo6; + + @JsonProperty("DfltStr39") + private String dfltStr39; + + @JsonProperty("DfltStr40") + private String dfltStr40; + + @JsonProperty("DfltStr41") + private String dfltStr41; + + @JsonProperty("AcctMgmGrp") + private String acctMgmGrp; + + @JsonProperty("IdTyp2") + private String idTyp2; + + @JsonProperty("IdNum2") + private String idNum2; + + @JsonProperty("ThrsldLmt") + private String thrsldLmt; + + @JsonProperty("CustRisk1") + private String custRisk1; + + @JsonProperty("FpwnFlag") + private String fpwnFlag; + + @JsonProperty("ExmptnFlag1") + private String exmptnFlag1; + + @JsonProperty("ExmptnFlag2") + private String exmptnFlag2; + + @JsonProperty("ExmptnFlag3") + private String exmptnFlag3; + + @JsonProperty("ExmptnFlag4") + private String exmptnFlag4; + + @JsonProperty("ExmptnFlag5") + private String exmptnFlag5; + + @JsonProperty("ExmptnFld1") + private String exmptnFld1; + + @JsonProperty("ExmptnFld2") + private String exmptnFld2; + + @JsonProperty("ExmptnFld3") + private String exmptnFld3; + + @JsonProperty("ExmptnFld4") + private String exmptnFld4; + + @JsonProperty("ExmptnFld5") + private String exmptnFld5; + + @JsonProperty("ExmptnFld6") + private String exmptnFld6; + + @JsonProperty("ExmptnFld7") + private String exmptnFld7; + + @JsonProperty("ExmptnFld8") + private String exmptnFld8; + + @JsonProperty("ExmptnFld9") + private String exmptnFld9; + + @JsonProperty("ExmptnFld10") + private String exmptnFld10; + + @JsonProperty("TrFlag") + private String trFlag; + + @JsonProperty("ExmptnProfile") + private String exmptnProfile; + + @JsonProperty("KycStat") + private String kycStat; + + @JsonProperty("KycDt") + private String kycDt; + + public String getAcctNum1() { return acctNum1; } + public void setAcctNum1(String acctNum1) { this.acctNum1 = acctNum1; } + + public String getCustTyp1() { return custTyp1; } + public void setCustTyp1(String custTyp1) { this.custTyp1 = custTyp1; } + + public String getDfltInt1() { return dfltInt1; } + public void setDfltInt1(String dfltInt1) { this.dfltInt1 = dfltInt1; } + + public String getTitle1() { return title1; } + public void setTitle1(String title1) { this.title1 = title1; } + + public String getDfltStr1() { return dfltStr1; } + public void setDfltStr1(String dfltStr1) { this.dfltStr1 = dfltStr1; } + + public String getDfltStr2() { return dfltStr2; } + public void setDfltStr2(String dfltStr2) { this.dfltStr2 = dfltStr2; } + + public String getDfltStr3() { return dfltStr3; } + public void setDfltStr3(String dfltStr3) { this.dfltStr3 = dfltStr3; } + + public String getDfltStr4() { return dfltStr4; } + public void setDfltStr4(String dfltStr4) { this.dfltStr4 = dfltStr4; } + + public String getDfltStr5() { return dfltStr5; } + public void setDfltStr5(String dfltStr5) { this.dfltStr5 = dfltStr5; } + + public String getDfltStr6() { return dfltStr6; } + public void setDfltStr6(String dfltStr6) { this.dfltStr6 = dfltStr6; } + + public String getDfltStr7() { return dfltStr7; } + public void setDfltStr7(String dfltStr7) { this.dfltStr7 = dfltStr7; } + + public String getDfltStr8() { return dfltStr8; } + public void setDfltStr8(String dfltStr8) { this.dfltStr8 = dfltStr8; } + + public String getCountryCode1() { return countryCode1; } + public void setCountryCode1(String countryCode1) { this.countryCode1 = countryCode1; } + + public String getDfltStr9() { return dfltStr9; } + public void setDfltStr9(String dfltStr9) { this.dfltStr9 = dfltStr9; } + + public String getDfltStr10() { return dfltStr10; } + public void setDfltStr10(String dfltStr10) { this.dfltStr10 = dfltStr10; } + + public String getDfltStr11() { return dfltStr11; } + public void setDfltStr11(String dfltStr11) { this.dfltStr11 = dfltStr11; } + + public String getDfltStr12() { return dfltStr12; } + public void setDfltStr12(String dfltStr12) { this.dfltStr12 = dfltStr12; } + + public String getNatlty1() { return natlty1; } + public void setNatlty1(String natlty1) { this.natlty1 = natlty1; } + + public String getOccup1() { return occup1; } + public void setOccup1(String occup1) { this.occup1 = occup1; } + + public String getLangCode1() { return langCode1; } + public void setLangCode1(String langCode1) { this.langCode1 = langCode1; } + + public String getDfltStr13() { return dfltStr13; } + public void setDfltStr13(String dfltStr13) { this.dfltStr13 = dfltStr13; } + + public String getDt1() { return dt1; } + public void setDt1(String dt1) { this.dt1 = dt1; } + + public String getDfltStr14() { return dfltStr14; } + public void setDfltStr14(String dfltStr14) { this.dfltStr14 = dfltStr14; } + + public String getIdTyp1() { return idTyp1; } + public void setIdTyp1(String idTyp1) { this.idTyp1 = idTyp1; } + + public String getDomRisk1() { return domRisk1; } + public void setDomRisk1(String domRisk1) { this.domRisk1 = domRisk1; } + + public String getBrdrRisk1() { return brdrRisk1; } + public void setBrdrRisk1(String brdrRisk1) { this.brdrRisk1 = brdrRisk1; } + + public String getDfltInt5() { return dfltInt5; } + public void setDfltInt5(String dfltInt5) { this.dfltInt5 = dfltInt5; } + + public String getDfltInt4() { return dfltInt4; } + public void setDfltInt4(String dfltInt4) { this.dfltInt4 = dfltInt4; } + + public String getSegCode1() { return segCode1; } + public void setSegCode1(String segCode1) { this.segCode1 = segCode1; } + + public String getVipCode2() { return vipCode2; } + public void setVipCode2(String vipCode2) { this.vipCode2 = vipCode2; } + + public String getDfltInt12() { return dfltInt12; } + public void setDfltInt12(String dfltInt12) { this.dfltInt12 = dfltInt12; } + + public String getDfltInt2() { return dfltInt2; } + public void setDfltInt2(String dfltInt2) { this.dfltInt2 = dfltInt2; } + + public String getYesNo1() { return yesNo1; } + public void setYesNo1(String yesNo1) { this.yesNo1 = yesNo1; } + + public String getYesNo2() { return yesNo2; } + public void setYesNo2(String yesNo2) { this.yesNo2 = yesNo2; } + + public String getYesNo3() { return yesNo3; } + public void setYesNo3(String yesNo3) { this.yesNo3 = yesNo3; } + + public String getYesNo4() { return yesNo4; } + public void setYesNo4(String yesNo4) { this.yesNo4 = yesNo4; } + + public String getYesNo5() { return yesNo5; } + public void setYesNo5(String yesNo5) { this.yesNo5 = yesNo5; } + + public String getIndCode1() { return indCode1; } + public void setIndCode1(String indCode1) { this.indCode1 = indCode1; } + + public String getCountryCode2() { return countryCode2; } + public void setCountryCode2(String countryCode2) { this.countryCode2 = countryCode2; } + + public String getDfltStr15() { return dfltStr15; } + public void setDfltStr15(String dfltStr15) { this.dfltStr15 = dfltStr15; } + + public String getDfltStr25() { return dfltStr25; } + public void setDfltStr25(String dfltStr25) { this.dfltStr25 = dfltStr25; } + + public String getDfltStr16() { return dfltStr16; } + public void setDfltStr16(String dfltStr16) { this.dfltStr16 = dfltStr16; } + + public String getDfltStr17() { return dfltStr17; } + public void setDfltStr17(String dfltStr17) { this.dfltStr17 = dfltStr17; } + + public String getDfltStr18() { return dfltStr18; } + public void setDfltStr18(String dfltStr18) { this.dfltStr18 = dfltStr18; } + + public String getDfltStr19() { return dfltStr19; } + public void setDfltStr19(String dfltStr19) { this.dfltStr19 = dfltStr19; } + + public String getDfltStr20() { return dfltStr20; } + public void setDfltStr20(String dfltStr20) { this.dfltStr20 = dfltStr20; } + + public String getDfltStr21() { return dfltStr21; } + public void setDfltStr21(String dfltStr21) { this.dfltStr21 = dfltStr21; } + + public String getDfltStr22() { return dfltStr22; } + public void setDfltStr22(String dfltStr22) { this.dfltStr22 = dfltStr22; } + + public String getDt2() { return dt2; } + public void setDt2(String dt2) { this.dt2 = dt2; } + + public String getDfltStr23() { return dfltStr23; } + public void setDfltStr23(String dfltStr23) { this.dfltStr23 = dfltStr23; } + + public String getDfltStr24() { return dfltStr24; } + public void setDfltStr24(String dfltStr24) { this.dfltStr24 = dfltStr24; } + + public String getDt3() { return dt3; } + public void setDt3(String dt3) { this.dt3 = dt3; } + + public String getDt4() { return dt4; } + public void setDt4(String dt4) { this.dt4 = dt4; } + + public String getDfltStr26() { return dfltStr26; } + public void setDfltStr26(String dfltStr26) { this.dfltStr26 = dfltStr26; } + + public String getDfltStr27() { return dfltStr27; } + public void setDfltStr27(String dfltStr27) { this.dfltStr27 = dfltStr27; } + + public String getDt5() { return dt5; } + public void setDt5(String dt5) { this.dt5 = dt5; } + + public String getDt6() { return dt6; } + public void setDt6(String dt6) { this.dt6 = dt6; } + + public String getDfltStr28() { return dfltStr28; } + public void setDfltStr28(String dfltStr28) { this.dfltStr28 = dfltStr28; } + + public String getDt7() { return dt7; } + public void setDt7(String dt7) { this.dt7 = dt7; } + + public String getDt8() { return dt8; } + public void setDt8(String dt8) { this.dt8 = dt8; } + + public String getDfltStr31() { return dfltStr31; } + public void setDfltStr31(String dfltStr31) { this.dfltStr31 = dfltStr31; } + + public String getDfltStr32() { return dfltStr32; } + public void setDfltStr32(String dfltStr32) { this.dfltStr32 = dfltStr32; } + + public String getDfltStr33() { return dfltStr33; } + public void setDfltStr33(String dfltStr33) { this.dfltStr33 = dfltStr33; } + + public String getDfltStr29() { return dfltStr29; } + public void setDfltStr29(String dfltStr29) { this.dfltStr29 = dfltStr29; } + + public String getDfltStr30() { return dfltStr30; } + public void setDfltStr30(String dfltStr30) { this.dfltStr30 = dfltStr30; } + + public String getDfltStr34() { return dfltStr34; } + public void setDfltStr34(String dfltStr34) { this.dfltStr34 = dfltStr34; } + + public String getDfltStr35() { return dfltStr35; } + public void setDfltStr35(String dfltStr35) { this.dfltStr35 = dfltStr35; } + + public String getDfltStr36() { return dfltStr36; } + public void setDfltStr36(String dfltStr36) { this.dfltStr36 = dfltStr36; } + + public String getDfltStr37() { return dfltStr37; } + public void setDfltStr37(String dfltStr37) { this.dfltStr37 = dfltStr37; } + + public String getDfltStr38() { return dfltStr38; } + public void setDfltStr38(String dfltStr38) { this.dfltStr38 = dfltStr38; } + + public String getYesNo6() { return yesNo6; } + public void setYesNo6(String yesNo6) { this.yesNo6 = yesNo6; } + + public String getDfltStr39() { return dfltStr39; } + public void setDfltStr39(String dfltStr39) { this.dfltStr39 = dfltStr39; } + + public String getDfltStr40() { return dfltStr40; } + public void setDfltStr40(String dfltStr40) { this.dfltStr40 = dfltStr40; } + + public String getDfltStr41() { return dfltStr41; } + public void setDfltStr41(String dfltStr41) { this.dfltStr41 = dfltStr41; } + + public String getAcctMgmGrp() { return acctMgmGrp; } + public void setAcctMgmGrp(String acctMgmGrp) { this.acctMgmGrp = acctMgmGrp; } + + public String getIdTyp2() { return idTyp2; } + public void setIdTyp2(String idTyp2) { this.idTyp2 = idTyp2; } + + public String getIdNum2() { return idNum2; } + public void setIdNum2(String idNum2) { this.idNum2 = idNum2; } + + public String getThrsldLmt() { return thrsldLmt; } + public void setThrsldLmt(String thrsldLmt) { this.thrsldLmt = thrsldLmt; } + + public String getCustRisk1() { return custRisk1; } + public void setCustRisk1(String custRisk1) { this.custRisk1 = custRisk1; } + + public String getFpwnFlag() { return fpwnFlag; } + public void setFpwnFlag(String fpwnFlag) { this.fpwnFlag = fpwnFlag; } + + public String getExmptnFlag1() { return exmptnFlag1; } + public void setExmptnFlag1(String exmptnFlag1) { this.exmptnFlag1 = exmptnFlag1; } + + public String getExmptnFlag2() { return exmptnFlag2; } + public void setExmptnFlag2(String exmptnFlag2) { this.exmptnFlag2 = exmptnFlag2; } + + public String getExmptnFlag3() { return exmptnFlag3; } + public void setExmptnFlag3(String exmptnFlag3) { this.exmptnFlag3 = exmptnFlag3; } + + public String getExmptnFlag4() { return exmptnFlag4; } + public void setExmptnFlag4(String exmptnFlag4) { this.exmptnFlag4 = exmptnFlag4; } + + public String getExmptnFlag5() { return exmptnFlag5; } + public void setExmptnFlag5(String exmptnFlag5) { this.exmptnFlag5 = exmptnFlag5; } + + public String getExmptnFld1() { return exmptnFld1; } + public void setExmptnFld1(String exmptnFld1) { this.exmptnFld1 = exmptnFld1; } + + public String getExmptnFld2() { return exmptnFld2; } + public void setExmptnFld2(String exmptnFld2) { this.exmptnFld2 = exmptnFld2; } + + public String getExmptnFld3() { return exmptnFld3; } + public void setExmptnFld3(String exmptnFld3) { this.exmptnFld3 = exmptnFld3; } + + public String getExmptnFld4() { return exmptnFld4; } + public void setExmptnFld4(String exmptnFld4) { this.exmptnFld4 = exmptnFld4; } + + public String getExmptnFld5() { return exmptnFld5; } + public void setExmptnFld5(String exmptnFld5) { this.exmptnFld5 = exmptnFld5; } + + public String getExmptnFld6() { return exmptnFld6; } + public void setExmptnFld6(String exmptnFld6) { this.exmptnFld6 = exmptnFld6; } + + public String getExmptnFld7() { return exmptnFld7; } + public void setExmptnFld7(String exmptnFld7) { this.exmptnFld7 = exmptnFld7; } + + public String getExmptnFld8() { return exmptnFld8; } + public void setExmptnFld8(String exmptnFld8) { this.exmptnFld8 = exmptnFld8; } + + public String getExmptnFld9() { return exmptnFld9; } + public void setExmptnFld9(String exmptnFld9) { this.exmptnFld9 = exmptnFld9; } + + public String getExmptnFld10() { return exmptnFld10; } + public void setExmptnFld10(String exmptnFld10) { this.exmptnFld10 = exmptnFld10; } + + public String getTrFlag() { return trFlag; } + public void setTrFlag(String trFlag) { this.trFlag = trFlag; } + + public String getExmptnProfile() { return exmptnProfile; } + public void setExmptnProfile(String exmptnProfile) { this.exmptnProfile = exmptnProfile; } + + public String getKycStat() { return kycStat; } + public void setKycStat(String kycStat) { this.kycStat = kycStat; } + + public String getKycDt() { return kycDt; } + public void setKycDt(String kycDt) { this.kycDt = kycDt; } +} diff --git a/src/main/java/com/main/ipks/dto/amend/IpksAmendCustomerRequest.java b/src/main/java/com/main/ipks/dto/amend/IpksAmendCustomerRequest.java new file mode 100644 index 0000000..0339526 --- /dev/null +++ b/src/main/java/com/main/ipks/dto/amend/IpksAmendCustomerRequest.java @@ -0,0 +1,205 @@ +package com.main.ipks.dto.amend; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.constraints.NotBlank; + +@JsonIgnoreProperties(ignoreUnknown = true) +public class IpksAmendCustomerRequest { + + public IpksAmendCustomerRequest() {} + + @JsonProperty("customer_type") + private String customerType; + + @JsonProperty("title") + private String title; + + @JsonProperty("first_name") + private String firstName; + + @JsonProperty("middle_name") + private String middleName; + + @JsonProperty("last_name") + private String lastName; + + @JsonProperty("guardian_name") + private String guardianName; + + @JsonProperty("relative_code") + private String relativeCode; + + @JsonProperty("marital_status") + private String maritalStatus; + + @JsonProperty("mother_maiden_name") + private String motherMaidenName; + + @JsonProperty("address_1") + private String address1; + + @JsonProperty("address_2") + private String address2; + + @JsonProperty("address_3") + private String address3; + + @JsonProperty("block") + private String block; + + @JsonProperty("pin_code") + private String pinCode; + + @JsonProperty("ward") + private String ward; + + @JsonProperty("district") + private String district; + + @JsonProperty("city") + private String city; + + @JsonProperty("state") + private String state; + + @JsonProperty("birth_date") + private String birthDate; + + @JsonProperty("membership_no") + private String membershipNo; + + @JsonProperty("farmer_type") + private String farmerType; + + @JsonProperty("gender") + private String gender; + + @JsonProperty("religion") + private String religion; + + @JsonProperty("caste") + private String caste; + + @JsonProperty("mobile_no") + private String mobileNo; + + @JsonProperty("email_id") + private String emailId; + + @NotBlank(message = "bank_cif must not be blank") + @JsonProperty("bank_cif") + private String bankCif; + + @JsonProperty("customer_risk") + private String customerRisk; + + @JsonProperty("resident_status") + private String residentStatus; + + @JsonProperty("country") + private String country; + + @JsonProperty("fatca") + private String fatca; + + @NotBlank(message = "bankId must not be blank") + @JsonProperty("bankId") + private String bankId; + + public String getCustomerType() { return customerType; } + public void setCustomerType(String customerType) { this.customerType = customerType; } + + public String getTitle() { return title; } + public void setTitle(String title) { this.title = title; } + + public String getFirstName() { return firstName; } + public void setFirstName(String firstName) { this.firstName = firstName; } + + public String getMiddleName() { return middleName; } + public void setMiddleName(String middleName) { this.middleName = middleName; } + + public String getLastName() { return lastName; } + public void setLastName(String lastName) { this.lastName = lastName; } + + public String getGuardianName() { return guardianName; } + public void setGuardianName(String guardianName) { this.guardianName = guardianName; } + + public String getRelativeCode() { return relativeCode; } + public void setRelativeCode(String relativeCode) { this.relativeCode = relativeCode; } + + public String getMaritalStatus() { return maritalStatus; } + public void setMaritalStatus(String maritalStatus) { this.maritalStatus = maritalStatus; } + + public String getMotherMaidenName() { return motherMaidenName; } + public void setMotherMaidenName(String motherMaidenName) { this.motherMaidenName = motherMaidenName; } + + public String getAddress1() { return address1; } + public void setAddress1(String address1) { this.address1 = address1; } + + public String getAddress2() { return address2; } + public void setAddress2(String address2) { this.address2 = address2; } + + public String getAddress3() { return address3; } + public void setAddress3(String address3) { this.address3 = address3; } + + public String getBlock() { return block; } + public void setBlock(String block) { this.block = block; } + + public String getPinCode() { return pinCode; } + public void setPinCode(String pinCode) { this.pinCode = pinCode; } + + public String getWard() { return ward; } + public void setWard(String ward) { this.ward = ward; } + + public String getDistrict() { return district; } + public void setDistrict(String district) { this.district = district; } + + public String getCity() { return city; } + public void setCity(String city) { this.city = city; } + + public String getState() { return state; } + public void setState(String state) { this.state = state; } + + public String getBirthDate() { return birthDate; } + public void setBirthDate(String birthDate) { this.birthDate = birthDate; } + + public String getMembershipNo() { return membershipNo; } + public void setMembershipNo(String membershipNo) { this.membershipNo = membershipNo; } + + public String getFarmerType() { return farmerType; } + public void setFarmerType(String farmerType) { this.farmerType = farmerType; } + + public String getGender() { return gender; } + public void setGender(String gender) { this.gender = gender; } + + public String getReligion() { return religion; } + public void setReligion(String religion) { this.religion = religion; } + + public String getCaste() { return caste; } + public void setCaste(String caste) { this.caste = caste; } + + public String getMobileNo() { return mobileNo; } + public void setMobileNo(String mobileNo) { this.mobileNo = mobileNo; } + + public String getEmailId() { return emailId; } + public void setEmailId(String emailId) { this.emailId = emailId; } + + public String getBankCif() { return bankCif; } + public void setBankCif(String bankCif) { this.bankCif = bankCif; } + + public String getCustomerRisk() { return customerRisk; } + public void setCustomerRisk(String customerRisk) { this.customerRisk = customerRisk; } + + public String getResidentStatus() { return residentStatus; } + public void setResidentStatus(String residentStatus) { this.residentStatus = residentStatus; } + + public String getCountry() { return country; } + public void setCountry(String country) { this.country = country; } + + public String getFatca() { return fatca; } + public void setFatca(String fatca) { this.fatca = fatca; } + + public String getBankId() { return bankId; } + public void setBankId(String bankId) { this.bankId = bankId; } +} diff --git a/src/main/java/com/main/ipks/service/CbsIntegrationService.java b/src/main/java/com/main/ipks/service/CbsIntegrationService.java new file mode 100644 index 0000000..81442a8 --- /dev/null +++ b/src/main/java/com/main/ipks/service/CbsIntegrationService.java @@ -0,0 +1,221 @@ +package com.main.ipks.service; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.main.ipks.dto.amend.ChangeDetailPromptRequest; +import com.main.ipks.dto.amend.CustomerData; +import com.main.ipks.dto.amend.IpksAmendCustomerRequest; +import com.main.ipks.service.errors.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.*; +import org.springframework.stereotype.Service; +import org.springframework.web.client.HttpStatusCodeException; +import org.springframework.web.client.RestClientException; +import org.springframework.web.client.RestTemplate; + +import java.util.List; +import java.util.function.Consumer; + +@Service +public class CbsIntegrationService { + + private static final Logger log = LoggerFactory.getLogger(CbsIntegrationService.class); + + private final RestTemplate restTemplate; + private final TokenService tokenService; + private final ObjectMapper om = new ObjectMapper(); + + public CbsIntegrationService(RestTemplate restTemplate, TokenService tokenService) { + this.restTemplate = restTemplate; + this.tokenService = tokenService; + } + + @Value("${cedge.changePrompt.url}") + private String changePromptUrl; + + @Value("${cedge.amendCustomer.url}") + private String amendCustomerUrl; + + // ---------------- Amend Customer (API-1 + API-2) ---------------- + public String amendCustomer(IpksAmendCustomerRequest in) { + try { + log.info("Amend Customer Request For BANK-ID- {}", in.getBankId()); + log.info("Fetching Token for BankID: {} ", in.getBankId()); + + String token = tokenService.getToken(in.getBankId()); + HttpHeaders headers = buildHeaders(token); + + // --- API-1: ChangeDetailPromptNew --- + ChangeDetailPromptRequest api1Req = new ChangeDetailPromptRequest(); + api1Req.setAcctNum1(Long.parseLong(in.getBankCif())); + + log.debug("[C-EDGE] API-1 (ChangeDetailPrompt) Request JSON: {}", safeJson(api1Req)); + + HttpEntity api1Entity = new HttpEntity<>(api1Req, headers); + + ResponseEntity api1Raw; + String url = changePromptUrl + "/" + in.getBankId(); + log.info("Calling 1st API (ChangeDetailPrompt) for bank {}. url={}", in.getBankId(), url); + + try { + api1Raw = restTemplate.exchange(url, HttpMethod.POST, api1Entity, String.class); + } catch (HttpStatusCodeException ex) { + if (ex.getStatusCode() == HttpStatus.UNAUTHORIZED || ex.getStatusCode() == HttpStatus.FORBIDDEN) { + log.warn("[C-EDGE] API-1 unauthorized. Refresh + retry once URL:{}", url); + HttpEntity retryEntity = + new HttpEntity<>(api1Req, buildHeaders(tokenService.refreshToken(in.getBankId()))); + api1Raw = restTemplate.exchange(url, HttpMethod.POST, retryEntity, String.class); + } else { + String raw = ex.getResponseBodyAsString(); + if (raw != null && !raw.isBlank() && looksJson(raw)) { + throw new DownstreamBusinessPayloadException(raw, HttpStatus.BAD_GATEWAY); + } + throw ex; + } + } + + String api1Str = api1Raw.getBody(); + if (api1Str == null || api1Str.isBlank()) { + throw new DownstreamEmptyBodyException("Empty response from C-EDGE API-1 (ChangeDetailPrompt)"); + } + + JsonNode api1Node = om.readTree(api1Str); + String api1Status = api1Node.path("status").asText(null); + + if (!"SUCCESS".equalsIgnoreCase(api1Status)) { + log.error("[C-EDGE] API-1 business failure. Payload returned to client: {}", api1Str); + throw new DownstreamBusinessPayloadException(api1Str, HttpStatus.BAD_REQUEST); + } + + // Take the response fields of API-1 and override them with request data + CustomerData data = om.treeToValue(api1Node.path("response"), CustomerData.class); + if (data == null) { + throw new DownstreamEmptyBodyException("C-EDGE API-1 (ChangeDetailPrompt) response data is null"); + } + + applyOverrides(in, data); + + log.debug("[C-EDGE] API-2 (AmendCustomer) Request JSON: {}", safeJson(data)); + + // --- API-2: amendcustomernabard --- + HttpEntity api2Entity = new HttpEntity<>(data, headers); + + ResponseEntity api2Raw; + String url2 = amendCustomerUrl + "/" + in.getBankId(); + log.info("Calling 2nd API (AmendCustomer) for bank {}. url={}", in.getBankId(), url2); + + try { + api2Raw = restTemplate.exchange(url2, HttpMethod.POST, api2Entity, String.class); + } catch (HttpStatusCodeException ex) { + if (ex.getStatusCode() == HttpStatus.UNAUTHORIZED || ex.getStatusCode() == HttpStatus.FORBIDDEN) { + log.warn("[C-EDGE] API-2 unauthorized. Refresh + retry once URL: {}", url2); + HttpEntity retryEntity = + new HttpEntity<>(data, buildHeaders(tokenService.refreshToken(in.getBankId()))); + api2Raw = restTemplate.exchange(url2, HttpMethod.POST, retryEntity, String.class); + } else { + String raw = ex.getResponseBodyAsString(); + if (raw != null && !raw.isBlank() && looksJson(raw)) { + throw new DownstreamBusinessPayloadException(raw, HttpStatus.BAD_GATEWAY); + } + throw ex; + } + } + + String api2Str = api2Raw.getBody(); + if (api2Str == null || api2Str.isBlank()) { + throw new DownstreamEmptyBodyException("Empty response from C-EDGE API-2 (AmendCustomer)"); + } + + JsonNode api2Node = om.readTree(api2Str); + String api2Status = api2Node.path("status").asText(null); + + if (!"SUCCESS".equalsIgnoreCase(api2Status)) { + log.error("[C-EDGE] API-2 business failure. Payload returned to client: {}", api2Str); + throw new DownstreamBusinessPayloadException(api2Str, HttpStatus.BAD_REQUEST); + } + + // Return the C-EDGE response as-is + log.info("Customer Amended..."); + log.info("Full AmendCustomer Response: {}", api2Str); + return api2Str; + + } catch (HttpStatusCodeException ex) { + throw new DownstreamBadResponseException( + "C-EDGE AmendCustomer call failed: HTTP " + ex.getStatusCode().value(), ex); + } catch (RestClientException e) { + throw new DownstreamBadResponseException("C-EDGE AmendCustomer failed: " + e.getMessage(), e); + } catch (DownstreamBusinessPayloadException | DownstreamEmptyBodyException e) { + throw e; + } catch (Exception e) { + throw new DownstreamBadResponseException("AmendCustomer response processing error: " + e.getMessage(), e); + } + } + + // ------------ Mapping (IpksAmendCustomerRequest -> CustomerData overrides) ------------ + + private void applyOverrides(IpksAmendCustomerRequest in, CustomerData d) { + set(d::setAcctNum1, in.getBankCif()); + set(d::setCustTyp1, in.getCustomerType()); + set(d::setTitle1, in.getTitle()); + set(d::setDfltStr1, in.getFirstName()); + set(d::setDfltStr3, in.getMiddleName()); + set(d::setDfltStr2, in.getLastName()); + set(d::setDfltStr16, in.getGuardianName()); + set(d::setDfltStr20, in.getRelativeCode()); + set(d::setDfltStr23, in.getMaritalStatus()); + set(d::setDfltStr18, in.getMotherMaidenName()); + set(d::setDfltStr4, in.getAddress1()); + set(d::setDfltStr5, in.getAddress2()); + set(d::setDfltStr6, in.getAddress3()); + set(d::setDfltStr37, in.getBlock()); + set(d::setDfltStr8, in.getPinCode()); + set(d::setDfltStr31, in.getWard()); + set(d::setDfltStr36, in.getDistrict()); + set(d::setDfltStr7, in.getCity()); + set(d::setDfltStr35, in.getState()); + set(d::setDt2, in.getBirthDate()); + set(d::setDfltStr30, in.getMembershipNo()); + set(d::setDfltStr26, in.getFarmerType()); + set(d::setDfltStr19, in.getGender()); + set(d::setDfltStr24, in.getReligion()); + set(d::setDfltStr25, in.getCaste()); + set(d::setDfltStr12, in.getMobileNo()); + set(d::setDfltStr14, in.getEmailId()); + set(d::setCustRisk1, in.getCustomerRisk()); + set(d::setDfltStr11, in.getResidentStatus()); + set(d::setCountryCode1, in.getCountry()); + set(d::setDfltStr40, in.getFatca()); + } + + private static void set(Consumer setter, String value) { + if (value != null && !value.isBlank()) { + setter.accept(value.trim()); + } + } + + // ------------Helpers--------------- + + private HttpHeaders buildHeaders(String token) { + HttpHeaders headers = new HttpHeaders(); + headers.setBearerAuth(token); + headers.setContentType(MediaType.APPLICATION_JSON); + headers.setAccept(List.of(MediaType.APPLICATION_JSON)); + return headers; + } + + private String safeJson(Object o) { + try { + return om.writeValueAsString(o); + } catch (com.fasterxml.jackson.core.JsonProcessingException e) { + return ""; + } + } + + private static boolean looksJson(String s) { + String t = s.trim(); + return (t.startsWith("{") && t.endsWith("}")) || (t.startsWith("[") && t.endsWith("]")); + } + +} diff --git a/src/main/java/com/main/ipks/service/TokenService.java b/src/main/java/com/main/ipks/service/TokenService.java new file mode 100644 index 0000000..4ee90e6 --- /dev/null +++ b/src/main/java/com/main/ipks/service/TokenService.java @@ -0,0 +1,152 @@ +package com.main.ipks.service; + +import com.main.ipks.dto.TokenResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; +import com.main.ipks.service.errors.TokenFetchException; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +@Service +public class TokenService { + + private static final Logger log = LoggerFactory.getLogger(TokenService.class); + + private final RestTemplate restTemplate; + + @Value("${cedge.token.url}") + private String tokenUrl; + + // Per-bank cache + private final Map tokenCache = new ConcurrentHashMap<>(); + private final Map expiryCache = new ConcurrentHashMap<>(); + + + + private final Object tokenLock = new Object(); + + private static final long DEFAULT_TOKEN_TTL_MS = 7L * 60 * 60 * 1000; + + public TokenService(RestTemplate restTemplate) { + this.restTemplate = restTemplate; + } + + public String getToken(String bankId) { + + if (bankId == null || bankId.isBlank()) { + log.error("Invalid bankId received: {}", bankId); + } + + + long now = System.currentTimeMillis(); + String cachedToken = tokenCache.get(bankId); + Long expiry = expiryCache.get(bankId); + + if (cachedToken != null && expiry != null && now < expiry) { + return cachedToken; + } + + synchronized (tokenLock) { + now = System.currentTimeMillis(); + + cachedToken = tokenCache.get(bankId); + expiry = expiryCache.get(bankId); + + if (cachedToken != null && expiry != null && now < expiry) { + return cachedToken; + } + + String url = tokenUrl + "/" + bankId; + + log.info("Token fetching for bank {}. url={}", bankId, url); + + // Only 2 attempts total + for (int attempt = 1; attempt <= 2; attempt++) { + try { + log.info("Fetching new C-EDGE token (attempt {}/2)", attempt); + + ResponseEntity resp = + restTemplate.getForEntity(url,TokenResponse.class); + + TokenResponse body = resp.getBody(); + + if (body == null) { + throw new TokenFetchException("Token API returned empty body"); + } + + + String status = body.getStatus(); // "SUCCESS" or "FAIL" + String message = body.getMessage(); // reason + String token = body.getResponse(); // JWT when SUCCESS + int error = body.getError(); // 0 or 1 + + if (!"SUCCESS".equalsIgnoreCase(status)) { + // Upstream business failure → fail with upstream message + String msg = (message != null && !message.isBlank()) + ? message : "Token API returned FAIL"; + throw new TokenFetchException(msg); + } + + if (error != 0) { + // Defensive: If error is non-zero even with SUCCESS (shouldn't happen) + throw new TokenFetchException("Token API error: " + + (message != null ? message : "unknown")); + } + + if (token == null || token.isBlank()) { + throw new TokenFetchException("Token string missing in response"); + } + + // Store per bankId + tokenCache.put(bankId, token); + expiryCache.put(bankId, now + DEFAULT_TOKEN_TTL_MS); + + + String preview = token.substring(0, Math.min(12, token.length())) + "..."; + log.info("Token fetched successfully for bank {}. Preview={}", bankId, preview); + + return token; + + } catch (RuntimeException e) { + // RestClientException also comes here because it extends RuntimeException + log.warn("Token fetch failed for bank {} (attempt {}/2): {}", bankId, attempt, e.getMessage()); + if (attempt == 1) { + try { + Thread.sleep(300); // small delay before retry + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + throw new TokenFetchException("Interrupted during token retry wait", ie); + } + continue; + } + + // second attempt failed -> hard fail + log.error("Token generation failed for bank {} after 2 attempts", bankId, e); + if (e instanceof TokenFetchException) { + throw e; + } + throw new TokenFetchException("Token generation failed", e); + } + } + + // should never reach here + throw new TokenFetchException("Token generation failed"); + } + } + + /** + * Force refresh token: clear cache and fetch again (with 2 attempts total). + * Used when C-EDGE returns 401/403 unauthorized. + */ + public String refreshToken(String bankId) { + synchronized (tokenLock) { + tokenCache.remove(bankId); + expiryCache.remove(bankId); + } + return getToken(bankId); + } +} \ No newline at end of file diff --git a/src/main/java/com/main/ipks/service/errors/DownstreamBadResponseException.java b/src/main/java/com/main/ipks/service/errors/DownstreamBadResponseException.java new file mode 100644 index 0000000..156b3b4 --- /dev/null +++ b/src/main/java/com/main/ipks/service/errors/DownstreamBadResponseException.java @@ -0,0 +1,6 @@ +package com.main.ipks.service.errors; + +public class DownstreamBadResponseException extends RuntimeException { + public DownstreamBadResponseException(String message, Throwable cause) { super(message, cause); } + public DownstreamBadResponseException(String message) { super(message); } +} \ No newline at end of file diff --git a/src/main/java/com/main/ipks/service/errors/DownstreamBusinessPayloadException.java b/src/main/java/com/main/ipks/service/errors/DownstreamBusinessPayloadException.java new file mode 100644 index 0000000..b6502fe --- /dev/null +++ b/src/main/java/com/main/ipks/service/errors/DownstreamBusinessPayloadException.java @@ -0,0 +1,17 @@ +package com.main.ipks.service.errors; + +import org.springframework.http.HttpStatus; + +public class DownstreamBusinessPayloadException extends RuntimeException { + private final String rawJson; + private final HttpStatus statusToReturn; + + public DownstreamBusinessPayloadException(String rawJson, HttpStatus statusToReturn) { + super("Downstream business failure"); // message not exposed to client; rawJson is + this.rawJson = rawJson; + this.statusToReturn = statusToReturn; + } + + public String getRawJson() { return rawJson; } + public HttpStatus getStatusToReturn() { return statusToReturn; } +} \ No newline at end of file diff --git a/src/main/java/com/main/ipks/service/errors/DownstreamEmptyBodyException.java b/src/main/java/com/main/ipks/service/errors/DownstreamEmptyBodyException.java new file mode 100644 index 0000000..6b8e9f5 --- /dev/null +++ b/src/main/java/com/main/ipks/service/errors/DownstreamEmptyBodyException.java @@ -0,0 +1,5 @@ +package com.main.ipks.service.errors; + +public class DownstreamEmptyBodyException extends RuntimeException { + public DownstreamEmptyBodyException(String message) { super(message); } +} \ No newline at end of file diff --git a/src/main/java/com/main/ipks/service/errors/TokenFetchException.java b/src/main/java/com/main/ipks/service/errors/TokenFetchException.java new file mode 100644 index 0000000..33c3709 --- /dev/null +++ b/src/main/java/com/main/ipks/service/errors/TokenFetchException.java @@ -0,0 +1,6 @@ +package com.main.ipks.service.errors; + +public class TokenFetchException extends RuntimeException { + public TokenFetchException(String message, Throwable cause) { super(message, cause); } + public TokenFetchException(String message) { super(message); } +} \ No newline at end of file diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties new file mode 100644 index 0000000..ac1df74 --- /dev/null +++ b/src/main/resources/application.properties @@ -0,0 +1,20 @@ +spring.application.name=ipks + + + +logging.level.root=INFO +logging.level.com.main.ipks.service=DEBUG + + +# TOKEN +cedge.token.url=https://nabard.cedgeapiservices.in/BANCEDGEAPI/api/getToken + +# API1 - Change Detail Prompt +cedge.changePrompt.url=https://nabard.cedgeapiservices.in/BANCEDGEAPI/api/ChangeDetailPromptNew + +# API2 - Amend Customer +cedge.amendCustomer.url=https://nabard.cedgeapiservices.in/BANCEDGEAPI/api/amendcustomernabard + + + + diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml new file mode 100644 index 0000000..f57088d --- /dev/null +++ b/src/main/resources/logback-spring.xml @@ -0,0 +1,46 @@ + + + + + + + + + ${LOG_PATH}/application.log + + + ${LOG_ARCHIVE}/application.%d{yyyy-MM-dd}.%i.log.gz + 10MB + 60 + 20GB + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger{36} - %msg%n + + + + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger{36} - %msg%n + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/java/com/main/ipks_cbi/IpksCbiApplicationTests.java b/src/test/java/com/main/ipks_cbi/IpksCbiApplicationTests.java new file mode 100644 index 0000000..3298987 --- /dev/null +++ b/src/test/java/com/main/ipks_cbi/IpksCbiApplicationTests.java @@ -0,0 +1,13 @@ +package com.main.ipks; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class IpksCbiApplicationTests { + + @Test + void contextLoads() { + } + +}