From c0d5893a9b5618e10c7ef6943eb392c1f1658785 Mon Sep 17 00:00:00 2001 From: Chinenye Nmoh Date: Fri, 6 Jun 2025 13:56:50 -0400 Subject: [PATCH 1/3] added collect loan --- app/integrations/simbrella.py | 73 +++++++++------- app/models/loan.py | 1 + app/models/repayment.py | 153 +++++++++++++++++++++++++++++++++- app/routes/autocall.py | 21 ++++- app/services/repayment.py | 44 +++++++++- 5 files changed, 257 insertions(+), 35 deletions(-) diff --git a/app/integrations/simbrella.py b/app/integrations/simbrella.py index 228d628..cadf101 100644 --- a/app/integrations/simbrella.py +++ b/app/integrations/simbrella.py @@ -89,7 +89,7 @@ class SimbrellaClient: logger.info(f"Disbursement response: {response.json()}") result = response.json() LoanService.set_disbursement_result(loan_data['debtId'],result.get('responseCode', ''), result.get('responseMessage', '')) - + return ResponseHelper.success(response.json(), "Successful") except Exception as e: logger.info(f"Failed to call Disbursement endpoint: {e}") return 0 @@ -98,7 +98,7 @@ class SimbrellaClient: @staticmethod def verify_transaction(data): - api_url = f"{SimbrellaClient.BANK_CALL_BASE_URL}/{SimbrellaClient.BANK_CALL_TRANSACTION_VERIFY}" + api_url = f"{SimbrellaClient.BANK_CALL_BASE_URL}{SimbrellaClient.BANK_CALL_TRANSACTION_VERIFY}" sms_url = f"{SimbrellaClient.BANK_CALL_SMS_BASE_URL}/singleSMS" logger.info(f"Calling TransactionVerify api_url==> : {api_url}") @@ -162,7 +162,7 @@ class SimbrellaClient: logger.info(f"SMS Response JSON: {result}") if result.get('isSuccess'): logger.info(f"sms sent successfully") - return ResponseHelper.success(response, "Successful") + return ResponseHelper.success(response.json(), "Successful") logger.info(f"sms failed!") return 1 except requests.RequestException as e: @@ -175,51 +175,66 @@ class SimbrellaClient: @staticmethod def collect_loan(data): - api_url = f"{SimbrellaClient.BANK_CALL_BASE_URL}/{SimbrellaClient.BANK_CALL_COLLECT_LOAN_ENDPOINT}" - logger.info(f"Calling CollectLoan api_url==> : {api_url}") + api_url = f"{SimbrellaClient.BANK_CALL_BASE_URL}{SimbrellaClient.BANK_CALL_COLLECT_LOAN_ENDPOINT}" + logger.info(f"Calling CollectLoan api_url==> : {api_url}") logger.info(f"Calling CollectLoan endpoint with data: {data}") # Check if the repayment exists logger.info(f"Checking if repayment exists") repayment = RepaymentService.get_repayment_by_transaction_id(transaction_id=data['transactionId']) logger.info(f"Repayment Response From Database ** : {repayment}") - loan = LoanService.get_loan_by_debt_id(debt_id=repayment.loan_id) - # If repayment is not found if not repayment: - logger.info(f"Repayment id: {data['transactionId']}, was not found") - return 0 + logger.info(f"Repayment with transactionId: {data['transactionId']}, was not found") + return ResponseHelper.error("Repayment not found") - repayAmount = loan.repayment_amount + repayment_data = repayment.to_dict() + loan = LoanService.get_loan_by_transaction_id(transaction_id=repayment_data['transactionId']) - collectAmount = repayAmount + # If loan is not found + if not loan: + logger.info(f"Loan with debtId: {repayment_data.loan_id}, was not found") + return ResponseHelper.error("Loan not found") + loan_data = loan.to_dict() + + if repayment_data['repayDate'] is not None: + logger.info( + f"Please call verify collection : {data['transactionId']} repayment send for processing at {repayment_data['repayDate']}") + return ResponseHelper.error("Repayment already processed") + + # let us set repay date + RepaymentService.set_repay_date(repayment_data['Id'], repayment_data['customerId']) + repayment = RepaymentService.get_repayment_by_transaction_id(transaction_id=data['transactionId']) + repayment_data = repayment.to_dict() + logger.info(f"Here is your repayment data after setting repay date: {repayment_data}") + debtId = str(loan_data.get('debtId', "")).strip().zfill(6) collect_loan_data = { - "transactionId": repayment.transaction_id, - "fbnTransactionId": loan.reference, - "debtId": repayment.loan_id, - "customerId": repayment.customer_id, - "accountId": loan.account_id, - "productId": repayment.product_id, - "collectAmount": collectAmount, - "penalCharge": 0, - "channel": "USSD", - "collectionMethod": 1, - "lienAmount": 0, - "countryId": "01", - "comment": "Testing CollectionLoanRequest" - } + "transactionId": repayment_data['transactionId'], + "fbnTransactionId": loan_data['transactionId'], + "debtId": debtId, + "customerId": repayment_data['customerId'], + "accountId": loan_data['accountId'], + "productId": repayment_data['productId'], + "collectAmount": loan_data['repaymentAmount'], + "penalCharge": 5, + "channel": "USSD", + "collectionMethod": "1", + "lienAmount": 0, + "countryId": "01", + "comment": "COLLECT LOAN" + } try: logger.info(f"Here is your CollectLoan Request data ***** : {collect_loan_data}") response = requests.post(api_url, json=collect_loan_data, headers=get_headers()) + logger.info(f"CollectLoan response: {response.json()}") - + RepaymentService.set_repay_result(repayment_data['Id'], response.json().get('responseCode', ''), response.json().get('responseMessage', '')) + return ResponseHelper.success(response.json(), "Successful") except Exception as e: logger.info(f"Failed to call CollectLoan endpoint: {e}") - return 0 - - return 1 + return ResponseHelper.error("Failed to call CollectLoan endpoint") @staticmethod diff --git a/app/models/loan.py b/app/models/loan.py index a247ad0..2fc3645 100644 --- a/app/models/loan.py +++ b/app/models/loan.py @@ -74,6 +74,7 @@ class Loan(db.Model): 'defaultPenaltyFee': self.default_penalty_fee, 'continuousFee': self.continuous_fee, 'collectionType': self.collection_type, + 'repaymentAmount':self.repayment_amount, 'status': self.status, 'productId': self.product_id, 'disburseResult': self.disburse_result, diff --git a/app/models/repayment.py b/app/models/repayment.py index bc18452..3ea9db5 100644 --- a/app/models/repayment.py +++ b/app/models/repayment.py @@ -1,5 +1,6 @@ from app.extensions import db from datetime import datetime, timezone +from app.utils.logger import logger class Repayment(db.Model): __tablename__ = "repayments" @@ -15,10 +16,158 @@ class Repayment(db.Model): transaction_id = db.Column(db.String(50), nullable=False) created_at = db.Column(db.DateTime, default=datetime.now(timezone.utc)) updated_at = db.Column(db.DateTime, default=datetime.now(timezone.utc), onupdate=datetime.now(timezone.utc)) - + repay_date = db.Column(db.DateTime, nullable=True) + verify_date = db.Column(db.DateTime, nullable=True) + repay_result = db.Column(db.String(10), nullable=True) + repay_description = db.Column(db.String(100), nullable=True) + verify_result = db.Column(db.String(10), nullable=True) + verify_description = db.Column(db.String(100), nullable=True) def __repr__(self): return f'' + + def to_dict(self): + """ + Convert the Repayment object to a dictionary format for JSON serialization. + """ + return { + 'Id': self.id, + "customerId": self.customer_id, + 'loanId': self.loan_id, + 'productId': self.product_id, + 'repayResult': self.repay_result, + 'repayDescription': self.repay_description, + 'verifyResult': self.verify_result, + 'verifyDescription': self.verify_description, + 'transactionId': self.transaction_id, + 'repayDate': self.repay_date.isoformat() if self.repay_date else None, + 'VerifyDate': self.verify_date.isoformat() if self.verify_date else None, + } + + @classmethod def get_repayment_by_transaction_id(cls, transaction_id): - return cls.query.filter_by(transaction_id=transaction_id).first() \ No newline at end of file + return cls.query.filter_by(transaction_id=transaction_id).first() + + @classmethod + def set_repay_date(cls, repayment_id, customer_id): + """ + Update the repay date of the loan with the given loan_id. + """ + # Retrieve repayment + repayment = cls.query.get(repayment_id) + + if not repayment: + raise ValueError(f"repayment with ID {repayment_id} does not exist.") + + # Check if customer_id matches + if repayment.customer_id != customer_id: + raise ValueError(f"Customer ID {customer_id} does not match the repayment's customer ID.") + + current_time = datetime.now() + logger.info(f"What is now ======= ==== ==> : {current_time}") + # Update repayment date + repayment.repay_date = current_time + + # Commit changes to database + try: + logger.info(f"Updating repay date for repayment ID {repayment_id} to {current_time}") + db.session.commit() + except Exception as e: + db.session.rollback() + logger.error(f"Failed to update repay date: {e}") + raise + @classmethod + def set_repay_verify_date(cls, repayment_id, customer_id): + """ + Update the repayment verify date of the loan with the given repayment_id. + """ + # Retrieve repayment + repayment = cls.query.get(repayment_id) + + if not repayment: + raise ValueError(f"repayment with ID {repayment_id} does not exist.") + + # Check if customer_id matches + if repayment.customer_id != customer_id: + raise ValueError(f"Customer ID {customer_id} does not match the repayment's customer ID.") + + current_time = datetime.now() + logger.info(f"What is now ======= ==== ==> : {current_time}") + # Update repayment verify_date + repayment.verify_date = current_time + + # Commit changes to database + try: + logger.info(f"Updating repay verify date for repayment ID {repayment_id} to {current_time}") + db.session.commit() + except Exception as e: + db.session.rollback() + logger.error(f"Failed to update repay verify date: {e}") + raise + + + @classmethod + def set_repay_result(cls, repayment_id, result, description): + """ + Update the repayment result and description of the repayment with the given repayment_id. + """ + # Retrieve loan + repayment = cls.query.get(repayment_id) + + if not repayment: + raise ValueError(f"repayment with ID {repayment_id} does not exist.") + + # Update repayment result and description + repayment.repay_result = result + repayment.repay_description = description + + # Commit changes to database + try: + logger.info(f"Updating repayment result for repayment ID {repayment_id} to {result} with description {description}") + db.session.commit() + except Exception as e: + db.session.rollback() + logger.error(f"Failed to update repayment result: {e}") + raise + @classmethod + def set_verify_date_result(cls, repayment_id, result, description): + """ + Update the verify result and description of the repayment with the given repayment_id. + """ + # Retrieve repayment + repayment = cls.query.get(repayment_id) + + if not repayment: + raise ValueError(f"repayment with ID {repayment_id} does not exist.") + + # Update disburse result and description + repayment.verify_result = result + repayment.verify_description = description + + # Commit changes to database + try: + logger.info(f"Updating verify result for repayment ID {repayment_id} to {result} with description {description}") + db.session.commit() + except Exception as e: + db.session.rollback() + logger.error(f"Failed to update verify result: {e}") + raise + @classmethod + def get_latest_repayment_without_repay_date(cls): + """ + Get the latest repayment without a repay date. + """ + return cls.query.filter( + cls.repay_date.is_(None) + ).order_by(cls.created_at.desc()).first() + + @classmethod + def get_latest_loan_with_repay_date(cls): + """ + Get the latest repayment with a repay date and no verification date. + """ + return cls.query.filter( + cls.repay_date.isnot(None), + cls.verify_date.is_(None) + ).order_by(cls.created_at.desc()).first() \ No newline at end of file diff --git a/app/routes/autocall.py b/app/routes/autocall.py index 68d2dc3..968bea9 100644 --- a/app/routes/autocall.py +++ b/app/routes/autocall.py @@ -6,6 +6,7 @@ from app.utils.auth import get_headers from app.utils.logger import logger from app.integrations.simbrella import SimbrellaClient from app.services.loan import LoanService +from app.services.repayment import RepaymentService autocall_bp = Blueprint("autocall", __name__) @@ -65,17 +66,31 @@ def refresh_verify_collection(): return response - @autocall_bp.route("/refresh-collection", methods=["GET"]) def refresh_collection(): - data = request.get_json() + #data = request.get_json() logger.info(f"Calling Collection ") + #grab the last repayments with repay date is none + repayment = RepaymentService.get_latest_repayment_without_repay_date() + if not repayment: + logger.info(f"No repayment found without disbursement date") + return 0 + logger.info(f"Calling repay loan endpoint with data: {repayment}") + repayment_data = repayment.to_dict() + logger.info(f"here is the dict form of repayment {repayment_data}") + data = { + "transactionId": repayment_data['transactionId'], + "debtId": repayment_data['loanId'], + "customerId": repayment_data['customerId'], + "productId": repayment_data['productId'], + } + logger.info(f"Data being sent to Simbrella: {data}") + logger.info(f"calling simbrella") response = SimbrellaClient.collect_loan(data) return response - @autocall_bp.route("/payment-callback", methods=["POST"]) def payment_callback(): data = request.get_json() diff --git a/app/services/repayment.py b/app/services/repayment.py index b2249ff..f4baece 100644 --- a/app/services/repayment.py +++ b/app/services/repayment.py @@ -7,4 +7,46 @@ class RepaymentService: """ Get the repayment by transaction ID """ - return Repayment.get_repayment_by_transaction_id(transaction_id) \ No newline at end of file + return Repayment.get_repayment_by_transaction_id(transaction_id) + + @classmethod + def set_repay_date(cls, repayment_id, customer_id): + """ + Update the repay status of the repayment with the given repayment_id. + """ + return Repayment.set_repay_date(repayment_id, customer_id) + + @classmethod + def set_repay_verify_date(cls, repayment_id, customer_id): + """ + Update the verify date of the repayment with the given repayment_id. + """ + return Repayment.set_repay_verify_date(repayment_id, customer_id) + + @classmethod + def set_repay_result(cls, repayment_id, result, description): + """ + Update the repay result of the repayment with the given repayment_id. + """ + return Repayment.set_repay_result(repayment_id, result, description) + + @classmethod + def set_verify_date_result(cls, repayment_id, result, description): + """ + Update the verify result of the repayment with the given repayment_id. + """ + return Repayment.set_verify_date_result(repayment_id, result, description) + + @classmethod + def get_latest_repayment_without_repay_date(cls): + """ + Get the latest repayment without a repay date. + """ + return Repayment.get_latest_repayment_without_repay_date() + + @classmethod + def get_latest_loan_with_repay_date(cls): + """ + Get the latest repayment with a repay date and no verification date. + """ + return Repayment.get_latest_loan_with_repay_date() \ No newline at end of file From 321de4bd6fad0dece9d0df025af72e40387de0fa Mon Sep 17 00:00:00 2001 From: Chinenye Nmoh Date: Fri, 6 Jun 2025 14:02:35 -0400 Subject: [PATCH 2/3] added collect loan --- app/integrations/simbrella copy.py | 285 +++++++++++++++++++++++++++++ 1 file changed, 285 insertions(+) create mode 100644 app/integrations/simbrella copy.py diff --git a/app/integrations/simbrella copy.py b/app/integrations/simbrella copy.py new file mode 100644 index 0000000..cadf101 --- /dev/null +++ b/app/integrations/simbrella copy.py @@ -0,0 +1,285 @@ +import requests +from app.config import settings +from app.helpers.response_helper import ResponseHelper +from app.services.loan import LoanService +from app.utils.auth import get_headers +from app.utils.extras import preprocess_loan_charges_data +from app.utils.logger import logger +from flask import jsonify, current_app +from app.services.transactions import TransactionService +from app.services.repayment import RepaymentService +from app.extensions import db + + +class SimbrellaClient: + + BANK_CALL_BASE_URL = settings.BANK_CALL_BASE_URL + BANK_CALL_SMS_BASE_URL = settings.BANK_CALL_SMS_BASE_URL + BANK_CALL_DISBURSE_LOAN_ENDPOINT = settings.BANK_CALL_DISBURSE_LOAN_ENDPOINT + BANK_CALL_COLLECT_LOAN_ENDPOINT = settings.BANK_CALL_COLLECT_LOAN_ENDPOINT + BANK_CALL_TRANSACTION_VERIFY = settings.BANK_CALL_TRANSACTION_VERIFY + + @staticmethod + def disburse_loan(data): + api_url = f"{SimbrellaClient.BANK_CALL_BASE_URL}/{SimbrellaClient.BANK_CALL_DISBURSE_LOAN_ENDPOINT}" + logger.info(f"Calling DisburseLoan api_url==> : {api_url}") + logger.info(f"Calling DisburseLoan endpoint with data: {data}") + + # Check if the transaction exists + logger.info(f"Checking if transaction exists") + transaction = TransactionService.get_transaction_by_transaction_id(transaction_id=data['transactionId']) + logger.info(f"Loan Response From Database ** : {transaction}") + + # If transaction is not found + if not transaction: + logger.info(f"Transaction id: {data['transactionId']}, was not found") + return 0 + + # Fetch the loan based on the transaction_id + logger.info(f"Fetching the loan with transaction ID: {data['transactionId']}") + loan = LoanService.get_loan_by_transaction_id(transaction_id=data['transactionId']) + logger.info(f"Response from database: {loan}") + + # If loan is not found + if not loan: + logger.info(f"Could not find loan with transaction id: {data['transactionId']}") + return 0 + + loan_data = loan.to_dict() + logger.info(f"Here is your loan data: {loan_data}") + + if loan_data['disburseDate'] is not None: + logger.info( + f"Please call verify loan : {data['transactionId']} loan send for processing at {loan_data['disburseDate']}") + return 0 + + # let us set disbursement date + LoanService.set_disbursement_date(loan_data['debtId'],loan_data['customerId']) # toda this must return something + logger.info(f"Here is your loan data after setting disbursement date: {loan_data}") + + loan_charges = preprocess_loan_charges_data([loan_charge.to_dict() for loan_charge in loan.loan_charges]) + logger.info(f"Here are your loan_charges: {loan_charges}") + + mgt_fee = loan_charges.get("MGTFEE")['amount'] + vat_fee = loan_charges.get("VAT")['amount'] + interest_fee = loan_charges.get("INTEREST")['amount'] + insurance_fee = loan_charges.get("INSURANCE")['amount'] + + debtId = str(loan_data.get('debtId', "")).strip().zfill(6) + + disbursement_data = { + "transactionId": loan_data.get('transactionId'), + "FbnTransactionId": loan_data.get('transactionId'), + "debtId": debtId, + "customerId": loan_data.get('customerId'), + "accountId": loan_data.get('accountId'), + "productId": str(loan_data.get('productId', "")), + "provideAmount": loan_data.get('currentLoanAmount'), + "collectAmountInterest": interest_fee, + "collectAmountMgtFee": mgt_fee, + "collectAmountInsurance": insurance_fee, + "collectAmountVAT": vat_fee, + "countryId": "01", + "comment": "Loan Disbursement", + } + + try: + logger.info(f"Here is your Disbursement Request data ****** : {disbursement_data}") + response = requests.post(api_url, json=disbursement_data, timeout=10, headers=get_headers()) + logger.info(f"Disbursement response: {response.json()}") + result = response.json() + LoanService.set_disbursement_result(loan_data['debtId'],result.get('responseCode', ''), result.get('responseMessage', '')) + return ResponseHelper.success(response.json(), "Successful") + except Exception as e: + logger.info(f"Failed to call Disbursement endpoint: {e}") + return 0 + + return 1 + + @staticmethod + def verify_transaction(data): + api_url = f"{SimbrellaClient.BANK_CALL_BASE_URL}{SimbrellaClient.BANK_CALL_TRANSACTION_VERIFY}" + sms_url = f"{SimbrellaClient.BANK_CALL_SMS_BASE_URL}/singleSMS" + logger.info(f"Calling TransactionVerify api_url==> : {api_url}") + + # Check if the transaction exists + logger.info(f"Checking if transaction exists") + transaction = TransactionService.get_transaction_by_transaction_id(transaction_id=data['transactionId']) + logger.info(f"Loan Response From Database ** : {transaction}") + + # If transaction is not found + if not transaction: + logger.info(f"Transaction id: {data['transactionId']}, was not found") + return 0 + + # Fetch the loan based on the transaction_id + logger.info(f"Fetching the loan with transaction ID: {data['transactionId']}") + loan = LoanService.get_loan_by_transaction_id(transaction_id=data['transactionId']) + logger.info(f"Response from database: {loan}") + + # If loan is not found + if not loan: + logger.info(f"Could not find loan with transaction id: {data['transactionId']}") + return 0 + + loan_data = loan.to_dict() + logger.info(f"Here is your loan data: {loan_data}") + + if loan_data['disburseDate'] is not None and loan_data['disburseVerify'] is None : + LoanService.set_disburse_verify_date(loan_data['debtId'],loan_data['customerId']) + loan_data = loan.to_dict() + logger.info(f"Here is your loan data after setting verify date: {loan_data}") + logger.info(f"Good to Verify transaction id: {data['transactionId']}") + else: + logger.info( + f"Please call disburse loan : {data['transactionId']} loan send for processing first") + return 0 + + + verify_data = { + "customerId": loan_data.get('customerId'), + "accountId": loan_data.get('accountId'), + "transactionId": loan_data.get('transactionId'), + "transactionType": "provide", + "countryId": "NG", + "requestId": loan_data.get('transactionId') + } + + try: + logger.info(f"Here is your TransactionVerify Request data ****** : {verify_data}") + response = requests.post(api_url, json=verify_data, timeout=10, headers=get_headers()) + result = response.json() + LoanService.set_disburse_verify_result(loan_data['debtId'],result.get('responseCode', ''), result.get('responseMessage', '')) + sms_data = { + "dest": "2347038224367", + "text": "test", + "unicode": True + } + try: + sms_response = requests.post(sms_url, json=sms_data, timeout=10, headers=get_headers()) + sms_response.raise_for_status() # Raise an exception for 4xx or 5xx status codes + result = sms_response.json() + logger.info(f"SMS Response JSON: {result}") + if result.get('isSuccess'): + logger.info(f"sms sent successfully") + return ResponseHelper.success(response.json(), "Successful") + logger.info(f"sms failed!") + return 1 + except requests.RequestException as e: + # Handle the exception + logger.error(f"Failed to send SMS: {e}") + return 0 + except Exception as e: + logger.info(f"Failed to call TransactionVerify endpoint: {e}") + return 0 + + @staticmethod + def collect_loan(data): + api_url = f"{SimbrellaClient.BANK_CALL_BASE_URL}{SimbrellaClient.BANK_CALL_COLLECT_LOAN_ENDPOINT}" + logger.info(f"Calling CollectLoan api_url==> : {api_url}") + logger.info(f"Calling CollectLoan endpoint with data: {data}") + + # Check if the repayment exists + logger.info(f"Checking if repayment exists") + repayment = RepaymentService.get_repayment_by_transaction_id(transaction_id=data['transactionId']) + logger.info(f"Repayment Response From Database ** : {repayment}") + + if not repayment: + logger.info(f"Repayment with transactionId: {data['transactionId']}, was not found") + return ResponseHelper.error("Repayment not found") + + repayment_data = repayment.to_dict() + loan = LoanService.get_loan_by_transaction_id(transaction_id=repayment_data['transactionId']) + + # If loan is not found + if not loan: + logger.info(f"Loan with debtId: {repayment_data.loan_id}, was not found") + return ResponseHelper.error("Loan not found") + + loan_data = loan.to_dict() + + if repayment_data['repayDate'] is not None: + logger.info( + f"Please call verify collection : {data['transactionId']} repayment send for processing at {repayment_data['repayDate']}") + return ResponseHelper.error("Repayment already processed") + + # let us set repay date + RepaymentService.set_repay_date(repayment_data['Id'], repayment_data['customerId']) + repayment = RepaymentService.get_repayment_by_transaction_id(transaction_id=data['transactionId']) + repayment_data = repayment.to_dict() + logger.info(f"Here is your repayment data after setting repay date: {repayment_data}") + debtId = str(loan_data.get('debtId', "")).strip().zfill(6) + collect_loan_data = { + "transactionId": repayment_data['transactionId'], + "fbnTransactionId": loan_data['transactionId'], + "debtId": debtId, + "customerId": repayment_data['customerId'], + "accountId": loan_data['accountId'], + "productId": repayment_data['productId'], + "collectAmount": loan_data['repaymentAmount'], + "penalCharge": 5, + "channel": "USSD", + "collectionMethod": "1", + "lienAmount": 0, + "countryId": "01", + "comment": "COLLECT LOAN" + } + + try: + logger.info(f"Here is your CollectLoan Request data ***** : {collect_loan_data}") + response = requests.post(api_url, json=collect_loan_data, headers=get_headers()) + + logger.info(f"CollectLoan response: {response.json()}") + RepaymentService.set_repay_result(repayment_data['Id'], response.json().get('responseCode', ''), response.json().get('responseMessage', '')) + return ResponseHelper.success(response.json(), "Successful") + except Exception as e: + logger.info(f"Failed to call CollectLoan endpoint: {e}") + return ResponseHelper.error("Failed to call CollectLoan endpoint") + + + @staticmethod + def refresh_disbursement(data): + + try: + logger.info(f"Here is your Disbursement Request data ***** : {data}") + + return ResponseHelper.success(data, "Successful") + + except Exception as e: + logger.info(f"Failed to call Disbursement endpoint: {e}") + raise + + @staticmethod + def payment_callback(data): + + try: + logger.info(f"Here is your Payment Callback Request data ***** : {data}") + + return ResponseHelper.success(data, "Successful") + + except Exception as e: + logger.info(f"Failed to call Payment Callback endpoint: {e}") + raise + + @staticmethod + def penal_charge(data): + + api_url = f"{SimbrellaClient.BANK_CALL_BASE_URL}/PenalCharge" + logger.info(f"Calling Penal Charge endpoint with data: {data}") + + try: + logger.info(f"Here is your Penal Charge Request data ***** : {data}") + + try: + logger.info(f"Here is your Penal Charge Request data ****** : {data}") + response = requests.post(api_url, json=data, timeout=10, headers=get_headers()) + logger.info(f"Penal Charge response: {response.json()}") + return ResponseHelper.success(response.json(), "Successful") + + except Exception as e: + logger.info(f"Failed to call Penal Charge endpoint: {e}") + return ResponseHelper.error("An error occurred", 500) + + except Exception as e: + logger.info(f"Failed to call Penal Charge endpoint: {e}") + raise \ No newline at end of file From b2816b947e89eb4852b365a78e280950f943bcae Mon Sep 17 00:00:00 2001 From: Chinenye Nmoh Date: Fri, 6 Jun 2025 14:10:58 -0400 Subject: [PATCH 3/3] added collect loan --- app/integrations/simbrella copy.py | 285 ----------------------------- 1 file changed, 285 deletions(-) delete mode 100644 app/integrations/simbrella copy.py diff --git a/app/integrations/simbrella copy.py b/app/integrations/simbrella copy.py deleted file mode 100644 index cadf101..0000000 --- a/app/integrations/simbrella copy.py +++ /dev/null @@ -1,285 +0,0 @@ -import requests -from app.config import settings -from app.helpers.response_helper import ResponseHelper -from app.services.loan import LoanService -from app.utils.auth import get_headers -from app.utils.extras import preprocess_loan_charges_data -from app.utils.logger import logger -from flask import jsonify, current_app -from app.services.transactions import TransactionService -from app.services.repayment import RepaymentService -from app.extensions import db - - -class SimbrellaClient: - - BANK_CALL_BASE_URL = settings.BANK_CALL_BASE_URL - BANK_CALL_SMS_BASE_URL = settings.BANK_CALL_SMS_BASE_URL - BANK_CALL_DISBURSE_LOAN_ENDPOINT = settings.BANK_CALL_DISBURSE_LOAN_ENDPOINT - BANK_CALL_COLLECT_LOAN_ENDPOINT = settings.BANK_CALL_COLLECT_LOAN_ENDPOINT - BANK_CALL_TRANSACTION_VERIFY = settings.BANK_CALL_TRANSACTION_VERIFY - - @staticmethod - def disburse_loan(data): - api_url = f"{SimbrellaClient.BANK_CALL_BASE_URL}/{SimbrellaClient.BANK_CALL_DISBURSE_LOAN_ENDPOINT}" - logger.info(f"Calling DisburseLoan api_url==> : {api_url}") - logger.info(f"Calling DisburseLoan endpoint with data: {data}") - - # Check if the transaction exists - logger.info(f"Checking if transaction exists") - transaction = TransactionService.get_transaction_by_transaction_id(transaction_id=data['transactionId']) - logger.info(f"Loan Response From Database ** : {transaction}") - - # If transaction is not found - if not transaction: - logger.info(f"Transaction id: {data['transactionId']}, was not found") - return 0 - - # Fetch the loan based on the transaction_id - logger.info(f"Fetching the loan with transaction ID: {data['transactionId']}") - loan = LoanService.get_loan_by_transaction_id(transaction_id=data['transactionId']) - logger.info(f"Response from database: {loan}") - - # If loan is not found - if not loan: - logger.info(f"Could not find loan with transaction id: {data['transactionId']}") - return 0 - - loan_data = loan.to_dict() - logger.info(f"Here is your loan data: {loan_data}") - - if loan_data['disburseDate'] is not None: - logger.info( - f"Please call verify loan : {data['transactionId']} loan send for processing at {loan_data['disburseDate']}") - return 0 - - # let us set disbursement date - LoanService.set_disbursement_date(loan_data['debtId'],loan_data['customerId']) # toda this must return something - logger.info(f"Here is your loan data after setting disbursement date: {loan_data}") - - loan_charges = preprocess_loan_charges_data([loan_charge.to_dict() for loan_charge in loan.loan_charges]) - logger.info(f"Here are your loan_charges: {loan_charges}") - - mgt_fee = loan_charges.get("MGTFEE")['amount'] - vat_fee = loan_charges.get("VAT")['amount'] - interest_fee = loan_charges.get("INTEREST")['amount'] - insurance_fee = loan_charges.get("INSURANCE")['amount'] - - debtId = str(loan_data.get('debtId', "")).strip().zfill(6) - - disbursement_data = { - "transactionId": loan_data.get('transactionId'), - "FbnTransactionId": loan_data.get('transactionId'), - "debtId": debtId, - "customerId": loan_data.get('customerId'), - "accountId": loan_data.get('accountId'), - "productId": str(loan_data.get('productId', "")), - "provideAmount": loan_data.get('currentLoanAmount'), - "collectAmountInterest": interest_fee, - "collectAmountMgtFee": mgt_fee, - "collectAmountInsurance": insurance_fee, - "collectAmountVAT": vat_fee, - "countryId": "01", - "comment": "Loan Disbursement", - } - - try: - logger.info(f"Here is your Disbursement Request data ****** : {disbursement_data}") - response = requests.post(api_url, json=disbursement_data, timeout=10, headers=get_headers()) - logger.info(f"Disbursement response: {response.json()}") - result = response.json() - LoanService.set_disbursement_result(loan_data['debtId'],result.get('responseCode', ''), result.get('responseMessage', '')) - return ResponseHelper.success(response.json(), "Successful") - except Exception as e: - logger.info(f"Failed to call Disbursement endpoint: {e}") - return 0 - - return 1 - - @staticmethod - def verify_transaction(data): - api_url = f"{SimbrellaClient.BANK_CALL_BASE_URL}{SimbrellaClient.BANK_CALL_TRANSACTION_VERIFY}" - sms_url = f"{SimbrellaClient.BANK_CALL_SMS_BASE_URL}/singleSMS" - logger.info(f"Calling TransactionVerify api_url==> : {api_url}") - - # Check if the transaction exists - logger.info(f"Checking if transaction exists") - transaction = TransactionService.get_transaction_by_transaction_id(transaction_id=data['transactionId']) - logger.info(f"Loan Response From Database ** : {transaction}") - - # If transaction is not found - if not transaction: - logger.info(f"Transaction id: {data['transactionId']}, was not found") - return 0 - - # Fetch the loan based on the transaction_id - logger.info(f"Fetching the loan with transaction ID: {data['transactionId']}") - loan = LoanService.get_loan_by_transaction_id(transaction_id=data['transactionId']) - logger.info(f"Response from database: {loan}") - - # If loan is not found - if not loan: - logger.info(f"Could not find loan with transaction id: {data['transactionId']}") - return 0 - - loan_data = loan.to_dict() - logger.info(f"Here is your loan data: {loan_data}") - - if loan_data['disburseDate'] is not None and loan_data['disburseVerify'] is None : - LoanService.set_disburse_verify_date(loan_data['debtId'],loan_data['customerId']) - loan_data = loan.to_dict() - logger.info(f"Here is your loan data after setting verify date: {loan_data}") - logger.info(f"Good to Verify transaction id: {data['transactionId']}") - else: - logger.info( - f"Please call disburse loan : {data['transactionId']} loan send for processing first") - return 0 - - - verify_data = { - "customerId": loan_data.get('customerId'), - "accountId": loan_data.get('accountId'), - "transactionId": loan_data.get('transactionId'), - "transactionType": "provide", - "countryId": "NG", - "requestId": loan_data.get('transactionId') - } - - try: - logger.info(f"Here is your TransactionVerify Request data ****** : {verify_data}") - response = requests.post(api_url, json=verify_data, timeout=10, headers=get_headers()) - result = response.json() - LoanService.set_disburse_verify_result(loan_data['debtId'],result.get('responseCode', ''), result.get('responseMessage', '')) - sms_data = { - "dest": "2347038224367", - "text": "test", - "unicode": True - } - try: - sms_response = requests.post(sms_url, json=sms_data, timeout=10, headers=get_headers()) - sms_response.raise_for_status() # Raise an exception for 4xx or 5xx status codes - result = sms_response.json() - logger.info(f"SMS Response JSON: {result}") - if result.get('isSuccess'): - logger.info(f"sms sent successfully") - return ResponseHelper.success(response.json(), "Successful") - logger.info(f"sms failed!") - return 1 - except requests.RequestException as e: - # Handle the exception - logger.error(f"Failed to send SMS: {e}") - return 0 - except Exception as e: - logger.info(f"Failed to call TransactionVerify endpoint: {e}") - return 0 - - @staticmethod - def collect_loan(data): - api_url = f"{SimbrellaClient.BANK_CALL_BASE_URL}{SimbrellaClient.BANK_CALL_COLLECT_LOAN_ENDPOINT}" - logger.info(f"Calling CollectLoan api_url==> : {api_url}") - logger.info(f"Calling CollectLoan endpoint with data: {data}") - - # Check if the repayment exists - logger.info(f"Checking if repayment exists") - repayment = RepaymentService.get_repayment_by_transaction_id(transaction_id=data['transactionId']) - logger.info(f"Repayment Response From Database ** : {repayment}") - - if not repayment: - logger.info(f"Repayment with transactionId: {data['transactionId']}, was not found") - return ResponseHelper.error("Repayment not found") - - repayment_data = repayment.to_dict() - loan = LoanService.get_loan_by_transaction_id(transaction_id=repayment_data['transactionId']) - - # If loan is not found - if not loan: - logger.info(f"Loan with debtId: {repayment_data.loan_id}, was not found") - return ResponseHelper.error("Loan not found") - - loan_data = loan.to_dict() - - if repayment_data['repayDate'] is not None: - logger.info( - f"Please call verify collection : {data['transactionId']} repayment send for processing at {repayment_data['repayDate']}") - return ResponseHelper.error("Repayment already processed") - - # let us set repay date - RepaymentService.set_repay_date(repayment_data['Id'], repayment_data['customerId']) - repayment = RepaymentService.get_repayment_by_transaction_id(transaction_id=data['transactionId']) - repayment_data = repayment.to_dict() - logger.info(f"Here is your repayment data after setting repay date: {repayment_data}") - debtId = str(loan_data.get('debtId', "")).strip().zfill(6) - collect_loan_data = { - "transactionId": repayment_data['transactionId'], - "fbnTransactionId": loan_data['transactionId'], - "debtId": debtId, - "customerId": repayment_data['customerId'], - "accountId": loan_data['accountId'], - "productId": repayment_data['productId'], - "collectAmount": loan_data['repaymentAmount'], - "penalCharge": 5, - "channel": "USSD", - "collectionMethod": "1", - "lienAmount": 0, - "countryId": "01", - "comment": "COLLECT LOAN" - } - - try: - logger.info(f"Here is your CollectLoan Request data ***** : {collect_loan_data}") - response = requests.post(api_url, json=collect_loan_data, headers=get_headers()) - - logger.info(f"CollectLoan response: {response.json()}") - RepaymentService.set_repay_result(repayment_data['Id'], response.json().get('responseCode', ''), response.json().get('responseMessage', '')) - return ResponseHelper.success(response.json(), "Successful") - except Exception as e: - logger.info(f"Failed to call CollectLoan endpoint: {e}") - return ResponseHelper.error("Failed to call CollectLoan endpoint") - - - @staticmethod - def refresh_disbursement(data): - - try: - logger.info(f"Here is your Disbursement Request data ***** : {data}") - - return ResponseHelper.success(data, "Successful") - - except Exception as e: - logger.info(f"Failed to call Disbursement endpoint: {e}") - raise - - @staticmethod - def payment_callback(data): - - try: - logger.info(f"Here is your Payment Callback Request data ***** : {data}") - - return ResponseHelper.success(data, "Successful") - - except Exception as e: - logger.info(f"Failed to call Payment Callback endpoint: {e}") - raise - - @staticmethod - def penal_charge(data): - - api_url = f"{SimbrellaClient.BANK_CALL_BASE_URL}/PenalCharge" - logger.info(f"Calling Penal Charge endpoint with data: {data}") - - try: - logger.info(f"Here is your Penal Charge Request data ***** : {data}") - - try: - logger.info(f"Here is your Penal Charge Request data ****** : {data}") - response = requests.post(api_url, json=data, timeout=10, headers=get_headers()) - logger.info(f"Penal Charge response: {response.json()}") - return ResponseHelper.success(response.json(), "Successful") - - except Exception as e: - logger.info(f"Failed to call Penal Charge endpoint: {e}") - return ResponseHelper.error("An error occurred", 500) - - except Exception as e: - logger.info(f"Failed to call Penal Charge endpoint: {e}") - raise \ No newline at end of file