Compare commits

..

No commits in common. "9252b2fee531dd32e402d5312223bace11aae82d" and "8046dd0e623c675ed5e626a55e05ab7cf0e63de1" have entirely different histories.

22 changed files with 335 additions and 448 deletions

View File

@ -1,22 +0,0 @@
<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>

View File

@ -1,3 +1,4 @@
val kotlin_version: String by project
val logback_version: String by project val logback_version: String by project
plugins { plugins {
@ -31,5 +32,6 @@ dependencies {
implementation("com.squareup.okhttp3:okhttp:4.12.0") implementation("com.squareup.okhttp3:okhttp:4.12.0")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.1") implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.1")
implementation("org.slf4j:slf4j-api:2.0.16") implementation("org.slf4j:slf4j-api:2.0.16")
implementation("com.zaxxer:HikariCP:6.0.0") testImplementation("io.ktor:ktor-server-test-host-jvm")
testImplementation("org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version")
} }

View File

@ -1,15 +1,16 @@
package net.ipksindia package net.ipksindia
import io.ktor.server.application.* import io.ktor.server.application.*
import io.ktor.server.engine.*
import io.ktor.server.netty.* import io.ktor.server.netty.*
import net.ipksindia.config.configureApplication import net.ipksindia.plugins.*
import net.ipksindia.plugins.configureRouting
import net.ipksindia.plugins.configureSerialization
fun main(args: Array<String>):Unit = EngineMain.main(args) fun main() {
embeddedServer(Netty, port = 8083, host = "0.0.0.0", module = Application::module)
.start(wait = true)
}
fun Application.module() { fun Application.module() {
configureApplication()
configureSerialization() configureSerialization()
configureRouting() configureRouting()
} }

View File

@ -1,3 +0,0 @@
package net.ipksindia
class ItemNotFoundException(type: String, name: String) : Exception("Not Found $type: $name")

View File

@ -1,114 +1,90 @@
package net.ipksindia package net.ipksindia
import model.TransactionRequest import dao.TellerDao
import net.ipksindia.config.AppConfig
import net.ipksindia.dao.TellerDao
import net.ipksindia.dao.TransactionDao 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.Logger
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import response.TransactionFailureResponse import response.TransactionFailureResponse
import response.TransactionResponse
import response.TransactionSuccessResponse import response.TransactionSuccessResponse
class NeftRequestProcessor { class NeftRequestProcessor {
companion object { companion object {
private val logger: Logger = LoggerFactory.getLogger(NeftRequestProcessor::class.java) private val logger: Logger = LoggerFactory.getLogger(NeftRequestProcessor::class.java)
private val migratedDCCBCodes = AppConfig.bankCodes private val migratedDCCBCodes = listOf("0003","0015")
val bankDccbToSftpMap = mutableMapOf<String, String>(
"0015" to "0005", //Tamluk
"0003" to "0021", //Balageria
// "0016" to "0001", //WBSCB
)
/** 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')
if (!isDCCBCodeMigrated(dccbCode)) { val outwardTransaction = TransactionDao().getTransactionRequest(transactionNumber) ?: run {
logDCCBCodeNotMigrated(transactionNumber) logger.error("TXN: #{} FAILED REASON: Transaction not found", transactionNumber)
return OutwardNeftResponse(0, "DCCB NOT MIGRATED", null, null) return null
} }
val teller = TellerDao().getTeller(dccbCode, branchCode)
val transactionPair = TransactionFactory(transactionRequest, teller)
.createTransactionPair()
executeAndProcessTransaction(transactionNumber, transactionRequest, transactionPair)
} catch (e: Exception) {
logger.error("TXN: #{} FAILED REASON: {}", transactionNumber, e.toString())
OutwardNeftResponse(0, "ERROR OCCURRED DURING PROCESSING", null, null)
}
}
/**
* Fetch the transaction request using the transaction number.
*/
private fun fetchTransactionRequest(transactionNumber: String): TransactionRequest {
val transactionRequest = TransactionDao().getTransactionRequest(transactionNumber)
logger.info("TXN: #{} FOUND", transactionNumber) logger.info("TXN: #{} FOUND", transactionNumber)
return transactionRequest
}
/** val dccbCode = outwardTransaction.dccbCode.padStart(4, '0')
* Check if the DCCB code is not migrated. val branchCode = outwardTransaction.branchCode.padStart(5, '0')
*/
private fun isDCCBCodeMigrated(dccbCode: String)= dccbCode in migratedDCCBCodes
/** if(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) logger.error("TXN: #{} FAILED REASON: DCCB Code not migrated", transactionNumber)
return null
} }
/** val makerTeller = TellerDao.getTeller(dccbCode, branchCode) ?: run {
* Execute the transaction pair and process the results. logger.error("TXN: #{} FAILED REASON: Teller not found", transactionNumber)
*/ return null
private fun executeAndProcessTransaction( }
transactionNumber: String,
transactionRequest: TransactionRequest,
transactionPair: Pair<TransferTransaction, NeftTransaction>
): OutwardNeftResponse {
val (transferResponse, neftResponse) = TransactionExecutor().executePair(transactionPair)
return if (isSuccess(transferResponse, neftResponse)) { 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)
} catch (e: Exception) {
logger.error("TXN: #{} FAILED REASON: {}", transactionNumber, e.message)
return null
}
logger.info(
"TXN: #{} TRF_TXN: {} NEFT_TXN: {}",
transactionNumber,
transferResponse.status,
neftResponse.status
)
if (transferResponse.status == "SUCCESS" && neftResponse.status == "SUCCESS") {
val transferQueueNumber = (transferResponse as TransactionSuccessResponse).queueNumber val transferQueueNumber = (transferResponse as TransactionSuccessResponse).queueNumber
val neftQueueNumber = (neftResponse as TransactionSuccessResponse).queueNumber val neftQueueNumber = (neftResponse as TransactionSuccessResponse).queueNumber
TransactionDao().updateSuccessTransaction(transactionRequest, transferQueueNumber, neftQueueNumber) try {
TransactionDao().updateSuccessTransaction(outwardTransaction, transferQueueNumber, neftQueueNumber)
logger.info("TXN: #{} UPDATED RESULTS SUCCESSFULLY", transactionNumber) logger.info("TXN: #{} UPDATED RESULTS SUCCESSFULLY", transactionNumber)
OutwardNeftResponse(1, "transaction successful", transferQueueNumber, neftQueueNumber) } catch (e: Exception) {
logger.error(
"TXN: #{} QUEUE INITIATED BUT FAILED TO UPDATE RESULT: {}",
transactionNumber,
e.message
)
}
return Pair(transferQueueNumber, neftQueueNumber)
} else { } else {
logTransactionFailure(transactionNumber, transferResponse, neftResponse) val transferErrorMsg = (transferResponse as TransactionFailureResponse).errorMsg
OutwardNeftResponse(0, "TRANSACTION FAILED", null, null) 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
)
} }
} 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)
} }
} }
} }

View File

@ -0,0 +1 @@
class TellerNotFoundException(s: String) : Exception(s)

View File

@ -1,76 +1,76 @@
package net.ipksindia package net.ipksindia
import kotlinx.serialization.encodeToString import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import net.ipksindia.config.AppConfig
import net.ipksindia.model.NeftTransaction import net.ipksindia.model.NeftTransaction
import net.ipksindia.model.TransferTransaction import net.ipksindia.model.TransferTransaction
import okhttp3.MediaType.Companion.toMediaType import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.RequestBody.Companion.toRequestBody
import org.json.JSONException
import org.json.JSONObject import org.json.JSONObject
import org.slf4j.LoggerFactory
import response.TransactionFailureResponse import response.TransactionFailureResponse
import response.TransactionResponse import response.TransactionResponse
import response.TransactionSuccessResponse import response.TransactionSuccessResponse
import java.io.IOException import java.io.IOException
import javax.net.ssl.HostnameVerifier
class TransactionExecutor() { class TransactionExecutor() {
private val logger = LoggerFactory.getLogger(TransactionExecutor::class.java)
private val transactionUrl = AppConfig.remoteServerConfig.transactionUrl 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"
fun executePair(transactionPair: Pair<TransferTransaction, NeftTransaction>): Pair<TransactionResponse, TransactionResponse> { fun executePair(transactionPair: Pair<TransferTransaction, NeftTransaction>): Pair<TransactionResponse, TransactionResponse> {
val transferTransaction = transactionPair.first val transferTransaction = transactionPair.first
val neftTransaction = transactionPair.second val neftTransaction = transactionPair.second
val transferResponseString = execute(Json.encodeToString(transferTransaction)) val transferResponse = execute(Json.encodeToString(transferTransaction))
logger.debug("TRANSFER-RRN: {} - CBS Response: {}", transferTransaction.rrn, transferResponseString) val neftResponse = execute(Json.encodeToString(neftTransaction))
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) return Pair(transferResponse, neftResponse)
} }
private fun execute(postBody: String): String { private fun execute(postBody: String): TransactionResponse {
// println(postBody)
val transferRoute = "IpksApi"
val transferURL = "$remoteUrl/$transferRoute"
val jsonMediaType = "application/json; charset=utf-8".toMediaType() val jsonMediaType = "application/json; charset=utf-8".toMediaType()
val httpClient = OkHttpClient val httpClient = OkHttpClient
.Builder() .Builder()
.hostnameVerifier { _, _ -> true } .hostnameVerifier(HostnameVerifier { _, _ -> true })
.build() .build()
val request = Request val request = Request
.Builder() .Builder()
.url(transactionUrl) .url(transferURL)
.post(postBody.toRequestBody(jsonMediaType)) .post(postBody.toRequestBody(jsonMediaType))
.build() .build()
return httpClient.newCall(request).execute().use { response -> val responseBody = httpClient.newCall(request).execute().use { response ->
if (!response.isSuccessful) { if (!response.isSuccessful) {
throw IOException("Unexpected response: ${response.body}") throw IOException("Unexpected response: ${response.body}")
} }
response.body?.string() ?: throw IOException("no response body") response.body?.string() ?: throw IOException("no response body")
} }
}
private fun processResponse(responseBody: String): TransactionResponse {
val responseObj = JSONObject(responseBody) val responseObj = JSONObject(responseBody)
val status = responseObj.getString("status") val status = try {responseObj.getString("status") } catch(_: JSONException) { "" }
val message = responseObj.getString("message") val message = try { responseObj.getString("message") }catch(_: JSONException) { "" }
val error = responseObj.getInt("error") val error = try { responseObj.getInt("error") } catch(_: JSONException) { 1 }
if(responseBody.contains("SUCCESS")) { val response = if(responseBody.contains("SUCCESS")) {
val queueNo = responseObj.getJSONObject("response").getString("QueueId") val queueNo = try { responseObj.getJSONObject("response").getString("QueueId") } catch(_: JSONException) { "" }
return TransactionSuccessResponse(status, message, queueNo, error) return TransactionSuccessResponse(status, message, queueNo, error)
} else { } else {
val errorMsg = responseObj.getJSONObject("response").getString("errorMsg") val errorMsg = try { responseObj.getJSONObject("response").getString("errorMsg") } catch(_: JSONException) { responseBody }
return TransactionFailureResponse(status, message, errorMsg, error) return TransactionFailureResponse(status, message, errorMsg, error)
} }
return response
} }
} }

View File

@ -5,18 +5,19 @@ import net.ipksindia.model.NeftTransaction
import model.Teller import model.Teller
import model.TransactionRequest import model.TransactionRequest
import net.ipksindia.model.TransferTransaction import net.ipksindia.model.TransferTransaction
import net.ipksindia.NeftRequestProcessor.Companion.bankDccbToSftpMap
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
class TransactionFactory(private val transactionRequest: TransactionRequest, private val teller: Teller) { 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 date = transactionRequest.date.format(DateTimeFormatter.ofPattern("dd-MM-yyyy"))
private val rrn = transactionRequest.date.format(DateTimeFormatter.ofPattern("ddMM")) + transactionRequest.transactionNumber.takeLast(4) private val rrn = transactionRequest.date.format(DateTimeFormatter.ofPattern("ddMM")) + transactionRequest.transactionNumber.takeLast(4)
private fun createTransferTransaction(): TransferTransaction { private fun createTransferTransaction(): TransferTransaction {
val bankCode = bankDccbToSftpMap[transactionRequest.dccbCode.padStart(4, '0')] ?: throw ItemNotFoundException("SFTP code for", transactionRequest.dccbCode)
return TransferTransaction( return TransferTransaction(
bankCode = bankCode, bankCode = bankDccbToSftpMap[transactionRequest.dccbCode.padStart(4, '0')]!!,
branchCode = transactionRequest.branchCode.padStart(3,'0'), branchCode = transactionRequest.branchCode.padStart(3,'0'),
// branchCode = "99999", for UAT
cbsTellerId = teller.tellerId, cbsTellerId = teller.tellerId,
cbsTellerUserType = teller.userType, cbsTellerUserType = teller.userType,
queIdType = "5", queIdType = "5",
@ -41,10 +42,10 @@ class TransactionFactory(private val transactionRequest: TransactionRequest, pri
} }
private fun createNEFTTransaction(): NeftTransaction { private fun createNEFTTransaction(): NeftTransaction {
val bankCode = bankDccbToSftpMap[transactionRequest.dccbCode.padStart(4, '0')] ?: throw ItemNotFoundException("SFTP code for", transactionRequest.dccbCode)
return NeftTransaction( return NeftTransaction(
bankCode = bankCode, bankCode = bankDccbToSftpMap[transactionRequest.dccbCode.padStart(4, '0')]!!,
branchCode = transactionRequest.branchCode.padStart(3,'0'), branchCode = transactionRequest.branchCode.padStart(3,'0'),
// branchCode = "99999", for UAT
cbsTellerId = teller.tellerId, cbsTellerId = teller.tellerId,
cbsTellerUserType = teller.userType, cbsTellerUserType = teller.userType,
queIdType = "5", queIdType = "5",

View File

@ -1,92 +0,0 @@
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)
}
}

View File

@ -1,13 +0,0 @@
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()
}

View File

@ -1,25 +0,0 @@
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()
}
}

View File

@ -1,9 +1,9 @@
package net.ipksindia.dao package dao
import model.Teller import model.Teller
import net.ipksindia.ItemNotFoundException
class TellerDao { class TellerDao {
companion object {
private val tellerMap = mapOf( private val tellerMap = mapOf(
"0003" to mapOf( "0003" to mapOf(
@ -46,13 +46,15 @@ class TellerDao {
), ),
) )
fun getTeller(dccbCode: String, branchCode: String): Teller { fun getTeller(dccbCode: String, branchCode: String): Teller? {
val branchList = tellerMap[dccbCode] ?: throw ItemNotFoundException("Branch Code", branchCode) val branchList = tellerMap[dccbCode] ?: return null
val tellerId = branchList[branchCode] ?: throw ItemNotFoundException("DCCB Code", branchCode) val tellerId = branchList[branchCode] ?: return null
val teller = Teller( val teller = Teller(
tellerId, dccbCode, branchCode tellerId,
dccbCode,
branchCode
) )
return teller return teller
} }
}
} }

View File

@ -1,12 +1,44 @@
package net.ipksindia.dao package net.ipksindia.dao
import model.TransactionRequest import model.TransactionRequest
import net.ipksindia.ItemNotFoundException
import java.sql.Date import java.sql.Date
import java.sql.DriverManager
import java.sql.ResultSet import java.sql.ResultSet
import java.sql.SQLException import java.util.*
class TransactionDao { 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 = """ private val singleTransactionRequestQuery = """
SELECT SELECT
@ -65,8 +97,10 @@ class TransactionDao {
""".trimIndent() """.trimIndent()
fun updateSuccessTransaction(request: TransactionRequest, transferQueueNumber: String, neftQueueNumber: String) { fun updateSuccessTransaction(request: TransactionRequest, transferQueueNumber: String, neftQueueNumber: String) {
try { val dbUrl = getDatabaseUrl()
DatabaseFactory.instance.getConnection().use { connection -> val (dbUser, dbPassword) = getUserCredentials()
DriverManager.getConnection(dbUrl, dbUser, dbPassword).use { connection ->
connection.prepareStatement(transactionUpdateQuery).also { connection.prepareStatement(transactionUpdateQuery).also {
it.setString(1, request.transactionNumber) it.setString(1, request.transactionNumber)
it.setString(2, request.pacsCurrentAccountNumber) it.setString(2, request.pacsCurrentAccountNumber)
@ -87,31 +121,73 @@ class TransactionDao {
it.setString(17, transferQueueNumber) it.setString(17, transferQueueNumber)
it.setString(18, request.pacsAccountNumber) it.setString(18, request.pacsAccountNumber)
it.setString(19, neftQueueNumber) it.setString(19, neftQueueNumber)
}.use { it.executeUpdate() } }.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)
} }
} }
fun getTransactionRequest(transactionNumber: String): TransactionRequest { return transactionList
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) { fun getTransactionRequest(transactionNumber: String): TransactionRequest? {
throw SQLException("Failed to connect to the database") 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()
} }
} }
} }
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")
private fun mapToObject(rs: ResultSet): TransactionRequest? { return "jdbc:oracle:thin:@$dbHost:$dbPort:$dbName"
}
if (rs.next()) { private fun getUserCredentials(): Pair<String, String> {
return TransactionRequest( 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"), transactionNumber = rs.getString("txn_no"),
pacsCurrentAccountNumber = rs.getString("src_ac_no"), pacsCurrentAccountNumber = rs.getString("src_ac_no"),
neftBeneficiaryAccountNumber = rs.getString("dest_ac_no"), neftBeneficiaryAccountNumber = rs.getString("dest_ac_no"),
@ -131,7 +207,10 @@ private fun mapToObject(rs: ResultSet): TransactionRequest? {
pacsAccountNumber = rs.getString("pacs_acc_no"), pacsAccountNumber = rs.getString("pacs_acc_no"),
linkedCBSAccountNumber = rs.getString("cbs_sb_acc_no"), linkedCBSAccountNumber = rs.getString("cbs_sb_acc_no"),
) )
list.add(transactionRequest)
}
return list
} }
return null
}
}

View File

@ -1,11 +0,0 @@
package net.ipksindia.model
import kotlinx.serialization.Serializable
@Serializable
data class OutwardNeftResponse(
val status: Int,
val message: String,
val transferQueueNumber: String?,
val neftQueueNumber: String?
)

View File

@ -13,8 +13,8 @@ fun Application.configureRouting() {
post { post {
val neftRequest = call.receive<OutwardNeftRequest>() val neftRequest = call.receive<OutwardNeftRequest>()
val transactionNumber = neftRequest.transactionNumber val transactionNumber = neftRequest.transactionNumber
val response = NeftRequestProcessor.process(transactionNumber) val response = NeftRequestProcessor.process(transactionNumber) ?: Pair("500", "Error doing outward neft" )
call.respond(response) call.respond("${response.first}\n${response.second}")
} }
} }

View File

@ -3,9 +3,16 @@ package net.ipksindia.plugins
import io.ktor.serialization.kotlinx.json.* import io.ktor.serialization.kotlinx.json.*
import io.ktor.server.application.* import io.ktor.server.application.*
import io.ktor.server.plugins.contentnegotiation.* import io.ktor.server.plugins.contentnegotiation.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
fun Application.configureSerialization() { fun Application.configureSerialization() {
install(ContentNegotiation) { install(ContentNegotiation) {
json() json()
} }
routing {
get("/json/kotlinx-serialization") {
call.respond(mapOf("hello" to "world"))
}
}
} }

View File

@ -1,18 +0,0 @@
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"]
}

View File

@ -1,17 +0,0 @@
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"
}
}

View File

@ -1,10 +0,0 @@
ktor {
environment = ${KTOR_ENV}
deployment {
port = 8083
watch = [ classes ]
}
application {
modules = [ net.ipksindia.ApplicationKt.module ]
}
}

View File

@ -0,0 +1,7 @@
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

View File

@ -23,7 +23,8 @@
</encoder> </encoder>
</appender> </appender>
<logger name="io.netty" level="WARN"/> <!-- <logger name="org.eclipse.jetty" level="WARN"/>-->
<!-- <logger name="io.netty" level="WARN"/>-->
<logger name="net.ipksindia" level="DEBUG" additivity="false"> <logger name="net.ipksindia" level="DEBUG" additivity="false">
<appender-ref ref="FILE-ROLLING"/> <appender-ref ref="FILE-ROLLING"/>
<appender-ref ref="STDOUT" /> <appender-ref ref="STDOUT" />

View File

@ -0,0 +1,21 @@
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())
}
}
}