from os import access import httpx import time from app.utils.logger import logger from app.config import settings class SimbrellaIntegration: BASE_URL = settings.SIMBRELLA_BASE_URL ENDPOINT_RAC_CHECKS = settings.SIMBRELLA_ENDPOINT_RAC_CHECKS HEALTH_ENDPOINT = settings.SIMBRELLA_HEALTH AUTH_ENDPOINT = settings.BANK_CALL_AUTH_ENDPOINT SIMBRELLA_VERIFY_BALANCE_ENDPOINT = settings.SIMBRELLA_VERIFY_BALANCE_ENDPOINT TIMEOUT = settings.TIMEOUT _access_token = None # cache token in memory _token_expiry = 0 @staticmethod def generate_token(): """ Generate a new access token using the username and password from settings. """ url = f"{SimbrellaIntegration.BASE_URL}{SimbrellaIntegration.AUTH_ENDPOINT}" payload = { "username": settings.BANK_CALL_USERNAME, "password": settings.BANK_CALL_PASSWORD, "grant_type": "password" } headers = {"Content-Type": "application/json"} try: logger.info(f"Requesting Bank token from {url}") response = httpx.post(url, json=payload, headers=headers, timeout=SimbrellaIntegration.TIMEOUT) response.raise_for_status() data = response.json() expires_in = data.get("expires_in", 1800) SimbrellaIntegration._access_token = data.get("access_token") SimbrellaIntegration._token_expiry = time.time() + expires_in - 60 if not SimbrellaIntegration._access_token: raise Exception("Access token not found in Bank Authorization response") logger.info("Successfully retrieved Bank access token") return SimbrellaIntegration._access_token except Exception as e: logger.error(f"Token generation failed: {str(e)}", exc_info=True) raise Exception(f"Token generation failed: {str(e)}") @staticmethod def _get_token(): """ Return a valid token, refreshing if expired or missing """ if not SimbrellaIntegration._access_token or time.time() >= SimbrellaIntegration._token_expiry: return SimbrellaIntegration.generate_token() return SimbrellaIntegration._access_token @staticmethod def rac_check(customer_id, account_id, transaction_id): """ Calls the RACCheck endpoit """ url = f"{SimbrellaIntegration.BASE_URL}/{SimbrellaIntegration.ENDPOINT_RAC_CHECKS}" logger.info(f"Contacting Rack Checks EndPoint: {str(url)}", exc_info=True) payload = { "customerId": customer_id, "accountId": account_id, "transactionId": str(transaction_id), "fbnTransactionId": str(transaction_id), "countryCode": "NG", "channel": "USSD" } try: access_token = SimbrellaIntegration._get_token() headers = { "Content-Type": "application/json", "Authorization": f"Bearer {access_token}" } response = httpx.post(url, json=payload, headers=headers, timeout=SimbrellaIntegration.TIMEOUT) logger.info(f"This is Response: {str(response)}", exc_info=True) return response except Exception as e: logger.error(f"RACCheck API call failed: {str(e)}", exc_info=True) raise Exception(f"RACCheck API call failed: {str(e)}") @staticmethod def verify_account_balance(account_id: str, amount: float, request_id: str): """ Calls the Verify Account Balance endpoint """ url = f"{SimbrellaIntegration.BASE_URL}/{SimbrellaIntegration.SIMBRELLA_VERIFY_BALANCE_ENDPOINT}" logger.info(f"Contacting Verify Account Balance Endpoint: {url}") payload = { "accountId": account_id, "amount": amount, "requestId": str(request_id), } try: access_token = SimbrellaIntegration._get_token() headers = { "Content-Type": "application/json", "Authorization": f"Bearer {access_token}", } response = httpx.post( url, json=payload, headers=headers, timeout=SimbrellaIntegration.TIMEOUT, ) logger.info( f"Verify Account Balance Response: " f"status={response.status_code}, body={response.text}" ) response.raise_for_status() return response except httpx.HTTPStatusError as e: logger.error( f"Verify Account Balance failed with status " f"{e.response.status_code}: {e.response.text}", exc_info=True, ) raise Exception("Verify Account Balance API returned an error") except Exception as e: logger.error( f"Verify Account Balance API call failed: {str(e)}", exc_info=True, ) raise Exception(f"Verify Account Balance API call failed: {str(e)}") @staticmethod def health_check(): """ Health check for Bank Service """ url = f"{SimbrellaIntegration.BASE_URL}/{SimbrellaIntegration.HEALTH_ENDPOINT}" logger.info(f"Bank Health Check URL: {url}") try: access_token = SimbrellaIntegration._get_token() headers = { "Content-Type": "application/json", "Authorization": f"Bearer {access_token}" } response = httpx.get(url, headers=headers, timeout=SimbrellaIntegration.TIMEOUT) logger.info(f"Bank Health Check Response: {response.text}") return response except Exception as e: logger.error(f"Bank Health Check API call failed: {str(e)}", exc_info=True) raise Exception(f"Bank Health Check API call failed: {str(e)}")