Compare commits
10 Commits
8046dd0e62
...
9252b2fee5
Author | SHA1 | Date | |
---|---|---|---|
9252b2fee5 | |||
76bf6f2847 | |||
d3ab9c4766 | |||
46df0377ee | |||
20d91d5c47 | |||
c62c233778 | |||
21a3f61152 | |||
5715030c7a | |||
0516c2c964 | |||
48877ddcec |
22
.run/ApplicationKt.run.xml
Normal file
22
.run/ApplicationKt.run.xml
Normal file
@ -0,0 +1,22 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="ApplicationKt" type="KtorApplicationConfigurationType" factoryName="Ktor" nameIsGenerated="true">
|
||||
<envs>
|
||||
<env name="KTOR_ENV" value="dev" />
|
||||
<env name="DB_PASSWORD" value="pacs_db" />
|
||||
</envs>
|
||||
<option name="MAIN_CLASS_NAME" value="net.ipksindia.ApplicationKt" />
|
||||
<module name="neft-server.main" />
|
||||
<option name="alternativeJrePath" />
|
||||
<option name="alternativeJrePathEnabled" value="false" />
|
||||
<option name="includeProvidedScope" value="false" />
|
||||
<option name="mainClass" value="net.ipksindia.ApplicationKt" />
|
||||
<option name="passParentEnvs" value="true" />
|
||||
<option name="programParameters" value="" />
|
||||
<option name="shortenCommandLine" />
|
||||
<option name="vmParameters" />
|
||||
<option name="workingDirectory" value="D:\work\neft-server" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
@ -1,4 +1,3 @@
|
||||
val kotlin_version: String by project
|
||||
val logback_version: String by project
|
||||
|
||||
plugins {
|
||||
@ -32,6 +31,5 @@ dependencies {
|
||||
implementation("com.squareup.okhttp3:okhttp:4.12.0")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.1")
|
||||
implementation("org.slf4j:slf4j-api:2.0.16")
|
||||
testImplementation("io.ktor:ktor-server-test-host-jvm")
|
||||
testImplementation("org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version")
|
||||
implementation("com.zaxxer:HikariCP:6.0.0")
|
||||
}
|
||||
|
@ -1,16 +1,15 @@
|
||||
package net.ipksindia
|
||||
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.engine.*
|
||||
import io.ktor.server.netty.*
|
||||
import net.ipksindia.plugins.*
|
||||
import net.ipksindia.config.configureApplication
|
||||
import net.ipksindia.plugins.configureRouting
|
||||
import net.ipksindia.plugins.configureSerialization
|
||||
|
||||
fun main() {
|
||||
embeddedServer(Netty, port = 8083, host = "0.0.0.0", module = Application::module)
|
||||
.start(wait = true)
|
||||
}
|
||||
fun main(args: Array<String>):Unit = EngineMain.main(args)
|
||||
|
||||
fun Application.module() {
|
||||
configureApplication()
|
||||
configureSerialization()
|
||||
configureRouting()
|
||||
}
|
||||
|
3
src/main/kotlin/net/ipksindia/ItemNotFoundException.kt
Normal file
3
src/main/kotlin/net/ipksindia/ItemNotFoundException.kt
Normal file
@ -0,0 +1,3 @@
|
||||
package net.ipksindia
|
||||
|
||||
class ItemNotFoundException(type: String, name: String) : Exception("Not Found $type: $name")
|
@ -1,90 +1,114 @@
|
||||
package net.ipksindia
|
||||
|
||||
import dao.TellerDao
|
||||
import model.TransactionRequest
|
||||
import net.ipksindia.config.AppConfig
|
||||
import net.ipksindia.dao.TellerDao
|
||||
import net.ipksindia.dao.TransactionDao
|
||||
import net.ipksindia.model.NeftTransaction
|
||||
import net.ipksindia.model.OutwardNeftResponse
|
||||
import net.ipksindia.model.TransferTransaction
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import response.TransactionFailureResponse
|
||||
import response.TransactionResponse
|
||||
import response.TransactionSuccessResponse
|
||||
|
||||
class NeftRequestProcessor {
|
||||
|
||||
companion object {
|
||||
private val logger: Logger = LoggerFactory.getLogger(NeftRequestProcessor::class.java)
|
||||
private val migratedDCCBCodes = listOf("0003","0015")
|
||||
val bankDccbToSftpMap = mutableMapOf<String, String>(
|
||||
"0015" to "0005", //Tamluk
|
||||
"0003" to "0021", //Balageria
|
||||
// "0016" to "0001", //WBSCB
|
||||
)
|
||||
private val migratedDCCBCodes = AppConfig.bankCodes
|
||||
|
||||
fun process(transactionNumber: String): Pair<String, String>? {
|
||||
/**
|
||||
* Process the transaction request based on the transaction number.
|
||||
* @param transactionNumber The transaction number to be processed.
|
||||
* @return A pair of transferQueueNumber and neftQueueNumber if successful, null otherwise.
|
||||
*/
|
||||
fun process(transactionNumber: String): OutwardNeftResponse {
|
||||
return try {
|
||||
val transactionRequest = fetchTransactionRequest(transactionNumber)
|
||||
val dccbCode = transactionRequest.dccbCode.padStart(4, '0')
|
||||
val branchCode = transactionRequest.branchCode.padStart(5, '0')
|
||||
|
||||
val outwardTransaction = TransactionDao().getTransactionRequest(transactionNumber) ?: run {
|
||||
logger.error("TXN: #{} FAILED REASON: Transaction not found", transactionNumber)
|
||||
return null
|
||||
}
|
||||
logger.info("TXN: #{} FOUND", transactionNumber)
|
||||
if (!isDCCBCodeMigrated(dccbCode)) {
|
||||
logDCCBCodeNotMigrated(transactionNumber)
|
||||
return OutwardNeftResponse(0, "DCCB NOT MIGRATED", null, null)
|
||||
}
|
||||
|
||||
val dccbCode = outwardTransaction.dccbCode.padStart(4, '0')
|
||||
val branchCode = outwardTransaction.branchCode.padStart(5, '0')
|
||||
val teller = TellerDao().getTeller(dccbCode, branchCode)
|
||||
val transactionPair = TransactionFactory(transactionRequest, teller)
|
||||
.createTransactionPair()
|
||||
|
||||
if(dccbCode !in migratedDCCBCodes) {
|
||||
logger.error("TXN: #{} FAILED REASON: DCCB Code not migrated", transactionNumber)
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
val makerTeller = TellerDao.getTeller(dccbCode, branchCode) ?: run {
|
||||
logger.error("TXN: #{} FAILED REASON: Teller not found", transactionNumber)
|
||||
return null
|
||||
}
|
||||
|
||||
val transactionPair = TransactionFactory(outwardTransaction, makerTeller).createTransactionPair()
|
||||
logger.info("TXN: #{} TRANSFER RRN: {} NEFT RRN: {}", transactionNumber, transactionPair.first.rrn, transactionPair.second.rrn)
|
||||
val (transferResponse, neftResponse) = try {
|
||||
TransactionExecutor().executePair(transactionPair)
|
||||
executeAndProcessTransaction(transactionNumber, transactionRequest, transactionPair)
|
||||
} catch (e: Exception) {
|
||||
logger.error("TXN: #{} FAILED REASON: {}", transactionNumber, e.message)
|
||||
return null
|
||||
logger.error("TXN: #{} FAILED REASON: {}", transactionNumber, e.toString())
|
||||
OutwardNeftResponse(0, "ERROR OCCURRED DURING PROCESSING", null, null)
|
||||
}
|
||||
}
|
||||
|
||||
logger.info(
|
||||
"TXN: #{} TRF_TXN: {} NEFT_TXN: {}",
|
||||
transactionNumber,
|
||||
transferResponse.status,
|
||||
neftResponse.status
|
||||
)
|
||||
/**
|
||||
* Fetch the transaction request using the transaction number.
|
||||
*/
|
||||
private fun fetchTransactionRequest(transactionNumber: String): TransactionRequest {
|
||||
val transactionRequest = TransactionDao().getTransactionRequest(transactionNumber)
|
||||
logger.info("TXN: #{} FOUND", transactionNumber)
|
||||
return transactionRequest
|
||||
}
|
||||
|
||||
if (transferResponse.status == "SUCCESS" && neftResponse.status == "SUCCESS") {
|
||||
/**
|
||||
* Check if the DCCB code is not migrated.
|
||||
*/
|
||||
private fun isDCCBCodeMigrated(dccbCode: String)= dccbCode in migratedDCCBCodes
|
||||
|
||||
/**
|
||||
* Log an error if the DCCB code is not migrated.
|
||||
*/
|
||||
private fun logDCCBCodeNotMigrated(transactionNumber: String) {
|
||||
logger.error("TXN: #{} FAILED REASON: DCCB Code not migrated", transactionNumber)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Execute the transaction pair and process the results.
|
||||
*/
|
||||
private fun executeAndProcessTransaction(
|
||||
transactionNumber: String,
|
||||
transactionRequest: TransactionRequest,
|
||||
transactionPair: Pair<TransferTransaction, NeftTransaction>
|
||||
): OutwardNeftResponse {
|
||||
val (transferResponse, neftResponse) = TransactionExecutor().executePair(transactionPair)
|
||||
|
||||
return if (isSuccess(transferResponse, neftResponse)) {
|
||||
val transferQueueNumber = (transferResponse as TransactionSuccessResponse).queueNumber
|
||||
val neftQueueNumber = (neftResponse as TransactionSuccessResponse).queueNumber
|
||||
try {
|
||||
TransactionDao().updateSuccessTransaction(outwardTransaction, transferQueueNumber, neftQueueNumber)
|
||||
logger.info("TXN: #{} UPDATED RESULTS SUCCESSFULLY", transactionNumber)
|
||||
} catch (e: Exception) {
|
||||
logger.error(
|
||||
"TXN: #{} QUEUE INITIATED BUT FAILED TO UPDATE RESULT: {}",
|
||||
transactionNumber,
|
||||
e.message
|
||||
)
|
||||
}
|
||||
return Pair(transferQueueNumber, neftQueueNumber)
|
||||
TransactionDao().updateSuccessTransaction(transactionRequest, transferQueueNumber, neftQueueNumber)
|
||||
|
||||
logger.info("TXN: #{} UPDATED RESULTS SUCCESSFULLY", transactionNumber)
|
||||
OutwardNeftResponse(1, "transaction successful", transferQueueNumber, neftQueueNumber)
|
||||
} else {
|
||||
val transferErrorMsg = (transferResponse as TransactionFailureResponse).errorMsg
|
||||
val neftErrorMsg = (neftResponse as TransactionFailureResponse).errorMsg
|
||||
logger.error(
|
||||
"TXN: #{} TRANSFER TXN FAILED DUE TO: {}",
|
||||
transactionNumber,
|
||||
transferErrorMsg
|
||||
)
|
||||
logger.error(
|
||||
"TXN: #{} NEFT TXN FAILED DUE TO: {}",
|
||||
transactionNumber,
|
||||
neftErrorMsg
|
||||
)
|
||||
logTransactionFailure(transactionNumber, transferResponse, neftResponse)
|
||||
OutwardNeftResponse(0, "TRANSACTION FAILED", null, null)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if both transfer and NEFT responses are successful.
|
||||
*/
|
||||
private fun isSuccess(transferResponse: TransactionResponse, neftResponse: TransactionResponse) = transferResponse is TransactionSuccessResponse && neftResponse is TransactionSuccessResponse
|
||||
|
||||
/**
|
||||
* Log errors if the transaction fails.
|
||||
*/
|
||||
private fun logTransactionFailure(
|
||||
transactionNumber: String,
|
||||
transferResponse: Any,
|
||||
neftResponse: Any
|
||||
) {
|
||||
val transferErrorMsg = (transferResponse as? TransactionFailureResponse)?.errorMsg ?: "Unknown Error"
|
||||
val neftErrorMsg = (neftResponse as? TransactionFailureResponse)?.errorMsg ?: "Unknown Error"
|
||||
|
||||
logger.error("TXN: #{} TRANSFER TXN FAILED DUE TO: {}", transactionNumber, transferErrorMsg)
|
||||
logger.error("TXN: #{} NEFT TXN FAILED DUE TO: {}", transactionNumber, neftErrorMsg)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1 +0,0 @@
|
||||
class TellerNotFoundException(s: String) : Exception(s)
|
@ -1,76 +1,76 @@
|
||||
package net.ipksindia
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import net.ipksindia.config.AppConfig
|
||||
import net.ipksindia.model.NeftTransaction
|
||||
import net.ipksindia.model.TransferTransaction
|
||||
import okhttp3.MediaType.Companion.toMediaType
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import org.json.JSONException
|
||||
import org.json.JSONObject
|
||||
import org.slf4j.LoggerFactory
|
||||
import response.TransactionFailureResponse
|
||||
import response.TransactionResponse
|
||||
import response.TransactionSuccessResponse
|
||||
import java.io.IOException
|
||||
import javax.net.ssl.HostnameVerifier
|
||||
|
||||
|
||||
class TransactionExecutor() {
|
||||
|
||||
private val protocol = "https"
|
||||
private val host = "180.179.110.185"
|
||||
private val port = "443"
|
||||
private val rootRoute = "IPKS_Queue_Generation"
|
||||
private val remoteUrl = "$protocol://$host:$port/$rootRoute"
|
||||
private val logger = LoggerFactory.getLogger(TransactionExecutor::class.java)
|
||||
private val transactionUrl = AppConfig.remoteServerConfig.transactionUrl
|
||||
|
||||
fun executePair(transactionPair: Pair<TransferTransaction, NeftTransaction>): Pair<TransactionResponse, TransactionResponse> {
|
||||
|
||||
val transferTransaction = transactionPair.first
|
||||
val neftTransaction = transactionPair.second
|
||||
|
||||
val transferResponse = execute(Json.encodeToString(transferTransaction))
|
||||
val neftResponse = execute(Json.encodeToString(neftTransaction))
|
||||
val transferResponseString = execute(Json.encodeToString(transferTransaction))
|
||||
logger.debug("TRANSFER-RRN: {} - CBS Response: {}", transferTransaction.rrn, transferResponseString)
|
||||
val transferResponse = processResponse(transferResponseString)
|
||||
val neftResponseString = execute(Json.encodeToString(neftTransaction))
|
||||
logger.debug("NEFT-RRN: {}, CBS Response: {}", neftTransaction.rrn, neftResponseString)
|
||||
val neftResponse = processResponse(neftResponseString)
|
||||
return Pair(transferResponse, neftResponse)
|
||||
}
|
||||
|
||||
private fun execute(postBody: String): TransactionResponse {
|
||||
// println(postBody)
|
||||
val transferRoute = "IpksApi"
|
||||
val transferURL = "$remoteUrl/$transferRoute"
|
||||
private fun execute(postBody: String): String {
|
||||
val jsonMediaType = "application/json; charset=utf-8".toMediaType()
|
||||
|
||||
val httpClient = OkHttpClient
|
||||
.Builder()
|
||||
.hostnameVerifier(HostnameVerifier { _, _ -> true })
|
||||
.hostnameVerifier { _, _ -> true }
|
||||
.build()
|
||||
|
||||
val request = Request
|
||||
.Builder()
|
||||
.url(transferURL)
|
||||
.url(transactionUrl)
|
||||
.post(postBody.toRequestBody(jsonMediaType))
|
||||
.build()
|
||||
|
||||
val responseBody = httpClient.newCall(request).execute().use { response ->
|
||||
return httpClient.newCall(request).execute().use { response ->
|
||||
if (!response.isSuccessful) {
|
||||
throw IOException("Unexpected response: ${response.body}")
|
||||
}
|
||||
response.body?.string() ?: throw IOException("no response body")
|
||||
}
|
||||
|
||||
val responseObj = JSONObject(responseBody)
|
||||
val status = try {responseObj.getString("status") } catch(_: JSONException) { "" }
|
||||
val message = try { responseObj.getString("message") }catch(_: JSONException) { "" }
|
||||
val error = try { responseObj.getInt("error") } catch(_: JSONException) { 1 }
|
||||
|
||||
val response = if(responseBody.contains("SUCCESS")) {
|
||||
val queueNo = try { responseObj.getJSONObject("response").getString("QueueId") } catch(_: JSONException) { "" }
|
||||
return TransactionSuccessResponse(status, message, queueNo, error)
|
||||
} else {
|
||||
val errorMsg = try { responseObj.getJSONObject("response").getString("errorMsg") } catch(_: JSONException) { responseBody }
|
||||
return TransactionFailureResponse(status, message, errorMsg, error)
|
||||
}
|
||||
return response
|
||||
}
|
||||
|
||||
private fun processResponse(responseBody: String): TransactionResponse {
|
||||
val responseObj = JSONObject(responseBody)
|
||||
val status = responseObj.getString("status")
|
||||
val message = responseObj.getString("message")
|
||||
val error = responseObj.getInt("error")
|
||||
|
||||
if(responseBody.contains("SUCCESS")) {
|
||||
val queueNo = responseObj.getJSONObject("response").getString("QueueId")
|
||||
return TransactionSuccessResponse(status, message, queueNo, error)
|
||||
} else {
|
||||
val errorMsg = responseObj.getJSONObject("response").getString("errorMsg")
|
||||
return TransactionFailureResponse(status, message, errorMsg, error)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -5,19 +5,18 @@ import net.ipksindia.model.NeftTransaction
|
||||
import model.Teller
|
||||
import model.TransactionRequest
|
||||
import net.ipksindia.model.TransferTransaction
|
||||
import net.ipksindia.NeftRequestProcessor.Companion.bankDccbToSftpMap
|
||||
import java.time.format.DateTimeFormatter
|
||||
|
||||
class TransactionFactory(private val transactionRequest: TransactionRequest, private val teller: Teller) {
|
||||
|
||||
private val bankDccbToSftpMap = mutableMapOf("0015" to "0005", "0003" to "0021")
|
||||
private val date = transactionRequest.date.format(DateTimeFormatter.ofPattern("dd-MM-yyyy"))
|
||||
private val rrn = transactionRequest.date.format(DateTimeFormatter.ofPattern("ddMM")) + transactionRequest.transactionNumber.takeLast(4)
|
||||
|
||||
private fun createTransferTransaction(): TransferTransaction {
|
||||
val bankCode = bankDccbToSftpMap[transactionRequest.dccbCode.padStart(4, '0')] ?: throw ItemNotFoundException("SFTP code for", transactionRequest.dccbCode)
|
||||
return TransferTransaction(
|
||||
bankCode = bankDccbToSftpMap[transactionRequest.dccbCode.padStart(4, '0')]!!,
|
||||
bankCode = bankCode,
|
||||
branchCode = transactionRequest.branchCode.padStart(3,'0'),
|
||||
// branchCode = "99999", for UAT
|
||||
cbsTellerId = teller.tellerId,
|
||||
cbsTellerUserType = teller.userType,
|
||||
queIdType = "5",
|
||||
@ -42,10 +41,10 @@ class TransactionFactory(private val transactionRequest: TransactionRequest, pri
|
||||
}
|
||||
|
||||
private fun createNEFTTransaction(): NeftTransaction {
|
||||
val bankCode = bankDccbToSftpMap[transactionRequest.dccbCode.padStart(4, '0')] ?: throw ItemNotFoundException("SFTP code for", transactionRequest.dccbCode)
|
||||
return NeftTransaction(
|
||||
bankCode = bankDccbToSftpMap[transactionRequest.dccbCode.padStart(4, '0')]!!,
|
||||
bankCode = bankCode,
|
||||
branchCode = transactionRequest.branchCode.padStart(3,'0'),
|
||||
// branchCode = "99999", for UAT
|
||||
cbsTellerId = teller.tellerId,
|
||||
cbsTellerUserType = teller.userType,
|
||||
queIdType = "5",
|
||||
|
92
src/main/kotlin/net/ipksindia/config/AppConfig.kt
Normal file
92
src/main/kotlin/net/ipksindia/config/AppConfig.kt
Normal file
@ -0,0 +1,92 @@
|
||||
package net.ipksindia.config
|
||||
|
||||
import com.typesafe.config.Config
|
||||
import com.typesafe.config.ConfigFactory
|
||||
import net.ipksindia.config.AppConfig.config
|
||||
import org.slf4j.LoggerFactory
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
object AppConfig {
|
||||
|
||||
private val environment = ConfigFactory.load().getString("ktor.environment")
|
||||
val config: Config = when (environment) {
|
||||
"prod" -> ConfigFactory.load("application-prod.conf")
|
||||
else -> ConfigFactory.load("application-dev.conf")
|
||||
}
|
||||
|
||||
val databaseConfig by lazy { DatabaseConfig(
|
||||
config.getString("database.host"),
|
||||
config.getInt("database.port"),
|
||||
config.getString("database.name"),
|
||||
config.getString("database.user"),
|
||||
config.getString("database.password")
|
||||
)}
|
||||
|
||||
val remoteServerConfig by lazy {
|
||||
RemoteServerConfig(
|
||||
config.getString("bank.server.protocol"),
|
||||
config.getString("bank.server.host"),
|
||||
config.getInt("bank.server.port"),
|
||||
config.getString("bank.server.rootRoute"),
|
||||
config.getString("bank.server.transactionRoute")
|
||||
)
|
||||
}
|
||||
|
||||
val bankCodes: MutableList<String> by lazy { config.getStringList("bank.codes")}
|
||||
}
|
||||
|
||||
data class RemoteServerConfig(
|
||||
val protocol: String,
|
||||
val host: String,
|
||||
val port: Int,
|
||||
val rootRoute: String,
|
||||
val transactionRoute: String
|
||||
) {
|
||||
val transactionUrl = "$protocol://$host:$port/$rootRoute/$transactionRoute"
|
||||
}
|
||||
|
||||
data class DatabaseConfig(
|
||||
val host: String,
|
||||
val port: Int,
|
||||
val name: String,
|
||||
val user: String,
|
||||
val password: String
|
||||
) {
|
||||
val url = "jdbc:oracle:thin:@$host:$port:$name"
|
||||
}
|
||||
|
||||
fun validateAppConfigs() {
|
||||
val logger = LoggerFactory.getLogger(AppConfig::class.java)
|
||||
val requiredKeys = listOf(
|
||||
"database.host",
|
||||
"database.port",
|
||||
"database.name",
|
||||
"database.user",
|
||||
"database.password",
|
||||
"bank.server.protocol",
|
||||
"bank.server.host",
|
||||
"bank.server.port",
|
||||
"bank.server.rootRoute",
|
||||
"bank.server.transactionRoute",
|
||||
"bank.codes"
|
||||
)
|
||||
|
||||
val missingKeys = requiredKeys.filterNot { config.hasPath(it) }
|
||||
|
||||
if (missingKeys.isNotEmpty()) {
|
||||
logger.error("Missing configuration keys: {}", missingKeys.toString())
|
||||
exitProcess(1)
|
||||
}
|
||||
|
||||
val dbPort = config.getInt("database.port")
|
||||
val remoteServerPort = config.getInt("bank.server.port")
|
||||
if (dbPort !in 1..65535) {
|
||||
logger.error("Invalid database port: {}", dbPort)
|
||||
exitProcess(1)
|
||||
}
|
||||
|
||||
if (remoteServerPort !in 1..65535) {
|
||||
logger.error("Invalid remote server port: {}", remoteServerPort)
|
||||
exitProcess(1)
|
||||
}
|
||||
}
|
13
src/main/kotlin/net/ipksindia/config/Configuration.kt
Normal file
13
src/main/kotlin/net/ipksindia/config/Configuration.kt
Normal file
@ -0,0 +1,13 @@
|
||||
package net.ipksindia.config
|
||||
|
||||
import io.ktor.server.application.*
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
|
||||
fun Application.configureApplication() {
|
||||
val logger = LoggerFactory.getLogger(AppConfig::class.java)
|
||||
environment.config.propertyOrNull("ktor.environment")?.getString()?.let {
|
||||
logger.info("Application running on environment: $it")
|
||||
}
|
||||
validateAppConfigs()
|
||||
}
|
25
src/main/kotlin/net/ipksindia/dao/DatabaseFactory.kt
Normal file
25
src/main/kotlin/net/ipksindia/dao/DatabaseFactory.kt
Normal file
@ -0,0 +1,25 @@
|
||||
package net.ipksindia.dao
|
||||
|
||||
import com.zaxxer.hikari.HikariConfig
|
||||
import com.zaxxer.hikari.HikariDataSource
|
||||
import net.ipksindia.config.AppConfig
|
||||
import java.sql.Connection
|
||||
|
||||
class DatabaseFactory private constructor() {
|
||||
private val dataSource: HikariDataSource
|
||||
|
||||
init {
|
||||
val config = HikariConfig()
|
||||
config.jdbcUrl = AppConfig.databaseConfig.url
|
||||
config.username = AppConfig.databaseConfig.user
|
||||
config.password = AppConfig.databaseConfig.password
|
||||
config.maximumPoolSize = 5
|
||||
dataSource = HikariDataSource(config)
|
||||
}
|
||||
|
||||
fun getConnection(): Connection = dataSource.connection
|
||||
|
||||
companion object {
|
||||
val instance = DatabaseFactory()
|
||||
}
|
||||
}
|
@ -1,60 +1,58 @@
|
||||
package dao
|
||||
package net.ipksindia.dao
|
||||
|
||||
import model.Teller
|
||||
import net.ipksindia.ItemNotFoundException
|
||||
|
||||
class TellerDao {
|
||||
companion object {
|
||||
|
||||
private val tellerMap = mapOf(
|
||||
"0003" to mapOf(
|
||||
"00012" to "312",
|
||||
"00017" to "317",
|
||||
"00013" to "313",
|
||||
"00014" to "314",
|
||||
"00015" to "315",
|
||||
"00016" to "316",
|
||||
"00019" to "319",
|
||||
"00020" to "320",
|
||||
"00026" to "11126",
|
||||
"00010" to "310"
|
||||
),
|
||||
"0015" to mapOf(
|
||||
"00006" to "11106",
|
||||
"00005" to "11105",
|
||||
"00002" to "11102",
|
||||
"00004" to "11104",
|
||||
"00023" to "1234",
|
||||
"00008" to "11108",
|
||||
"00017" to "11117",
|
||||
"00011" to "10111",
|
||||
"00021" to "1234",
|
||||
"00001" to "1234",
|
||||
"00018" to "11118",
|
||||
"00012" to "11112",
|
||||
"00019" to "11119",
|
||||
"00003" to "11103",
|
||||
"00009" to "11109",
|
||||
"00015" to "11115",
|
||||
"00020" to "11120",
|
||||
"00013" to "11113",
|
||||
"00014" to "1234",
|
||||
"00016" to "11116",
|
||||
"00010" to "11110",
|
||||
"00007" to "11107",
|
||||
"00022" to "1234",
|
||||
"00026" to "11126"
|
||||
),
|
||||
private val tellerMap = mapOf(
|
||||
"0003" to mapOf(
|
||||
"00012" to "312",
|
||||
"00017" to "317",
|
||||
"00013" to "313",
|
||||
"00014" to "314",
|
||||
"00015" to "315",
|
||||
"00016" to "316",
|
||||
"00019" to "319",
|
||||
"00020" to "320",
|
||||
"00026" to "11126",
|
||||
"00010" to "310"
|
||||
),
|
||||
"0015" to mapOf(
|
||||
"00006" to "11106",
|
||||
"00005" to "11105",
|
||||
"00002" to "11102",
|
||||
"00004" to "11104",
|
||||
"00023" to "1234",
|
||||
"00008" to "11108",
|
||||
"00017" to "11117",
|
||||
"00011" to "10111",
|
||||
"00021" to "1234",
|
||||
"00001" to "1234",
|
||||
"00018" to "11118",
|
||||
"00012" to "11112",
|
||||
"00019" to "11119",
|
||||
"00003" to "11103",
|
||||
"00009" to "11109",
|
||||
"00015" to "11115",
|
||||
"00020" to "11120",
|
||||
"00013" to "11113",
|
||||
"00014" to "1234",
|
||||
"00016" to "11116",
|
||||
"00010" to "11110",
|
||||
"00007" to "11107",
|
||||
"00022" to "1234",
|
||||
"00026" to "11126"
|
||||
),
|
||||
)
|
||||
|
||||
fun getTeller(dccbCode: String, branchCode: String): Teller {
|
||||
val branchList = tellerMap[dccbCode] ?: throw ItemNotFoundException("Branch Code", branchCode)
|
||||
val tellerId = branchList[branchCode] ?: throw ItemNotFoundException("DCCB Code", branchCode)
|
||||
val teller = Teller(
|
||||
tellerId, dccbCode, branchCode
|
||||
)
|
||||
|
||||
fun getTeller(dccbCode: String, branchCode: String): Teller? {
|
||||
val branchList = tellerMap[dccbCode] ?: return null
|
||||
val tellerId = branchList[branchCode] ?: return null
|
||||
val teller = Teller(
|
||||
tellerId,
|
||||
dccbCode,
|
||||
branchCode
|
||||
)
|
||||
return teller
|
||||
}
|
||||
return teller
|
||||
}
|
||||
|
||||
}
|
@ -1,44 +1,12 @@
|
||||
package net.ipksindia.dao
|
||||
|
||||
import model.TransactionRequest
|
||||
import net.ipksindia.ItemNotFoundException
|
||||
import java.sql.Date
|
||||
import java.sql.DriverManager
|
||||
import java.sql.ResultSet
|
||||
import java.util.*
|
||||
import java.sql.SQLException
|
||||
|
||||
class TransactionDao {
|
||||
private val transactionRequestQuery = """
|
||||
SELECT
|
||||
txn_no,
|
||||
TRIM(src_ac_no) AS src_ac_no,
|
||||
TRIM(dest_ac_no) AS dest_ac_no,
|
||||
ifsc_code,
|
||||
txn_amt,
|
||||
txn_date,
|
||||
t.teller_id,
|
||||
CASE
|
||||
WHEN t.ifsc_code LIKE 'WBSC%' THEN 'FAILED'
|
||||
ELSE 'RECEIVED'
|
||||
END AS status,
|
||||
SUBSTR(REGEXP_REPLACE(REGEXP_REPLACE(UPPER(beneficiary_name), '[^A-Z0-9 ]', ''), ' {2,}', ' '), 1, 35) AS beneficiary_name,
|
||||
beneficiary_add,
|
||||
t.pacs_id,
|
||||
comm_txn_no,
|
||||
comm_txn_amt,
|
||||
dccb_code,
|
||||
TO_NUMBER(cbs_br_code) AS br_code,
|
||||
SUBSTR(REGEXP_REPLACE(REGEXP_REPLACE(UPPER(remm_name), '[^A-Z0-9 ]', ''), ' {2,}', ' '),1,35) AS remitter_name,
|
||||
ipks_accno AS pacs_acc_no,
|
||||
da.link_accno AS cbs_sb_acc_no,
|
||||
'pacs_db' AS db_name
|
||||
FROM neft_rtgs_txn t
|
||||
JOIN dep_account da ON t.ipks_accno = da.key_1
|
||||
WHERE
|
||||
t.txn_date = (SELECT system_date FROM system_date)
|
||||
AND t.STATUS = 'A'
|
||||
AND t.bank_channel = 'SCB'
|
||||
AND da.link_accno IS NOT NULL
|
||||
""".trimIndent()
|
||||
|
||||
private val singleTransactionRequestQuery = """
|
||||
SELECT
|
||||
@ -97,120 +65,73 @@ class TransactionDao {
|
||||
""".trimIndent()
|
||||
|
||||
fun updateSuccessTransaction(request: TransactionRequest, transferQueueNumber: String, neftQueueNumber: String) {
|
||||
val dbUrl = getDatabaseUrl()
|
||||
val (dbUser, dbPassword) = getUserCredentials()
|
||||
|
||||
DriverManager.getConnection(dbUrl, dbUser, dbPassword).use { connection ->
|
||||
connection.prepareStatement(transactionUpdateQuery).also {
|
||||
it.setString(1, request.transactionNumber)
|
||||
it.setString(2, request.pacsCurrentAccountNumber)
|
||||
it.setString(3, request.neftBeneficiaryAccountNumber)
|
||||
it.setString(4, request.ifscCode)
|
||||
it.setString(5, request.amount)
|
||||
it.setDate(6, Date.valueOf(request.date))
|
||||
it.setString(7, request.tellerId)
|
||||
it.setString(8, "PROCESSED")
|
||||
it.setString(9, request.beneficiaryName)
|
||||
it.setString(10, request.beneficiaryAddress)
|
||||
it.setString(11, request.pacsId)
|
||||
it.setString(12, request.commissionTransactionNumber)
|
||||
it.setString(13, request.commissionAmount)
|
||||
it.setString(14, request.dccbCode)
|
||||
it.setString(15, request.branchCode)
|
||||
it.setString(16, request.remitterName)
|
||||
it.setString(17, transferQueueNumber)
|
||||
it.setString(18, request.pacsAccountNumber)
|
||||
it.setString(19, neftQueueNumber)
|
||||
}.use {
|
||||
it.executeUpdate()
|
||||
try {
|
||||
DatabaseFactory.instance.getConnection().use { connection ->
|
||||
connection.prepareStatement(transactionUpdateQuery).also {
|
||||
it.setString(1, request.transactionNumber)
|
||||
it.setString(2, request.pacsCurrentAccountNumber)
|
||||
it.setString(3, request.neftBeneficiaryAccountNumber)
|
||||
it.setString(4, request.ifscCode)
|
||||
it.setString(5, request.amount)
|
||||
it.setDate(6, Date.valueOf(request.date))
|
||||
it.setString(7, request.tellerId)
|
||||
it.setString(8, "PROCESSED")
|
||||
it.setString(9, request.beneficiaryName)
|
||||
it.setString(10, request.beneficiaryAddress)
|
||||
it.setString(11, request.pacsId)
|
||||
it.setString(12, request.commissionTransactionNumber)
|
||||
it.setString(13, request.commissionAmount)
|
||||
it.setString(14, request.dccbCode)
|
||||
it.setString(15, request.branchCode)
|
||||
it.setString(16, request.remitterName)
|
||||
it.setString(17, transferQueueNumber)
|
||||
it.setString(18, request.pacsAccountNumber)
|
||||
it.setString(19, neftQueueNumber)
|
||||
}.use { it.executeUpdate() }
|
||||
}
|
||||
|
||||
}catch (e: ExceptionInInitializerError) {
|
||||
throw SQLException("Failed to connect to the database")
|
||||
}
|
||||
}
|
||||
fun getTransactionRequests(): List<TransactionRequest> {
|
||||
|
||||
val transactionList: List<TransactionRequest>
|
||||
val dbUrl = getDatabaseUrl()
|
||||
val (dbUser, dbPassword) = getUserCredentials()
|
||||
|
||||
DriverManager.getConnection(dbUrl, dbUser, dbPassword).use { connection ->
|
||||
connection.prepareStatement(transactionRequestQuery).executeQuery().use {
|
||||
transactionList = mapToObject(it)
|
||||
}
|
||||
}
|
||||
|
||||
return transactionList
|
||||
}
|
||||
fun getTransactionRequest(transactionNumber: String): TransactionRequest? {
|
||||
val dbUrl = getDatabaseUrl()
|
||||
val (dbUser, dbPassword) = getUserCredentials()
|
||||
|
||||
return DriverManager.getConnection(dbUrl, dbUser, dbPassword).use { connection ->
|
||||
connection.prepareStatement(singleTransactionRequestQuery).apply {
|
||||
setString(1, transactionNumber)
|
||||
}.executeQuery().use {
|
||||
mapToObject(it).firstOrNull()
|
||||
}
|
||||
fun getTransactionRequest(transactionNumber: String): TransactionRequest {
|
||||
return try {
|
||||
DatabaseFactory.instance.getConnection().use { connection ->
|
||||
connection.prepareStatement(singleTransactionRequestQuery).apply { setString(1, transactionNumber) }
|
||||
.executeQuery()
|
||||
.use { mapToObject(it) ?: throw ItemNotFoundException("Transaction Number", transactionNumber) }
|
||||
}
|
||||
} catch (e: ExceptionInInitializerError) {
|
||||
throw SQLException("Failed to connect to the database")
|
||||
}
|
||||
}
|
||||
private fun getDatabaseUrl(): String {
|
||||
val prop = loadProp()
|
||||
}
|
||||
|
||||
val dbHost = getProp(prop, "DB_HOST")
|
||||
val dbPort = getProp(prop, "DB_PORT")
|
||||
val dbName = getProp(prop, "DB_NAME")
|
||||
|
||||
return "jdbc:oracle:thin:@$dbHost:$dbPort:$dbName"
|
||||
private fun mapToObject(rs: ResultSet): TransactionRequest? {
|
||||
|
||||
if (rs.next()) {
|
||||
return TransactionRequest(
|
||||
transactionNumber = rs.getString("txn_no"),
|
||||
pacsCurrentAccountNumber = rs.getString("src_ac_no"),
|
||||
neftBeneficiaryAccountNumber = rs.getString("dest_ac_no"),
|
||||
ifscCode = rs.getString("ifsc_code"),
|
||||
amount = rs.getString("txn_amt"),
|
||||
date = rs.getDate("txn_date").toLocalDate(),
|
||||
tellerId = rs.getString("teller_id"),
|
||||
status = rs.getString("status"),
|
||||
beneficiaryName = rs.getString("beneficiary_name"),
|
||||
beneficiaryAddress = rs.getString("beneficiary_add"),
|
||||
pacsId = rs.getString("pacs_id"),
|
||||
commissionTransactionNumber = rs.getString("comm_txn_no"),
|
||||
commissionAmount = rs.getString("comm_txn_amt"),
|
||||
dccbCode = rs.getString("dccb_code"),
|
||||
branchCode = rs.getString("br_Code"),
|
||||
remitterName = rs.getString("remitter_name"),
|
||||
pacsAccountNumber = rs.getString("pacs_acc_no"),
|
||||
linkedCBSAccountNumber = rs.getString("cbs_sb_acc_no"),
|
||||
)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun getUserCredentials(): Pair<String, String> {
|
||||
val prop = loadProp()
|
||||
|
||||
val dbUser = getProp(prop, "DB_USER")
|
||||
val dbPassword = getProp(prop, "DB_PASSWORD")
|
||||
|
||||
return Pair(dbUser, dbPassword)
|
||||
}
|
||||
|
||||
private fun loadProp(): Properties {
|
||||
|
||||
val props = javaClass.classLoader.getResourceAsStream("application.properties").use {
|
||||
Properties().apply { load(it) }
|
||||
}
|
||||
return props
|
||||
}
|
||||
|
||||
private fun getProp(prop: Properties, key: String): String {
|
||||
return prop.getProperty(key) ?: throw RuntimeException("property $prop not found")
|
||||
}
|
||||
|
||||
private fun mapToObject(rs: ResultSet): List<TransactionRequest> {
|
||||
val list = mutableListOf<TransactionRequest>()
|
||||
while (rs.next()) {
|
||||
val transactionRequest = TransactionRequest(
|
||||
transactionNumber = rs.getString("txn_no"),
|
||||
pacsCurrentAccountNumber = rs.getString("src_ac_no"),
|
||||
neftBeneficiaryAccountNumber = rs.getString("dest_ac_no"),
|
||||
ifscCode = rs.getString("ifsc_code"),
|
||||
amount = rs.getString("txn_amt"),
|
||||
date = rs.getDate("txn_date").toLocalDate(),
|
||||
tellerId = rs.getString("teller_id"),
|
||||
status = rs.getString("status"),
|
||||
beneficiaryName = rs.getString("beneficiary_name"),
|
||||
beneficiaryAddress = rs.getString("beneficiary_add"),
|
||||
pacsId = rs.getString("pacs_id"),
|
||||
commissionTransactionNumber = rs.getString("comm_txn_no"),
|
||||
commissionAmount = rs.getString("comm_txn_amt"),
|
||||
dccbCode = rs.getString("dccb_code"),
|
||||
branchCode = rs.getString("br_Code"),
|
||||
remitterName = rs.getString("remitter_name"),
|
||||
pacsAccountNumber = rs.getString("pacs_acc_no"),
|
||||
linkedCBSAccountNumber = rs.getString("cbs_sb_acc_no"),
|
||||
)
|
||||
list.add(transactionRequest)
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
|
||||
}
|
11
src/main/kotlin/net/ipksindia/model/OutwardNeftResponse.kt
Normal file
11
src/main/kotlin/net/ipksindia/model/OutwardNeftResponse.kt
Normal file
@ -0,0 +1,11 @@
|
||||
package net.ipksindia.model
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class OutwardNeftResponse(
|
||||
val status: Int,
|
||||
val message: String,
|
||||
val transferQueueNumber: String?,
|
||||
val neftQueueNumber: String?
|
||||
)
|
@ -13,8 +13,8 @@ fun Application.configureRouting() {
|
||||
post {
|
||||
val neftRequest = call.receive<OutwardNeftRequest>()
|
||||
val transactionNumber = neftRequest.transactionNumber
|
||||
val response = NeftRequestProcessor.process(transactionNumber) ?: Pair("500", "Error doing outward neft" )
|
||||
call.respond("${response.first}\n${response.second}")
|
||||
val response = NeftRequestProcessor.process(transactionNumber)
|
||||
call.respond(response)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,16 +3,9 @@ package net.ipksindia.plugins
|
||||
import io.ktor.serialization.kotlinx.json.*
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.plugins.contentnegotiation.*
|
||||
import io.ktor.server.response.*
|
||||
import io.ktor.server.routing.*
|
||||
|
||||
fun Application.configureSerialization() {
|
||||
install(ContentNegotiation) {
|
||||
json()
|
||||
}
|
||||
routing {
|
||||
get("/json/kotlinx-serialization") {
|
||||
call.respond(mapOf("hello" to "world"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
18
src/main/resources/application-dev.conf
Normal file
18
src/main/resources/application-dev.conf
Normal file
@ -0,0 +1,18 @@
|
||||
database {
|
||||
host = "localhost"
|
||||
port = 1521
|
||||
name = "IPKSDB"
|
||||
user = "pacs_db"
|
||||
password = "pacs_db"
|
||||
}
|
||||
|
||||
bank {
|
||||
server {
|
||||
protocol = "http"
|
||||
host = "localhost"
|
||||
port = 8080
|
||||
rootRoute = "IPKS_Queue_Generation"
|
||||
transactionRoute = "IpksApi"
|
||||
}
|
||||
codes = ["0003", "0015"]
|
||||
}
|
17
src/main/resources/application-prod.conf
Normal file
17
src/main/resources/application-prod.conf
Normal file
@ -0,0 +1,17 @@
|
||||
database {
|
||||
host = "ipksprod3.c7q7defafeea.ap-south-1.rds.amazonaws.com"
|
||||
port = 1521
|
||||
name = "IPKS"
|
||||
user = ${DB_USER}
|
||||
password = ${DB_PASSWORD}
|
||||
}
|
||||
|
||||
bank {
|
||||
server {
|
||||
protocol = "https"
|
||||
host = "180.179.110.185"
|
||||
port = 443
|
||||
rootRoute = "IPKS_Queue_Generation"
|
||||
transactionRoute = "IpksApi"
|
||||
}
|
||||
}
|
10
src/main/resources/application.conf
Normal file
10
src/main/resources/application.conf
Normal file
@ -0,0 +1,10 @@
|
||||
ktor {
|
||||
environment = ${KTOR_ENV}
|
||||
deployment {
|
||||
port = 8083
|
||||
watch = [ classes ]
|
||||
}
|
||||
application {
|
||||
modules = [ net.ipksindia.ApplicationKt.module ]
|
||||
}
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
DB_NAME=IPKS
|
||||
#DB_HOST=testipksdb.c7q7defafeea.ap-south-1.rds.amazonaws.com
|
||||
#DB_HOST=localhost
|
||||
DB_HOST=ipksprod3.c7q7defafeea.ap-south-1.rds.amazonaws.com
|
||||
DB_PORT=1521
|
||||
DB_USER=pacs_db
|
||||
DB_PASSWORD=pacs_db
|
@ -23,8 +23,7 @@
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<!-- <logger name="org.eclipse.jetty" level="WARN"/>-->
|
||||
<!-- <logger name="io.netty" level="WARN"/>-->
|
||||
<logger name="io.netty" level="WARN"/>
|
||||
<logger name="net.ipksindia" level="DEBUG" additivity="false">
|
||||
<appender-ref ref="FILE-ROLLING"/>
|
||||
<appender-ref ref="STDOUT" />
|
||||
|
@ -1,21 +0,0 @@
|
||||
package net.ipksindia
|
||||
|
||||
import io.ktor.client.request.*
|
||||
import io.ktor.client.statement.*
|
||||
import io.ktor.http.*
|
||||
import io.ktor.server.testing.*
|
||||
import kotlin.test.*
|
||||
import net.ipksindia.plugins.*
|
||||
|
||||
class ApplicationTest {
|
||||
@Test
|
||||
fun testRoot() = testApplication {
|
||||
application {
|
||||
configureRouting()
|
||||
}
|
||||
client.get("/").apply {
|
||||
assertEquals(HttpStatusCode.OK, status)
|
||||
assertEquals("Hello World!", bodyAsText())
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user