first commit

This commit is contained in:
2026-06-16 20:19:21 +05:30
commit 8a3c41b3db
28 changed files with 2602 additions and 0 deletions
+2
View File
@@ -0,0 +1,2 @@
/mvnw text eol=lf
*.cmd text eol=crlf
+33
View File
@@ -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/
+3
View File
@@ -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
Vendored
+295
View File
@@ -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-<version>,maven-mvnd-<version>-<platform>}/<hash>
[ -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 "$@"
Vendored
+189
View File
@@ -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-<version>,maven-mvnd-<version>-<platform>}/<hash>
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"
+72
View File
@@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.5</version> <!-- Stable Boot 3.x -->
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.main</groupId>
<artifactId>ipks</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>ipks</name>
<description>Account Opening Via API</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.14.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
@@ -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);
}
}
@@ -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<Map<String, Object>> 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<String> 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<Map<String, Object>> 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<Map<String, Object>> 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<Map<String, Object>> 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
));
}
}
@@ -0,0 +1,42 @@
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() {
try {
// Trust all certificates (TEMP ONLY)
TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; }
public void checkClientTrusted(X509Certificate[] certs, String authType) {}
public void checkServerTrusted(X509Certificate[] certs, String authType) {}
}
};
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustAllCerts, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
HttpsURLConnection.setDefaultHostnameVerifier((hostname, session) -> true);
SimpleClientHttpRequestFactory factory =
new SimpleClientHttpRequestFactory();
return new RestTemplate(factory);
} catch (Exception e) {
throw new RuntimeException("Failed to create SSL ignoring RestTemplate", e);
}
}
}
@@ -0,0 +1,63 @@
package com.main.ipks.controller;
import com.main.ipks.dto.cif.*;
import com.main.ipks.dto.account.*;
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 = "/customer",
consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE
)
public ResponseEntity<CifApiResponse> createCustomer(@Valid @RequestBody IpksCustomerRequest request) {
log.info("IPKS CIF creation request received...");
CifApiResponse response = cbsIntegrationService.createCustomer(request);
return ResponseEntity.ok(response);
}
@PostMapping(
value = "/account",
consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE
)
public ResponseEntity<AccountCreationResponse> createAccount(@Valid @RequestBody IpksAccountRequest request) {
log.info("IPKS Account creation request (CIF={})", request.getCif());
AccountCreationResponse resp = cbsIntegrationService.createAccount(request);
return ResponseEntity.ok(resp);
}
}
@@ -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;
}
}
@@ -0,0 +1,45 @@
package com.main.ipks.dto.account;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
@JsonIgnoreProperties(ignoreUnknown = true)
public class AccountCreationResponse {
@JsonProperty("status")
private String status;
@JsonProperty("message")
private String message;
@JsonProperty("response")
private ResponseData response;
@JsonProperty("error")
private int error;
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 ResponseData getResponse() { return response; }
public void setResponse(ResponseData response) { this.response = response; }
public int getError() { return error; }
public void setError(int error) { this.error = error; }
@JsonIgnoreProperties(ignoreUnknown = true)
public static class ResponseData {
@JsonProperty("RcptData")
private String rcptData;
public String getRcptData() { return rcptData; }
public void setRcptData(String rcptData) { this.rcptData = rcptData; }
}
}
@@ -0,0 +1,63 @@
package com.main.ipks.dto.account;
import com.fasterxml.jackson.annotation.JsonProperty;
public class CreateAccountRequest {
@JsonProperty("AcctNum1")
private Long acctNum1;
@JsonProperty("DfltStr1")
private String dfltStr1 = "";
@JsonProperty("DfltStr2")
private String dfltStr2 = "";
@JsonProperty("DfltStr3")
private String dfltStr3 = "";
@JsonProperty("DfltStr4")
private String dfltStr4 = "";
// --- Getters & Setters ---
public Long getAcctNum1() {
return acctNum1;
}
public void setAcctNum1(Long acctNum1) {
this.acctNum1 = acctNum1;
}
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;
}
}
@@ -0,0 +1,33 @@
package com.main.ipks.dto.account;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.Map;
@JsonIgnoreProperties(ignoreUnknown = true)
public class CreateAccountResponse {
@JsonProperty("status")
private String status;
@JsonProperty("message")
private String message;
@JsonProperty("response")
private Map<String, Object> response;
@JsonProperty("error")
private int error;
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 Map<String, Object> getResponse() { return response; }
public void setResponse(Map<String, Object> response) { this.response = response; }
public int getError() { return error; }
public void setError(int error) { this.error = error; }
}
@@ -0,0 +1,174 @@
package com.main.ipks.dto.account;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
@JsonInclude(JsonInclude.Include.ALWAYS)
public class CreateDepositAccountOkRequest {
// ---- Six dynamic fields (set in service) ----
@JsonProperty("AcctNum4") private long acctNum4;
@JsonProperty("DfltStr1") private String dfltStr1 = "";
@JsonProperty("DfltStr2") private String dfltStr2 = "";
@JsonProperty("DfltStr3") private String dfltStr3 = "";
@JsonProperty("DfltStr4") private String dfltStr4 = "";
@JsonProperty("DfltStr8") private String dfltStr8 = "";
// ---- Fixed defaults ----
@JsonProperty("AcctNum1") private int acctNum1 = 0;
@JsonProperty("DfltStr26") private String dfltStr26 = "N";
@JsonProperty("HomeBrch1") private int homeBrch1 = 0;
@JsonProperty("ATMRq1") private String atmRq1 = "";
@JsonProperty("Name11") private String name11 = "";
@JsonProperty("ATMCard1") private String atmCard1 = "DOM";
@JsonProperty("NomReqd1") private String nomReqd1 = "N";
@JsonProperty("DfltStr6") private String dfltStr6 = "IN";
@JsonProperty("NomiCIF1") private String nomiCIF1 = "";
@JsonProperty("AgentCode") private String agentCode = "";
@JsonProperty("Receipttype1") private int receipttype1 = 0;
@JsonProperty("DefInt21") private int defInt21 = 10;
@JsonProperty("DefInt22") private int defInt22 = 11;
@JsonProperty("DefInt23") private int defInt23 = 1;
@JsonProperty("DefInt24") private int defInt24 = 10;
@JsonProperty("DefInt25") private int defInt25 = 0;
@JsonProperty("Intpcode1") private String intpcode1 = "R";
@JsonProperty("AcctNum2") private int acctNum2 = 0;
@JsonProperty("DefInt9") private int defInt9 = 0;
@JsonProperty("Languagecode1") private int languagecode1 = 1;
@JsonProperty("DfltStr17") private String dfltStr17 = "";
@JsonProperty("DfltStr22") private String dfltStr22 = "";
@JsonProperty("DefInt1") private int defInt1 = 0;
@JsonProperty("Mailind1") private String mailind1 = "1";
@JsonProperty("Noteind1") private String noteind1 = "0";
@JsonProperty("AcctNum3") private int acctNum3 = 0;
@JsonProperty("Unsigned41") private String unsigned41 = "0.0";
@JsonProperty("Yesno3") private String yesno3 = "N";
@JsonProperty("Yesno2") private String yesno2 = "N";
@JsonProperty("FreqN1") private String freqN1 = "";
@JsonProperty("DefInt4") private int defInt4 = 0;
@JsonProperty("DefInt5") private int defInt5 = 0;
@JsonProperty("DefInt10") private int defInt10 = 0;
@JsonProperty("DefInt11") private int defInt11 = 0;
@JsonProperty("DefInt12") private int defInt12 = 0;
@JsonProperty("DfltStr49") private String dfltStr49 = "";
@JsonProperty("DefInt2") private int defInt2 = 0;
@JsonProperty("AcctNum5") private int acctNum5 = 0;
@JsonProperty("DfltStr12") private String dfltStr12 = "";
@JsonProperty("Acctmgmtgrp") private String acctmgmtgrp = "";
@JsonProperty("DfltStr19") private String dfltStr19 = "";
@JsonProperty("DfltStr20") private String dfltStr20 = "";
@JsonProperty("DefInt7") private int defInt7 = 0;
@JsonProperty("DefInt13") private int defInt13 = 0;
@JsonProperty("DfltStr21") private String dfltStr21 = "";
@JsonProperty("Amt2") private String amt2 = "0.0";
@JsonProperty("DfltStr15") private String dfltStr15 = "";
@JsonProperty("DfltStr10") private String dfltStr10 = "";
@JsonProperty("DfltStr11") private String dfltStr11 = "ZZ";
@JsonProperty("DfltStr28") private String dfltStr28 = "ZZ";
@JsonProperty("DfltStr27") private String dfltStr27 = "Z";
@JsonProperty("DfltStr25") private String dfltStr25 = "ZZ";
@JsonProperty("DfltStr18") private String dfltStr18 = "ZZ";
@JsonProperty("DfltStr9") private String dfltStr9 = "";
@JsonProperty("Dt1") private int dt1 = 0;
@JsonProperty("DefInt6") private int defInt6 = 0;
@JsonProperty("Notipros1") private String notipros1 = "0";
@JsonProperty("DfltStr14") private String dfltStr14 = "";
@JsonProperty("Signed42") private String signed42 = "0.0";
@JsonProperty("DefInt3") private int defInt3 = 0;
@JsonProperty("DfltStr16") private String dfltStr16 = "";
@JsonProperty("DfltStr13") private String dfltStr13 = "";
@JsonProperty("DfltStr23") private String dfltStr23 = "";
@JsonProperty("BrkrNum1") private int brkrNum1 = 0;
@JsonProperty("DefInt8") private int defInt8 = 0;
@JsonProperty("Signed41") private String signed41 = "0.0";
@JsonProperty("Unsigned1") private int unsigned1 = 706;
@JsonProperty("Yesno6") private String yesno6 = "N";
@JsonProperty("DefInt28") private int defInt28 = 0;
@JsonProperty("RMPB") private String rmpb = "";
@JsonProperty("AgentCode1") private String agentCode1 = "";
@JsonProperty("VendorCode") private int vendorCode = 0;
@JsonProperty("DefInt16") private int defInt16 = 0;
@JsonProperty("DefInt17") private int defInt17 = 0;
@JsonProperty("DefInt18") private int defInt18 = 0;
@JsonProperty("Dt2") private int dt2 = 0;
@JsonProperty("Yesno1") private String yesno1 = "N";
@JsonProperty("Dacctype2") private int dacctype2 = 0;
@JsonProperty("DefInt19") private int defInt19 = 0;
@JsonProperty("DfltStr31") private String dfltStr31 = "";
@JsonProperty("FreqN2") private String freqN2 = "";
@JsonProperty("Amt1") private String amt1 = "0.0";
@JsonProperty("DfltStr32") private String dfltStr32 = "0";
@JsonProperty("DefInt20") private int defInt20 = 0;
@JsonProperty("DfltStr33") private String dfltStr33 = "N";
@JsonProperty("DfltStr34") private String dfltStr34 = "";
@JsonProperty("DfltStr35") private String dfltStr35 = "L";
@JsonProperty("DefInt27") private int defInt27 = 0;
@JsonProperty("DdoCode") private int ddoCode = 0;
@JsonProperty("DfltStr37") private String dfltStr37 = "";
@JsonProperty("DfltStr38") private String dfltStr38 = "";
@JsonProperty("DfltStr39") private String dfltStr39 = "";
@JsonProperty("DfltStr40") private String dfltStr40 = "";
@JsonProperty("DfltStr41") private String dfltStr41 = "";
@JsonProperty("PrimAcctNum") private int primAcctNum = 0;
@JsonProperty("Dintcat2") private int dintcat2 = 0;
@JsonProperty("DfltStr42") private String dfltStr42 = "";
@JsonProperty("DefInt26") private int defInt26 = 0;
@JsonProperty("DfltStr43") private String dfltStr43 = "";
@JsonProperty("DfltStr44") private String dfltStr44 = "";
@JsonProperty("DfltStr45") private String dfltStr45 = "";
@JsonProperty("DfltStr46") private String dfltStr46 = "";
@JsonProperty("DfltStr47") private String dfltStr47 = "";
@JsonProperty("Instlmnt") private int instlmnt = 0;
@JsonProperty("PrdBal") private String prdBal = "0.0";
@JsonProperty("IrregularCnt") private int irregularCnt = 0;
@JsonProperty("ExtnCnt") private int extnCnt = 0;
@JsonProperty("AcctStat") private int acctStat = 0;
@JsonProperty("PrimAcct") private String primAcct = "";
@JsonProperty("AdvcReqd") private String advcReqd = "N";
@JsonProperty("TypeofGold") private String typeofGold = "";
@JsonProperty("DfltStr48") private String dfltStr48 = "";
@JsonProperty("MortgageOffsetInd") private String mortgageOffsetInd = "";
@JsonProperty("NomiFlag") private String nomiFlag = "";
// --- Getters/Setters for 6 dynamic fields (others optional) ---
public long getAcctNum4() { return acctNum4; }
public void setAcctNum4(long acctNum4) { this.acctNum4 = acctNum4; }
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 getDfltStr8() { return dfltStr8; }
public void setDfltStr8(String dfltStr8) { this.dfltStr8 = dfltStr8; }
}
@@ -0,0 +1,33 @@
package com.main.ipks.dto.account;
import com.fasterxml.jackson.annotation.JsonAlias;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
@JsonIgnoreProperties(ignoreUnknown = true)
public class IpksAccountRequest {
public IpksAccountRequest(){}
@NotBlank(message = "cif must not be blank")
@JsonProperty("CIF")
@JsonAlias({"cif","custNum","cust_num"})
private String cif;
// getters/setters
public String getCif() { return cif; }
public void setCif(String cif) { this.cif = cif; }
}
@@ -0,0 +1,45 @@
package com.main.ipks.dto.cif;
import com.fasterxml.jackson.annotation.JsonProperty;
public class CifApiResponse {
@JsonProperty("status")
private String status;
@JsonProperty("message")
private String message;
@JsonProperty("response")
private ResponseData response; // nullable in FAIL case
@JsonProperty("error")
private int error;
public String getStatus() { return status; }
public String getMessage() { return message; }
public ResponseData getResponse() { return response; }
public int getError() { return error; }
public void setStatus(String status) { this.status = status; }
public void setMessage(String message) { this.message = message; }
public void setResponse(ResponseData response) { this.response = response; }
public void setError(int error) { this.error = error; }
// Nested object for "response"
public static class ResponseData {
@JsonProperty("RcptData")
private String rcptData;
@JsonProperty("CustNum")
private String custNum;
public String getRcptData() { return rcptData; }
public String getCustNum() { return custNum; }
public void setRcptData(String rcptData) { this.rcptData = rcptData; }
public void setCustNum(String custNum) { this.custNum = custNum; }
}
}
@@ -0,0 +1,500 @@
package com.main.ipks.dto.cif;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
@JsonInclude(JsonInclude.Include.ALWAYS) // include nulls where CBS expects them (e.g., Exflag3/Exflag4)
public class CifCreateRequest {
// ---------- Mandatory / dynamic ----------
@JsonProperty("FirstName")
private String firstName;
@JsonProperty("LastName")
private String lastName;
@JsonProperty("Addr1")
private String addr1;
@JsonProperty("Addr2")
private String addr2;
@JsonProperty("Addr3")
private String addr3;
@JsonProperty("CityCode")
private String cityCode;
@JsonProperty("PstCode")
private String pstCode;
@JsonProperty("CustType")
private String custType;
@JsonProperty("Dob")
private String dob;
@JsonProperty("Gndr")
private String gndr;
@JsonProperty("KYCDate")
private String kycDate;
@JsonProperty("MaritalStat")
private String maritalStat;
@JsonProperty("MotherMdnName")
private String motherMdnName;
@JsonProperty("IdentityTyp")
private String identityTyp;
@JsonProperty("IdNum")
private String idNum;
@JsonProperty("KycStatus")
private int kycStatus;
@JsonProperty("IdTyp2")
private String idTyp2;
@JsonProperty("Addr4")
private String addr4;
@JsonProperty("CustMidName")
private String custMidName;
@JsonProperty("Title")
private String title ;
@JsonProperty("Natlty")
private String natlty;
@JsonProperty("TelexNum")
private String telexNum;
@JsonProperty("ResdncyStat")
private String resdncyStat;
@JsonProperty("CycInd")
private String cycInd;
@JsonProperty("FatherOrSpouseName")
private String fatherOrSpouseName;
// ---------- Optional (defaulted permanently) ----------
@JsonProperty("CountryCode1")
private String countryCode1 = "IN";
@JsonProperty("Ocpncode1")
private String ocpncode1 = "0100";
@JsonProperty("DmstcRisk")
private String dmstcRisk = "ZZ";
@JsonProperty("XbrdrRisk")
private String xbrdrRisk = "ZZ";
@JsonProperty("IdtypeHidden")
private String idtypeHidden = "";
// @JsonProperty("CycInd")
// private String cycInd = "M";
@JsonProperty("OrigCode")
private String origCode = "41";
@JsonProperty("BsrOrigCode")
private String bsrOrigCode = "41";
@JsonProperty("Incomerange")
private String incomerange = "01";
@JsonProperty("Trflag")
private String trflag = "";
@JsonProperty("Exemptionprofile")
private int exemptionprofile = 0;
@JsonProperty("TradeEmailRepflag")
private String tradeEmailRepflag = "";
@JsonProperty("CustNum")
private long custNum = 0;
@JsonProperty("CustTyp")
private String custTyp = "00";
@JsonProperty("RelnshpMgr")
private long relnshpMgr = 0;
@JsonProperty("BussCustName")
private String bussCustName = "";
@JsonProperty("ThrsldLmt")
private long thrsldLmt = 0;
@JsonProperty("HomePhnNum")
private String homePhnNum = "";
@JsonProperty("FaxNum")
private String faxNum = "";
@JsonProperty("BussPhnNum")
private String bussPhnNum = "";
@JsonProperty("Occup")
private String occup = "";
@JsonProperty("Lang")
private String lang = "01";
@JsonProperty("IdIssueDt")
private String idIssueDt = "00000000";
@JsonProperty("IdIssuePlace")
private String idIssuePlace = "";
@JsonProperty("VipCode")
private int vipCode = 0;
@JsonProperty("Stat")
private int stat = 0;
@JsonProperty("SegCode")
private String segCode = "0706";
@JsonProperty("LckrHldr")
private String lckrHldr = "";
@JsonProperty("TfnInd")
private int tfnInd = 0;
@JsonProperty("HomeBrch")
private int homeBrch = 0;
@JsonProperty("Cust")
private String cust = "";
@JsonProperty("CustParent")
private String custParent = "";
@JsonProperty("Indust")
private String indust = "";
@JsonProperty("Country")
private String country = "";
@JsonProperty("Sec")
private String sec = "";
@JsonProperty("IndustClassification")
private String industClassification = "01";
@JsonProperty("CountryRisk")
private String countryRisk = "IN";
@JsonProperty("GrpCode")
private String grpCode = "";
@JsonProperty("BussSecCode")
private String bussSecCode = "";
@JsonProperty("EstDt")
private String estDt = "00000000";
@JsonProperty("StrtDt")
private String strtDt = "00000000";
@JsonProperty("CustGrp")
private String custGrp = "0102";
@JsonProperty("DfltStr27")
private String dfltStr27 = "";
@JsonProperty("VisaIssueDt")
private String visaIssueDt = "00000000";
@JsonProperty("VisaExpryDt")
private String visaExpryDt = "00000000";
@JsonProperty("Detail")
private String detail = "";
@JsonProperty("LiquidDt")
private String liquidDt = "00000000";
@JsonProperty("PremDt")
private String premDt = "00000000";
@JsonProperty("NPATrade")
private String npaTrade = "";
@JsonProperty("NPANonTrade")
private String npaNonTrade = "";
@JsonProperty("TradeCustNum")
private String tradeCustNum = "N";
@JsonProperty("RefNum")
private long refNum = 0;
@JsonProperty("ChanCode")
private int chanCode = 0;
@JsonProperty("CountryCode")
private String countryCode = "IN";
@JsonProperty("StateCode")
private String stateCode = "32";
@JsonProperty("VoterIdNum")
private String voterIdNum = "";
@JsonProperty("AppTDS")
private String appTDS = "Y";
@JsonProperty("Rmrks")
private String rmrks = "";
@JsonProperty("RevaltnFlag")
private String revaltnFlag = "N";
@JsonProperty("GreetingReq")
private String greetingReq = "";
@JsonProperty("AcctNum2")
private long acctNum2 = 0;
@JsonProperty("ChkFlag4")
private String chkFlag4 = "N";
@JsonProperty("Addr5")
private String addr5 = "";
@JsonProperty("EmailAddr")
private String emailAddr = "";
@JsonProperty("Internetkit")
private String internetkit = "";
@JsonProperty("FundMgmtUnit")
private int fundMgmtUnit = 0;
@JsonProperty("SrcOtherExpns")
private String srcOtherExpns = "";
@JsonProperty("CustRisk")
private String custRisk = "00";
@JsonProperty("DiffAble")
private String diffAble = "";
@JsonProperty("Exflag1")
private String exflag1 = "";
@JsonProperty("Exflag2")
private String exflag2 = "";
@JsonProperty("Exflag3")
private String exflag3 = null;
@JsonProperty("Exflag4")
private String exflag4 = null;
@JsonProperty("Exflag5")
private String exflag5 = "";
@JsonProperty("Exfield1")
private String exfield1 = "";
@JsonProperty("Exfield2")
private String exfield2 = "";
@JsonProperty("Exfield3")
private String exfield3 = "";
@JsonProperty("Exfield4")
private String exfield4 = "";
@JsonProperty("Exfield5")
private String exfield5 = "";
@JsonProperty("Exfield6")
private String exfield6 = "";
@JsonProperty("Exfield7")
private String exfield7 = "";
@JsonProperty("Exfield8")
private String exfield8 = "";
@JsonProperty("Exfield9")
private String exfield9 = "";
@JsonProperty("Exfield10")
private String exfield10 = "";
@JsonProperty("TradeGoldcif")
private String tradeGoldcif = "";
@JsonProperty("Tinvat")
private String tinvat = "";
@JsonProperty("Tan")
private String tan = "";
@JsonProperty("ServiceTaxRegno")
private String serviceTaxRegno = "";
@JsonProperty("DfltStr45")
private String dfltStr45 = "";
@JsonProperty("Npoflag")
private String npoflag = "";
@JsonProperty("VillageCode")
private String villageCode = "Sector";
// @JsonProperty("FatherOrSpouseName")
// private String fatherOrSpouseName = "";
// ---------- Getters & Setters ----------
public String getFirstName() { return firstName; }
public void setFirstName(String firstName) { this.firstName = firstName; }
public String getLastName() { return lastName; }
public void setLastName(String lastName) { this.lastName = lastName; }
public String getAddr1() { return addr1; }
public void setAddr1(String addr1) { this.addr1 = addr1; }
public String getAddr2() { return addr2; }
public void setAddr2(String addr2) { this.addr2 = addr2; }
public String getAddr3() { return addr3; }
public void setAddr3(String addr3) { this.addr3 = addr3; }
public String getCityCode() { return cityCode; }
public void setCityCode(String cityCode) { this.cityCode = cityCode; }
public String getPstCode() { return pstCode; }
public void setPstCode(String pstCode) { this.pstCode = pstCode; }
public String getCustType() { return custType; }
public void setCustType(String custType) { this.custType = custType; }
public String getDob() { return dob; }
public void setDob(String dob) { this.dob = dob; }
public String getGndr() { return gndr; }
public void setGndr(String gndr) { this.gndr = gndr; }
public String getKycDate() { return kycDate; }
public void setKycDate(String kycDate) { this.kycDate = kycDate; }
public String getMaritalStat() { return maritalStat; }
public void setMaritalStat(String maritalStat) { this.maritalStat = maritalStat; }
public String getMotherMdnName() { return motherMdnName; }
public void setMotherMdnName(String motherMdnName) { this.motherMdnName = motherMdnName; }
public String getIdentityTyp() { return identityTyp; }
public void setIdentityTyp(String identityTyp) { this.identityTyp = identityTyp; }
public String getIdNum() { return idNum; }
public void setIdNum(String idNum) { this.idNum = idNum; }
public int getKycStatus() { return kycStatus; }
public void setKycStatus(int kycStatus) { this.kycStatus = kycStatus; }
public String getIdTyp2() { return idTyp2; }
public void setIdTyp2(String idTyp2) { this.idTyp2 = idTyp2; }
public String getAddr4() { return addr4; }
public void setAddr4(String addr4) { this.addr4 = addr4; }
public String getCustMidName() { return custMidName; }
public void setCustMidName(String custMidName) { this.custMidName = custMidName; }
public String getTitle() { return title; }
public void setTitle(String title) { this.title = title; }
public String getNatlty() { return natlty; }
public void setNatlty(String natlty) { this.natlty = natlty; }
public String getTelexNum() { return telexNum; }
public void setTelexNum(String telexNum) { this.telexNum = telexNum; }
public String getResdncyStat() { return resdncyStat; }
public void setResdncyStat(String resdncyStat) { this.resdncyStat = resdncyStat; }
public String getCycInd() { return cycInd; }
public void setCycInd(String cycInd) { this.cycInd = cycInd; }
public String getFatherOrSpouseName() { return fatherOrSpouseName; }
public void setFatherOrSpouseName(String fatherOrSpouseName) { this.fatherOrSpouseName = fatherOrSpouseName; }
}
@@ -0,0 +1,306 @@
package com.main.ipks.dto.cif;
import com.fasterxml.jackson.annotation.JsonAlias;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
@JsonIgnoreProperties(ignoreUnknown = true)
public class IpksCustomerRequest {
public IpksCustomerRequest() {}
@NotBlank(message = "firstName must not be blank")
@JsonProperty("FIRST_NAME")
@JsonAlias({"firstName","first_name"})
private String firstName;
@JsonProperty("LAST_NAME")
@JsonAlias({"lastName","last_name"})
private String lastName;
@NotBlank(message = "addr1 must not be blank")
@JsonProperty("ADD_1")
@JsonAlias({"addr1","ADDR1","add_1"})
private String addr1;
@JsonProperty("ADD_2")
@JsonAlias({"addr2","ADDR2","add_2"})
private String addr2;
@JsonProperty("ADD_3")
@JsonAlias({"addr3","ADDR3","add_3"})
private String addr3;
@NotBlank(message = "cityCode must not be blank")
@JsonProperty("CITY_CODE")
@JsonAlias({"cityCode","city_code"})
private String cityCode;
@NotBlank(message = "pstCode/PO_NUMBER must not be blank")
@JsonProperty("PO_NUMBER")
@JsonAlias({"poNumber","po_number","pstCode","postCode"})
private String pstCode;
@NotBlank(message = "custType must not be blank")
@JsonProperty("CUSTOMER_TYPE")
@JsonAlias({"custType","customerType","customer_type"})
private String custType;
@NotBlank(message = "dob/BIRTH_DATE must not be blank")
@JsonProperty("BIRTH_DATE")
@JsonAlias({"dob","birthDate","birth_date"})
@Pattern(regexp = "\\d{8}", message = "dob must be in ddmmyyyy")
private String dob;
@NotBlank(message = "gender must not be blank")
@JsonProperty("GENDER")
@JsonAlias({"gndr","gender"})
private String gndr;
@NotBlank(message = "kycDate/SEND_DATE must not be blank")
@JsonProperty("SEND_DATE")
@JsonAlias({"kycDate","sendDate","send_date"})
@Pattern(regexp = "\\d{8}", message = "kycDate must be in ddmmyyyy")
private String kycDate;
@JsonProperty("STATUS")
@JsonAlias({"maritalStat","marital_status"})
private String maritalStat;
@JsonProperty("MOTHER_NAME")
@JsonAlias({"motherMdnName","motherName","mother_name"})
private String motherMdnName;
@NotNull(message = "identityTyp/ID_TYPE must not be null")
@JsonProperty("ID_TYPE")
@JsonAlias({"identityTyp","idType","id_type"})
private String identityTyp;
@NotBlank(message = "idNum/ID_NUMBER must not be blank")
@JsonProperty("ID_NUMBER")
@JsonAlias({"idNum","idNumber","id_number"})
private String idNum;
@JsonProperty("KYC_STATUS")
@JsonAlias({"kycStatus","kyc_status"})
private Integer kycStatus;
@JsonProperty("ID_TYPE_TWO")
@JsonAlias({"idTyp2","idType2","id_type_two"})
private String idTyp2;
@JsonProperty("ADD4")
@JsonAlias({"ADD_4","add4","idNumberTwo"})
private String addr4;
@JsonProperty("TITLE")
@JsonAlias({"title"})
private String title;
@JsonProperty("CustMidName")
@JsonAlias({"middleName","middle_Name"})
private String custMidName;
@NotBlank(message = "natlty/Natlty must not be blank")
@JsonProperty("Natlty")
@JsonAlias({"nationality_Code","nationalityCode"})
private String natlty;
@NotBlank(message = "telexNum/TelexNum must not be blank")
@JsonProperty("TelexNum")
@JsonAlias({"telexNum","phNo","ph_No"})
private String telexNum;
@NotBlank(message = "resdncyStat/ResdncyStat must not be blank")
@JsonProperty("ResdncyStat")
@JsonAlias({"residency_status","residencyStatus"})
private String resdncyStat;
@JsonProperty("Relative_code")
@JsonAlias({"relative_code"})
private String relativeCode;
@JsonProperty("Relative_name")
@JsonAlias({"relative_name"})
private String relativeName;
// ===== Getters & Setters (camelCase) =====
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getAddr1() {
return addr1;
}
public void setAddr1(String addr1) {
this.addr1 = addr1;
}
public String getAddr2() {
return addr2;
}
public void setAddr2(String addr2) {
this.addr2 = addr2;
}
public String getAddr3() {
return addr3;
}
public void setAddr3(String addr3) {
this.addr3 = addr3;
}
public String getCityCode() {
return cityCode;
}
public void setCityCode(String cityCode) {
this.cityCode = cityCode;
}
public String getPstCode() {
return pstCode;
}
public void setPstCode(String pstCode) {
this.pstCode = pstCode;
}
public String getCustType() {
return custType;
}
public void setCustType(String custType) {
this.custType = custType;
}
public String getDob() {
return dob;
}
public void setDob(String dob) {
this.dob = dob;
}
public String getGndr() {
return gndr;
}
public void setGndr(String gndr) {
this.gndr = gndr;
}
public String getKycDate() {
return kycDate;
}
public void setKycDate(String kycDate) {
this.kycDate = kycDate;
}
public String getMaritalStat() {
return maritalStat;
}
public void setMaritalStat(String maritalStat) {
this.maritalStat = maritalStat;
}
public String getMotherMdnName() {
return motherMdnName;
}
public void setMotherMdnName(String motherMdnName) {
this.motherMdnName = motherMdnName;
}
public String getIdentityTyp() {
return identityTyp;
}
public void setIdentityTyp(String identityTyp) {
this.identityTyp = identityTyp;
}
public String getIdNum() {
return idNum;
}
public void setIdNum(String idNum) {
this.idNum = idNum;
}
public Integer getKycStatus() {
return kycStatus;
}
public void setKycStatus(Integer kycStatus) {
this.kycStatus = kycStatus;
}
public String getIdTyp2() {
return idTyp2;
}
public void setIdTyp2(String idTyp2) {
this.idTyp2 = idTyp2;
}
public String getAddr4() {
return addr4;
}
public void setAddr4(String addr4) {
this.addr4 = addr4;
}
public String getCustMidName() { return custMidName; }
public void setCustMidName(String custMidName) { this.custMidName = custMidName; }
public String getTitle() { return title; }
public void setTitle(String title) { this.title = title; }
public String getNatlty() { return natlty; }
public void setNatlty(String natlty) { this.natlty = natlty; }
public String getTelexNum() { return telexNum; }
public void setTelexNum(String telexNum) { this.telexNum = telexNum; }
public String getResdncyStat() { return resdncyStat; }
public void setResdncyStat(String resdncyStat) { this.resdncyStat = resdncyStat; }
public String getRelativeCode() { return relativeCode; }
public void setRelativeCode(String relativeCode) { this.relativeCode = relativeCode; }
public String getRelativeName() { return relativeName; }
public void setRelativeName(String relativeName) { this.relativeName = relativeName; }
}
@@ -0,0 +1,309 @@
package com.main.ipks.service;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.main.ipks.dto.account.*;
import com.main.ipks.dto.cif.CifApiResponse;
import com.main.ipks.dto.cif.CifCreateRequest;
import com.main.ipks.dto.cif.IpksCustomerRequest;
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.Map;
@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.cif.url}")
private String cifUrl;
@Value("${cedge.account.url}")
private String accountUrl;
@Value("${cedge.accountOk.url}")
private String accountOkUrl;
// ---------------- CIF Creation ----------------
public CifApiResponse createCustomer(IpksCustomerRequest in) {
try {
// Token + headers (TokenFetchException handled globally -> 503)
String token = tokenService.getToken();
HttpHeaders headers = buildHeaders(token);
// Map inbound to C-EDGE payload (null-safe defaults)
CifCreateRequest req = mapToCbs(in);
log.debug("[C-EDGE] CIF-CREATION Request JSON: {}", safeJson(req));
HttpEntity<CifCreateRequest> entity = new HttpEntity<>(req, headers);
ResponseEntity<String> response;
try {
response = restTemplate.exchange(cifUrl, HttpMethod.POST, entity, String.class);
} catch (HttpStatusCodeException ex) {
if (ex.getStatusCode() == HttpStatus.UNAUTHORIZED || ex.getStatusCode() == HttpStatus.FORBIDDEN) {
log.warn("[C-EDGE] CIF call unauthorized. Refreshing token and retrying once...");
HttpEntity<CifCreateRequest> retryEntity =
new HttpEntity<>(req, buildHeaders(tokenService.refreshToken()));
response = restTemplate.exchange(cifUrl, HttpMethod.POST, retryEntity, String.class);
} else {
// If upstream provided a JSON body, propagate it to client (as 502)
String raw = ex.getResponseBodyAsString();
if (raw != null && !raw.isBlank() && looksJson(raw)) {
throw new DownstreamBusinessPayloadException(raw, HttpStatus.BAD_GATEWAY);
}
throw ex;
}
}
String bodyStr = response.getBody();
if (bodyStr == null || bodyStr.isBlank()) {
throw new DownstreamEmptyBodyException("Empty response from C-EDGE (CIF)");
}
// status/message to decide the path
JsonNode node = om.readTree(bodyStr);
String status = node.path("status").asText(null);
if ("SUCCESS".equalsIgnoreCase(status)) {
// Success -> map to DTO and return
CifApiResponse body = om.readValue(bodyStr, CifApiResponse.class);
if (body.getResponse() != null) {
log.info("CIF Created...");
}
return body;
} else {
// Business FAIL from C-EDGE -> propagate raw JSON as-is to client with 400
log.info("[C-EDGE] CIF Creation Failed (business). Returning upstream payload as-is. {}",bodyStr);
throw new DownstreamBusinessPayloadException(bodyStr, HttpStatus.BAD_REQUEST);
}
} catch (HttpStatusCodeException ex) {
// Non-401/403 HTTP errors without propagatable JSON
throw new DownstreamBadResponseException(
"C-EDGE CIF call failed: HTTP " + ex.getStatusCode().value(), ex);
} catch (RestClientException e) {
throw new DownstreamBadResponseException("C-EDGE CIF Creation failed: " + e.getMessage(), e);
} catch (DownstreamBusinessPayloadException | DownstreamEmptyBodyException e) {
throw e; // already mapped
} catch (Exception e) {
// JSON parsing/mapping
throw new DownstreamBadResponseException("CIF response processing error: " + e.getMessage(), e);
}
}
// ---------------- Account Creation (API-1 + API-2) ----------------
public AccountCreationResponse createAccount(IpksAccountRequest reqDto) {
try {
String token = tokenService.getToken();
HttpHeaders headers = buildHeaders(token);
// --- API-1 ---
CreateAccountRequest api1Req = new CreateAccountRequest();
api1Req.setAcctNum1(Long.parseLong(reqDto.getCif()));
HttpEntity<CreateAccountRequest> api1Entity = new HttpEntity<>(api1Req, headers);
log.debug("[C-EDGE] API-1 for Account Creation: Request JSON: {}", safeJson(api1Req));
ResponseEntity<String> api1Raw;
try {
api1Raw = restTemplate.exchange(accountUrl, 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");
HttpEntity<CreateAccountRequest> retryEntity =
new HttpEntity<>(api1Req, buildHeaders(tokenService.refreshToken()));
api1Raw = restTemplate.exchange(accountUrl, 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 for Account Creation");
}
JsonNode api1Node = om.readTree(api1Str);
String api1Status = api1Node.path("status").asText(null);
if (!"SUCCESS".equalsIgnoreCase(api1Status)) {
// Propagate upstream business FAIL as-is with 400
log.error("[C-EDGE] API-1 business failure. Payload returned to client: {}", api1Str);
throw new DownstreamBusinessPayloadException(api1Str, HttpStatus.BAD_REQUEST);
}
// Map to DTO to get response map
CreateAccountResponse api1Dto = om.readValue(api1Str, CreateAccountResponse.class);
Map<String, Object> m = api1Dto.getResponse();
if (m == null) {
throw new DownstreamEmptyBodyException("C-EDGE API-1 for Account Creation response map is null");
}
// Build API-2 request
CreateDepositAccountOkRequest api2Req = new CreateDepositAccountOkRequest();
String rawAcctNum4 = s(m.get("AcctNum4"));
String trimmedAcctNum4 = removeFirstFiveLeadingZeros(rawAcctNum4);
long acctNum4 = toLong(trimmedAcctNum4, 0L);
api2Req.setAcctNum4(acctNum4);
api2Req.setDfltStr1(s(m.get("DfltStr1")));
api2Req.setDfltStr2(s(m.get("DfltStr2")));
api2Req.setDfltStr3(s(m.get("DfltStr3")));
api2Req.setDfltStr4(s(m.get("DfltStr4")));
api2Req.setDfltStr8(s(m.get("DfltStr8")));
log.debug("[C-EDGE] API-2 for Account Creation: Request JSON: {}", safeJson(api2Req));
// --- API-2 ---
HttpEntity<CreateDepositAccountOkRequest> api2Entity = new HttpEntity<>(api2Req, headers);
ResponseEntity<String> api2Raw;
try {
api2Raw = restTemplate.exchange(accountOkUrl, 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");
HttpEntity<CreateDepositAccountOkRequest> retryEntity =
new HttpEntity<>(api2Req, buildHeaders(tokenService.refreshToken()));
api2Raw = restTemplate.exchange(accountOkUrl, 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 for Account Creation");
}
JsonNode api2Node = om.readTree(api2Str);
String api2Status = api2Node.path("status").asText(null);
if (!"SUCCESS".equalsIgnoreCase(api2Status)) {
// Propagate upstream business FAIL as-is with 400
log.error("[C-EDGE] API-2 business failure. Payload returned to client: {}", api2Str);
throw new DownstreamBusinessPayloadException(api2Str, HttpStatus.BAD_REQUEST);
}
// Success -> map final
AccountCreationResponse finalResp = om.readValue(api2Str, AccountCreationResponse.class);
log.info("Account Created...");
return finalResp;
} catch (HttpStatusCodeException ex) {
throw new DownstreamBadResponseException(
"C-EDGE Account call failed: HTTP " + ex.getStatusCode().value(), ex);
} catch (RestClientException e) {
throw new DownstreamBadResponseException("C-EDGE Account Creation failed: " + e.getMessage(), e);
} catch (DownstreamBusinessPayloadException | DownstreamEmptyBodyException e) {
throw e;
} catch (Exception e) {
throw new DownstreamBadResponseException("Account response processing error: " + e.getMessage(), e);
}
}
// ------------ Mapping (IpksCustomerRequest -> CifCreateRequest) ------------
private CifCreateRequest mapToCbs(IpksCustomerRequest in) {
CifCreateRequest req = new CifCreateRequest();
req.setFirstName(def(in.getFirstName(), ""));
req.setLastName(def(in.getLastName(), ""));
req.setCustMidName(def(in.getCustMidName(), ""));
req.setTitle(def(in.getTitle(), "01"));
req.setMotherMdnName(def(in.getMotherMdnName(), ""));
req.setAddr1(def(in.getAddr1(), ""));
req.setAddr2(def(in.getAddr2(), ""));
req.setAddr3(def(in.getAddr3(), ""));
req.setCityCode(def(in.getCityCode(), "000"));
req.setPstCode(def(in.getPstCode(), "000000"));
req.setCustType(def(in.getCustType(), ""));
req.setIdentityTyp(def(in.getIdentityTyp(), ""));
req.setIdNum(def(in.getIdNum(), ""));
req.setDob(def(in.getDob(), "00000000"));
req.setGndr(def(in.getGndr(), ""));
req.setMaritalStat(def(in.getMaritalStat(), "U"));
req.setKycDate(def(in.getKycDate(), "00000000"));
req.setKycStatus(def(in.getKycStatus(), 0));
req.setIdTyp2(def(in.getIdTyp2(), ""));
req.setAddr4(def(in.getAddr4(), ""));
req.setTelexNum(def(in.getTelexNum(), "0000000000"));
req.setResdncyStat(def(in.getResdncyStat(), "1"));
req.setNatlty(def(in.getNatlty(), "IN"));
req.setCycInd(def(in.getRelativeCode(),""));
req.setFatherOrSpouseName(def(in.getRelativeName(),""));
return req;
}
// ------------Helpers---------------
private static String def(String v, String d) {
return (v == null || v.isBlank()) ? d : v.trim();
}
private static int def(Integer v, int d) {
return v == null ? d : v;
}
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 static String s(Object o) { return (o == null) ? "" : o.toString(); }
private static String removeFirstFiveLeadingZeros(String s) {
if (s == null) return "";
if (s.startsWith("00000")) return s.substring(5);
return s;
}
private static long toLong(String s, long def) {
if (s == null || s.isBlank()) return def;
try { return Long.parseLong(s); } catch (Exception e) { return def; }
}
private String safeJson(Object o) {
try {
return new ObjectMapper().writeValueAsString(o);
} catch (com.fasterxml.jackson.core.JsonProcessingException e) {
return "<json-serialization-error: " + e.getMessage() + ">";
}
}
private static boolean looksJson(String s) {
String t = s.trim();
return (t.startsWith("{") && t.endsWith("}")) || (t.startsWith("[") && t.endsWith("]"));
}
}
@@ -0,0 +1,128 @@
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;
@Service
public class TokenService {
private static final Logger log = LoggerFactory.getLogger(TokenService.class);
private final RestTemplate restTemplate;
@Value("${cedge.token.url}")
private String tokenUrl;
private volatile String cachedToken;
private volatile long tokenExpiryTimeMillis;
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() {
long now = System.currentTimeMillis();
if (cachedToken != null && now < tokenExpiryTimeMillis) {
return cachedToken;
}
synchronized (tokenLock) {
now = System.currentTimeMillis();
if (cachedToken != null && now < tokenExpiryTimeMillis) {
return cachedToken;
}
// Only 2 attempts total
for (int attempt = 1; attempt <= 2; attempt++) {
try {
log.info("Fetching new C-EDGE token (attempt {}/2)", attempt);
ResponseEntity<TokenResponse> resp =
restTemplate.getForEntity(tokenUrl, 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");
}
cachedToken = token;
tokenExpiryTimeMillis = System.currentTimeMillis() + DEFAULT_TOKEN_TTL_MS;
String preview = cachedToken.substring(0, Math.min(12, cachedToken.length())) + "...";
log.info("Token fetched successfully. Preview={}", preview);
return cachedToken;
} catch (RuntimeException e) {
// RestClientException also comes here because it extends RuntimeException
log.warn("Token fetch failed on attempt {}/2: {}", 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 after 2 attempts", 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() {
synchronized (tokenLock) {
cachedToken = null;
tokenExpiryTimeMillis = 0L;
}
return getToken();
}
}
@@ -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); }
}
@@ -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; }
}
@@ -0,0 +1,5 @@
package com.main.ipks.service.errors;
public class DownstreamEmptyBodyException extends RuntimeException {
public DownstreamEmptyBodyException(String message) { super(message); }
}
@@ -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); }
}
+22
View File
@@ -0,0 +1,22 @@
spring.application.name=ipks
logging.level.root=INFO
logging.level.com.main.ipks.service=DEBUG
# TOKEN
cedge.token.url=https://142.79.251.107:443/BANCEDGEAPI/api/getToken/HASAN
# Api for CIF-Creation
cedge.cif.url=https://142.79.251.107:443/BANCEDGEAPI/api/cifCreation/HASAN
# Api for account creation
# API1
cedge.account.url=https://142.79.251.107:443/BANCEDGEAPI/api/depositsCreateDepositAccount/HASAN
# API2
cedge.accountOk.url=https://142.79.251.107:443/BANCEDGEAPI/api/createDepositAccountOk/HASAN
+46
View File
@@ -0,0 +1,46 @@
<configuration>
<!-- ========== Properties ========== -->
<property name="LOG_PATH" value="./logs" />
<property name="LOG_ARCHIVE" value="${LOG_PATH}/archive" />
<!-- ========== FILE APPENDER ========== -->
<appender name="FILE-ROLLING" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/application.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_ARCHIVE}/application.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<maxFileSize>10MB</maxFileSize>
<maxHistory>60</maxHistory>
<totalSizeCap>20GB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!-- ========== CONSOLE APPENDER ========== -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!-- ========== LOGGERS ========== -->
<!-- Reduce Netty noise -->
<logger name="io.netty" level="WARN"/>
<!-- Application logs -->
<logger name="com.main.ipks" level="DEBUG" additivity="false">
<appender-ref ref="FILE-ROLLING"/>
</logger>
<!-- ========== ROOT LOGGER ========== -->
<root level="INFO">
<appender-ref ref="STDOUT"/>
</root>
</configuration>
@@ -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() {
}
}