Initial commit

This commit is contained in:
Azeez Muibi
2025-03-20 13:35:44 +01:00
commit af9a6148a1
48 changed files with 2451 additions and 0 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+81
View File
@@ -0,0 +1,81 @@
"""
Controller for loan collection endpoints.
"""
from flask import Blueprint, request, jsonify
from api.middleware import api_key_required
from api.models import CollectLoanRequest, CollectLoanResponse
import logging
# Configure logger
logger = logging.getLogger(__name__)
# Create blueprint
collection_bp = Blueprint('collection', __name__)
@collection_bp.route('/CollectLoan', methods=['POST'])
@api_key_required
def collect_loan():
"""
Endpoint to process loan collection requests from Simbrella.
This method handles requests to collect money from user accounts.
When a request is received, FirstBank should check all user accounts
and collect as much money as possible to cover the existing loan
either partially or fully.
Returns:
JSON response with collection status
"""
try:
# Parse and validate request
data = request.get_json()
if not data:
return jsonify({
'resultCode': '400',
'resultDescription': 'Invalid JSON payload'
}), 400
# Validate required fields
required_fields = ['transactionId', 'fbnTransactionId', 'debtId', 'customerId',
'accountId', 'productId', 'collectAmount', 'collectionMethod',
'lienAmount', 'countryId']
for field in required_fields:
if field not in data:
return jsonify({
'resultCode': '422',
'resultDescription': f'Missing required field: {field}'
}), 422
# Create request model
req = CollectLoanRequest.from_dict(data)
# Process collection request (this would connect to the business logic)
# For demonstration, we'll return a mock response with partial collection
# Assume we collected 75% of the requested amount
collected_amount = req.collectAmount * 0.75
remaining_lien = req.lienAmount - collected_amount
# Create response
response = CollectLoanResponse(
transactionId=req.transactionId,
debtId=req.debtId,
customerId=req.customerId,
accountId=req.accountId,
productId=req.productId,
collectAmount=collected_amount,
lienAmount=remaining_lien,
resultCode="00",
resultDescription="Loan Collection Successful",
penalCharge=req.penalCharge if hasattr(req, 'penalCharge') else 0.0
)
logger.info(f"Processed collection for customer {req.customerId}, collected {collected_amount}")
return jsonify(response.to_dict())
except Exception as e:
logger.error(f"Error processing collection: {str(e)}")
return jsonify({
'resultCode': '500',
'resultDescription': 'Internal server error'
}), 500
+123
View File
@@ -0,0 +1,123 @@
"""
Controller for customer consent endpoints.
"""
from flask import Blueprint, request, jsonify
from api.middleware import basic_auth_required, api_key_required
from api.models import (
CustomerConsentRequest, CustomerConsentResponse,
RevokeEnableConsentRequest, RevokeEnableConsentResponse
)
import logging
# Configure logger
logger = logging.getLogger(__name__)
# Create blueprint
consent_bp = Blueprint('consent', __name__)
@consent_bp.route('/CustomerConsent', methods=['POST'])
@basic_auth_required
def customer_consent():
"""
Endpoint to process customer consent requests.
This method handles customer consent for loan services.
Returns:
JSON response with consent status
"""
try:
# Parse and validate request
data = request.get_json()
if not data:
return jsonify({
'resultCode': '400',
'resultDescription': 'Invalid JSON payload'
}), 400
# Validate required fields
required_fields = ['$type', 'transactionId', 'customerId', 'accountId',
'requestTime', 'consentType', 'channel']
for field in required_fields:
if field not in data:
return jsonify({
'resultCode': '422',
'resultDescription': f'Missing required field: {field}'
}), 422
# Create request model
req = CustomerConsentRequest.from_dict(data)
# Process consent request (this would connect to the business logic)
# For demonstration, we'll return a mock response
# Create response
response = CustomerConsentResponse(
resultCode="00",
resultDescription="Request is received"
)
logger.info(f"Processed consent request for customer {req.customerId}, type {req.consentType}")
return jsonify(response.to_dict())
except Exception as e:
logger.error(f"Error processing consent request: {str(e)}")
return jsonify({
'resultCode': '500',
'resultDescription': 'Internal server error'
}), 500
@consent_bp.route('/RevokeEnableConsent', methods=['POST'])
@api_key_required
def revoke_enable_consent():
"""
Endpoint to process consent revocation or enablement.
This method handles requests from Simbrella to revoke or enable customer consent.
Returns:
JSON response with operation status
"""
try:
# Parse and validate request
data = request.get_json()
if not data:
return jsonify({
'resultCode': '400',
'resultDescription': 'Invalid JSON payload'
}), 400
# Validate required fields
required_fields = ['transactionId', 'fbnTransactionId', 'customerId', 'accountId',
'processTime', 'consentType', 'countryId']
for field in required_fields:
if field not in data:
return jsonify({
'resultCode': '422',
'resultDescription': f'Missing required field: {field}'
}), 422
# Create request model
req = RevokeEnableConsentRequest.from_dict(data)
# Process revoke/enable consent request (this would connect to the business logic)
# For demonstration, we'll return a mock response
# Create response
response = RevokeEnableConsentResponse(
type="RevokeEnableConsentResponse",
customerId=req.customerId,
accountId=req.accountId,
resultCode="00",
resultDescription="Success"
)
logger.info(f"Processed revoke/enable consent for customer {req.customerId}, type {req.consentType}")
return jsonify(response.to_dict())
except Exception as e:
logger.error(f"Error processing revoke/enable consent: {str(e)}")
return jsonify({
'resultCode': '500',
'resultDescription': 'Internal server error'
}), 500
+78
View File
@@ -0,0 +1,78 @@
"""
Controller for loan disbursement endpoints.
"""
from flask import Blueprint, request, jsonify
from api.middleware import api_key_required
from api.models import DisbursementRequest, DisbursementResponse
import logging
# Configure logger
logger = logging.getLogger(__name__)
# Create blueprint
disbursement_bp = Blueprint('disbursement', __name__)
@disbursement_bp.route('/Disbursement', methods=['POST'])
@api_key_required
def disbursement():
"""
Endpoint to process loan disbursement requests from Simbrella.
This method handles requests to disburse loans to customer accounts.
The operation should be executed atomically, providing the loan and
collecting upfront fees within the same transaction.
Returns:
JSON response with disbursement status
"""
try:
# Parse and validate request
data = request.get_json()
if not data:
return jsonify({
'resultCode': '400',
'resultDescription': 'Invalid JSON payload'
}), 400
# Validate required fields
required_fields = ['requestId', 'debtId', 'transactionId', 'customerId',
'accountId', 'productId', 'provideAmount', 'countryId']
for field in required_fields:
if field not in data:
return jsonify({
'resultCode': '422',
'resultDescription': f'Missing required field: {field}'
}), 422
# Create request model
req = DisbursementRequest.from_dict(data)
# Process disbursement request (this would connect to the business logic)
# For demonstration, we'll return a mock response
# Create response
response = DisbursementResponse(
requestId=req.requestId,
debtId=req.debtId,
transactionId=req.transactionId,
customerId=req.customerId,
accountId=req.accountId,
productId=req.productId,
provideAmount=req.provideAmount,
resultCode="00",
resultDescription="Loan Request Completed Successfully!",
collectAmountInterest=req.collectAmountInterest,
collectAmountMgtFee=req.collectAmountMgtFee,
collectAmountInsurance=req.collectAmountInsurance,
collectAmountVAT=req.collectAmountVAT
)
logger.info(f"Processed disbursement for customer {req.customerId}, amount {req.provideAmount}")
return jsonify(response.to_dict())
except Exception as e:
logger.error(f"Error processing disbursement: {str(e)}")
return jsonify({
'resultCode': '500',
'resultDescription': 'Internal server error'
}), 500
+91
View File
@@ -0,0 +1,91 @@
"""
Controller for eligibility check endpoints.
"""
from flask import Blueprint, request, jsonify, current_app
from flask.typing import ResponseReturnValue
from api.middleware import basic_auth_required
from api.models import EligibilityCheckRequest, EligibilityCheckResponse, EligibleOffer
import logging
from typing import Dict, Any, List
# Configure logger
logger = logging.getLogger(__name__)
# Create blueprint
eligibility_bp = Blueprint('eligibility', __name__)
@eligibility_bp.route('/EligibilityCheck', methods=['POST'])
@basic_auth_required
def eligibility_check() -> ResponseReturnValue:
"""
Endpoint to check customer eligibility for loans.
This endpoint initiates the eligibility check process and performs RAC checks.
Returns:
JSON response with eligibility status and available offers
"""
try:
# Parse and validate request
data = request.get_json()
if not data:
logger.warning("Invalid JSON payload received")
return jsonify({
'resultCode': '400',
'resultDescription': 'Invalid JSON payload'
}), 400
# Validate required fields
required_fields = ['$type', 'transactionId', 'countryCode', 'customerId',
'accountId', 'lienAmount', 'channel']
missing_fields = [field for field in required_fields if field not in data]
if missing_fields:
logger.warning(f"Missing required fields: {', '.join(missing_fields)}")
return jsonify({
'resultCode': '422',
'resultDescription': f'Missing required fields: {", ".join(missing_fields)}'
}), 422
# Create request model
req = EligibilityCheckRequest.from_dict(data)
# Process eligibility check (this would connect to the business logic)
# For demonstration, we'll return a mock response
# Create sample offers
offers: List[EligibleOffer] = [
EligibleOffer(
minamount=5000.0,
maxamount=20000.0,
productId=101,
offerid=101,
Tenor=30
),
EligibleOffer(
minamount=10000.0,
maxamount=50000.0,
productId=102,
offerid=102,
Tenor=60
)
]
# Create response
response = EligibilityCheckResponse(
customerId=req.customerId,
transactionId=req.transactionId,
eligibleOffers=[offer.to_dict() for offer in offers],
resultCode="00",
resultDescription="Successful",
msisdn=req.msisdn
)
logger.info(f"Processed eligibility check for customer {req.customerId}")
return jsonify(response.to_dict())
except Exception as e:
logger.exception(f"Error processing eligibility check: {str(e)}")
return jsonify({
'resultCode': '500',
'resultDescription': f'Internal server error: {str(e)}'
}), 500
+65
View File
@@ -0,0 +1,65 @@
"""
Controller for lien check endpoints.
"""
from flask import Blueprint, request, jsonify
from api.middleware import api_key_required
from api.models import LienCheckRequest, LienCheckResponse
import logging
# Configure logger
logger = logging.getLogger(__name__)
# Create blueprint
lien_bp = Blueprint('lien', __name__)
@lien_bp.route('/LienCheck', methods=['POST'])
@api_key_required
def lien_check():
"""
Endpoint to check lien amount on an account.
This method is used to get the applied lien amount for a specific account.
Returns:
JSON response with lien amount details
"""
try:
# Parse and validate request
data = request.get_json()
if not data:
return jsonify({
'resultCode': '400',
'resultDescription': 'Invalid JSON payload'
}), 400
# Validate required fields
required_fields = ['transactionId', 'customerId', 'accountId', 'countryId']
for field in required_fields:
if field not in data:
return jsonify({
'resultCode': '422',
'resultDescription': f'Missing required field: {field}'
}), 422
# Create request model
req = LienCheckRequest.from_dict(data)
# Process lien check (this would connect to the business logic)
# For demonstration, we'll return a mock response
# Create response
response = LienCheckResponse(
lienAmount=20000.0,
resultCode="00",
resultDescription="Successful"
)
logger.info(f"Processed lien check for customer {req.customerId}, account {req.accountId}")
return jsonify(response.to_dict())
except Exception as e:
logger.error(f"Error processing lien check: {str(e)}")
return jsonify({
'resultCode': '500',
'resultDescription': 'Internal server error'
}), 500
+147
View File
@@ -0,0 +1,147 @@
"""
Controller for loan-related endpoints.
"""
from flask import Blueprint, request, jsonify
from api.middleware import basic_auth_required
from api.models import (
ProvideLoanRequest, ProvideLoanResponse,
LoanInformationRequest, LoanInformationResponse, Loan
)
from datetime import datetime, timedelta
import logging
# Configure logger
logger = logging.getLogger(__name__)
# Create blueprint
loan_bp = Blueprint('loan', __name__)
@loan_bp.route('/ProvideLoan', methods=['POST'])
@basic_auth_required
def provide_loan():
"""
Endpoint to process loan provision requests.
This method handles the request to provide a loan to a customer.
Returns:
JSON response with loan provision status
"""
try:
# Parse and validate request
data = request.get_json()
if not data:
return jsonify({
'resultCode': '400',
'resultDescription': 'Invalid JSON payload'
}), 400
# Validate required fields
required_fields = ['$type', 'requestId', 'transactionId', 'customerId',
'accountId', 'productId', 'lienAmount', 'requestedAmount',
'collectionType', 'loanType', 'channel']
for field in required_fields:
if field not in data:
return jsonify({
'resultCode': '422',
'resultDescription': f'Missing required field: {field}'
}), 422
# Create request model
req = ProvideLoanRequest.from_dict(data)
# Process loan provision (this would connect to the business logic)
# For demonstration, we'll return a mock response
# Create response
response = ProvideLoanResponse(
requestId=req.requestId,
transactionId=req.transactionId,
customerId=req.customerId,
accountId=req.accountId,
resultCode="00",
resultDescription="Loan provided successfully",
msisdn=req.msisdn if hasattr(req, 'msisdn') else None
)
logger.info(f"Processed loan provision for customer {req.customerId}")
return jsonify(response.to_dict())
except Exception as e:
logger.error(f"Error processing loan provision: {str(e)}")
return jsonify({
'resultCode': '500',
'resultDescription': 'Internal server error'
}), 500
@loan_bp.route('/LoanInformation', methods=['POST'])
@basic_auth_required
def loan_information():
"""
Endpoint to retrieve loan information.
This method provides information about a customer's existing loans.
Returns:
JSON response with loan information
"""
try:
# Parse and validate request
data = request.get_json()
if not data:
return jsonify({
'resultCode': '400',
'resultDescription': 'Invalid JSON payload'
}), 400
# Validate required fields
required_fields = ['$type', 'transactionId', 'customerId', 'channel']
for field in required_fields:
if field not in data:
return jsonify({
'resultCode': '422',
'resultDescription': f'Missing required field: {field}'
}), 422
# Create request model
req = LoanInformationRequest.from_dict(data)
# Process loan information request (this would connect to the business logic)
# For demonstration, we'll return a mock response
# Create sample loans
now = datetime.now()
loan_date = now - timedelta(days=15)
due_date = now + timedelta(days=15)
loans = [
Loan(
debtId="123456789",
loanDate=loan_date.strftime("%Y-%m-%d %H:%M:%S.%f")[:-3],
dueDate=due_date.strftime("%Y-%m-%d %H:%M:%S.%f")[:-3],
currentLoanAmount=8500.0,
initialLoanAmount=10000.0,
defaultFee=0.0,
continiousFee=0.0,
productId="101"
)
]
# Create response
response = LoanInformationResponse(
customerId=req.customerId,
loans=[loan.to_dict() for loan in loans],
resultCode="00",
resultDescription="Successful",
totalDebtAmount=8500.0
)
logger.info(f"Processed loan information request for customer {req.customerId}")
return jsonify(response.to_dict())
except Exception as e:
logger.error(f"Error processing loan information request: {str(e)}")
return jsonify({
'resultCode': '500',
'resultDescription': 'Internal server error'
}), 500
+70
View File
@@ -0,0 +1,70 @@
"""
Controller for notification callback endpoints.
"""
from flask import Blueprint, request, jsonify
from api.middleware import basic_auth_required
from api.models import NotificationCallbackRequest, NotificationCallbackResponse
import logging
# Configure logger
logger = logging.getLogger(__name__)
# Create blueprint
notification_bp = Blueprint('notification', __name__)
@notification_bp.route('/NotificationCallback', methods=['POST'])
@basic_auth_required
def notification_callback():
"""
Endpoint to receive transaction status notifications.
This method is used for informing Simbrella about the status of transactions
that FirstBank has processed.
Returns:
JSON response acknowledging receipt of notification
"""
try:
# Parse and validate request
data = request.get_json()
if not data:
return jsonify({
'resultCode': '400',
'resultDescription': 'Invalid JSON payload'
}), 400
# Validate required fields
required_fields = ['fbnTransactionId', 'transactionId', 'customerId', 'accountId',
'debtId', 'transactionType', 'amountProvided', 'amountCollected',
'responseCode', 'responseDescription']
for field in required_fields:
if field not in data:
return jsonify({
'resultCode': '422',
'resultDescription': f'Missing required field: {field}'
}), 422
# Create request model
req = NotificationCallbackRequest.from_dict(data)
# Process notification (this would connect to the business logic)
# For demonstration, we'll return a mock response
# Log the notification details
logger.info(f"Received notification for transaction {req.transactionId}, "
f"type {req.transactionType}, status {req.responseCode}")
# Create response
response = NotificationCallbackResponse(
resultCode="00",
resultDescription="Successful"
)
return jsonify(response.to_dict())
except Exception as e:
logger.error(f"Error processing notification callback: {str(e)}")
return jsonify({
'resultCode': '500',
'resultDescription': 'Internal server error'
}), 500
+92
View File
@@ -0,0 +1,92 @@
"""
Controller for offer selection endpoints.
"""
from flask import Blueprint, request, jsonify
from api.middleware import basic_auth_required
from api.models import SelectOffersRequest, SelectOffersResponse, Offer
import logging
# Configure logger
logger = logging.getLogger(__name__)
# Create blueprint
offers_bp = Blueprint('offers', __name__)
@offers_bp.route('/SelectOffer', methods=['POST'])
@basic_auth_required
def select_offer():
"""
Endpoint to send the offer the customer selected to Simbrella.
This method processes the customer's selected offer and returns detailed offer information.
Returns:
JSON response with detailed offer information
"""
try:
# Parse and validate request
data = request.get_json()
if not data:
return jsonify({
'resultCode': '400',
'resultDescription': 'Invalid JSON payload'
}), 400
# Validate required fields
required_fields = ['requestId', 'transactionId', 'customerId', 'accountId',
'msisdn', 'requestedAmount', 'productid', 'channel']
for field in required_fields:
if field not in data:
return jsonify({
'resultCode': '422',
'resultDescription': f'Missing required field: {field}'
}), 422
# Create request model
req = SelectOffersRequest.from_dict(data)
# Process offer selection (this would connect to the business logic)
# For demonstration, we'll return a mock response
# Create sample offers
offers = [
Offer(
offerId="14451",
productId=req.productid,
amount=req.requestedAmount,
upfrontPayment=req.requestedAmount * 0.1, # 10% upfront
interestRate=3.0,
Interest=req.requestedAmount * 0.03, # 3% interest
ManagementRate=1.0,
ManagementFee=req.requestedAmount * 0.01, # 1% management fee
InsuranceRate=1.0,
InsuranceFee=req.requestedAmount * 0.01, # 1% insurance
VATRate=7.5,
VATamount=(req.requestedAmount * 0.01) * 0.075, # VAT on management fee
recommendedRepaymentDates=["2023-04-30", "2023-05-30", "2023-06-29"],
installmentAmount=req.requestedAmount * 1.05 / 3, # Split into 3 payments with 5% total fees
totalRepaymentAmount=req.requestedAmount * 1.05 # Total with 5% fees
)
]
# Create response
response = SelectOffersResponse(
requestId=req.requestId,
transactionId=req.transactionId,
customerId=req.customerId,
accountId=req.accountId,
offers=[offer.to_dict() for offer in offers],
resultCode="00",
resultDescription="Successful",
outstandingDebtAmount=0.0
)
logger.info(f"Processed offer selection for customer {req.customerId}")
return jsonify(response.to_dict())
except Exception as e:
logger.error(f"Error processing offer selection: {str(e)}")
return jsonify({
'resultCode': '500',
'resultDescription': 'Internal server error'
}), 500
+67
View File
@@ -0,0 +1,67 @@
"""
Controller for penal charge endpoints.
"""
from flask import Blueprint, request, jsonify
from api.middleware import api_key_required
from api.models import PenalChargeRequest, PenalChargeResponse
import logging
# Configure logger
logger = logging.getLogger(__name__)
# Create blueprint
penal_bp = Blueprint('penal', __name__)
@penal_bp.route('/PenalCharge', methods=['POST'])
@api_key_required
def penal_charge():
"""
Endpoint to process penalty charge requests.
This method handles requests to charge customers for penalties
as per existing debt. Results of these requests will be received
from the NotificationCallback endpoint.
Returns:
JSON response acknowledging the penalty charge request
"""
try:
# Parse and validate request
data = request.get_json()
if not data:
return jsonify({
'resultCode': '400',
'resultDescription': 'Invalid JSON payload'
}), 400
# Validate required fields
required_fields = ['transactionId', 'fbnTransactionId', 'debtId', 'customerId',
'accountId', 'penalCharge', 'lienAmount', 'countryId']
for field in required_fields:
if field not in data:
return jsonify({
'resultCode': '422',
'resultDescription': f'Missing required field: {field}'
}), 422
# Create request model
req = PenalChargeRequest.from_dict(data)
# Process penal charge request (this would connect to the business logic)
# For demonstration, we'll return a mock response
# Create response
response = PenalChargeResponse(
resultCode="00",
resultDescription="Penal charge debited successfully"
)
logger.info(f"Processed penal charge for customer {req.customerId}, amount {req.penalCharge}")
return jsonify(response.to_dict())
except Exception as e:
logger.error(f"Error processing penal charge: {str(e)}")
return jsonify({
'resultCode': '500',
'resultDescription': 'Internal server error'
}), 500
+81
View File
@@ -0,0 +1,81 @@
"""
Controller for Risk Acceptance Criteria check endpoints.
"""
from flask import Blueprint, request, jsonify
from api.middleware import api_key_required
from api.models import RACCheckRequest, RACCheckResponse, RACResponse
import logging
# Configure logger
logger = logging.getLogger(__name__)
# Create blueprint
rac_bp = Blueprint('rac', __name__)
@rac_bp.route('/RACCheck', methods=['POST'])
@api_key_required
def rac_check():
"""
Endpoint to check if a customer passes the Risk Acceptance Criteria.
This method evaluates a customer against the bank's risk criteria.
Returns:
JSON response with RAC check results
"""
try:
# Parse and validate request
data = request.get_json()
if not data:
return jsonify({
'resultCode': '400',
'resultDescription': 'Invalid JSON payload'
}), 400
# Validate required fields
required_fields = ['transactionId', 'fbnTransactionId', 'customerId',
'accountId', 'RAC_Array']
for field in required_fields:
if field not in data:
return jsonify({
'resultCode': '422',
'resultDescription': f'Missing required field: {field}'
}), 422
# Create request model
req = RACCheckRequest.from_dict(data)
# Process RAC check (this would connect to the business logic)
# For demonstration, we'll return a mock response with all checks passing
# Create RAC response object
rac_response = RACResponse(
SalaryAccount="1",
BVN="1",
BVNAttachedtoAccount="1",
CRMS="1",
CRC="1",
AccountStatus="1",
Lien="1",
NoBounchedCheck="1",
Whitelist="1",
NoPastDueSalaryLoan="1",
NoPastDueOtherLoan="1"
)
# Create response
response = RACCheckResponse(
RACResponse=rac_response.to_dict(),
resultCode="00",
resultDescription="RAC Check Successful"
)
logger.info(f"Processed RAC check for customer {req.customerId}")
return jsonify(response.to_dict())
except Exception as e:
logger.error(f"Error processing RAC check: {str(e)}")
return jsonify({
'resultCode': '500',
'resultDescription': 'Internal server error'
}), 500
+68
View File
@@ -0,0 +1,68 @@
"""
Controller for repayment endpoints.
"""
from flask import Blueprint, request, jsonify
from api.middleware import basic_auth_required
from api.models import RepaymentRequest, RepaymentResponse
import logging
# Configure logger
logger = logging.getLogger(__name__)
# Create blueprint
repayment_bp = Blueprint('repayment', __name__)
@repayment_bp.route('/Repayment', methods=['POST'])
@basic_auth_required
def repayment():
"""
Endpoint to process loan repayment requests.
This method handles customer repayment of loans.
Returns:
JSON response with repayment status
"""
try:
# Parse and validate request
data = request.get_json()
if not data:
return jsonify({
'resultCode': '400',
'resultDescription': 'Invalid JSON payload'
}), 400
# Validate required fields
required_fields = ['$type', 'transactionId', 'customerId', 'debtId',
'productId', 'channel']
for field in required_fields:
if field not in data:
return jsonify({
'resultCode': '422',
'resultDescription': f'Missing required field: {field}'
}), 422
# Create request model
req = RepaymentRequest.from_dict(data)
# Process repayment request (this would connect to the business logic)
# For demonstration, we'll return a mock response
# Create response
response = RepaymentResponse(
customerId=req.customerId,
productId=req.productId,
debtId=req.debtId,
resultCode="00",
resultDescription="Repayment processed successfully"
)
logger.info(f"Processed repayment for customer {req.customerId}, debt {req.debtId}")
return jsonify(response.to_dict())
except Exception as e:
logger.error(f"Error processing repayment: {str(e)}")
return jsonify({
'resultCode': '500',
'resultDescription': 'Internal server error'
}), 500
+132
View File
@@ -0,0 +1,132 @@
"""
Controller for SMS notification endpoints.
"""
from flask import Blueprint, request, jsonify
from api.middleware import api_key_required
from api.models import SMSRequest, SMSResponse
import logging
# Configure logger
logger = logging.getLogger(__name__)
# Create blueprint
sms_bp = Blueprint('sms', __name__)
@sms_bp.route('/SMS', methods=['POST'])
@api_key_required
def send_sms():
"""
Endpoint to send SMS notifications.
This method handles requests to send SMS messages to customers.
Returns:
JSON response with SMS sending status
"""
try:
# Parse and validate request
data = request.get_json()
if not data:
return jsonify({
'resultCode': '400',
'resultDescription': 'Invalid JSON payload'
}), 400
# Validate required fields
required_fields = ['text', 'dest', 'unicode']
for field in required_fields:
if field not in data:
return jsonify({
'resultCode': '422',
'resultDescription': f'Missing required field: {field}'
}), 422
# Create request model
req = SMSRequest.from_dict(data)
# Process SMS request (this would connect to your business logic)
# For demonstration, we'll return a mock response
# Create response
response = SMSResponse(
data="",
statusCode=200,
IsSuccessful=True,
errorMessage=None
)
logger.info(f"Processed SMS request to {req.dest}")
return jsonify(response.to_dict())
except Exception as e:
logger.error(f"Error processing SMS request: {str(e)}")
return jsonify({
'resultCode': '500',
'resultDescription': 'Internal server error'
}), 500
@sms_bp.route('/BulkSMS', methods=['POST'])
@api_key_required
def send_bulk_sms():
"""
Endpoint to send bulk SMS notifications.
This method handles requests to send multiple SMS messages (up to 20)
in a single request.
Returns:
JSON response with bulk SMS sending status
"""
try:
# Parse and validate request
data = request.get_json()
if not data:
return jsonify({
'resultCode': '400',
'resultDescription': 'Invalid JSON payload'
}), 400
# Validate that data is an array
if not isinstance(data, list):
return jsonify({
'resultCode': '422',
'resultDescription': 'Request must be an array of SMS messages'
}), 422
# Validate array length
if len(data) > 20:
return jsonify({
'resultCode': '422',
'resultDescription': 'Maximum of 20 SMS messages allowed per request'
}), 422
# Validate each SMS in the array
for i, sms in enumerate(data):
required_fields = ['text', 'dest', 'unicode']
for field in required_fields:
if field not in sms:
return jsonify({
'resultCode': '422',
'resultDescription': f'Missing required field: {field} in SMS at index {i}'
}), 422
# Process bulk SMS request (this would connect to the business logic)
# For demonstration, we'll return a mock response
# Create response
response = SMSResponse(
data="",
statusCode=200,
IsSuccessful=True,
errorMessage=None
)
logger.info(f"Processed bulk SMS request with {len(data)} messages")
return jsonify(response.to_dict())
except Exception as e:
logger.error(f"Error processing bulk SMS request: {str(e)}")
return jsonify({
'resultCode': '500',
'resultDescription': 'Internal server error'
}), 500
+68
View File
@@ -0,0 +1,68 @@
"""
Controller for token validation endpoints.
"""
from flask import Blueprint, request, jsonify
from api.middleware import api_key_required
from api.models import ValidateTokenRequest, ValidateTokenResponse
import logging
# Configure logger
logger = logging.getLogger(__name__)
# Create blueprint
token_bp = Blueprint('token', __name__)
@token_bp.route('/ValidateToken', methods=['POST'])
@api_key_required
def validate_token():
"""
Endpoint to validate user authentication tokens.
This method is used when users from FirstBank access the Customer Care Portal.
It validates the soft/hard token code entered by the user.
Returns:
JSON response with token validation results
"""
try:
# Parse and validate request
data = request.get_json()
if not data:
return jsonify({
'resultCode': '400',
'resultDescription': 'Invalid JSON payload'
}), 400
# Validate required fields
required_fields = ['RequestId', 'UserId', 'CountryId', 'TokenCode']
for field in required_fields:
if field not in data:
return jsonify({
'resultCode': '422',
'resultDescription': f'Missing required field: {field}'
}), 422
# Create request model
req = ValidateTokenRequest.from_dict(data)
# Process token validation (this would connect to the business logic)
# For demonstration, we'll return a mock response with successful validation
# Create response
response = ValidateTokenResponse(
Authenticated=True,
AuthenticatedMessage=f"The user with ID {req.UserId} has successfully authenticated!",
ResponseCode="00",
ResponseMessage="Successful",
RequestId=req.RequestId
)
logger.info(f"Processed token validation for user {req.UserId}")
return jsonify(response.to_dict())
except Exception as e:
logger.error(f"Error processing token validation: {str(e)}")
return jsonify({
'resultCode': '500',
'resultDescription': 'Internal server error'
}), 500
+169
View File
@@ -0,0 +1,169 @@
"""
Controller for transaction check endpoints.
"""
from flask import Blueprint, request, jsonify
from flask.typing import ResponseReturnValue
from api.middleware import api_key_required
from api.models import (
TransactionCheckRequest, TransactionCheckResponse,
NewTransactionCheckRequest, NewTransactionCheckResponse,
TransactionData
)
import logging
from typing import Dict, Any
# Configure logger
logger = logging.getLogger(__name__)
# Create blueprint
transaction_bp = Blueprint('transaction', __name__)
@transaction_bp.route('/TransactionCheck', methods=['POST'])
@api_key_required
def transaction_check() -> ResponseReturnValue:
"""
Endpoint to check transaction status.
This method is used to double-check the response received from DisburseLoan
and CollectLoan Synchronous APIs. It verifies transaction results on FirstBank.
Returns:
JSON response with transaction status details
"""
try:
# Parse and validate request
data = request.get_json()
if not data:
logger.warning("Invalid JSON payload received")
return jsonify({
'resultCode': '400',
'resultDescription': 'Invalid JSON payload'
}), 400
# Validate required fields
required_fields = ['counter', 'TransactionId', 'requestID', 'customerId',
'accountId', 'countryId', 'transactionType']
missing_fields = [field for field in required_fields if field not in data]
if missing_fields:
logger.warning(f"Missing required fields: {', '.join(missing_fields)}")
return jsonify({
'resultCode': '422',
'resultDescription': f'Missing required fields: {", ".join(missing_fields)}'
}), 422
# Create request model
req = TransactionCheckRequest.from_dict(data)
# Process transaction check (this would connect to the business logic)
# For demonstration, we'll return a mock response
# Create response based on transaction type
provided_amount = 0.0
collected_amount = 0.0
if req.transactionType == "Disbursement":
provided_amount = 10000.0
elif req.transactionType == "Collection" or req.transactionType == "Penalty":
collected_amount = 7.50
# Create response
response = TransactionCheckResponse(
type_field="TransactionCheckResponse", # This will be converted to $type in JSON
nativeId=f"FBN20191031104405{req.customerId}",
customerId=req.customerId,
accountId=req.accountId,
providedAmount=provided_amount,
collectedAmount=collected_amount,
resultCode="00",
resultDescription=f"{req.transactionType} Status retrieved successfully."
)
logger.info(f"Processed transaction check for {req.transactionType}, ID {req.TransactionId}")
return jsonify(response.to_dict())
except Exception as e:
logger.exception(f"Error processing transaction check: {str(e)}")
return jsonify({
'resultCode': '500',
'resultDescription': f'Internal server error: {str(e)}'
}), 500
@transaction_bp.route('/NewTransactionCheck', methods=['POST'])
@api_key_required
def new_transaction_check() -> ResponseReturnValue:
"""
Endpoint to check status of transactions in asynchronous requests.
This method is used for checking the status of transactions when Simbrella
doesn't receive a callback notification within 5 minutes of the initial request.
Returns:
JSON response with detailed transaction status
"""
try:
# Parse and validate request
data = request.get_json()
if not data:
logger.warning("Invalid JSON payload received")
return jsonify({
'resultCode': '400',
'resultDescription': 'Invalid JSON payload'
}), 400
# Validate required fields
required_fields = ['transactionId', 'debtId', 'transactionType',
'fbnTransactionId', 'origTransactionId', 'customerId']
missing_fields = [field for field in required_fields if field not in data]
if missing_fields:
logger.warning(f"Missing required fields: {', '.join(missing_fields)}")
return jsonify({
'resultCode': '422',
'resultDescription': f'Missing required fields: {", ".join(missing_fields)}'
}), 422
# Create request model
req = NewTransactionCheckRequest.from_dict(data)
# Process new transaction check (this would connect to the business logic)
# For demonstration, we'll return a mock response
# Create transaction data based on transaction type
provided_amount = 0.0
collected_amount = 0.0
if req.transactionType == "Disbursement":
provided_amount = 1000.0
result_description = "Loan Provision is successful"
elif req.transactionType == "Collection":
collected_amount = 500.0
result_description = "Loan Collection is successful"
else: # PenalCharge
collected_amount = 50.0
result_description = "Penal Charge is successful"
# Create transaction data
transaction_data = TransactionData(
transactionId=req.origTransactionId,
providedAmount=provided_amount,
collectedAmount=collected_amount,
resultCode="00",
resultDescription=result_description
)
# Create response
response = NewTransactionCheckResponse(
transactionId=req.transactionId,
data=transaction_data.to_dict(),
resultCode="00",
resultDescription="SUCCESS"
)
logger.info(f"Processed new transaction check for {req.transactionType}, ID {req.transactionId}")
return jsonify(response.to_dict())
except Exception as e:
logger.exception(f"Error processing new transaction check: {str(e)}")
return jsonify({
'resultCode': '500',
'resultDescription': f'Internal server error: {str(e)}'
}), 500