diff --git a/.idea/digifi-BankEmulator.iml b/.idea/digifi-BankEmulator.iml
index c956989..6e6fb1f 100644
--- a/.idea/digifi-BankEmulator.iml
+++ b/.idea/digifi-BankEmulator.iml
@@ -2,7 +2,7 @@
-
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..1d3ce46
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/api/routes/routes.py b/app/api/routes/routes.py
index 01b6092..767a86a 100644
--- a/app/api/routes/routes.py
+++ b/app/api/routes/routes.py
@@ -1,4 +1,4 @@
-from flask import Flask, Blueprint, request, jsonify, send_from_directory
+from flask import Flask, Blueprint, request, jsonify, send_from_directory
import os
from app.api.services import (
RACCheckService,
@@ -10,9 +10,11 @@ from app.api.services import (
TokenValidationService,
LienCheckService,
NewTransactionCheckService,
+ RepaymentService, # Added RepaymentService
+ StatusCallService, # Added StatusCallService
)
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__)
@@ -32,7 +34,6 @@ def swagger_json():
return send_from_directory(swagger_dir, "digifi_swagger.json")
-
@api.route('/swagger/')
def serve_paths(filename):
swagger_dir = os.path.join("swagger")
@@ -53,7 +54,6 @@ def rac_check():
@require_api_key
@require_app_id
def disbursement():
-
data = request.get_json()
# logger.info(f"Disbursement request received: {data}")
response = DisbursementService.process_request(data)
@@ -129,7 +129,27 @@ def new_transaction_check():
response = NewTransactionCheckService.process_request(data)
return response
+# Repayment Endpoint - Added based on updated Repayment.json
+@api.route('/Repayment', methods=['POST'])
+@require_api_key
+@require_app_id
+def repayment():
+ data = request.get_json()
+ # logger.info(f"Repayment request received: {data}")
+ response = RepaymentService.process_request(data)
+ return response
+
+# StatusCall Endpoint - Added based on updated StatusCall.json
+@api.route('/StatusCall', methods=['POST'])
+@require_api_key
+@require_app_id
+def status_call():
+ data = request.get_json()
+ # logger.info(f"StatusCall request received: {data}")
+ response = StatusCallService.process_request(data)
+ return response
+
# Health Check Endpoint
@api.route('/health', methods=['GET'])
def health_check():
- return {"status": "ok"} , 200
\ No newline at end of file
+ return {"status": "ok"}, 200
\ No newline at end of file
diff --git a/app/api/schemas/disbursement.py b/app/api/schemas/disbursement.py
index 16f7aa1..6172e51 100644
--- a/app/api/schemas/disbursement.py
+++ b/app/api/schemas/disbursement.py
@@ -1,17 +1,38 @@
-from marshmallow import Schema, fields
+from marshmallow import Schema, fields, validate
-# Disbursement Schema
+# Schema for the fees details object
+class FeesDetailsSchema(Schema):
+ collectAmountInterest = fields.Float(required=True, description="Interest Amount to be collected immediately after loan is provided (Only for 30 days)")
+ collectAmountMgtFee = fields.Float(required=True, description="Management Fee Amount to be collected immediately after loan is provided")
+ collectAmountInsurance = fields.Float(required=True, description="Insurance Amount to be collected immediately after loan is provided")
+ collectAmountVAT = fields.Float(required=True, description="VAT Amount to be collected immediately after loan is provided")
+
+# Disbursement Request Schema
class DisbursementSchema(Schema):
- requestId = fields.Str(required=True)
- debtId = fields.Str(required=True)
- transactionId = fields.Str(required=True)
- customerId = fields.Str(required=True)
- accountId = fields.Str(required=True)
- productId = fields.Str(required=True)
- provideAmount = fields.Float(required=True)
- collectAmountInterest = fields.Float(required=False) # Optional
- collectAmountMgtFee = fields.Float(required=True)
- collectAmountInsurance = fields.Float(required=True)
- collectAmountVAT = fields.Float(required=True)
- countryId = fields.Str(required=True)
- comment = fields.Str(required=False) # Optional
\ No newline at end of file
+ requestId = fields.Str(required=True, description="Unique identifier of request")
+ countryCode = fields.Str(required=True, description="Unique country code. Please refer to Country Codes table")
+ transactionId = fields.Str(required=True, description="Unique identifier of transaction in Simbrella system")
+ debtId = fields.Str(required=True, description="Unique identifier of a loan in Simbrella system that is going to be collected (it correlates with provision request)")
+ customerId = fields.Str(required=True, description="Unique identifier of a user")
+ accountId = fields.Str(required=True, description="Specific identifier of a user's account")
+ productId = fields.Str(required=True, description="Identifier of a product to be provided to a user")
+ provideAmount = fields.Float(required=True, description="Amount of loan (including service fee) to be provided on a specific account of a user")
+ totalFees = fields.Float(required=True, description="Total amount of all fees combined")
+ feesDetails = fields.Nested(FeesDetailsSchema, required=True, description="Detailed breakdown of all fees")
+ countryId = fields.Str(required=True, description="Set to static value '01'")
+ comment = fields.Str(required=False, description="Any additional comment for provided loan operation")
+
+# Disbursement Response Schema
+class DisbursementResponseSchema(Schema):
+ requestId = fields.Str(required=True, description="Unique identifier of request")
+ countryCode = fields.Str(required=True, description="Unique country code.")
+ transactionId = fields.Str(required=True, description="Unique ID of customer's USSD session. Must be consistent throughout whole USSD journey")
+ debtId = fields.Str(required=True, description="Unique identifier of a loan in Simbrella system that is going to be collected (it correlates with provision request)")
+ customerId = fields.Str(required=True, description="Unique identifier of a user")
+ accountId = fields.Str(required=True, description="Specific identifier of a user's account")
+ productId = fields.Str(required=True, description="Identifier of a product to be provided to a user")
+ provideAmount = fields.Float(required=True, description="Amount of loan (including service fee) to be provided on a specific account of a user")
+ totalFees = fields.Float(required=True, description="Total amount of all fees combined")
+ feesDetails = fields.Nested(FeesDetailsSchema, required=True, description="Detailed breakdown of all fees")
+ resultCode = fields.Str(required=True, description="Result code of executed transaction, e.g. (00 – Success etc.) see result codes table")
+ resultDescription = fields.Str(required=True, description="Description of provided result code")
\ No newline at end of file
diff --git a/app/api/services/__init__.py b/app/api/services/__init__.py
index 085ead8..c58bbf7 100644
--- a/app/api/services/__init__.py
+++ b/app/api/services/__init__.py
@@ -7,3 +7,5 @@ from app.api.services.revoke_enable_consent import RevokeEnableConsentService
from app.api.services.token_validation import TokenValidationService
from app.api.services.lien_check import LienCheckService
from app.api.services.new_transaction_check import NewTransactionCheckService
+from app.api.services.repayment import RepaymentService
+from app.api.services.status_call import StatusCallService
\ No newline at end of file
diff --git a/app/api/services/disbursement.py b/app/api/services/disbursement.py
index c94c910..245d2f7 100644
--- a/app/api/services/disbursement.py
+++ b/app/api/services/disbursement.py
@@ -1,8 +1,7 @@
from flask import request, jsonify
from marshmallow import ValidationError
from app.utils.logger import logger
-from app.api.helpers.response_helper import ResponseHelper
-from app.api.schemas.disbursement import DisbursementSchema
+from app.api.schemas.disbursement import DisbursementSchema, DisbursementResponseSchema
class DisbursementService:
@staticmethod
@@ -23,40 +22,47 @@ class DisbursementService:
schema = DisbursementSchema()
validated_data = schema.load(data) # Raises ValidationError if invalid
+ # Extract fees details for easier access
+ fees_details = validated_data.get('feesDetails', {})
+
# Simulated processing logic
+ # In a real implementation, this would interact with your business logic
response_data = {
- "transactionId": "T001",
- "TransactionId": "Tr201712RK9232P115",
- "debtId": "273194670",
- "customerId": "CN621868",
- "accountId": "2017821799",
- "productId": "101",
- "provideAmount": 100000.0,
- "collectAmountInterest": 5000,
- "collectAmountMgtFee": 1000,
- "collectAmountInsurance": 1000,
- "collectAmountVAT": 75,
- "countryId": "01",
+ "requestId": validated_data.get('requestId'),
+ "countryCode": validated_data.get('countryCode'),
+ "transactionId": validated_data.get('transactionId'),
+ "debtId": validated_data.get('debtId'),
+ "customerId": validated_data.get('customerId'),
+ "accountId": validated_data.get('accountId'),
+ "productId": validated_data.get('productId'),
+ "provideAmount": validated_data.get('provideAmount'),
+ "totalFees": validated_data.get('totalFees'),
+ "feesDetails": {
+ "collectAmountInterest": fees_details.get('collectAmountInterest'),
+ "collectAmountMgtFee": fees_details.get('collectAmountMgtFee'),
+ "collectAmountInsurance": fees_details.get('collectAmountInsurance'),
+ "collectAmountVAT": fees_details.get('collectAmountVAT')
+ },
"resultCode": "00",
"resultDescription": "Loan Request Completed Successfully!"
}
+ # Validate the response using the response schema
+ response_schema = DisbursementResponseSchema()
+ validated_response = response_schema.dump(response_data)
- # return ResponseHelper.success(
- # data=response_data,
- # message="Disbursement completed successfully"
- # )
-
- return response_data
+ return jsonify(validated_response)
except ValidationError as err:
logger.error(f"Validation Error: {err.messages}")
return jsonify({
- "message": "Validation exception"
- }) , 422
+ "resultCode": "01",
+ "resultDescription": f"Validation error: {err.messages}"
+ }), 422
except Exception as e:
logger.error(f"An error occurred: {str(e)}", exc_info=True)
return jsonify({
- "message": "Internal Server Error"
- }) , 500
+ "resultCode": "08",
+ "resultDescription": f"Error occurred: {str(e)}"
+ }), 500
\ No newline at end of file
diff --git a/app/api/services/repayment.py b/app/api/services/repayment.py
new file mode 100644
index 0000000..779c0d3
--- /dev/null
+++ b/app/api/services/repayment.py
@@ -0,0 +1,64 @@
+from flask import jsonify
+from app.utils.logger import logger
+
+
+class RepaymentService:
+ @staticmethod
+ def process_request(data):
+ """
+ Process a repayment request from Simbrella to FirstBank
+
+ Args:
+ data (dict): The request data containing repayment details
+
+ Returns:
+ flask.Response: JSON response with the result of the repayment operation
+ """
+ try:
+ # Log the incoming request
+ logger.info(f"Processing repayment request: {data}")
+
+ # Validate required fields
+ required_fields = [
+ "requestId", "countryCode", "transactionId", "debtId",
+ "customerId", "accountId", "productId", "collectAmount",
+ "collectionMethod", "lienAmount"
+ ]
+
+ for field in required_fields:
+ if field not in data:
+ logger.error(f"Missing required field: {field}")
+ return jsonify({
+ "resultCode": "01",
+ "resultDescription": f"Missing required field: {field}"
+ }), 400
+
+ # Process the repayment request
+ # This is where you would implement the actual business logic
+ # For now, we'll just return a successful response
+
+ response = {
+ "requestId": data.get("requestId"),
+ "countryCode": data.get("countryCode"),
+ "transactionId": data.get("transactionId"),
+ "debtId": data.get("debtId"),
+ "customerId": data.get("customerId"),
+ "accountId": data.get("accountId"),
+ "productId": data.get("productId"),
+ "collectAmount": data.get("collectAmount"),
+ "penalCharge": data.get("penalCharge", 0),
+ "lienAmount": data.get("lienAmount"),
+ "comment": data.get("comment", ""),
+ "resultCode": "00",
+ "resultDescription": "Loan Collection Successful"
+ }
+
+ logger.info(f"Repayment response: {response}")
+ return jsonify(response)
+
+ except Exception as e:
+ logger.error(f"Error processing repayment request: {str(e)}")
+ return jsonify({
+ "resultCode": "08",
+ "resultDescription": "Error occurred"
+ }), 500
\ No newline at end of file
diff --git a/app/api/services/status_call.py b/app/api/services/status_call.py
new file mode 100644
index 0000000..2437c35
--- /dev/null
+++ b/app/api/services/status_call.py
@@ -0,0 +1,84 @@
+from flask import jsonify
+from app.utils.logger import logger
+
+
+class StatusCallService:
+ @staticmethod
+ def process_request(data):
+ """
+ Process a status call request to check transaction status
+
+ Args:
+ data (dict): The request data containing transaction details to check
+
+ Returns:
+ flask.Response: JSON response with the status of the transaction
+ """
+ try:
+ # Log the incoming request
+ logger.info(f"Processing status call request: {data}")
+
+ # Validate required fields
+ required_fields = [
+ "transactionId", "transactionType", "customerId"
+ ]
+
+ for field in required_fields:
+ if field not in data:
+ logger.error(f"Missing required field: {field}")
+ return jsonify({
+ "resultCode": "01",
+ "resultDescription": f"Missing required field: {field}"
+ }), 400
+
+ # Process the status call request based on transaction type
+ transaction_type = data.get("transactionType")
+
+ # Prepare the response based on transaction type
+ if transaction_type == "Disbursement":
+ status = {
+ "providedAmount": 1000.00,
+ "collectedAmount": 0.00,
+ "resultCode": "00",
+ "resultDescription": "Loan Provision is successful"
+ }
+ elif transaction_type == "Collection":
+ status = {
+ "providedAmount": 0.00,
+ "collectedAmount": 100.00,
+ "resultCode": "00",
+ "resultDescription": "Loan Collection is successful"
+ }
+ elif transaction_type == "PenalCharge":
+ status = {
+ "transactionId": data.get("transactionId"),
+ "providedAmount": 0.00,
+ "collectedAmount": 100.00,
+ "resultCode": "00",
+ "resultDescription": "Penal Charge is successful"
+ }
+ else:
+ logger.error(f"Invalid transaction type: {transaction_type}")
+ return jsonify({
+ "resultCode": "01",
+ "resultDescription": f"Invalid transaction type: {transaction_type}"
+ }), 400
+
+ response = {
+ "requestId": data.get("requestId", ""),
+ "countryCode": data.get("countryCode", "NGR"),
+ "transactionId": data.get("transactionId"),
+ "Status": status,
+ "resultCode": "00",
+ "resultDescription": "SUCCESS"
+ }
+
+ logger.info(f"Status call response: {response}")
+ return jsonify(response)
+
+ except Exception as e:
+ logger.error(f"Error processing status call request: {str(e)}")
+ return jsonify({
+ "resultCode": "08",
+ "resultDescription": "Error occurred"
+ }), 500
\ No newline at end of file