17 Commits

Author SHA1 Message Date
VivianDee b48ccacd7c [add]: Make all customers valid 2025-09-26 12:03:33 +01:00
VivianDee c0f1845c5d [add]: Partial loan collection 2025-08-29 12:51:28 +01:00
ameye cfc40b89dc Merge branch 'health_check' of DigiFi/digifi-BankEmulator into master 2025-06-28 13:50:03 +00:00
VivianDee eb7c0f6221 [add]: health check 2025-06-27 15:58:12 +01:00
ameye d69bcd11ea Merge branch 'randomize_rac_check_values' of DigiFi/digifi-BankEmulator into master 2025-06-25 17:32:57 +00:00
VivianDee c4d7db5c59 Update rac_check.py 2025-06-25 17:31:02 +01:00
CHIEFSOFT\ameye d359532775 Fbn trax 2025-06-21 10:52:09 -04:00
CHIEFSOFT\ameye 2b12e1ab0b Partial simulation 2025-06-21 10:41:53 -04:00
vivian.d.simbrellang.com 23b352d9c3 Merge branch 'rac_check' of DigiFi/digifi-BankEmulator into master 2025-06-12 14:33:11 +00:00
VivianDee f665d7b8e4 Update rac_check.py 2025-06-12 15:32:00 +01:00
ameye 855f343626 Merge branch 'sync_payload' of DigiFi/digifi-BankEmulator into master 2025-06-09 19:40:35 +00:00
Chinenye Nmoh e7279a8c65 expanded disbursement endpoint 2025-06-09 20:31:32 +01:00
CHIEFSOFT\ameye 3e4fad5418 responseCode 2025-06-05 22:50:45 -04:00
CHIEFSOFT\ameye 4b018a26e9 res 2025-06-05 22:46:46 -04:00
CHIEFSOFT\ameye dfdfa51583 res[pose code 2025-06-05 22:43:38 -04:00
CHIEFSOFT\ameye 82053f41ce Fix emulator data 2025-06-05 18:18:56 -04:00
ameye 0833d0d0f2 Merge branch 'rac_check_update' of DigiFi/digifi-BankEmulator into master 2025-06-05 14:49:16 +00:00
15 changed files with 268 additions and 70 deletions
+31 -2
View File
@@ -1,4 +1,5 @@
from flask import Flask, Blueprint, request, jsonify, send_from_directory from flask import Flask, Blueprint, request, jsonify, send_from_directory
import sys
import os import os
from app.api.services import ( from app.api.services import (
RACCheckService, RACCheckService,
@@ -16,6 +17,7 @@ from app.utils.logger import logger
from app.api.middlewares import require_api_key, require_app_id, enforce_json from app.api.middlewares import require_api_key, require_app_id, enforce_json
api = Blueprint("api", __name__) api = Blueprint("api", __name__)
@@ -162,7 +164,34 @@ def new_transaction_check():
response = NewTransactionCheckService.process_request(data) response = NewTransactionCheckService.process_request(data)
return response return response
# Health Check Endpoint # Health Check Endpoint
@api.route('/health', methods=['GET']) @api.route('/system-health-check', methods=['GET'])
def health_check(): def health_check():
return {"status": "ok"} , 200 """Basic system health check"""
try:
checks = {
"python_version": sys.version_info >= (3, 6),
"disk_space": os.statvfs('/').f_bavail * os.statvfs('/').f_frsize > 500 * 1024 * 1024,
"system_operational": True
}
if all(checks.values()):
return jsonify({
"status": "Active",
"responseCode": "00",
"responseMessage": "Successful"
}), 200
else:
return jsonify({
"status": "Degraded",
"responseCode": "01",
"responseMessage": "System check failed"
}), 200
except Exception as e:
return jsonify({
"status": "Error",
"responseCode": "99",
"responseMessage": f"Health check failed: {str(e)}"
}), 500
+8 -3
View File
@@ -16,15 +16,20 @@ class CollectLoanSchema(Schema):
comment = fields.Str(allow_none=True) comment = fields.Str(allow_none=True)
class CollectLoanResponseSchema(Schema): class CollectLoanResponseSchema(Schema):
responseCode = fields.Str(allow_none=True)
responseDescr = fields.Str(allow_none=True)
fullDescription = fields.Str(allow_none=True)
transactionId = fields.Str(allow_none=True) transactionId = fields.Str(allow_none=True)
fbnTransactionId = fields.Str(allow_none=True)
debtId = fields.Str(allow_none=True) debtId = fields.Str(allow_none=True)
customerId = fields.Str(allow_none=True) customerId = fields.Str(allow_none=True)
accountId = fields.Str(allow_none=True) accountId = fields.Str(allow_none=True)
productId = fields.Str(allow_none=True) productId = fields.Str(allow_none=True)
amountCollected = fields.Float(required=True) amountCollected = fields.Float(required=True)
interestCollected = fields.Float(required=True)
penalChargeCollected = fields.Float(required=True)
lienAmount = fields.Float(required=True)
countryId = fields.Str(allow_none=True) countryId = fields.Str(allow_none=True)
comment = fields.Str(allow_none=True) comment = fields.Str(allow_none=True)
responseCode = fields.Str(allow_none=True)
responseMessage = fields.Str(allow_none=True) responseMessage = fields.Str(allow_none=True)
responseDescr = fields.Str(allow_none=True)
fullDescription = fields.Str(allow_none=True)
+8
View File
@@ -14,6 +14,7 @@ class DisbursementSchema(Schema):
collectAmountVAT = fields.Float(required=True) collectAmountVAT = fields.Float(required=True)
countryId = fields.Str(required=False, allow_none=True) countryId = fields.Str(required=False, allow_none=True)
comment = fields.Str(required=False, allow_none=True) comment = fields.Str(required=False, allow_none=True)
class DisburseLoanResponseSchema(Schema): class DisburseLoanResponseSchema(Schema):
@@ -31,5 +32,12 @@ class DisburseLoanResponseSchema(Schema):
countryId = fields.Str(allow_none=True) countryId = fields.Str(allow_none=True)
responseCode = fields.Str(allow_none=True) responseCode = fields.Str(allow_none=True)
responseMessage = fields.Str(allow_none=True) responseMessage = fields.Str(allow_none=True)
disburseMessage = fields.Str(allow_none=True)
disburseDate = fields.Str(allow_none=True)
disburseVerify = fields.Str(allow_none=True)
disburseDescription = fields.Str(allow_none=True)
verifyResult = fields.Str(allow_none=True)
verifyDescription = fields.Str(allow_none=True)
+5 -1
View File
@@ -18,4 +18,8 @@ class TransactionVerifyResponseSchema(Schema):
providedAmount = fields.Float(required=True) providedAmount = fields.Float(required=True)
collectedAmount = fields.Float(required=True) collectedAmount = fields.Float(required=True)
transactionId = fields.Str(allow_none=True) transactionId = fields.Str(allow_none=True)
transactionType = fields.Str(allow_none=True) transactionType = fields.Str(allow_none=True)
disburseVerify = fields.Str(allow_none=True)
verifyDescription = fields.Str(allow_none=True)
verifyResult = fields.Str(allow_none=True)
+69 -25
View File
@@ -1,21 +1,23 @@
import random
from flask import request, jsonify from flask import request, jsonify
from marshmallow import ValidationError from marshmallow import ValidationError
from app.utils.logger import logger from app.utils.logger import logger
from app.api.helpers.response_helper import ResponseHelper from app.api.helpers.response_helper import ResponseHelper
from app.api.schemas.collect_loan import CollectLoanSchema, CollectLoanResponseSchema from app.api.schemas.collect_loan import CollectLoanSchema, CollectLoanResponseSchema
from app.config import Config
"""
Process the CollectLoan request.
Args:
data (dict): The request data.
Returns:
tuple: JSON response and status code.
"""
class CollectLoanService: class CollectLoanService:
@staticmethod @staticmethod
def process_request(data): def process_request(data):
"""
Process the CollectLoan request.
Args:
data (dict): The request data.
Returns:
tuple: JSON response and status code.
"""
try: try:
logger.info("Processing CollectLoan request") logger.info("Processing CollectLoan request")
@@ -23,21 +25,63 @@ class CollectLoanService:
schema = CollectLoanSchema() schema = CollectLoanSchema()
validated_data = schema.load(data) validated_data = schema.load(data)
# Simulated processing logic amountForCollection = validated_data.get("collectAmount")
response_data = { productId = validated_data.get("productId")
"transactionId": validated_data.get("transactionId", "T002"), customerId = validated_data.get("customerId")
"debtId": validated_data.get("debtId", "273194670"), amountCollected = amountForCollection
"customerId": validated_data.get("customerId", "CN621868"), responseDescr= "Loan Collection Successful EMULATOR"
"accountId": validated_data.get("accountId", "2017821799"), fullDescription= "Loan collection completed successfully EMULATOR"
"productId": validated_data.get("productId", "101"), responseMessage= "Loan collection completed successfully EMULATOR"
"amountCollected": validated_data.get("collectAmount", 60000.00), interestCollected = amountForCollection * 0.03
"countryId": validated_data.get("countryId", "01"), lienAmount = validated_data.get("lienAmount", 0)
"comment": validated_data.get("comment", "Testing CollectionLoanRequest"),
"responseCode": "00", isValid = not (
"responseDescr": "Loan Collection Successful", int(customerId[-1]) in [2, 7, 9]
"fullDescription": "Loan collection completed successfully", )
"responseMessage": "Loan collection completed successfully" if Config.MIN_AMOUNT_FOR_COLLECTION <= amountForCollection <= Config.MAX_AMOUNT_FOR_COLLECTION:
} amountCollected = amountForCollection if lienAmount >= amountForCollection else 0
responseDescr = "Partial Loan Collection Successful EMULATOR"
fullDescription = "Partial Loan collection completed successfully EMULATOR"
responseMessage = "Partial Loan collection completed successfully EMULATOR"
response_data = {
"transactionId": validated_data.get("transactionId"),
"fbnTransactionId": validated_data.get("fbnTransactionId"),
"debtId": validated_data.get("debtId"),
"customerId": validated_data.get("customerId"),
"accountId": validated_data.get("accountId"),
"productId": validated_data.get("productId"),
"amountCollected": amountCollected,
"interestCollected": interestCollected,
"penalChargeCollected": 0,
"lienAmount": lienAmount if amountCollected == 0 else 0,
"countryId": validated_data.get("countryId"),
"comment": validated_data.get("comment", "Testing CollectionLoanRequest EMULATOR"),
"responseCode": "00",
"responseDescr": responseDescr,
"fullDescription": fullDescription,
"responseMessage": responseMessage
}
else:
response_data = {
"transactionId": validated_data.get("transactionId"),
"fbnTransactionId": validated_data.get("fbnTransactionId"),
"debtId": validated_data.get("debtId"),
"customerId": validated_data.get("customerId"),
"accountId": validated_data.get("accountId"),
"productId": validated_data.get("productId"),
"amountCollected": amountCollected,
"interestCollected": interestCollected,
"penalChargeCollected": 0,
"amountCollected": amountCollected,
"countryId": validated_data.get("countryId"),
"comment": validated_data.get("comment", "Testing CollectionLoanRequest EMULATOR"),
"responseCode": "00",
"responseDescr": responseDescr,
"fullDescription": fullDescription,
"responseMessage": responseMessage
}
# Validate and serialize the response data # Validate and serialize the response data
response_schema = CollectLoanResponseSchema() response_schema = CollectLoanResponseSchema()
+6 -2
View File
@@ -2,6 +2,7 @@ from flask import request, jsonify
from marshmallow import ValidationError from marshmallow import ValidationError
from app.utils.logger import logger from app.utils.logger import logger
from app.api.schemas.disbursement import DisbursementSchema, DisburseLoanResponseSchema from app.api.schemas.disbursement import DisbursementSchema, DisburseLoanResponseSchema
import datetime
class DisbursementService: class DisbursementService:
@staticmethod @staticmethod
@@ -26,7 +27,7 @@ class DisbursementService:
# For demo purposes, we simulate a response using the validated data # For demo purposes, we simulate a response using the validated data
response_data = { response_data = {
"transactionId": validated_data.get("transactionId"), "transactionId": validated_data.get("transactionId"),
"FbnTransactionId": validated_data.get("FbnTransactionId"), # Example or generated value "fbnTransactionId": validated_data.get("fbnTransactionId"), # Example or generated value
"debtId": validated_data.get("debtId"), "debtId": validated_data.get("debtId"),
"customerId": validated_data.get("customerId"), "customerId": validated_data.get("customerId"),
"accountId": validated_data.get("accountId"), "accountId": validated_data.get("accountId"),
@@ -38,7 +39,10 @@ class DisbursementService:
"collectAmountVAT": validated_data.get("collectAmountVAT"), "collectAmountVAT": validated_data.get("collectAmountVAT"),
"countryId": validated_data.get("countryId"), "countryId": validated_data.get("countryId"),
"responseCode": "00", # success code example "responseCode": "00", # success code example
"responseMessage": "Loan Request Completed Successfully!" "responseMessage": "Loan Request Completed Successfully!",
"disburseDate": datetime.datetime.now().isoformat(),
"disburseResult": "00",
"disburseDescription": "Loan Request Completed Successfully!",
} }
# Serialize response # Serialize response
+47 -36
View File
@@ -25,53 +25,64 @@ class RACCheckService:
schema = RACCheckSchema() schema = RACCheckSchema()
validated_data = schema.load(data) validated_data = schema.load(data)
# Simulated RAC check logic — create racResponse manually or via logic customer_id = validated_data["customerId"]
# rac_response = { is_valid = True
# "hasSalaryAccount": True,
# "bvnValidated": True,
# "creditBureauCheck": False, try:
# "crmsCheck": True, salary_count = int(str(customer_id)[-1]) + 1
# "accountStatus": True, if salary_count < 1 or salary_count > 6:
# "hasLien": False, salary_count = 3
# "noBouncedCheck": True, except ValueError:
# "isWhitelisted": True, salary_count = 3
# "hasPastDueLoan": False
# } salary_payments = {}
total_salary = 0
for i in range(1, salary_count + 1):
salary = (((salary_count + i) * 7919) % 200000 + 10000) * 5
salary_payments[f"salarypaymenT_{i}"] = salary
total_salary += salary
average_salary = total_salary // salary_count if salary_count > 0 else 0
rac_response = { rac_response = {
"procesS_DATE": datetime.strptime("2025-06-05", "%Y-%m-%d").date(), "procesS_DATE": datetime.strptime("2025-06-05", "%Y-%m-%d").date(),
"ciF_ID": "416405737", "ciF_ID": "416405737",
"customeR_id": "7032744", "customeR_id": customer_id,
"salaccT_1": "4142904114", "salaccT_1": "4142904114",
"alerT_PHONE": "2348039301606", "alerT_PHONE": "2348039301606",
"averagE_SALARY": 5000, "averagE_SALARY": average_salary,
"loaN_OUSTANDING_BAL": 0, "loaN_OUSTANDING_BAL": 0,
"emi": 1000, "emi": 1000,
"eliG_AMT": 25000, "eliG_AMT": 25000,
"rule1_45day_sal": True, "rule1_45day_sal": is_valid,
"rule2_2m_sal": True, "rule2_2m_sal": is_valid,
"rule3_no_bounced_check": True, "rule3_no_bounced_check": is_valid,
"rule4_current_loan_payments": True, "rule4_current_loan_payments": True if is_valid is False else is_valid,
"rule5_no_past_due_fadv_loan": True, "rule5_no_past_due_fadv_loan": is_valid,
"rule6_no_past_due_other_loan": True, "rule6_no_past_due_other_loan": is_valid,
"rule7_consistent_salary_amount": True, "rule7_consistent_salary_amount": is_valid,
"rule8_whitelisted": True, "rule8_whitelisted": True if is_valid is False else is_valid,
"rule9_regular_account": True, "rule9_regular_account": True if is_valid is False else is_valid,
"rule10_bvn_validation": True, "rule10_bvn_validation": is_valid,
"rule11_CRC_no_delinquency": True, "rule11_CRC_no_delinquency": is_valid,
"rule12_CRMS_no_delinquency": True, "rule12_CRMS_no_delinquency": True if is_valid is False else is_valid,
"rule13_BVN_ignore": True, "rule13_BVN_ignore": is_valid,
"rule14_no_lien": True, "rule14_no_lien": is_valid,
"rule15_null_ignore": True, "rule15_null_ignore": True if is_valid is False else is_valid,
"overalL_ELIG": True, "overalL_ELIG": is_valid
"salarypaymenT_1": 180000, # "salarypaymenT_1": 180000,
"salarypaymenT_2": 50000, # "salarypaymenT_2": 50000,
"salarypaymenT_3": 70000, # "salarypaymenT_3": 70000,
"salarypaymenT_4": 0, # "salarypaymenT_4": 0,
"salarypaymenT_5": 0, # "salarypaymenT_5": 0,
"salarypaymenT_6": 0 # "salarypaymenT_6": 0
} }
rac_response.update(salary_payments)
full_response = { full_response = {
"transactionId": validated_data["transactionId"], "transactionId": validated_data["transactionId"],
+5 -1
View File
@@ -6,6 +6,7 @@ from app.api.schemas.transaction_verify import (
TransactionVerifySchema, TransactionVerifySchema,
TransactionVerifyResponseSchema TransactionVerifyResponseSchema
) )
import datetime
class TransactionVerifyService: class TransactionVerifyService:
@@ -37,7 +38,10 @@ class TransactionVerifyService:
"providedAmount": 0.0, "providedAmount": 0.0,
"collectedAmount": 7.50, "collectedAmount": 7.50,
"transactionId": validated_data.get("transactionId"), "transactionId": validated_data.get("transactionId"),
"transactionType": validated_data.get("transactionType") "transactionType": validated_data.get("transactionType"),
"disburseVerify": datetime.datetime.now().isoformat(),
"verifyResult": "00",
"verifyDescription": "Collect Status retrieved successfully.",
} }
# Validate and serialize response with TransactionVerifyResponseSchema # Validate and serialize response with TransactionVerifyResponseSchema
+3
View File
@@ -1,4 +1,5 @@
import os import os
from re import M
from dotenv import load_dotenv from dotenv import load_dotenv
class Config: class Config:
@@ -10,6 +11,8 @@ class Config:
DEBUG = True DEBUG = True
VALID_APP_ID = os.getenv("VALID_APP_ID", "app1") VALID_APP_ID = os.getenv("VALID_APP_ID", "app1")
VALID_API_KEY = os.getenv("VALID_API_KEY", "test-api-key-12345") VALID_API_KEY = os.getenv("VALID_API_KEY", "test-api-key-12345")
MIN_AMOUNT_FOR_COLLECTION = int(os.getenv("MIN_AMOUNT_FOR_COLLECTION", 10000))
MAX_AMOUNT_FOR_COLLECTION = int(os.getenv("MAX_AMOUNT_FOR_COLLECTION", 25000))
# SQLALCHEMY_DATABASE_URI =os.environ.get("DATABASE_URL", "database_url") # SQLALCHEMY_DATABASE_URI =os.environ.get("DATABASE_URL", "database_url")
# SQLALCHEMY_TRACK_MODIFICATIONS = False # SQLALCHEMY_TRACK_MODIFICATIONS = False
+3
View File
@@ -110,6 +110,9 @@
} }
], ],
"paths": { "paths": {
"/api/system-health-check": {
"$ref": "swagger/paths/HealthCheck.json"
},
"/api/rac-check": { "/api/rac-check": {
"$ref": "swagger/paths/RACCheck.json" "$ref": "swagger/paths/RACCheck.json"
}, },
+19
View File
@@ -0,0 +1,19 @@
{
"get": {
"tags": ["System"],
"summary": "System Health Check",
"description": "Returns the current health status of the system",
"responses": {
"200": {
"description": "Successful operation",
"content": {
"application/json": {
"schema": {
"$ref": "../schemas/HealthCheckResponse.json"
}
}
}
}
}
}
}
@@ -70,6 +70,22 @@
"type": "string", "type": "string",
"example": "Loan Request Completed Successfully!", "example": "Loan Request Completed Successfully!",
"nullable": true "nullable": true
},
"disburseDate": {
"type": "string",
"format": "date-time",
"example": "2023-10-01T12:00:00Z",
"nullable": true
},
"disburseResult": {
"type": "string",
"example": "00",
"nullable": true
},
"disburseDescription": {
"type": "string",
"example": "Loan Request Completed Successfully!",
"nullable": true
} }
}, },
"required": [ "required": [
@@ -0,0 +1,17 @@
{
"type": "object",
"properties": {
"status": {
"type": "string",
"example": "Active"
},
"responseCode": {
"type": "string",
"example": "00"
},
"responseMessage": {
"type": "string",
"example": "Successful"
}
}
}
@@ -0,0 +1,17 @@
{
"type": "object",
"properties": {
"status": {
"type": "string",
"example": "Active"
},
"responseCode": {
"type": "string",
"example": "00"
},
"responseMessage": {
"type": "string",
"example": "Successful"
}
}
}
@@ -38,6 +38,20 @@
"transactionType": { "transactionType": {
"type": "string", "type": "string",
"example": "Disbursement" "example": "Disbursement"
},
"disburseVerify":{
"type": "string",
"format": "date-time",
"example": "2023-10-01T12:00:00Z",
"nullable": true
},
"verifyResult": {
"type": "string",
"example": "Success"
},
"verifyDescription": {
"type": "string",
"example": "Disbursement was verified and collection completed."
} }
}, },
"required": [ "required": [