From e77edb9b45a77208ada9a4664b74033749e0d8eb Mon Sep 17 00:00:00 2001 From: "CHIEFSOFT\\ameye" Date: Thu, 20 Mar 2025 20:59:29 -0400 Subject: [PATCH] first commit --- .DS_Store | Bin 0 -> 6148 bytes .idea/.gitignore | 8 + .idea/digifi-BankEmulator.iml | 8 + .idea/modules.xml | 8 + .idea/php.xml | 19 ++ Dockerfile | 21 ++ README.md | 56 ++++ app.log | 258 ++++++++++++++++++ app.py | 6 + app/__init__.py | 19 ++ app/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 535 bytes app/__pycache__/config.cpython-39.pyc | Bin 0 -> 337 bytes app/blueprints/__init__.py | 18 ++ .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 1441 bytes .../__pycache__/bulk_sms.cpython-39.pyc | Bin 0 -> 1407 bytes .../__pycache__/collect_loan.cpython-39.pyc | Bin 0 -> 1729 bytes .../customer_consent.cpython-39.pyc | Bin 0 -> 1495 bytes .../__pycache__/disbursement.cpython-39.pyc | Bin 0 -> 1813 bytes .../eligibility_check.cpython-39.pyc | Bin 0 -> 1805 bytes .../__pycache__/lien_check.cpython-39.pyc | Bin 0 -> 1432 bytes .../loan_information.cpython-39.pyc | Bin 0 -> 1760 bytes .../new_transaction_check.cpython-39.pyc | Bin 0 -> 1651 bytes .../notification_callback.cpython-39.pyc | Bin 0 -> 1521 bytes .../__pycache__/penal_charge.cpython-39.pyc | Bin 0 -> 1481 bytes .../__pycache__/provide_loan.cpython-39.pyc | Bin 0 -> 1609 bytes .../__pycache__/rac_check.cpython-39.pyc | Bin 0 -> 1657 bytes .../__pycache__/repayment.cpython-39.pyc | Bin 0 -> 1465 bytes .../revoke_enable_consent.cpython-39.pyc | Bin 0 -> 1605 bytes .../__pycache__/select_offer.cpython-39.pyc | Bin 0 -> 2244 bytes app/blueprints/__pycache__/sms.cpython-39.pyc | Bin 0 -> 1376 bytes .../token_validation.cpython-39.pyc | Bin 0 -> 1616 bytes .../transaction_verify.cpython-39.pyc | Bin 0 -> 1717 bytes app/blueprints/bulk_sms.py | 55 ++++ app/blueprints/collect_loan.py | 63 +++++ app/blueprints/customer_consent.py | 55 ++++ app/blueprints/disbursement.py | 66 +++++ app/blueprints/eligibility_check.py | 77 ++++++ app/blueprints/lien_check.py | 54 ++++ app/blueprints/loan_information.py | 67 +++++ app/blueprints/new_transaction_check.py | 61 +++++ app/blueprints/notification_callback.py | 54 ++++ app/blueprints/penal_charge.py | 55 ++++ app/blueprints/provide_loan.py | 59 ++++ app/blueprints/rac_check.py | 67 +++++ app/blueprints/repayment.py | 54 ++++ app/blueprints/revoke_enable_consent.py | 58 ++++ app/blueprints/select_offer.py | 109 ++++++++ app/blueprints/sms.py | 56 ++++ app/blueprints/token_validation.py | 58 ++++ app/blueprints/transaction_verify.py | 61 +++++ app/config.py | 10 + .../response_helper.cpython-39.pyc | Bin 0 -> 7816 bytes app/helpers/response_helper.py | 213 +++++++++++++++ app/middlewares/__init__.py | 0 app/middlewares/cors.py | 9 + app/middlewares/encryption.py | 14 + app/middlewares/request_validator.py | 11 + app/middlewares/verify_api_key.py | 8 + app/routes/__init__.py | 1 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 152 bytes app/routes/__pycache__/routes.cpython-39.pyc | Bin 0 -> 4539 bytes app/routes/routes.py | 175 ++++++++++++ app/schemas/__init__.py | 0 .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 119 bytes .../__pycache__/bulk_sms.cpython-39.pyc | Bin 0 -> 449 bytes .../__pycache__/collect_loan.cpython-39.pyc | Bin 0 -> 703 bytes .../customer_consent.cpython-39.pyc | Bin 0 -> 603 bytes .../__pycache__/disbursement.cpython-39.pyc | Bin 0 -> 850 bytes .../eligibility_check.cpython-39.pyc | Bin 0 -> 811 bytes .../__pycache__/lien_check.cpython-39.pyc | Bin 0 -> 643 bytes .../loan_information.cpython-39.pyc | Bin 0 -> 400 bytes .../new_transaction_check.cpython-39.pyc | Bin 0 -> 647 bytes .../notification_callback.cpython-39.pyc | Bin 0 -> 683 bytes .../__pycache__/penal_charge.cpython-39.pyc | Bin 0 -> 1144 bytes .../__pycache__/provide_loan.cpython-39.pyc | Bin 0 -> 688 bytes .../__pycache__/rac_check.cpython-39.pyc | Bin 0 -> 977 bytes .../__pycache__/repayment.cpython-39.pyc | Bin 0 -> 532 bytes .../revoke_enable_consent.cpython-39.pyc | Bin 0 -> 1040 bytes .../__pycache__/select_offer.cpython-39.pyc | Bin 0 -> 758 bytes app/schemas/__pycache__/sms.cpython-39.pyc | Bin 0 -> 421 bytes .../token_validation.cpython-39.pyc | Bin 0 -> 474 bytes .../transaction_verify.cpython-39.pyc | Bin 0 -> 565 bytes app/schemas/bulk_sms.py | 6 + app/schemas/collect_loan.py | 16 ++ app/schemas/customer_consent.py | 11 + app/schemas/disbursement.py | 17 ++ app/schemas/eligibility_check.py | 11 + app/schemas/lien_check.py | 8 + app/schemas/loan_information.py | 5 + app/schemas/new_transaction_check.py | 12 + app/schemas/notification_callback.py | 14 + app/schemas/penal_charge.py | 14 + app/schemas/provide_loan.py | 16 ++ app/schemas/rac_check.py | 23 ++ app/schemas/repayment.py | 11 + app/schemas/revoke_enable_consent.py | 13 + app/schemas/select_offer.py | 13 + app/schemas/sms.py | 7 + app/schemas/token_validation.py | 8 + app/schemas/transaction_verify.py | 12 + app/static/css/main.css | 0 app/static/js/main.js | 0 app/templates/index.html | 11 + app/utils/__pycache__/logger.cpython-39.pyc | Bin 0 -> 370 bytes app/utils/logger.py | 13 + docker-compose.yml | 11 + requirements.txt | 14 + 107 files changed, 2257 insertions(+) create mode 100644 .DS_Store create mode 100644 .idea/.gitignore create mode 100644 .idea/digifi-BankEmulator.iml create mode 100644 .idea/modules.xml create mode 100644 .idea/php.xml create mode 100644 Dockerfile create mode 100644 README.md create mode 100644 app.log create mode 100644 app.py create mode 100644 app/__init__.py create mode 100644 app/__pycache__/__init__.cpython-39.pyc create mode 100644 app/__pycache__/config.cpython-39.pyc create mode 100644 app/blueprints/__init__.py create mode 100644 app/blueprints/__pycache__/__init__.cpython-39.pyc create mode 100644 app/blueprints/__pycache__/bulk_sms.cpython-39.pyc create mode 100644 app/blueprints/__pycache__/collect_loan.cpython-39.pyc create mode 100644 app/blueprints/__pycache__/customer_consent.cpython-39.pyc create mode 100644 app/blueprints/__pycache__/disbursement.cpython-39.pyc create mode 100644 app/blueprints/__pycache__/eligibility_check.cpython-39.pyc create mode 100644 app/blueprints/__pycache__/lien_check.cpython-39.pyc create mode 100644 app/blueprints/__pycache__/loan_information.cpython-39.pyc create mode 100644 app/blueprints/__pycache__/new_transaction_check.cpython-39.pyc create mode 100644 app/blueprints/__pycache__/notification_callback.cpython-39.pyc create mode 100644 app/blueprints/__pycache__/penal_charge.cpython-39.pyc create mode 100644 app/blueprints/__pycache__/provide_loan.cpython-39.pyc create mode 100644 app/blueprints/__pycache__/rac_check.cpython-39.pyc create mode 100644 app/blueprints/__pycache__/repayment.cpython-39.pyc create mode 100644 app/blueprints/__pycache__/revoke_enable_consent.cpython-39.pyc create mode 100644 app/blueprints/__pycache__/select_offer.cpython-39.pyc create mode 100644 app/blueprints/__pycache__/sms.cpython-39.pyc create mode 100644 app/blueprints/__pycache__/token_validation.cpython-39.pyc create mode 100644 app/blueprints/__pycache__/transaction_verify.cpython-39.pyc create mode 100644 app/blueprints/bulk_sms.py create mode 100644 app/blueprints/collect_loan.py create mode 100644 app/blueprints/customer_consent.py create mode 100644 app/blueprints/disbursement.py create mode 100644 app/blueprints/eligibility_check.py create mode 100644 app/blueprints/lien_check.py create mode 100644 app/blueprints/loan_information.py create mode 100644 app/blueprints/new_transaction_check.py create mode 100644 app/blueprints/notification_callback.py create mode 100644 app/blueprints/penal_charge.py create mode 100644 app/blueprints/provide_loan.py create mode 100644 app/blueprints/rac_check.py create mode 100644 app/blueprints/repayment.py create mode 100644 app/blueprints/revoke_enable_consent.py create mode 100644 app/blueprints/select_offer.py create mode 100644 app/blueprints/sms.py create mode 100644 app/blueprints/token_validation.py create mode 100644 app/blueprints/transaction_verify.py create mode 100644 app/config.py create mode 100644 app/helpers/__pycache__/response_helper.cpython-39.pyc create mode 100644 app/helpers/response_helper.py create mode 100644 app/middlewares/__init__.py create mode 100644 app/middlewares/cors.py create mode 100644 app/middlewares/encryption.py create mode 100644 app/middlewares/request_validator.py create mode 100644 app/middlewares/verify_api_key.py create mode 100644 app/routes/__init__.py create mode 100644 app/routes/__pycache__/__init__.cpython-39.pyc create mode 100644 app/routes/__pycache__/routes.cpython-39.pyc create mode 100644 app/routes/routes.py create mode 100644 app/schemas/__init__.py create mode 100644 app/schemas/__pycache__/__init__.cpython-39.pyc create mode 100644 app/schemas/__pycache__/bulk_sms.cpython-39.pyc create mode 100644 app/schemas/__pycache__/collect_loan.cpython-39.pyc create mode 100644 app/schemas/__pycache__/customer_consent.cpython-39.pyc create mode 100644 app/schemas/__pycache__/disbursement.cpython-39.pyc create mode 100644 app/schemas/__pycache__/eligibility_check.cpython-39.pyc create mode 100644 app/schemas/__pycache__/lien_check.cpython-39.pyc create mode 100644 app/schemas/__pycache__/loan_information.cpython-39.pyc create mode 100644 app/schemas/__pycache__/new_transaction_check.cpython-39.pyc create mode 100644 app/schemas/__pycache__/notification_callback.cpython-39.pyc create mode 100644 app/schemas/__pycache__/penal_charge.cpython-39.pyc create mode 100644 app/schemas/__pycache__/provide_loan.cpython-39.pyc create mode 100644 app/schemas/__pycache__/rac_check.cpython-39.pyc create mode 100644 app/schemas/__pycache__/repayment.cpython-39.pyc create mode 100644 app/schemas/__pycache__/revoke_enable_consent.cpython-39.pyc create mode 100644 app/schemas/__pycache__/select_offer.cpython-39.pyc create mode 100644 app/schemas/__pycache__/sms.cpython-39.pyc create mode 100644 app/schemas/__pycache__/token_validation.cpython-39.pyc create mode 100644 app/schemas/__pycache__/transaction_verify.cpython-39.pyc create mode 100644 app/schemas/bulk_sms.py create mode 100644 app/schemas/collect_loan.py create mode 100644 app/schemas/customer_consent.py create mode 100644 app/schemas/disbursement.py create mode 100644 app/schemas/eligibility_check.py create mode 100644 app/schemas/lien_check.py create mode 100644 app/schemas/loan_information.py create mode 100644 app/schemas/new_transaction_check.py create mode 100644 app/schemas/notification_callback.py create mode 100644 app/schemas/penal_charge.py create mode 100644 app/schemas/provide_loan.py create mode 100644 app/schemas/rac_check.py create mode 100644 app/schemas/repayment.py create mode 100644 app/schemas/revoke_enable_consent.py create mode 100644 app/schemas/select_offer.py create mode 100644 app/schemas/sms.py create mode 100644 app/schemas/token_validation.py create mode 100644 app/schemas/transaction_verify.py create mode 100644 app/static/css/main.css create mode 100644 app/static/js/main.js create mode 100644 app/templates/index.html create mode 100644 app/utils/__pycache__/logger.cpython-39.pyc create mode 100644 app/utils/logger.py create mode 100644 docker-compose.yml create mode 100644 requirements.txt diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..e81c242592628868a41ba832ff3693730f757c78 GIT binary patch literal 6148 zcmeH~JqiLr422WdLa^D=avBfd4F=H@cme-PK|$>2=)U~0;A$-*FOa;MY{If%u`>}7 zT|dq%kzPb*a8p@Y7@1<<$w6*%JKs;2q+Xuer(ih*ggixzBPT3r}OfCNSay4d%2{%_%*=KoO(w?mHq-SE8F0$NjBC^9f!1RMhc34E2n6WFp5 AYybcN literal 0 HcmV?d00001 diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/digifi-BankEmulator.iml b/.idea/digifi-BankEmulator.iml new file mode 100644 index 0000000..c956989 --- /dev/null +++ b/.idea/digifi-BankEmulator.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..6d3cfdc --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/php.xml b/.idea/php.xml new file mode 100644 index 0000000..f324872 --- /dev/null +++ b/.idea/php.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..3e3f6f1 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,21 @@ +# Use an official Python runtime as a parent image +FROM python:3.9-slim + +# Set the working directory in the container +WORKDIR /app + +# Copy the current directory contents into the container at /app +COPY . /app + +# Install dependencies +RUN pip install --no-cache-dir -r requirements.txt + +# Expose port 5000 for the Flask app +EXPOSE 5000 + +# Set environment variables +ENV FLASK_APP=app.py +ENV FLASK_RUN_HOST=0.0.0.0 + +# Run the application +CMD ["flask", "run"] diff --git a/README.md b/README.md new file mode 100644 index 0000000..35a41e4 --- /dev/null +++ b/README.md @@ -0,0 +1,56 @@ +# Setup + +This guide provides instructions on how to set up and run the application. + +## Prerequisites + +Ensure you have the following installed on your system: +- **Docker**: Install Docker from [docker.com](https://www.docker.com/get-started) +- **Docker Compose**: Docker Compose is included with Docker Desktop (for macOS/Windows) or can be installed separately on Linux. + +## Steps to Set Up the Application + +### 1. Clone the Repository + +First, clone the repository to your local machine: + +```bash +git clone https://github.com/username/repository.git +cd repository +``` + +### 2. Run the Application with Docker Compose + +Once you have the repository cloned, you can easily set up and run the application using Docker Compose. Simply execute the following command: + +```bash +docker-compose up --build +``` + +This command will build the Docker image and start the Flask application in a container. By default, the application will be accessible at `http://localhost:5000`. + +### 3. Health Check + +You can check if the Flask application is running by accessing the `/health` endpoint. To perform a health check, run the following command: + +```bash +curl http://localhost:5000/health +``` + +If the application is running properly, you should receive a response similar to this: + +```json +{ + "status": "ok" +} +``` + +### 4. Stop the Application + +To stop the application, use: + +```bash +docker-compose down +``` + +This will stop and remove the containers created by Docker Compose. diff --git a/app.log b/app.log new file mode 100644 index 0000000..386398e --- /dev/null +++ b/app.log @@ -0,0 +1,258 @@ +2025-03-20 14:13:09,801 - INFO - WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on all addresses (0.0.0.0) + * Running on http://127.0.0.1:5000 + * Running on http://192.168.43.192:5000 +2025-03-20 14:13:09,802 - INFO - Press CTRL+C to quit +2025-03-20 14:13:09,804 - INFO - * Restarting with stat +2025-03-20 14:13:10,513 - WARNING - * Debugger is active! +2025-03-20 14:13:10,534 - INFO - * Debugger PIN: 716-001-293 +2025-03-20 14:13:18,851 - INFO - 127.0.0.1 - - [20/Mar/2025 14:13:18] "GET / HTTP/1.1" 404 - +2025-03-20 14:13:19,654 - INFO - 127.0.0.1 - - [20/Mar/2025 14:13:19] "GET /favicon.ico HTTP/1.1" 404 - +2025-03-20 14:14:03,987 - INFO - 127.0.0.1 - - [20/Mar/2025 14:14:03] "GET /EligibilityCheck HTTP/1.1" 405 - +2025-03-20 14:14:14,898 - INFO - 127.0.0.1 - - [20/Mar/2025 14:14:14] "GET /EligibilityCheck HTTP/1.1" 405 - +2025-03-20 14:14:21,063 - INFO - 127.0.0.1 - - [20/Mar/2025 14:14:21] "POST /EligibilityCheck HTTP/1.1" 415 - +2025-03-20 14:14:40,328 - INFO - 127.0.0.1 - - [20/Mar/2025 14:14:40] "POST /EligibilityCheck HTTP/1.1" 415 - +2025-03-20 14:15:52,642 - INFO - EligibilityCheck request received: {'hi': 'bye'} +2025-03-20 14:15:52,643 - INFO - Processing EligibilityCheck request +2025-03-20 14:15:52,647 - ERROR - An error occurred during EligibilityCheck processing: {'income': ['Missing data for required field.'], 'user_id': ['Missing data for required field.'], 'credit_score': ['Missing data for required field.'], 'hi': ['Unknown field.']} +Traceback (most recent call last): + File "/Users/viviandagbue/Documents/livelinessCheckYOLO/app/blueprints/eligibility_check.py", line 23, in process_request + validated_data = schema.load(data) # Raises an error if invalid + File "/Users/viviandagbue/Documents/venv/lib/python3.9/site-packages/marshmallow/schema.py", line 722, in load + return self._do_load( + File "/Users/viviandagbue/Documents/venv/lib/python3.9/site-packages/marshmallow/schema.py", line 909, in _do_load + raise exc +marshmallow.exceptions.ValidationError: {'income': ['Missing data for required field.'], 'user_id': ['Missing data for required field.'], 'credit_score': ['Missing data for required field.'], 'hi': ['Unknown field.']} +2025-03-20 14:15:52,683 - INFO - 127.0.0.1 - - [20/Mar/2025 14:15:52] "POST /EligibilityCheck HTTP/1.1" 200 - +2025-03-20 14:27:54,479 - INFO - * Detected change in '/Users/viviandagbue/Documents/livelinessCheckYOLO/app/routes/main.py', reloading +2025-03-20 14:27:54,721 - INFO - * Restarting with stat +2025-03-20 14:27:56,270 - WARNING - * Debugger is active! +2025-03-20 14:27:56,301 - INFO - * Debugger PIN: 716-001-293 +2025-03-20 14:28:00,496 - INFO - * Detected change in '/Users/viviandagbue/Documents/livelinessCheckYOLO/app/routes/main.py', reloading +2025-03-20 14:28:00,704 - INFO - * Restarting with stat +2025-03-20 14:28:01,613 - WARNING - * Debugger is active! +2025-03-20 14:28:01,691 - INFO - * Debugger PIN: 716-001-293 +2025-03-20 14:28:03,809 - INFO - * Detected change in '/Users/viviandagbue/Documents/livelinessCheckYOLO/app/routes/main.py', reloading +2025-03-20 14:28:04,018 - INFO - * Restarting with stat +2025-03-20 14:28:04,870 - WARNING - * Debugger is active! +2025-03-20 14:28:04,886 - INFO - * Debugger PIN: 716-001-293 +2025-03-20 14:28:07,012 - INFO - * Detected change in '/Users/viviandagbue/Documents/livelinessCheckYOLO/app/routes/main.py', reloading +2025-03-20 14:28:07,200 - INFO - * Restarting with stat +2025-03-20 14:28:08,193 - WARNING - * Debugger is active! +2025-03-20 14:28:08,206 - INFO - * Debugger PIN: 716-001-293 +2025-03-20 14:28:10,289 - INFO - * Detected change in '/Users/viviandagbue/Documents/livelinessCheckYOLO/app/routes/main.py', reloading +2025-03-20 14:28:10,503 - INFO - * Restarting with stat +2025-03-20 14:28:11,437 - WARNING - * Debugger is active! +2025-03-20 14:28:11,456 - INFO - * Debugger PIN: 716-001-293 +2025-03-20 14:28:13,532 - INFO - * Detected change in '/Users/viviandagbue/Documents/livelinessCheckYOLO/app/routes/main.py', reloading +2025-03-20 14:28:13,750 - INFO - * Restarting with stat +2025-03-20 14:28:14,774 - WARNING - * Debugger is active! +2025-03-20 14:28:14,815 - INFO - * Debugger PIN: 716-001-293 +2025-03-20 14:28:17,101 - INFO - * Detected change in '/Users/viviandagbue/Documents/livelinessCheckYOLO/app/routes/main.py', reloading +2025-03-20 14:28:17,262 - INFO - * Restarting with stat +2025-03-20 14:28:18,410 - WARNING - * Debugger is active! +2025-03-20 14:28:18,428 - INFO - * Debugger PIN: 716-001-293 +2025-03-20 14:28:19,512 - INFO - * Detected change in '/Users/viviandagbue/Documents/livelinessCheckYOLO/app/routes/main.py', reloading +2025-03-20 14:28:19,690 - INFO - * Restarting with stat +2025-03-20 14:28:20,801 - WARNING - * Debugger is active! +2025-03-20 14:28:20,936 - INFO - * Debugger PIN: 716-001-293 +2025-03-20 14:28:41,355 - INFO - * Detected change in '/Users/viviandagbue/Documents/livelinessCheckYOLO/app/config.py', reloading +2025-03-20 14:28:41,499 - INFO - * Restarting with stat +2025-03-20 14:28:42,243 - WARNING - * Debugger is active! +2025-03-20 14:28:42,255 - INFO - * Debugger PIN: 716-001-293 +2025-03-20 14:28:45,329 - INFO - * Detected change in '/Users/viviandagbue/Documents/livelinessCheckYOLO/app/config.py', reloading +2025-03-20 14:28:45,519 - INFO - * Restarting with stat +2025-03-20 14:28:46,382 - WARNING - * Debugger is active! +2025-03-20 14:28:46,403 - INFO - * Debugger PIN: 716-001-293 +2025-03-20 14:28:51,552 - INFO - * Detected change in '/Users/viviandagbue/Documents/livelinessCheckYOLO/app/config.py', reloading +2025-03-20 14:28:51,712 - INFO - * Restarting with stat +2025-03-20 14:28:52,758 - WARNING - * Debugger is active! +2025-03-20 14:28:52,792 - INFO - * Debugger PIN: 716-001-293 +2025-03-20 14:29:13,307 - INFO - * Detected change in '/Users/viviandagbue/Documents/livelinessCheckYOLO/app/routes/main.py', reloading +2025-03-20 14:29:13,443 - INFO - * Restarting with stat +2025-03-20 14:29:15,136 - WARNING - * Debugger is active! +2025-03-20 14:29:15,155 - INFO - * Debugger PIN: 716-001-293 +2025-03-20 14:29:24,320 - INFO - * Detected change in '/Users/viviandagbue/Documents/livelinessCheckYOLO/app/routes/main.py', reloading +2025-03-20 14:29:24,462 - INFO - * Restarting with stat +2025-03-20 14:29:25,635 - WARNING - * Debugger is active! +2025-03-20 14:29:25,668 - INFO - * Debugger PIN: 716-001-293 +2025-03-20 14:29:39,423 - INFO - 127.0.0.1 - - [20/Mar/2025 14:29:39] "POST /health HTTP/1.1" 405 - +2025-03-20 14:30:17,001 - INFO - * Detected change in '/Users/viviandagbue/Documents/livelinessCheckYOLO/app/routes/__init__.py', reloading +2025-03-20 14:30:17,444 - INFO - * Restarting with stat +2025-03-20 14:30:20,724 - WARNING - * Debugger is active! +2025-03-20 14:30:20,742 - INFO - * Debugger PIN: 716-001-293 +2025-03-20 14:37:21,240 - INFO - * Detected change in '/Users/viviandagbue/Documents/livelinessCheckYOLO/app/middlewares/__init__.py', reloading +2025-03-20 14:37:21,737 - INFO - * Restarting with stat +2025-03-20 14:37:23,075 - WARNING - * Debugger is active! +2025-03-20 14:37:23,088 - INFO - * Debugger PIN: 716-001-293 +2025-03-20 14:39:23,292 - INFO - * Detected change in '/Users/viviandagbue/Documents/livelinessCheckYOLO/app/middlewares/verify_api_key.py', reloading +2025-03-20 14:39:23,563 - INFO - * Restarting with stat +2025-03-20 14:39:25,229 - WARNING - * Debugger is active! +2025-03-20 14:39:25,254 - INFO - * Debugger PIN: 716-001-293 +2025-03-20 14:39:26,355 - INFO - * Detected change in '/Users/viviandagbue/Documents/livelinessCheckYOLO/app/middlewares/verify_api_key.py', reloading +2025-03-20 14:39:26,517 - INFO - * Restarting with stat +2025-03-20 14:39:27,457 - WARNING - * Debugger is active! +2025-03-20 14:39:27,476 - INFO - * Debugger PIN: 716-001-293 +2025-03-20 14:41:20,187 - INFO - * Detected change in '/Users/viviandagbue/Documents/livelinessCheckYOLO/app/middlewares/encryption.py', reloading +2025-03-20 14:41:20,429 - INFO - * Restarting with stat +2025-03-20 14:41:21,622 - WARNING - * Debugger is active! +2025-03-20 14:41:21,634 - INFO - * Debugger PIN: 716-001-293 +2025-03-20 14:42:03,558 - INFO - * Detected change in '/Users/viviandagbue/Documents/livelinessCheckYOLO/app/middlewares/cors.py', reloading +2025-03-20 14:42:03,709 - INFO - * Restarting with stat +2025-03-20 14:42:05,112 - WARNING - * Debugger is active! +2025-03-20 14:42:05,137 - INFO - * Debugger PIN: 716-001-293 +2025-03-20 14:42:30,565 - INFO - * Detected change in '/Users/viviandagbue/Documents/livelinessCheckYOLO/app/middlewares/cors.py', reloading +2025-03-20 14:42:30,976 - INFO - * Restarting with stat +2025-03-20 14:42:32,051 - WARNING - * Debugger is active! +2025-03-20 14:42:32,065 - INFO - * Debugger PIN: 716-001-293 +2025-03-20 14:43:14,856 - INFO - * Detected change in '/Users/viviandagbue/Documents/livelinessCheckYOLO/app/middlewares/request_validator.py', reloading +2025-03-20 14:43:15,006 - INFO - * Restarting with stat +2025-03-20 14:43:16,050 - WARNING - * Debugger is active! +2025-03-20 14:43:16,062 - INFO - * Debugger PIN: 716-001-293 +2025-03-20 14:44:03,834 - INFO - * Detected change in '/Users/viviandagbue/Documents/livelinessCheckYOLO/app/middlewares/request_validator.py', reloading +2025-03-20 14:44:04,003 - INFO - * Restarting with stat +2025-03-20 14:44:05,142 - WARNING - * Debugger is active! +2025-03-20 14:44:05,182 - INFO - * Debugger PIN: 716-001-293 +2025-03-20 14:44:09,364 - INFO - * Detected change in '/Users/viviandagbue/Documents/livelinessCheckYOLO/app/middlewares/request_validator.py', reloading +2025-03-20 14:44:09,512 - INFO - * Restarting with stat +2025-03-20 14:44:10,512 - WARNING - * Debugger is active! +2025-03-20 14:44:10,548 - INFO - * Debugger PIN: 716-001-293 +2025-03-20 14:44:11,658 - INFO - * Detected change in '/Users/viviandagbue/Documents/livelinessCheckYOLO/app/middlewares/request_validator.py', reloading +2025-03-20 14:44:11,834 - INFO - * Restarting with stat +2025-03-20 14:44:12,885 - WARNING - * Debugger is active! +2025-03-20 14:44:12,904 - INFO - * Debugger PIN: 716-001-293 +2025-03-20 14:44:37,478 - INFO - * Detected change in '/Users/viviandagbue/Documents/livelinessCheckYOLO/app/middlewares/request_validator.py', reloading +2025-03-20 14:44:37,636 - INFO - * Restarting with stat +2025-03-20 14:44:38,872 - WARNING - * Debugger is active! +2025-03-20 14:44:38,889 - INFO - * Debugger PIN: 716-001-293 +2025-03-20 14:44:52,116 - INFO - * Detected change in '/Users/viviandagbue/Documents/livelinessCheckYOLO/app/middlewares/request_validator.py', reloading +2025-03-20 14:44:52,260 - INFO - * Restarting with stat +2025-03-20 14:44:53,048 - WARNING - * Debugger is active! +2025-03-20 14:44:53,061 - INFO - * Debugger PIN: 716-001-293 +2025-03-20 14:44:54,180 - INFO - * Detected change in '/Users/viviandagbue/Documents/livelinessCheckYOLO/app/middlewares/request_validator.py', reloading +2025-03-20 14:44:54,328 - INFO - * Restarting with stat +2025-03-20 14:44:55,536 - WARNING - * Debugger is active! +2025-03-20 14:44:55,549 - INFO - * Debugger PIN: 716-001-293 +2025-03-20 14:44:56,596 - INFO - * Detected change in '/Users/viviandagbue/Documents/livelinessCheckYOLO/app/middlewares/request_validator.py', reloading +2025-03-20 14:44:56,745 - INFO - * Restarting with stat +2025-03-20 14:44:57,615 - WARNING - * Debugger is active! +2025-03-20 14:44:57,627 - INFO - * Debugger PIN: 716-001-293 +2025-03-20 14:44:59,795 - INFO - * Detected change in '/Users/viviandagbue/Documents/livelinessCheckYOLO/app/middlewares/request_validator.py', reloading +2025-03-20 14:45:00,013 - INFO - * Restarting with stat +2025-03-20 14:45:00,839 - WARNING - * Debugger is active! +2025-03-20 14:45:00,854 - INFO - * Debugger PIN: 716-001-293 +2025-03-20 14:45:05,953 - INFO - * Detected change in '/Users/viviandagbue/Documents/livelinessCheckYOLO/app/middlewares/request_validator.py', reloading +2025-03-20 14:45:06,143 - INFO - * Restarting with stat +2025-03-20 14:45:06,976 - WARNING - * Debugger is active! +2025-03-20 14:45:06,992 - INFO - * Debugger PIN: 716-001-293 +2025-03-20 14:45:09,077 - INFO - * Detected change in '/Users/viviandagbue/Documents/livelinessCheckYOLO/app/middlewares/request_validator.py', reloading +2025-03-20 14:45:09,366 - INFO - * Restarting with stat +2025-03-20 14:45:10,260 - WARNING - * Debugger is active! +2025-03-20 14:45:10,491 - INFO - * Debugger PIN: 716-001-293 +2025-03-20 14:45:11,571 - INFO - * Detected change in '/Users/viviandagbue/Documents/livelinessCheckYOLO/app/middlewares/request_validator.py', reloading +2025-03-20 14:45:11,748 - INFO - * Restarting with stat +2025-03-20 14:45:12,615 - WARNING - * Debugger is active! +2025-03-20 14:45:12,631 - INFO - * Debugger PIN: 716-001-293 +2025-03-20 14:45:14,690 - INFO - * Detected change in '/Users/viviandagbue/Documents/livelinessCheckYOLO/app/middlewares/request_validator.py', reloading +2025-03-20 14:45:14,821 - INFO - * Restarting with stat +2025-03-20 14:45:15,576 - WARNING - * Debugger is active! +2025-03-20 14:45:15,597 - INFO - * Debugger PIN: 716-001-293 +2025-03-20 14:47:55,793 - INFO - * Detected change in '/Users/viviandagbue/Documents/livelinessCheckYOLO/app/__init__.py', reloading +2025-03-20 14:47:56,010 - INFO - * Restarting with stat +2025-03-20 14:48:23,203 - INFO - WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on all addresses (0.0.0.0) + * Running on http://127.0.0.1:5000 + * Running on http://192.168.43.192:5000 +2025-03-20 14:48:23,204 - INFO - Press CTRL+C to quit +2025-03-20 14:48:23,208 - INFO - * Restarting with stat +2025-03-20 14:48:25,258 - WARNING - * Debugger is active! +2025-03-20 14:48:25,331 - INFO - * Debugger PIN: 716-001-293 +2025-03-20 14:48:30,183 - INFO - 127.0.0.1 - - [20/Mar/2025 14:48:30] "POST /api/health HTTP/1.1" 405 - +2025-03-20 14:48:35,969 - INFO - 127.0.0.1 - - [20/Mar/2025 14:48:35] "GET /api/health HTTP/1.1" 200 - +2025-03-20 14:53:13,504 - INFO - * Detected change in '/Users/viviandagbue/Documents/livelinessCheckYOLO/app/schemas/eligibility_check.py', reloading +2025-03-20 14:53:14,721 - INFO - * Restarting with stat +2025-03-20 14:53:20,279 - WARNING - * Debugger is active! +2025-03-20 14:53:20,384 - INFO - * Debugger PIN: 716-001-293 +2025-03-20 14:54:16,579 - INFO - * Detected change in '/Users/viviandagbue/Documents/livelinessCheckYOLO/app/blueprints/eligibility_check.py', reloading +2025-03-20 14:54:17,245 - INFO - * Restarting with stat +2025-03-20 14:54:21,211 - WARNING - * Debugger is active! +2025-03-20 14:54:21,298 - INFO - * Debugger PIN: 716-001-293 +2025-03-20 14:54:32,913 - INFO - * Detected change in '/Users/viviandagbue/Documents/livelinessCheckYOLO/app/blueprints/eligibility_check.py', reloading +2025-03-20 14:54:33,587 - INFO - * Restarting with stat +2025-03-20 14:54:36,684 - WARNING - * Debugger is active! +2025-03-20 14:54:36,850 - INFO - * Debugger PIN: 716-001-293 +2025-03-20 14:54:38,191 - INFO - * Detected change in '/Users/viviandagbue/Documents/livelinessCheckYOLO/app/blueprints/eligibility_check.py', reloading +2025-03-20 14:54:38,906 - INFO - * Restarting with stat +2025-03-20 14:54:44,502 - WARNING - * Debugger is active! +2025-03-20 14:54:44,539 - INFO - * Debugger PIN: 716-001-293 +2025-03-20 14:55:04,187 - INFO - 127.0.0.1 - - [20/Mar/2025 14:55:04] "GET /api/EligibilityCheck HTTP/1.1" 405 - +2025-03-20 14:55:10,064 - INFO - Processing EligibilityCheck request +2025-03-20 14:55:10,073 - ERROR - An error occurred during EligibilityCheck processing: {'lien_amount': ['Missing data for required field.'], 'lienAmount': ['Unknown field.']} +Traceback (most recent call last): + File "/Users/viviandagbue/Documents/livelinessCheckYOLO/app/blueprints/eligibility_check.py", line 23, in process_request + validated_data = schema.load(data) # Raises an error if invalid + File "/Users/viviandagbue/Documents/venv/lib/python3.9/site-packages/marshmallow/schema.py", line 722, in load + return self._do_load( + File "/Users/viviandagbue/Documents/venv/lib/python3.9/site-packages/marshmallow/schema.py", line 909, in _do_load + raise exc +marshmallow.exceptions.ValidationError: {'lien_amount': ['Missing data for required field.'], 'lienAmount': ['Unknown field.']} +2025-03-20 14:55:10,103 - INFO - 127.0.0.1 - - [20/Mar/2025 14:55:10] "POST /api/EligibilityCheck HTTP/1.1" 200 - +2025-03-20 14:55:37,613 - INFO - * Detected change in '/Users/viviandagbue/Documents/livelinessCheckYOLO/app/schemas/eligibility_check.py', reloading +2025-03-20 14:55:38,428 - INFO - * Restarting with stat +2025-03-20 14:55:47,297 - WARNING - * Debugger is active! +2025-03-20 14:55:47,413 - INFO - * Debugger PIN: 716-001-293 +2025-03-20 14:55:51,943 - INFO - * Detected change in '/Users/viviandagbue/Documents/livelinessCheckYOLO/app/schemas/eligibility_check.py', reloading +2025-03-20 14:55:52,455 - INFO - * Restarting with stat +2025-03-20 14:55:56,940 - WARNING - * Debugger is active! +2025-03-20 14:55:56,968 - INFO - * Debugger PIN: 716-001-293 +2025-03-20 14:56:01,718 - INFO - Processing EligibilityCheck request +2025-03-20 14:56:01,737 - INFO - 127.0.0.1 - - [20/Mar/2025 14:56:01] "POST /api/EligibilityCheck HTTP/1.1" 200 - +2025-03-20 14:59:24,543 - INFO - * Detected change in '/Users/viviandagbue/Documents/livelinessCheckYOLO/app/schemas/select_offer.py', reloading +2025-03-20 14:59:25,271 - INFO - * Restarting with stat +2025-03-20 14:59:31,688 - WARNING - * Debugger is active! +2025-03-20 14:59:31,738 - INFO - * Debugger PIN: 716-001-293 +2025-03-20 15:00:44,032 - INFO - * Detected change in '/Users/viviandagbue/Documents/livelinessCheckYOLO/app/blueprints/select_offer.py', reloading +2025-03-20 15:00:44,863 - INFO - * Restarting with stat +2025-03-20 15:00:52,010 - WARNING - * Debugger is active! +2025-03-20 15:00:52,134 - INFO - * Debugger PIN: 716-001-293 +2025-03-20 15:00:53,368 - INFO - * Detected change in '/Users/viviandagbue/Documents/livelinessCheckYOLO/app/blueprints/select_offer.py', reloading +2025-03-20 15:00:53,663 - INFO - * Restarting with stat +2025-03-20 17:05:48,612 - INFO - WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on all addresses (0.0.0.0) + * Running on http://127.0.0.1:5000 + * Running on http://192.168.43.192:5000 +2025-03-20 17:05:48,613 - INFO - Press CTRL+C to quit +2025-03-20 17:05:48,614 - INFO - * Restarting with stat +2025-03-20 17:05:49,355 - WARNING - * Debugger is active! +2025-03-20 17:05:49,373 - INFO - * Debugger PIN: 716-001-293 +2025-03-20 17:05:52,329 - INFO - Processing EligibilityCheck request +2025-03-20 17:05:52,333 - INFO - 127.0.0.1 - - [20/Mar/2025 17:05:52] "POST /api/EligibilityCheck HTTP/1.1" 200 - +2025-03-20 17:16:19,388 - INFO - WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on http://127.0.0.1:5000 +2025-03-20 17:16:19,388 - INFO - Press CTRL+C to quit +2025-03-20 17:17:44,903 - INFO - Processing SelectOffer request +2025-03-20 17:17:44,908 - ERROR - Validation Error: {'productId': ['Missing data for required field.'], 'requestId': ['Missing data for required field.'], 'requestedAmount': ['Missing data for required field.'], 'countryCode': ['Unknown field.'], 'lienAmount': ['Unknown field.'], '$type': ['Unknown field.']} +2025-03-20 17:17:44,916 - INFO - 127.0.0.1 - - [20/Mar/2025 17:17:44] "POST /api/SelectOffer HTTP/1.1" 200 - +2025-03-20 17:45:41,511 - INFO - WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on http://127.0.0.1:5000 +2025-03-20 17:45:41,513 - INFO - Press CTRL+C to quit +2025-03-20 17:51:07,000 - INFO - WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on all addresses (0.0.0.0) + * Running on http://127.0.0.1:5000 + * Running on http://172.20.0.2:5000 +2025-03-20 17:51:07,001 - INFO - Press CTRL+C to quit +2025-03-20 17:51:28,286 - INFO - 172.20.0.1 - - [20/Mar/2025 17:51:28] "GET / HTTP/1.1" 404 - +2025-03-20 17:51:28,603 - INFO - 172.20.0.1 - - [20/Mar/2025 17:51:28] "GET /favicon.ico HTTP/1.1" 404 - +2025-03-20 17:51:38,063 - INFO - 172.20.0.1 - - [20/Mar/2025 17:51:38] "GET /api/health HTTP/1.1" 200 - +2025-03-20 17:52:29,740 - INFO - 172.20.0.1 - - [20/Mar/2025 17:52:29] "GET /health HTTP/1.1" 404 - +2025-03-20 17:53:14,937 - INFO - WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on all addresses (0.0.0.0) + * Running on http://127.0.0.1:5000 + * Running on http://172.20.0.2:5000 +2025-03-20 17:53:14,938 - INFO - Press CTRL+C to quit +2025-03-20 17:53:49,208 - INFO - 172.20.0.1 - - [20/Mar/2025 17:53:49] "GET /health HTTP/1.1" 200 - +2025-03-21 00:36:08,145 - INFO - WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on all addresses (0.0.0.0) + * Running on http://127.0.0.1:5000 + * Running on http://172.23.0.2:5000 +2025-03-21 00:36:08,146 - INFO - Press CTRL+C to quit diff --git a/app.py b/app.py new file mode 100644 index 0000000..f61366d --- /dev/null +++ b/app.py @@ -0,0 +1,6 @@ +from app import create_app + +app = create_app() + +if __name__ == "__main__": + app.run(host="0.0.0.0", port=5000, debug=True) diff --git a/app/__init__.py b/app/__init__.py new file mode 100644 index 0000000..179b2a7 --- /dev/null +++ b/app/__init__.py @@ -0,0 +1,19 @@ +from flask import Flask +from flask_cors import CORS +from app.config import Config +from app.routes import api + +def create_app(): + """ Factory function to create a Flask app instance """ + app = Flask(__name__) + + # Load configuration + app.config.from_object(Config) + + + CORS(app) + + # Register blueprints + app.register_blueprint(api) + + return app diff --git a/app/__pycache__/__init__.cpython-39.pyc b/app/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2a7bdd5b9732b6df32ad2ed5bdc0d187bd32b819 GIT binary patch literal 535 zcmYk3y-ve05XbE}U!?7VcmS_qC}Lo%5K^U%Aki(0 z{Tm=50=@*-mkhJ;h+%@ban#pQ?;$?+w*KqvAd)M&Ji5A`-Ot~l%h^8B&;AKtN-;Pcx6hsA kZv50|Ou5L$sZz)wolo+Ae^9$T7xJ^g{yaYEk literal 0 HcmV?d00001 diff --git a/app/blueprints/__init__.py b/app/blueprints/__init__.py new file mode 100644 index 0000000..fcdb467 --- /dev/null +++ b/app/blueprints/__init__.py @@ -0,0 +1,18 @@ +from app.blueprints.eligibility_check import EligibilityCheckService +from app.blueprints.select_offer import SelectOfferService +from app.blueprints.provide_loan import ProvideLoanService +from app.blueprints.loan_information import LoanInformationService +from app.blueprints.repayment import RepaymentService +from app.blueprints.customer_consent import CustomerConsentService +from app.blueprints.notification_callback import NotificationCallbackService +from app.blueprints.rac_check import RACCheckService +from app.blueprints.disbursement import DisbursementService +from app.blueprints.collect_loan import CollectLoanService +from app.blueprints.transaction_verify import TransactionVerifyService +from app.blueprints.penal_charge import PenalChargeService +from app.blueprints.revoke_enable_consent import RevokeEnableConsentService +from app.blueprints.token_validation import TokenValidationService +from app.blueprints.lien_check import LienCheckService +from app.blueprints.new_transaction_check import NewTransactionCheckService +from app.blueprints.sms import SMSService +from app.blueprints.bulk_sms import BulkSMSService diff --git a/app/blueprints/__pycache__/__init__.cpython-39.pyc b/app/blueprints/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..92cc8b7fe79e95ea0b93ba5b1b6ea30ab8f8adb6 GIT binary patch literal 1441 zcmah|yKdVs6qRB-e!pbLcI>9kYsr#K-HM{EnxR0N26m8v3qi|tLgZ2sDA|EOscSzc z$kwU9(5WQtMhat~GDIKp9Fgbh9+7IbWXbE@&!^icYgxb4Bz9UI1d$GMOBRpP~&w}*SH7` z-b77}OW^PpYH3`CHs3)z8dsphyQr&i6?%LZ?P^?uJ|Ccg#&vkZ_t2ik4S36kXsB@$ z_W1!i(Aa@PeuR!BZjG&z_HS9Cbz(nu&w<^t2?G`{-8<^tkEvKNk4nlJQ=ne_eKw;a zwR$Cj1)I`~fcVL3M@_%_vp{eXvmnJZMl>W#PW?EgcIQzXa4Ouuk6ti?%OGYm=IKpb z0x%(IOZCww*KpbT!lKDsMD*nmjvIh-|I%jfS`a@Xp0apD1)D7sQ_dCj3AlGe+|p#V zKcb7^o}T+;0`!&L&b8$H8v<-YL(ub4*gDs7}jgxb0pXYF&tX!TPqkR&F zrxTddP%uA^PHECP?5WlXm0vV4WRdP74pbXOW|JaZbrgf_p^)E&g@(o+ioq;tugED? z&rpefofg}hHu2U?$KI;zBCk}(#wLC$0N7h6KomBfN4$06MMay1PgB7V>cfAf@KzaF z6<$_~1H(<6gIlI#ylwB?^3{tVBgRNqM= g&#=r0i!nY8myd<_Jebabez4^;(V-kF_Wyo=0WqS*djJ3c literal 0 HcmV?d00001 diff --git a/app/blueprints/__pycache__/bulk_sms.cpython-39.pyc b/app/blueprints/__pycache__/bulk_sms.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d1992902596940ffd54a80549187bb036e439ed8 GIT binary patch literal 1407 zcmZ8h&yUnL6t)v5lgZA^qJ;xm+LB}E66S)0MyTq75cI&RVo)n`i9&2<0{MmQENnEx zWw|2tUMejYB(D7%dZ_v*SiSYcy{D>3eRgK2!#c9_-izPo_rCX>WH|H@jO)MtIX`s~ z`b#U@!v^JBnBo>J6j7X`0!J8Y*~+cLj%*{_IVqgTF*3>Bq8Ifrx{Iho-Pee^CsyRq z-VBYskAQ>5Snd9hWZ-4@1wl^dKpB>p>Iwx4;z?p+L7Q~~m zhIG_@`3%i@v7Sx~cAimdshZ_kXhmrrV}L zyZ2prq+2k^%DRyzhwLZlhq0}CMdzn>I{Y}*2RH_@y?9UtMhdDlZ8&Fi8bHf>>}48f z<&&!2hYeXN884Ik+|KN8eSpt(yDFl0bH|8zl~8Vh$al5wtpgC8XtjUmWy)%ORg^6x zAG^G-&8{$)L5)lviDWc3tPIa+?Nl40RhWH1{0eyebM;zM*JeJ>8&-1&Ra|=veGrR6 zg!Qr7t-A(^*X8YjmzcnUb}a1MF7}Cq&GWB2bgdEI!^G(1)4n^xm)Dy6sr~Bn+ZLV! zJ{&vBi{mmWSR5-qj*E&mxt0fU{Jcr>^_PLJLY5XxE-K2ugg{ItTgw;PrthzwLUZOp zM8yo>OR fHB&@$+Ez@OY_0=S-pwwtZ}i_rc(4qxd942c&+&GL literal 0 HcmV?d00001 diff --git a/app/blueprints/__pycache__/collect_loan.cpython-39.pyc b/app/blueprints/__pycache__/collect_loan.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6a873b21dd4365f26d44d2ad40756e3bed7537a4 GIT binary patch literal 1729 zcmZuy&2Jnv6u0MVXJ>blCM^(xKqENNy=-R#w^4m26~2Zic3o&CJT}(AKh*Ie91SXxYx(yc>2g`VtX` zbe%iGeTT@^ zGwZQ6X}^I&aZi2+6o`GjEfCmT6Wvq$94j317J2)r0sbkhcgR|^@;@~Dv8~bI8=wGi zAsX-2FFe0(U$Ziyocm%%{e=+uEoUR|V&^`a^4*J>G6y1K|2jz%G2Hc!0H8I}3k#Na zsi;_S*%-`r{e7Q{xF9hjX-x?LG#N0uIH&%g^)M}_Z*nlSj)GuAb~m;@8Si|$xfMwF z;Ba$eyuG;$YkNtx%g5YmHSRhc*_36(32 z`~~F+OXtdsdiB3j^L4H5`t|DV;-M-)KP~2!&@`n#K`e%*?BsBp7>?}$A)?|@qHt}h zs`RkFx?lKO^vfiv7^4IThmv*aqa;d;jq!C#5# z3&cJKw0}Oj5zptk-^(gGXW%Qp(MCT4M~cz>L@om}iq)u1WOy_ACRx z@%PfX<`_$O6;k9<=ysAH3sI5u!{ERLgb~v=98%0H&(B#T*ql#@NUXcM3Olt?Y7Ac(G??!u?_p53KC*pcuwOj6Oir zr-R3c4z8Uzq`?ABhra_RnqoZ=>_Nk%0`c(El;<>6c~#yOq7q>8d9_?JBTX)ttgBM8 z4;inm<)yPms;Xe(%$Q5H$X2XKGlRQ3wprW+D?>4+D0Y}jF?AlJYaA2mLOevjybs63 zWuCDN?|=bI$Z&QBQ~nG~`-VbKF~!e-73Ex`4PN7??ixMA8;3g7rQ};@(Ke+E1o{WtKPr%1VpCdZlkVjdzn^Gr=oqE7&%cd{RLJTI7PM7i$_ZYR-cBvo3{RM5O- z6ac!)&$j<*U+JUF%jN$Csh#<}y>ZcXSCmVD$yk2L=+x06KsH>RRg~!$JNbPkGm+P( z`P!p@8!@t-coMZoAC;G;J)^v=8)cKpzlPqJx_VFmU+I$R5rnN8nPdjnW2T4C+Z(54 zWW}h;vPK9-ffi`!fIZBTyu4Sn$FL(W6%%F3_qOJLm|ui~K(A?Hb_>&kn(!*6!nC#+ z8$IYlkis+S@b1Hm*<7?Mm6-aPn6hYJ+B2djHkDK{nppezABsEGg?Ke)-y?AhXn($T zC#`Eczu*n41;i@vWc}fTWbNsj)mQqk?s_NbOFjf|`6euA>ZW-GEmH?(SR0j~3wVY2!+PlY7`K55cu17^Glc8V zS)+0;XWd=Z)#{cpuk2MNXIpkVgRN0~+wAD-A!qw++A7-!cH%U}5GGh1ywfH7uDP6~ LxseWV|G0kxhOV1C literal 0 HcmV?d00001 diff --git a/app/blueprints/__pycache__/disbursement.cpython-39.pyc b/app/blueprints/__pycache__/disbursement.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2e21d52f89b65398add7a88a87b4e72444d1f2ba GIT binary patch literal 1813 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 literal 0 HcmV?d00001 diff --git a/app/blueprints/__pycache__/loan_information.cpython-39.pyc b/app/blueprints/__pycache__/loan_information.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..61a80270925d5d9a44d1f202bee6a14ef3fe1bf6 GIT binary patch literal 1760 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 I5BCrG8%P7CCIA2c literal 0 HcmV?d00001 diff --git a/app/blueprints/__pycache__/penal_charge.cpython-39.pyc b/app/blueprints/__pycache__/penal_charge.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ef8c78353ed2dd7a567ebc3e684a213b6240a9fd GIT binary patch literal 1481 zcmZux&yU$Bs_GyRxkPE~ok?+i)OJcY zn(1YGt;8LW+GDSrIdI~Su()yJ&IzfAXD19{g(Exfz4(23?|aWqr_&I@c<;wwS3mg( z{brQma6$P9raXs*B8oXGaE!5$9qtrv>{{97r0`~ka<3TZw#~6Krs82^v z5FK4PaY%zDnvH)4Ofr|}RGF!7E%?##n>@qk4D?>4+D0Y}jF?F7xD;yK*LM%id4xyNMm}e}* z10Y}r8LrM?%3ok<-%;opruaFqp`0tU#T)$0-Js`q>rjWflzi`O-HrPtS|WlJxfB>} z2}bP#{510l1L)LHi<%`f*288Iv+nYZeXYi2C}> z!`;qPv63e{&A3C+(&%lPXKHp5eGUMW2w+F2~xH*DllW}70S>|U+_Og5QG?V{oQa^~m|z#Fd4D$4YYPX3t5 zOysp`z4q|mHjHc^oKSuv`ztPz4yAPCwyV2`pSFE6V07DQy}e2drI`aO(g|p5^JCSLw2XS5U?u9&cDJAXa&+Hb*b%Xr9-X`k?N*C+Q1622c4eENJ53(DiXh9PIq%Pkd(| zA7X+*!{p_$zmJc4&41axIq17(cMRv(fX`>14w9ry3zj50Op>Ca4L9;$l00Zq-hbIM z#mKXQsdYugYY>RdX0Y5ewh=4=Olc?%ASP=$pG~xP!BhFQaKNr7MJnXFNI9>*v9#+~ zfRlNnaxUlH4b|1?rZKPW1tsS@Ry%{OO?+f_bajyPA&a)M_V0rjO#y@nR0kh)iGFCV MC28)WJ={O;-#CVn+5i9m literal 0 HcmV?d00001 diff --git a/app/blueprints/__pycache__/provide_loan.cpython-39.pyc b/app/blueprints/__pycache__/provide_loan.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ab5f7a7e65d577955634d01c8cbaa78520190139 GIT binary patch literal 1609 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 H_c{LpEWC(n literal 0 HcmV?d00001 diff --git a/app/blueprints/__pycache__/revoke_enable_consent.cpython-39.pyc b/app/blueprints/__pycache__/revoke_enable_consent.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..20451d740f3158c3b3bee20f8c83ddaec9fa260d GIT binary patch literal 1605 zcmaJ>&2Jnv6u0MVXJ?a5yIe|oV0u9;LfcIw+B8ClC`8c9s>&h|a~Vy??(VenRofGi z(QYs4wNmc|sXcOpKY|l~fyIpz_nttNc=k+}C5q~iJ?}mH{ro;1ZEku9#;3pjF=HKs z{#4BRFoF33CZE7U5Wy+RaDcI*jnv4@z|^#vTA3Z#nzm9W>jWK5+o_v%gDys2A>xqE zQ$#xFM&J>5iiX|4022+d>@fPcqFeyE^=*_UBoaxHA2C)i5IJcvn^8sek0~#UoYSvq zT58Gr$MmFlM33_5eo7D3RxYOToMut1u-C`RlWQQQD8K{-1~myL##3~T1B;kolO?w= zgQV;viD``203D!J><(e_-(ji80=>ipzk=up;~XvV0>3mD=oMZX#2_ZIh^=DqqrEg2 z=67g{EF`S6Q;e1tZnSOUF06;n3-ptrQ92K8(j}gfk=`@VanSdOY%HxO*0lYP6^eWI z8=ye+>pg)$XH#?+_BmEKq))b<8DNpZxAg#agP-- z<=hu@>R%EZzs=srTWK7y8Q))V3Iz8=(wzQ;sIJvdEvv+Gxdb{JD+k3k^x9{AM z#&{fbrd6y0F|?&6&Ppo1xZff_ zam5%V5MF2%m!8I9l0PWwZ8(wSg0ehHFC0x?t1CQaLG?;Yoo1@4rIi*DVX9%+K=E#C z0%x{jyGN%n)xJoR3pRA5rP3=Mt~*cmwJVXJB-HZFx2bAa6YQ0k{*2fMfcEz%H>0xD z^L|>f!ZH^T676{YRYhRRVA zXH?7!!ft?B+R3%WS2B2MkqeWn$h%;jmQ02{X+KCK{)ibMm%S`ve4a&VTKu51A+JEF zMwLiXK58DtCUw`9c&^_>K3WC45$Kc!H`IwHJ?eydGcN>JXI`7JW~@pO^OY^`XN||7 Ot0$1FcW(o?pZPEEGQZ>i literal 0 HcmV?d00001 diff --git a/app/blueprints/__pycache__/select_offer.cpython-39.pyc b/app/blueprints/__pycache__/select_offer.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5edb8ddf9f09ba5adb97f01285890c34b116765a GIT binary patch literal 2244 zcma)8OK%)S5bmDWKI~n`&I2cbuz3JkkT$LU2O}E|DTp-81VfRu1%R_f%C^eO+Bu z-LrP3QYP4DzxlcQ&ln-U3TC*e$h5IX%Qyf5>Jgt>lnPq$6koMeNvodb>y|EQ%`^Oh zRgkpq75$P`qU0?C1{7`(C~PZM8H%fb-WaUOCM z8D7}!GC@^WSkw!Hh`r6co|KHQGLJd&yK8HVuR80@w;h2w+;(;_iBd)^3dB;F3KWzZ zWSd$VsOXE9P7Fgae${oDLx+G2qz_;}gFSi;XZrVIa*qQ25~C3-+hmJw(tGMA`I2ra zpnwV*=wi=Mgc6it1V-gkfhtsBtV>}JKI3<*Fadi}W8ZEK*xyy)06ZggCgGseIW&yY zC2;sF3eUoGLZ_?35qKVIcZ*x_ffu&4>)LAmHU&!YHr<#<9L#TjJ|<}OVqDtPx2Zsam*C`01!Ex4 z>hN+l^B+9@x-RkXZ4wiaG2*ZE@0V+NyTC(-MNutYXSJa;)bhG$ly@|jcsE+y;e>)8~!IFKA^KPuzut*K^nYJjl$L%UwJ3ZXa~ z=OHcicnFCTBWc)vm;|v^O?qoQ4B`v+hR={6b%U7U?&B3ZW>(d=1G~$_jN~g%_%|7| zDsB)Z+zuQjXVEri#bn{Cof(yoSjtvA$e9!R2nP&SST9@iEUpw;<7gb)o+sAIwfDs# zN_O;esotEMo0%1Hn#<#aq3VmRPfs;lV(V+ze*5^_c24}*wv1-0g`pQl3j?DQ?Yhc~ z16vH6u+4GI=2UB;`_sdR58t``b-Vl7ojZ5FyY--*DyEs1R})86>RRH}i`M=yiDl(r zS)65;2CkR#3dBfPVvJB^JK{b=w(KNP9Qs*4Y{wB1cRD_vX;09joz9iS_VSrgk#g7ZS-c(sKaSg#F&i3=30VOL z(Ib(_BJM;=mg^&_zUJA{hg?B(*qCEo%yTlRu2ckel(co)9{3Xs^|(FA;s^l;-`QpABXN3Sf12eHO{(tr6M zn)AXjXz-qsIp9Lp$L*CL?8aBJN`+E^UW)*z2&8z{2&F@RzvEP{)MBrp8x$xBKIU1f zXN%x5IPRRxhXc)ePzS_Iqi*IWLdd746Gw>e}if~ zK7-mZ45ae`NjK_l#YHsA)Uru2p(jlqjRf}4L(r0yXiwhvR$!WI5ws@{%5&_vAS>%e zoA|RIpbchrHz<0)?x@F)Q#hb=3}pM^X&D$PsM55NQt(9p9qNhaX`Gc$s&)z+vQi6K zCi%6U*AhP$O>#Jozlv_O<&)V*B*N-|W3_nxPs#+%XEO3RrmhiG<{>HltG z@<_Dcv0-KWAEmu&L$nO)Soz%6N-{p;{C4{POt3rkKMR#2HEfMdk>Vbor z!$OWM>f0{$nMKX>k2`j)2|c0A=AW~DlS0IC=f`nT z@g_%k6vxk+B;S1*A^BNa2)(Yjyahp+1P+!d+VHjxU@D}_gJ_K9Y&PtUibSe)k>q*x zy&<>z08$Sdo#iU*e=U6_ZlQT?{wylolh+$e1)^iz(bKI$Aj_1^9CQ#4(kn5h&ik~_ P=`;MTC}eR&&13%ynvrQP literal 0 HcmV?d00001 diff --git a/app/blueprints/__pycache__/token_validation.cpython-39.pyc b/app/blueprints/__pycache__/token_validation.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8c27e419f70d9aa4e146b591cf586a00ae297397 GIT binary patch literal 1616 zcmZ`(&5ztP6u0v=GntPq%ZJLAL*M|+WjYiJ)d(S$s)8Q6pq*6_xkM)R&ZKpcshzaD z(M&H}ajV1~klG^$F8mRk_zNsmRf4 zS_u8AnayDX^C?Vu01HJFbClu`V@+G!O6|}#w9TE=4P8S!+)G9uhFG-(yH_$<0J(FU@Pv{g$+H_m!m>(aOQKwoBVf=Z^OS`pGgVtvfF5P+!Yv_c7>r=o>_PE9apzZT@48(mwqSP$25n zwE}@ozvwL8GpupI^5C%r9%-x{dZFHV0nL5r8Z`Ki(HL+c1}{~=_(3zi5_!y|42n4m zwgMA085{ZQ)B9p3Cu>eaKpIK#E=}TMI0=pbq){>(8=8l#C`God3~G~LKad5aE7hB%A%8lSkDxZM~FC1Yh#$CT+y#K+~4?h~ZVSj(~boj~(>@C0~GZyyFtM+*BvjtKH{?9GL2#1l8`%?7nV^AjuYGVM3RD5A`^-RV%G6s4fJlC}k9b0p&8? zHwV?r`&nSfAdllx2u3FX^iZ3f#8HwR=hYq@NV0;7jPR|K$=}+7FB~Xu<>=Ewm%Va$ zPN>k`Bzl@}Hy%jgYPNIZBxVcUyULb@7<$UlHK06c+ENY7O>|%*Bk!N*vu15@S7G)u z60ZZ=Kkr;7i-nor@{%nC_$n_Ky5FM)2MmoCr)p z{p$@pYk+sLVC7Z?%8GeT#U*gb+-+mo(=vE@Q5&Yz0dIq6MshIhD)*QZc~@8Z(K^Ppz+@_XL!YR#B1hXXHMuq(Z1|~DBDAxr T{c(NSPxPxO^;6ix&1e4yPOicn literal 0 HcmV?d00001 diff --git a/app/blueprints/__pycache__/transaction_verify.cpython-39.pyc b/app/blueprints/__pycache__/transaction_verify.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ddd910a6b4b953ce206f16e7e36d2d5425d2da30 GIT binary patch literal 1717 zcmZ`(&2QX96rb_edYxp`q##0(ijgYRy=-^WHccXg8bX0wR;XJQ;fv*ZCcAF^v7H$& zT`S2Yy;kB5NbQjm5`PCLjw5cIxN|^Am3TAWY`2LR%i}lC^M3Pt@6CJB%8G|z-2U~C z;hKxkpK7rlBkIf_V8#F)`64vwtqhkxBstXu4Svs;FJ3pYG42{)zY?EcOqI9J70&E=gEh4MO z)`>NkeY*<9o&E+W5dUghAh5Y4nn(62RybshtiLdTm%_SC-l_k*g=U}F8V$Z()CF9K z#k3jy+H+3R@o{d;W4cjqew0x6CB>m-Rqd)I#eFf$|lW5M!1 z6(!5h8-v-dzvpui9o^!r6s0g8n`*!_X#~9(u+$jii|Rln^7DWWiAd#&>FL1 zk`PMvGNlPtaky4-phS0`b8XE6)9U+Yj;M0Gg~d2_nyU6`yx#)Slf}7 zx=_;Ly4qz+yAlaXLakppyBzh>1ili}pAq{I(Ee@Th{j|6K1xeEX26x-5N{euIMJP? zGoH#-h`h>JIJ>S*u;<$_p(O)*rh`4pz;8YOIBSm4!t2=5dh7KSr-e6WE9Z4}{X+c= z>ITy}cT~S^OE(PjD5GI0y)euQQl?5?4#THql+OMvt0X0HM#ZQg>?5G2y5TsY;M?H~xrsv)OK{M5rQ;JeirD-^`o$e(%}y zYzzz(B)DGw`OnoyrzGibY@{z08kgaAFF`>iDp#eNT#;q0D^;bYR@9nS(Q27Wrk1T_ zYq?5Jmfn)6O0|0u)$S^lJk2ahrR)~qrIH-v-gh0_T3Ih;0_|PP^#bh;%k+ZG4cl_; zAb-8?!GEI~sB`wZiJ0_-vR;PUtt*nmq>4vL>3utQxLMRvb>2eXB|>dzHAJVN-)sYlBhp_Tu$2;n5T>v1~FAh!1AP zEak0Vf!U>OkOhqAyFsqTT-R7-fkq9_2#$o^y6I3BWEkfT53-zjKDW&bAmb|w6}+#& z?an~akQ!=3Zh)WoG9PXzwqi*QO|(cu!xmLGGaqLf=%=c25nM|Mgj;(Z_d$#<+n6mR zgXj3FdnsulJQSHsFFQ`PbctNEsbv}-bIF}G=B+VKpa6y3_L=LEJBCYK-!x&@D}FUp z3QmGoqzW?adK@O2OFAfXO>Qlts>8Wlzv>ZZB|UTNRH#fc9mkoQBJSM87&JpL9?gKz z{MId4WC>&wgFOdiL_MR$qE!S7S|?7~hfG_x7fo?>Y3aRK5okNXkKJbxhTTQJSTYEW zBpgSXXINF2fQXiDR(+U$hm46u7Q`mnOoThX2@}r{nDrK_7HlM9Fkgpl*MY9g_G10E zFnD|W`lOaUZBH#-#cO;va*2>=k?v`><$ET`=l(afj~Z zUWfa1Hpbfs8VzX<&DoKwJErM#&gj;s zz$swqHbD~~>z$>@Q8{7h@10m0uN%8oSCaP1B@wl@;|7NI{o&4mdC5~5kLURqunO4N zi6pIT6v6q`6cb0=m?#v`!~p8}++@V$3?AV$4r1`AuCBN69>Z_QL=|fBh4o$HB~==V zh?lDILnkICaZ)@wjYI-qrVyzsGY^ayy;2^eeB3L^Q^~QVt>3_;f@t^ERM->dZ{=8! zi%7Eoh%n*}?TC2MkQCGWIvB5wY3ANJ^;pX~?;`fu2V z@2xo;&-*>as#a2M_CzW_wFo>M#qs%&Q`?D%gFEQXTc5?8;-o`->Q9^IlN5bUw9zL$ zF6f(g__9SQv!8%6U-m#5d~pgFIHXJD#wmX}NVf28eWWMT!G06h>_Wk(R?8B!PA^Va_o43B`P8l$aE-e337s@1qnxi-DI(IOs zYAjb7!CaoV{sdvB*C*4?#ZsQOGedmHn|Zf_2hdlTtMWLy-sMBeDNiB;P z^m0in^HBiDEq)9e$Dt^fanJ z=z7heeiiHex_;X?s?jewU8jzT-IzeK%o==9aA=_6r-ndIARNDpaLm5mCcbp2+;+67C;R Dict[str, Any]: + """ + Build a standardized JSON response. + + Args: + status (bool): Indicates whether the request was successful. + message (str): A message describing the result of the request. + data (Optional[Union[Dict, List, str]]): The data to return in the response. + status_code (int): The HTTP status code for the response. + error (Optional[Union[Dict, str]]): Any error details to include in the response. + + Returns: + Dict[str, Any]: A dictionary representing the JSON response. + """ + response = { + "status": status, + "statusCode": status_code, + "message": message, + "data": data if data is not None else {}, + "error": error if error is not None else {}, + } + return response + + @staticmethod + def success( + data: Optional[Union[Dict, List, str]] = None, + message: str = "Successful", + status_code: int = 200, + error: Optional[Union[Dict, str]] = None, + ) -> Dict[str, Any]: + """ + Return a success response. + + Args: + data (Optional[Union[Dict, List, str]]): The data to return in the response. + message (str): A message describing the result of the request. + status_code (int): The HTTP status code for the response. + error (Optional[Union[Dict, str]]): Any error details to include in the response. + + Returns: + Dict[str, Any]: A dictionary representing the JSON response. + """ + return ResponseHelper.build_response(True, message, data, status_code, error) + + @staticmethod + def error( + message: str = "An error occurred", + status_code: int = 400, + data: Optional[Union[Dict, List, str]] = None, + error: Optional[Union[Dict, str]] = None, + ) -> Dict[str, Any]: + """ + Return an error response. + + Args: + message (str): A message describing the error. + status_code (int): The HTTP status code for the response. + data (Optional[Union[Dict, List, str]]): The data to return in the response. + error (Optional[Union[Dict, str]]): Any error details to include in the response. + + Returns: + Dict[str, Any]: A dictionary representing the JSON response. + """ + return ResponseHelper.build_response(False, message, data, status_code, error) + + @staticmethod + def created( + data: Optional[Union[Dict, List, str]] = None, + message: str = "Resource created successfully", + error: Optional[Union[Dict, str]] = None, + ) -> Dict[str, Any]: + """ + Return a response for a created resource. + + Args: + data (Optional[Union[Dict, List, str]]): The data to return in the response. + message (str): A message describing the result of the request. + error (Optional[Union[Dict, str]]): Any error details to include in the response. + + Returns: + Dict[str, Any]: A dictionary representing the JSON response. + """ + return ResponseHelper.build_response(True, message, data, 201, error) + + @staticmethod + def updated( + data: Optional[Union[Dict, List, str]] = None, + message: str = "Resource updated successfully", + error: Optional[Union[Dict, str]] = None, + ) -> Dict[str, Any]: + """ + Return a response for an updated resource. + + Args: + data (Optional[Union[Dict, List, str]]): The data to return in the response. + message (str): A message describing the result of the request. + error (Optional[Union[Dict, str]]): Any error details to include in the response. + + Returns: + Dict[str, Any]: A dictionary representing the JSON response. + """ + return ResponseHelper.build_response(True, message, data, 200, error) + + @staticmethod + def internal_server_error( + message: str = "Internal Server Error", + data: Optional[Union[Dict, List, str]] = None, + error: Optional[Union[Dict, str]] = None, + ) -> Dict[str, Any]: + """ + Return a response for an internal server error. + + Args: + message (str): A message describing the error. + data (Optional[Union[Dict, List, str]]): The data to return in the response. + error (Optional[Union[Dict, str]]): Any error details to include in the response. + + Returns: + Dict[str, Any]: A dictionary representing the JSON response. + """ + return ResponseHelper.build_response(False, message, data, 500, error) + + @staticmethod + def unauthorized( + message: str = "Unauthorized", + data: Optional[Union[Dict, List, str]] = None, + error: Optional[Union[Dict, str]] = None, + ) -> Dict[str, Any]: + """ + Return a response for an unauthorized request. + + Args: + message (str): A message describing the error. + data (Optional[Union[Dict, List, str]]): The data to return in the response. + error (Optional[Union[Dict, str]]): Any error details to include in the response. + + Returns: + Dict[str, Any]: A dictionary representing the JSON response. + """ + return ResponseHelper.build_response(False, message, data, 401, error) + + @staticmethod + def forbidden( + message: str = "Forbidden", + data: Optional[Union[Dict, List, str]] = None, + error: Optional[Union[Dict, str]] = None, + ) -> Dict[str, Any]: + """ + Return a response for a forbidden request. + + Args: + message (str): A message describing the error. + data (Optional[Union[Dict, List, str]]): The data to return in the response. + error (Optional[Union[Dict, str]]): Any error details to include in the response. + + Returns: + Dict[str, Any]: A dictionary representing the JSON response. + """ + return ResponseHelper.build_response(False, message, data, 403, error) + + @staticmethod + def not_found( + message: str = "Resource not found", + data: Optional[Union[Dict, List, str]] = None, + error: Optional[Union[Dict, str]] = None, + ) -> Dict[str, Any]: + """ + Return a response for a not found resource. + + Args: + message (str): A message describing the error. + data (Optional[Union[Dict, List, str]]): The data to return in the response. + error (Optional[Union[Dict, str]]): Any error details to include in the response. + + Returns: + Dict[str, Any]: A dictionary representing the JSON response. + """ + return ResponseHelper.build_response(False, message, data, 404, error) + + @staticmethod + def unprocessable_entity( + message: str = "Unprocessable entity", + data: Optional[Union[Dict, List, str]] = None, + error: Optional[Union[Dict, str]] = None, + ) -> Dict[str, Any]: + """ + Return a response for an unprocessable entity. + + Args: + message (str): A message describing the error. + data (Optional[Union[Dict, List, str]]): The data to return in the response. + error (Optional[Union[Dict, str]]): Any error details to include in the response. + + Returns: + Dict[str, Any]: A dictionary representing the JSON response. + """ + return ResponseHelper.build_response(False, message, data, 422, error) \ No newline at end of file diff --git a/app/middlewares/__init__.py b/app/middlewares/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/middlewares/cors.py b/app/middlewares/cors.py new file mode 100644 index 0000000..e655fac --- /dev/null +++ b/app/middlewares/cors.py @@ -0,0 +1,9 @@ +# app/middlewares/cors.py +from flask import request + +def cors_headers(response): + """Allow cross-origin requests""" + response.headers["Access-Control-Allow-Origin"] = "*" + response.headers["Access-Control-Allow-Methods"] = "GET, POST, PUT, PATCH, DELETE" + response.headers["Access-Control-Allow-Headers"] = "Authorization, Content-Type" + return response diff --git a/app/middlewares/encryption.py b/app/middlewares/encryption.py new file mode 100644 index 0000000..ccb3372 --- /dev/null +++ b/app/middlewares/encryption.py @@ -0,0 +1,14 @@ +# app/middlewares/encryption.py +from cryptography.fernet import Fernet +import os + +ENCRYPTION_KEY = os.getenv("ENCRYPTION_KEY", Fernet.generate_key()) +cipher = Fernet(ENCRYPTION_KEY) + +def encrypt_data(data): + """Encrypt sensitive data""" + return cipher.encrypt(data.encode()).decode() + +def decrypt_data(data): + """Decrypt sensitive data""" + return cipher.decrypt(data.encode()).decode() diff --git a/app/middlewares/request_validator.py b/app/middlewares/request_validator.py new file mode 100644 index 0000000..903e450 --- /dev/null +++ b/app/middlewares/request_validator.py @@ -0,0 +1,11 @@ +# app/middlewares/request_validator.py +from flask import request +from app.helpers.response_helper import ResponseHelper + +def validate_json(): + """Ensure request has valid JSON""" + if not request.is_json: + return ResponseHelper.error( + message="Request must be JSON", + status_code=415 + ) diff --git a/app/middlewares/verify_api_key.py b/app/middlewares/verify_api_key.py new file mode 100644 index 0000000..22831df --- /dev/null +++ b/app/middlewares/verify_api_key.py @@ -0,0 +1,8 @@ +# app/middlewares/auth.py +from flask import request, jsonify + +def require_api_key(): + """Middleware to check if API key is present""" + api_key = request.headers.get("X-API-KEY") + if not api_key: + return jsonify({"error": "Missing API key"}), 403 diff --git a/app/routes/__init__.py b/app/routes/__init__.py new file mode 100644 index 0000000..4ebb14a --- /dev/null +++ b/app/routes/__init__.py @@ -0,0 +1 @@ +from .routes import api diff --git a/app/routes/__pycache__/__init__.cpython-39.pyc b/app/routes/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..563400ee56f689542c986a3675e06ad10c404bbd GIT binary patch literal 152 zcmYe~<>g`k0@F=*(xrg(V-N=!FabFZKwK;UBvKes7;_kM8KW2(8B&;n88n$+G6ID) z8E-Ks7G(NqGTmY;$}cTREiM9STggzw0;IsiFKPY6f&wsxsL+p(&& literal 0 HcmV?d00001 diff --git a/app/routes/__pycache__/routes.cpython-39.pyc b/app/routes/__pycache__/routes.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7f0db77779aecd1c736cb64b4177ab47c6eceacd GIT binary patch literal 4539 zcmb`K%Wm676oyIaE=#s7OSWU@epeEkxu{k=A55Kxm=XQcH@tC zonK}p=`U!6x3p;77rXnlB1uFdvMn)LlbNC^ETyGn&=fn(GFpaZwJghNIhNP*te_QG zQ7f_uZGx4w5-V$EHmOarDQ${XvEkdydU(^Rh%` zlBS1VDcS;Ec(zC~B-@cm?zKYlr0}EkO4bgeUJUdJQbPR*>g7P6BvYs#MZH3%hy7H^ z4C=>FuLbwl$t>!}QJ)L+d2$H#6R0l)`XV`u`bpG}1o}~O4E0l}9}n~s5$G4lMbyusekstG$TI5ZQC9=~GP#0!1NEzcevMp5{Q~MY z0{terh5AL*KMVBF$rq?!Lj882-ywHVUqby}@SeXU_fcOa4_Z>=;d_X%M#?WdwEMKj zt*+tAVJpi|`N}q>@J#ekqdfXa~HuKfiKfU&(wIS}yG3sqO#5G}hN1I3+QcCPivAYZ6;7R-P8|kIw zDf_7x(i{1u?4@7GujH2WR$gsnINa>#JJi!55_krhQ$6mO)OGb?lyFE(Zxuf+9@_Bp zoVq=jx3>~MAujxVNHuyr+_}^DsH+ZsmV3MYG#$s3ZbDEU5&v+r;vgw`r6}`L;%u*^ zhCuE!u)D z6za*b<$xoGO-uuo`FP+8fa=yb1Kb!x7h$p+09{mreDG<~e-GUcr@9bNSKy;G zba#w}F?a#6DrzJtB?kvJKW45g<}f4b<{%}mL>MmuV_p4Nf=mu{A|ABf4W($^9Omf9 z5!y>YTTn+CJ2}EqJYj(w<{;O67ombRv{zQcWg|JVay(gygbLR85x8Z5E2*LKl^omT ze_%5QbrX{fjj*Y}R#ijwY*0p%Bb!%3gHDD~Nf%8g^ZYG|tXbZzP$TF`ZT-SlCrhdf#B}Z6`C#-krb3MFd(0=_KfrSda zmsW)VO%ARe50`S8`)dT{CZOcip&w2TM|@|;Rp0!kFyXpj`YpnA3z%~17By^d>j09t zAb~k;irs~(L}a2^GD)=+@y(T9^|P*LczxGbobC62it8cg8TFup?g!EIJM;$~OA5a& zj;jsT&$MmB-R7X8HA6eAO$yh&ygB-$bJ4mFEH8RX^lNB~(0*WP$LfiN3M&F8UCe8k zIB-#7>|=Q0G{Vc%Dlk&tvut;H@NL59gzV>ay=yS4>wF%X8MOUq=s7rNw9{z$I2bGr z4u*p<;7f3TddH!#aLC#m(j$ko!(m->SVSCxlS5c=mRx727=}EbmOJ@HEi!i89 literal 0 HcmV?d00001 diff --git a/app/routes/routes.py b/app/routes/routes.py new file mode 100644 index 0000000..1033b4c --- /dev/null +++ b/app/routes/routes.py @@ -0,0 +1,175 @@ +from flask import Blueprint, request, jsonify +from app.blueprints import ( + EligibilityCheckService, + SelectOfferService, + ProvideLoanService, + LoanInformationService, + RepaymentService, + CustomerConsentService, + NotificationCallbackService, + RACCheckService, + DisbursementService, + CollectLoanService, + TransactionVerifyService, + PenalChargeService, + RevokeEnableConsentService, + TokenValidationService, + LienCheckService, + NewTransactionCheckService, + SMSService, + BulkSMSService +) +from app.utils.logger import logger + + +api = Blueprint("api", __name__) + + +# EligibilityCheck Endpoint +@api.route('/EligibilityCheck', methods=['POST']) +def eligibility_check(): + data = request.get_json() + # logger.info(f"EligibilityCheck request received: {data}") + response = EligibilityCheckService.process_request(data) + return jsonify(response) + +# SelectOffer Endpoint +@api.route('/SelectOffer', methods=['POST']) +def select_offer(): + data = request.get_json() + # logger.info(f"SelectOffer request received: {data}") + response = SelectOfferService.process_request(data) + return jsonify(response) + +# ProvideLoan Endpoint +@api.route('/ProvideLoan', methods=['POST']) +def provide_loan(): + data = request.get_json() + # logger.info(f"ProvideLoan request received: {data}") + response = ProvideLoanService.process_request(data) + return jsonify(response) + +# LoanInformation Endpoint +@api.route('/LoanInformation', methods=['GET']) +def loan_information(): + data = request.args.to_dict() + # logger.info(f"LoanInformation request received: {data}") + response = LoanInformationService.process_request(data) + return jsonify(response) + +# Repayment Endpoint +@api.route('/Repayment', methods=['POST']) +def repayment(): + data = request.get_json() + # logger.info(f"Repayment request received: {data}") + response = RepaymentService.process_request(data) + return jsonify(response) + +# CustomerConsent Endpoint +@api.route('/CustomerConsent', methods=['POST']) +def customer_consent(): + data = request.get_json() + # logger.info(f"CustomerConsent request received: {data}") + response = CustomerConsentService.process_request(data) + return jsonify(response) + +# NotificationCallback Endpoint +@api.route('/NotificationCallback', methods=['POST']) +def notification_callback(): + data = request.get_json() + # logger.info(f"NotificationCallback request received: {data}") + response = NotificationCallbackService.process_request(data) + return jsonify(response) + +# RACCheck Endpoint +@api.route('/RACCheck', methods=['POST']) +def rac_check(): + data = request.get_json() + # logger.info(f"RACCheck request received: {data}") + response = RACCheckService.process_request(data) + return jsonify(response) + +# Disbursement Endpoint +@api.route('/Disbursement', methods=['POST']) +def disbursement(): + data = request.get_json() + # logger.info(f"Disbursement request received: {data}") + response = DisbursementService.process_request(data) + return jsonify(response) + +# CollectLoan Endpoint +@api.route('/CollectLoan', methods=['POST']) +def collect_loan(): + data = request.get_json() + # logger.info(f"CollectLoan request received: {data}") + response = CollectLoanService.process_request(data) + return jsonify(response) + +# TransactionVerify Endpoint +@api.route('/TransactionVerify', methods=['POST']) +def transaction_verify(): + data = request.get_json() + # logger.info(f"TransactionVerify request received: {data}") + response = TransactionVerifyService.process_request(data) + return jsonify(response) + +# PenalCharge Endpoint +@api.route('/PenalCharge', methods=['POST']) +def penal_charge(): + data = request.get_json() + # logger.info(f"PenalCharge request received: {data}") + response = PenalChargeService.process_request(data) + return jsonify(response) + +# RevokeEnableConsent Endpoint +@api.route('/RevokeEnableConsent', methods=['POST']) +def revoke_enable_consent(): + data = request.get_json() + # logger.info(f"RevokeEnableConsent request received: {data}") + response = RevokeEnableConsentService.process_request(data) + return jsonify(response) + +# TokenValidation Endpoint +@api.route('/TokenValidation', methods=['POST']) +def token_validation(): + data = request.get_json() + # logger.info(f"TokenValidation request received: {data}") + response = TokenValidationService.process_request(data) + return jsonify(response) + +# LienCheck Endpoint +@api.route('/LienCheck', methods=['POST']) +def lien_check(): + data = request.get_json() + # logger.info(f"LienCheck request received: {data}") + response = LienCheckService.process_request(data) + return jsonify(response) + +# NewTransactionCheck Endpoint +@api.route('/NewTransactionCheck', methods=['POST']) +def new_transaction_check(): + data = request.get_json() + # logger.info(f"NewTransactionCheck request received: {data}") + response = NewTransactionCheckService.process_request(data) + return jsonify(response) + +# SMS Endpoint +@api.route('/SMS', methods=['POST']) +def sms(): + data = request.get_json() + # logger.info(f"SMS request received: {data}") + response = SMSService.process_request(data) + return jsonify(response) + +# BulkSMS Endpoint +@api.route('/BulkSMS', methods=['POST']) +def bulk_sms(): + data = request.get_json() + # logger.info(f"BulkSMS request received: {data}") + response = BulkSMSService.process_request(data) + return jsonify(response) + +# Health Check Endpoint +@api.route('/health', methods=['GET']) +def health_check(): + return {"status": "ok"} , 200 \ No newline at end of file diff --git a/app/schemas/__init__.py b/app/schemas/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/schemas/__pycache__/__init__.cpython-39.pyc b/app/schemas/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5b171dcc2a095f2741847928236d975643f47a8a GIT binary patch literal 119 zcmYe~<>g`k0@F=*(n0iN5P=LBfgA@QE@lA|DGb33nv8xc8Hzx{2;!HFequoZ7#AmJ kq~<0T>&M4u=4F<|$LkeT-r}&y%}*)KNwov%`V7Pj09pkX(EtDd literal 0 HcmV?d00001 diff --git a/app/schemas/__pycache__/bulk_sms.cpython-39.pyc b/app/schemas/__pycache__/bulk_sms.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2a414f0968a02158cdb2d59fe015d08955787c41 GIT binary patch literal 449 zcmYjNy-ou$49?$OkJ3Xt00ZkSl!Xl;#KHnyjwwqN>Ocz#ZIhl5GpxJ=jI4}2O2o$0 zS75@qlS(bw@y};}j&)h~3Chvu_w5(HKPlK0fx#(iI79;ks3nd{>X>Ad00VBh6H-ue zPC!8RLLf8rnad3FnpC|Nc9Dw3ydF=C(=or98Vg}XQ<}!D`m*jS;{@E4HSs=X4UrUx zWQN}f!{0KKzf+*VUYYCxM&5euarv@=S@zEW-mjZOYQ zsYRo0SMeN~qPUobWz0t=EDiV!Q?ea|arhv<#6)X;_l9~tZ#A@k3&U&NKm`{L=1)FH zTxt#sO+njuRONBoX&)v|TRVI7sn)S_Y-`C{;6F>}?I11|$KBc|>2@KVXO|B69}=c! A)&Kwi literal 0 HcmV?d00001 diff --git a/app/schemas/__pycache__/collect_loan.cpython-39.pyc b/app/schemas/__pycache__/collect_loan.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f6a902d47b0f84073a1af03067d240ce9e77eaf8 GIT binary patch literal 703 zcmZvay>8Vo6vyrO-rL-dz%3nMWMIicCpLtNDiu{KU2dl=mTSAc7x^Q#9ih&!@&T#k4?;sx+>@L6Rh*|P$EHq@VwG;83$efKl>`WO9?;{LH! zO7d`Kndy&pQR7%TdDHEj6es8PK`bdXOiN1Rf>LcorxLFyz3G_hS6n)CB%9!36&y33 z@vyVzRK)ewrMdX0G86JLz{-MmK3FXs@SJgOJCn-u)^X%+y!EldTt)&U~$i0LO=k)mckhN_NpZ}{r z9PB7`H*ecpm+ndm{|ZuxSO0yo>b#!BrDo1=HB-udb#V1i{y2I0scTQkFP887Z~6hJ J7U_jG`2)_#x3>TQ literal 0 HcmV?d00001 diff --git a/app/schemas/__pycache__/customer_consent.cpython-39.pyc b/app/schemas/__pycache__/customer_consent.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..23f6f42a55112accdbd6c4c99bc0c0df12731027 GIT binary patch literal 603 zcmYjPJx{|h5Vf7Oq+bXS>R(=R8Grz=Q zW8yC`aZU>4qPx8F^ZA}nW`jW&fgQem+`eOkK3&o@ACgmWJ%qp!#~G41!IC5dqjSUw z_nr~=1b*@okNYu-T3@geMZ~t_bS5OT?de=(T&HfiX@5uGz^XtA<|rY;yYo5bfWa9vVltN^={Z$Gn>tBr(Gz#lS~_ES zF0xmoHlaxszJ~{Fv8Y^Elh+5o4d|+Nf3Z|hIuHtOz74 zLKVI1$pF)S35$M5J)*dyKGC3~m?-IJK$Lcr5M@9^$e9w2h%z}O%DdQzXxvdwH0fwe zGzG$wO^9YhnVb^M5+FTGj5@{cE2Zb=NBEHWKb6LD(53eImKz<$HDT zu~gR(^|>>=wp_SfQv+tAwXV_1fSqz7nzqJO>E1N56|@1&4?C$YHOAKdo@lB{33ud8 z?UaGKesSi0b>r4b`TJ+iO>J8!E|j01`QKc^`KhuwK<$BOG`4oNT==9cYpzvU`m8K9 z9H~NkRF-!wuZ|HD!H@OP))~_W%Oof%3cfirL>Ynj{<>H6Kj~d#D2n W|EqhnFr!bN9UH$0un2CLMt=bIwAjS} literal 0 HcmV?d00001 diff --git a/app/schemas/__pycache__/eligibility_check.cpython-39.pyc b/app/schemas/__pycache__/eligibility_check.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0cf83e693f1dfd9ef10ceb2f82c3e3b71cd8aed0 GIT binary patch literal 811 zcmYk4v2NQi5Qa&~mSx#;oD7{cv}gnb&D5=UJ_!my1)H6Xw*IW`4+?HH0O|AQI4FN7->qiida?DWf zasS(ZVVm~J%ZH9E4_WZ!=Yiu9aOimCcnlml4joT`r;f*tF^gTc2*}1}j;6cVk)xQ+ zq4Z<(?42m@%2g@KX1iE(df)eO(@j6~hnDLmX|@{<&4miMRGgU!~+)0A~(;zp55 z6KY|}y4lShljT+F=OH5vDen1J+4fZcVkWe1t43`XHRIjMqKAY6C8x{mQ)(jG>ZX=l zLCVQuXG1CA*hQ4z_D!bC+OYt{;DPmAKAC8Ccg7#w8$tWjoQKaU#O)

| zD|W(6Aa$u(l@H&GnlyQ23+e01N4pY!B6V!fTlx3moNP93Yd1#vobP{40WNy~YO_^# zKJDxE93;duJl{PtuwC>z9hiwEO0OjmqW-QtIO$4u;r%On>ja`EeA9RO7TeVJ!rc20 DPCeHG literal 0 HcmV?d00001 diff --git a/app/schemas/__pycache__/lien_check.cpython-39.pyc b/app/schemas/__pycache__/lien_check.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..89440e678181c833956ca2936e6502d7a10b577e GIT binary patch literal 643 zcmZuvv2GMG5Vd#rE}IJ{LMjSCs|bo~2o&iMLZXOHiqdRowpkg^gfp>s?`$vVu7#El zq2+TdI;#8vqF}tYTv9OBjK*&~GjD8*>2$(KPJjIT@P{+@D{k&eMw>UJ?kO2yz%?_x z;wGsQ&dwMlkbY&5BL9+BDP$#^jc({9nsFrYMzpZ&7PexV7_WXqMdKs#6vrdJf;2J*4E`{C;vGmdjpU|61-u(~Z^S z%j@UMwT+Jy)#YSze$=lXy_jX;zzFLWMrhrBwn;1+Y4zdT9$W7L1w#I6JI!nQYRtui G!}KqoudVR_ literal 0 HcmV?d00001 diff --git a/app/schemas/__pycache__/loan_information.cpython-39.pyc b/app/schemas/__pycache__/loan_information.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cbba475b07d02bd5377c60b1063553924ea52a51 GIT binary patch literal 400 zcmYjNJx{|h5cNkIqLxZ5Nc?~8!l8F;mG z;>4h-tl^Wmcz~4tW^>hb2|lgv=R?J|D(5#=8Ph(ysHJg9 UKFk=Z396yE9=5!p|7K@=4^9PKe*gdg literal 0 HcmV?d00001 diff --git a/app/schemas/__pycache__/new_transaction_check.cpython-39.pyc b/app/schemas/__pycache__/new_transaction_check.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8fd2c208ffbbcb51ac7aef9d705bf37d5f3e55c5 GIT binary patch literal 647 zcmY*Xy>8nu5ay4psIuKU-LiMl(oEfoB7h4NUW@?t#GwSOyVz<-$swg0ncFol(XDGA zhsf5U=qq$dNI5{%BkuU!AMcK&noe^P=-sdT?H@|W@2CtZ3Ccxa_a+D+Ks7N`P{RbH z*jzUzbf6KE4Y|^tlXxv4h`0=&*zQS*q4+y%-gHVIJ(kBTzG|(Xw$J@7z^8zlHteqk=Jo9!{p@N} z4@OylIBjgT{m z&4S42B&?KX4;hcB_b!XXkBzc!XZmZiEY^t=_3r1gNAx;4eD?l$5g)>LBQ7S)_#?aQ BtZV=P literal 0 HcmV?d00001 diff --git a/app/schemas/__pycache__/notification_callback.cpython-39.pyc b/app/schemas/__pycache__/notification_callback.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..39516cded5068272d90826033fe2b70abc817a2e GIT binary patch literal 683 zcmZWnv5wR*5VhmoCAYVGyAo8?G-76XU7_h4k|6@#*kc~hdT^FIMsv~z&=hi z)x}rJrx~7Q_)&(-44-6pj`;MU&LYFhOmmjum1fxbBtG4`u0GaEcC~9Z(wKu(pT=|S zD;i4=*M03lZ>x1Ig=nM&A>v#J>vV4tUI=mBOEYGCI%8zJ&hz#0(Coczf>f#YrH;$) zA39fk=$r+QsS~M`>zfYYOv3@+e3hy9H!Z~J8{=fh&!kPt_uhT3HK1fLk{=;5FP`++cFZaZ+u+Jg7vf=;$ literal 0 HcmV?d00001 diff --git a/app/schemas/__pycache__/penal_charge.cpython-39.pyc b/app/schemas/__pycache__/penal_charge.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3ea2b3a2d318dbdac63ba6726e8b89e0195bcd54 GIT binary patch literal 1144 zcmZuw&2H2%5KcC`+w5+)lpX+yi&b%8FBA}n3j)ed5tph`%4IK+H=fP9vg6ctiZ&-C zxbgrz0v8S(c$kP2oOlIJjI$wKsygydK99ed`NpQ>@zBHe@Y|2{d)M=Rxu5lhnD-33 zxrGDsm@ho(Pkq^$c6{%($2u(d=&=C&55Y8G-N>8texs!~=~VqFS%9Qff6hTLlQ?+2 zwbWk`1#jvzZ`y(2q|1Cq%YD}U)S32Lq}L$*7U?(0MvH7TNZ2A_gA5QE*35$j8MVl; zK{ne{qXrq*$R@@!Zjddug-Kqit{y=~#ft^iDXbDbu0{+@qIqt)%E||CGk#hC;S4g% zQ3FlXoLEgWLlb8~ct#>FXBvc{#4L>kiL!+nP7AJqRiOk+8MU;GTCSR70srx6NPwRK zBXg}JIe32Pm|qE=@);N0E{W3QNUJk$oL5H=#+vd`OS&Fl?hF~vQX&++G{*Agr3Wb~ z4CrEl#f)-DFyrJx?kkI*?>`$tlSCDnEuNxvEhI)srGtoFP)jlZW{6c}1|(5JKw|&p z=e(?Ql)L+Bsv)J$;W{pQYKWnc}3r!FTedcJHx=-R$5H`a2iCdjD|Jtwxe+ kv%p}4dZ#yWzU;qFB literal 0 HcmV?d00001 diff --git a/app/schemas/__pycache__/provide_loan.cpython-39.pyc b/app/schemas/__pycache__/provide_loan.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7e6f066fad53f71110a71d27261efa5b9e60b4fb GIT binary patch literal 688 zcmZvay>1jS5PD~_AV$jw0VQP z04g!D{J%7mRZ|niw_LzVpI_Mem>kkFMFdvTO*o>ZJLgV_ewZ zmX%`bAeUxn_qbI`rUp0AysaS5L<&26!;F#Y2Xe3Agh~B*d(8SiiBCV$A4AW02{h&% z_4h75jwt>Os0uIt`=zL>rVP89Ils|NDf`vITSMtK()Pcu6TODrP@PVf_#Vd=G2wtb E14J{iv;Y7A literal 0 HcmV?d00001 diff --git a/app/schemas/__pycache__/rac_check.cpython-39.pyc b/app/schemas/__pycache__/rac_check.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..151cb6dd14f3f0a3c662816773712e77cd2d343b GIT binary patch literal 977 zcmZ`&&2H2%5Vn(SHv5y^1te}fz+UJDZV1s93010=itWjl$hF;WXcBL2hq7n5BJl`t z?3Kr{xN+hYI5Bn>)>e$WGhiqo1T}V;+i>gxFCI( z>LUPw2>w&Bd{# z4H;aoN+T+;kb8|hq1rre#nPwp{+;oqSqbHA`Ua^SF6PCL2YYnA_FH?p^q!)u0LU&M zt~bifgR4T{2a)0NEsu}+huz?a{2<1ITAV8=IP zOONV7p2a*@eR!+PiGdUD@@fRa=0-vBl$laCN8HZN&zjReEJN06BWt0+JJ?bIFgb?q z+H@hb5!@F`29UhiSBh?So$S!WrJI7d3tPK?ZPzq!U(9=UR8yr_HLWUnts-~A-_o_r W$K3W&n?31<>?w9%cCoW)HvI`wROloC literal 0 HcmV?d00001 diff --git a/app/schemas/__pycache__/repayment.cpython-39.pyc b/app/schemas/__pycache__/repayment.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..400b10df9c2ec14aaf9572594881851751e93313 GIT binary patch literal 532 zcmYk3y-ve05XbE}P1CgHYiDE0LMJwam_RHYC{q?=a)t)QNoprToneLdVC8WvHYQ$y ziE|=^&$`p^eE#^`C+72npq+etKD<#vzFgv4h{-v+Il=%0Xh{^!s0y-xl1l;suonVM z(r1=22v;N>ed9`!1~yvdYpH~d?h9Fh$=$SX43l$obBjTei~`95$sR+X5WLcC)HCZj z^5(}L#~x2SPCT9tvh41)Y{XW{svU&gq}0Y*Zn{ECxLBq$8*^R>B{{bV=cVDS70ZN#*Mkdd8H@&dm;nn9jV>*OqB$r%GtER-!EHCmpzx ao!-9uf6+h(Sg`Tw@XRypJuz;>9{UCPc!5d) literal 0 HcmV?d00001 diff --git a/app/schemas/__pycache__/revoke_enable_consent.cpython-39.pyc b/app/schemas/__pycache__/revoke_enable_consent.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dd05ccef0cd855bfd0b98756bcd0fac0ee31327c GIT binary patch literal 1040 zcmZ8gL2uJA6n2`nOWPGq2qX|3tcgn{#D)Yngn+?-%MNR~hO(IL$p= zIN?D~L=Xp}6L$jgn(z(}KNB88@F|Q#-kp+3?>9!0NvG;h(*+1t_2(Jn+@ua(A1(Dc zqCnz+leh!nhb|8sZ5HtEmrmSkk)B8TEzbj9O&qknsjgcJ1Q;_ST9?&9nlRhpvTD2o2-))XwY ziW@rMM)x=fF!Ag zNg@<4b7yZR$#Kc@W=DruP!&$CuEuKz^Ojdz^CLOCJSs$nsTUOOftLWy$cZq+{^ODF z(>c}1mo!@zPz`Fm^b*52eA(ZLzJa;x<Qr}J9@l8SeVMkgZsJB2J!$lL8lY#~4_n>8Z?!mk literal 0 HcmV?d00001 diff --git a/app/schemas/__pycache__/select_offer.cpython-39.pyc b/app/schemas/__pycache__/select_offer.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..691fe8b242137481bd3eb864f5a10cb2d30146de GIT binary patch literal 758 zcmY+Cy^ho{5XYU5&E{jXEP{qE1qG4NUPXrxr#peJ;6TbS1sMlO9x^PXNOtI9t**m>>1l$ zZ=YwEMRA&+=W88SmB5@;m5!^5!`;UCtE##ga5F`u2c6bl(Mui8mCmS6`+LoRF_|TU zAK^(cxB*wSqCzj;G%fDS2X{&*N6nOZ<-HBPeLi-|Pr?on6X)3w5My%d6J( zeA|7N=0BLFHef<${ol0F^W4`7$5QP%ZkqO!gddCSqip!T+T=Zks19FFy?EuA)aJs* F`v>=+&?Eo= literal 0 HcmV?d00001 diff --git a/app/schemas/__pycache__/sms.cpython-39.pyc b/app/schemas/__pycache__/sms.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..135869009917e6657592c3a414abfd61cee53e8e GIT binary patch literal 421 zcmYjNy-ve05cXfww6ws+3$SFNENlo>EKGC>Qx?nW85(3ashtRzVdX(sc?cfDVq@YJ zm^dd$`8N0eL=h=9Ek zV2Zvl&LCcqtoMy4Nfx9|r?{#?6 z;YS^w0>hHAJ6%_gs=Spt2U+J;*_K{s)TKu4o7^Z^W__0mQOZII;RZq!6*SuOkr4Yv z>NaA0TaK-2`pLw}sTYdNz^2+BX%#|CL%c!hlO>?JO7ggT(rT GWA+O@Id9Vd literal 0 HcmV?d00001 diff --git a/app/schemas/__pycache__/transaction_verify.cpython-39.pyc b/app/schemas/__pycache__/transaction_verify.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fa915ac447b52e7fa8a47f87b7e8271ecbfd85a4 GIT binary patch literal 565 zcmYk4u};G<5J2rDZPT=+-S`27l!Z=g2z5Y0U07N%crjM&(x5m=?F5t=Rz8H458y*A zHYR?7iL*1*XWi+Y&-R_qX0us>KrTPOAHOg{Ki2Uj0%rxP&%kiRae*WzSOz4(=oayS zhi{070>6eNFi zEILpf;Ji83w(V;1fz%u_IP-VDWOeOSyN>Fmx0*h?t6bEbvTrp2)hA$LJoSc)-9Zy3 lk}18HtSG7%71$K^CN6x;*wX?6Ccf$$a1EaWX}e(&{R28Fj!6Ij literal 0 HcmV?d00001 diff --git a/app/schemas/bulk_sms.py b/app/schemas/bulk_sms.py new file mode 100644 index 0000000..40c2465 --- /dev/null +++ b/app/schemas/bulk_sms.py @@ -0,0 +1,6 @@ +from marshmallow import Schema, fields +from .sms import SMSSchema + +# Bulk SMS Schema +class BulkSMSSchema(Schema): + requests = fields.List(fields.Nested(SMSSchema), required=True) \ No newline at end of file diff --git a/app/schemas/collect_loan.py b/app/schemas/collect_loan.py new file mode 100644 index 0000000..fb5bc76 --- /dev/null +++ b/app/schemas/collect_loan.py @@ -0,0 +1,16 @@ +from marshmallow import Schema, fields + +# Collect Loan Schema +class CollectLoanSchema(Schema): + transactionId = fields.Str(required=True) + fbnTransactionId = fields.Str(required=True) + debtId = fields.Str(required=True) + customerId = fields.Str(required=True) + accountId = fields.Str(required=True) + productId = fields.Str(required=True) + collectAmount = fields.Float(required=True) + penalCharge = fields.Float(required=False) # Optional + collectionMethod = fields.Int(required=True) + lienAmount = fields.Float(required=True) + countryId = fields.Str(required=True) + comment = fields.Str(required=False) # Optional \ No newline at end of file diff --git a/app/schemas/customer_consent.py b/app/schemas/customer_consent.py new file mode 100644 index 0000000..09c2d9c --- /dev/null +++ b/app/schemas/customer_consent.py @@ -0,0 +1,11 @@ +from marshmallow import Schema, fields + +# Customer Consent Schema +class CustomerConsentSchema(Schema): + type = fields.Str(required=True) + transactionId = fields.Str(required=True) + customerId = fields.Str(required=True) + accountId = fields.Str(required=True) + requestTime = fields.DateTime(required=True, format="%Y-%m-%d %H:%M:%S.%f") + consentType = fields.Str(required=True) + channel = fields.Str(required=True) \ No newline at end of file diff --git a/app/schemas/disbursement.py b/app/schemas/disbursement.py new file mode 100644 index 0000000..b9ffe39 --- /dev/null +++ b/app/schemas/disbursement.py @@ -0,0 +1,17 @@ +from marshmallow import Schema, fields + +# Disbursement Schema +class DisbursementSchema(Schema): + requestId = fields.Str(required=True, data_key="requestId") + debtId = fields.Str(required=True, data_key="debtId") + transactionId = fields.Str(required=True, data_key="transactionId") + customerId = fields.Str(required=True, data_key="customerId") + accountId = fields.Str(required=True, data_key="accountId") + productId = fields.Str(required=True, data_key="productId") + provideAmount = fields.Float(required=True, data_key="provideAmount") + collectAmountInterest = fields.Float(required=False, data_key="collectAmountInterest") # Optional + collectAmountMgtFee = fields.Float(required=True, data_key="collectAmountMgtFee") + collectAmountInsurance = fields.Float(required=True, data_key="collectAmountInsurance") + collectAmountVAT = fields.Float(required=True, data_key="collectAmountVAT") + countryId = fields.Str(required=True, data_key="countryId") + comment = fields.Str(required=False, data_key="comment") # Optional \ No newline at end of file diff --git a/app/schemas/eligibility_check.py b/app/schemas/eligibility_check.py new file mode 100644 index 0000000..41e3a3e --- /dev/null +++ b/app/schemas/eligibility_check.py @@ -0,0 +1,11 @@ +from marshmallow import Schema, fields + +class EligibilityCheckSchema(Schema): + type = fields.Str(required=True, description="Request type") + transactionId = fields.Str(data_key="transactionId", required=True, description="Transaction ID") + countryCode = fields.Str(data_key="countryCode", required=True, description="Country code (ISO)") + customerId = fields.Str(data_key="customerId", required=True, description="Customer ID") + accountId = fields.Str(data_key="accountId", required=True, description="Account ID") + msisdn = fields.Str(required=True, description="Mobile number") + lienAmount = fields.Float(required=True, description="Amount for lien") + channel = fields.Str(required=True, description="Transaction channel (USSD, Mobile, Web)") diff --git a/app/schemas/lien_check.py b/app/schemas/lien_check.py new file mode 100644 index 0000000..79c509e --- /dev/null +++ b/app/schemas/lien_check.py @@ -0,0 +1,8 @@ +from marshmallow import Schema, fields + +# Lien Check Schema +class LienCheckSchema(Schema): + transactionId = fields.Str(required=True, metadata={"description": "Unique Identifier in Simbrella system"}) + customerId = fields.Str(required=True, metadata={"description": "Unique identifier of customer"}) + accountId = fields.Str(required=True, metadata={"description": "Unique identifier of account"}) + countryId = fields.Str(required=True, metadata={"description": 'Set to static value "01"'}) \ No newline at end of file diff --git a/app/schemas/loan_information.py b/app/schemas/loan_information.py new file mode 100644 index 0000000..4d9b5ab --- /dev/null +++ b/app/schemas/loan_information.py @@ -0,0 +1,5 @@ +from marshmallow import Schema, fields + +# Loan Information Schema +class LoanInformationSchema(Schema): + loan_id = fields.Str(required=True) \ No newline at end of file diff --git a/app/schemas/new_transaction_check.py b/app/schemas/new_transaction_check.py new file mode 100644 index 0000000..9e79864 --- /dev/null +++ b/app/schemas/new_transaction_check.py @@ -0,0 +1,12 @@ +from marshmallow import Schema, fields + +# New Transaction Check Schema +class NewTransactionCheckSchema(Schema): + transactionId = fields.Str(required=True) + debtId = fields.Str(required=True) + transactionType = fields.Str(required=True, metadata={ + "allowed_values": ["Disbursement", "Collection", "PenalCharge"] + }) + fbnTransactionId = fields.Str(required=True) + origTransactionId = fields.Str(required=True) + customerId = fields.Str(required=True) \ No newline at end of file diff --git a/app/schemas/notification_callback.py b/app/schemas/notification_callback.py new file mode 100644 index 0000000..19e6b4d --- /dev/null +++ b/app/schemas/notification_callback.py @@ -0,0 +1,14 @@ +from marshmallow import Schema, fields + +# Notification Callback Schema +class NotificationCallbackSchema(Schema): + fbnTransactionId = fields.Str(required=True) + transactionId = fields.Str(required=True) + customerId = fields.Str(required=True) + accountId = fields.Str(required=True) + debtId = fields.Str(required=True) + transactionType = fields.Str(required=True) + amountProvided = fields.Float(required=True) + amountCollected = fields.Float(required=True) + responseCode = fields.Str(required=True) + responseDescription = fields.Str(required=True) \ No newline at end of file diff --git a/app/schemas/penal_charge.py b/app/schemas/penal_charge.py new file mode 100644 index 0000000..cb04e4e --- /dev/null +++ b/app/schemas/penal_charge.py @@ -0,0 +1,14 @@ +from marshmallow import Schema, fields + + +# Penal Charge Schema +class PenalChargeSchema(Schema): + transactionId = fields.Str(required=True, metadata={"description": "Unique identifier of transaction in Simbrella system"}) + fbnTransactionId = fields.Str(required=True, metadata={"description": "Unique id of the transaction received from FBN in Eligibility or Provision requests"}) + debtId = fields.Str(required=True, metadata={"description": "Unique identifier of providing loan in Simbrella system"}) + customerId = fields.Str(required=True, metadata={"description": "Unique identifier of a user"}) + accountId = fields.Str(required=True, metadata={"description": "Specific identifier of a user’s account"}) + penalCharge = fields.Decimal(required=True, metadata={"description": "Penalty amount that needs to be collected from user’s account"}) + lienAmount = fields.Decimal(required=True, metadata={"description": "Aggregated (summed up) lien amount"}) + countryId = fields.Str(required=True, metadata={"description": 'Set to static value "01"'}) + comment = fields.Str(required=False, metadata={"description": "Any additional comment for provided loan operation"}) diff --git a/app/schemas/provide_loan.py b/app/schemas/provide_loan.py new file mode 100644 index 0000000..ca82352 --- /dev/null +++ b/app/schemas/provide_loan.py @@ -0,0 +1,16 @@ +from marshmallow import Schema, fields + +# Provide Loan Schema +class ProvideLoanSchema(Schema): + type = fields.Str(required=True) + requestId = fields.Str(required=True) + transactionId = fields.Str(required=True) + customerId = fields.Str(required=True) + accountId = fields.Str(required=True) + msisdn = fields.Str(required=False) + productId = fields.Str(required=True) + lienAmount = fields.Float(required=True) + requestedAmount = fields.Float(required=True) + collectionType = fields.Int(required=True) + loanType = fields.Int(required=True) + channel = fields.Str(required=True) \ No newline at end of file diff --git a/app/schemas/rac_check.py b/app/schemas/rac_check.py new file mode 100644 index 0000000..3b858d2 --- /dev/null +++ b/app/schemas/rac_check.py @@ -0,0 +1,23 @@ +from marshmallow import Schema, fields + +class RACItemSchema(Schema): + salaryAccount = fields.Bool(required=True) + bvn = fields.Str(required=True) + crc = fields.Bool(required=True) + crms = fields.Bool(required=True) + accountStatus = fields.Str(required=True) + lien = fields.Bool(required=True) + noBouncedCheck = fields.Bool(required=True) + existingLoan = fields.Bool(required=True) + whitelist = fields.Bool(required=True) + noPastDueSalaryLoan = fields.Bool(required=True) + noPastDueOtherLoans = fields.Bool(required=True) + + +# RAC Check Schema +class RACCheckSchema(Schema): + transactionId = fields.Str(required=True) + fbnTransactionId = fields.Str(required=True) + customerId = fields.Str(required=True) + accountId = fields.Str(required=True) + RAC_Array = fields.List(fields.Nested(RACItemSchema), required=True) \ No newline at end of file diff --git a/app/schemas/repayment.py b/app/schemas/repayment.py new file mode 100644 index 0000000..535419c --- /dev/null +++ b/app/schemas/repayment.py @@ -0,0 +1,11 @@ +from marshmallow import Schema, fields + +# Repayment Schema +class RepaymentSchema(Schema): + type = fields.Str(required=True) + msisdn = fields.Str(required=False) #optional + debtId = fields.Str(required=True) + productId = fields.Str(required=True) + transactionId = fields.Str(required=True) + customerId = fields.Str(required=True) + channel = fields.Str(required=True) diff --git a/app/schemas/revoke_enable_consent.py b/app/schemas/revoke_enable_consent.py new file mode 100644 index 0000000..fd617eb --- /dev/null +++ b/app/schemas/revoke_enable_consent.py @@ -0,0 +1,13 @@ +from marshmallow import Schema, fields + + +# Revoke Enable Consent Schema +class RevokeEnableConsentSchema(Schema): + transactionId = fields.Str(required=True, metadata={"description": "Unique identifier of transaction in Simbrella system"}) + fbnTransactionId = fields.Str(required=True, metadata={"description": "Unique id of the transaction received from FBN in CustomerConsentRequest"}) + customerId = fields.Str(required=True, metadata={"description": "Unique identifier of a user"}) + accountId = fields.Str(required=True, metadata={"description": "Specific identifier of a user’s account"}) + processTime = fields.DateTime(required=True, metadata={"description": "Date and time when consent request was processed"}) + consentType = fields.Str(required=True, metadata={"description": '“Enable” or “Revoke”'}) + countryId = fields.Str(required=True, metadata={"description": 'Set to static value "01"'}) + comment = fields.Str(required=False, metadata={"description": "Any additional comment for consent operation"}) diff --git a/app/schemas/select_offer.py b/app/schemas/select_offer.py new file mode 100644 index 0000000..99398e1 --- /dev/null +++ b/app/schemas/select_offer.py @@ -0,0 +1,13 @@ +from marshmallow import Schema, fields + +# Select Offer Schema +class SelectOfferSchema(Schema): + requestId = fields.Str(required=True, description="Unique request identifier") + transactionId = fields.Str(required=True, description="Transaction ID") + customerId = fields.Str(required=True, description="Customer ID") + accountId = fields.Str(required=True, description="Account ID") + msisdn = fields.Str(required=True, description="Mobile number") + requestedAmount = fields.Float(required=True, description="Amount requested") + productId = fields.Str(required=True, description="Product ID") + channel = fields.Str(required=True, description="Transaction channel (e.g., USSD)") + diff --git a/app/schemas/sms.py b/app/schemas/sms.py new file mode 100644 index 0000000..e8209c4 --- /dev/null +++ b/app/schemas/sms.py @@ -0,0 +1,7 @@ +from marshmallow import Schema, fields + +# SMS Schema +class SMSSchema(Schema): + text = fields.Str(required=True) + dest = fields.Str(required=True) + unicode = fields.Bool(required=True) \ No newline at end of file diff --git a/app/schemas/token_validation.py b/app/schemas/token_validation.py new file mode 100644 index 0000000..c8e067b --- /dev/null +++ b/app/schemas/token_validation.py @@ -0,0 +1,8 @@ +from marshmallow import Schema, fields + +# Token Validation Schema +class TokenValidationSchema(Schema): + RequestId = fields.Str(required=True) + UserId = fields.Str(required=True) + CountryId = fields.Str(required=True) + TokenCode = fields.Str(required=True) \ No newline at end of file diff --git a/app/schemas/transaction_verify.py b/app/schemas/transaction_verify.py new file mode 100644 index 0000000..021ded4 --- /dev/null +++ b/app/schemas/transaction_verify.py @@ -0,0 +1,12 @@ +from marshmallow import Schema, fields + + +# Transaction Verify Schema +class TransactionVerifySchema(Schema): + counter = fields.Str(required=True) + TransactionId = fields.Str(required=True) + requestID = fields.Str(required=True) + customerId = fields.Str(required=True) + accountId = fields.Str(required=True) + countryId = fields.Str(required=True) # Static value “01” + transactionType = fields.Str(required=True) \ No newline at end of file diff --git a/app/static/css/main.css b/app/static/css/main.css new file mode 100644 index 0000000..e69de29 diff --git a/app/static/js/main.js b/app/static/js/main.js new file mode 100644 index 0000000..e69de29 diff --git a/app/templates/index.html b/app/templates/index.html new file mode 100644 index 0000000..f29a4e2 --- /dev/null +++ b/app/templates/index.html @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/utils/__pycache__/logger.cpython-39.pyc b/app/utils/__pycache__/logger.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7651329bb3c2c43a9670f663f5d478f4730c7087 GIT binary patch literal 370 zcmYk2%}PTt5QUT6-&(J9>9U(bv#?#c5fMQt3Q~pcx{J*;Msj~hZVUDu`VQ@~TOTL5 zbLA@tiqjUwfpg#_`G%Q@EIS}rr>}47ffDk*4gV{}aEWqfXp%^3$d~|mrYI&EkX5kW z=1>MQl+ltYCgWv5@oW{zWKGAhq$9%959G;k!8={yCbiCiyY@-D(={*$Q;5HugL6XR z?IICX)ib5?RB-CUyp+HLAMF4iPfMGN+NV#VkOr(9x2~ZE>|7QjuyZ{Dp7;cDr3>Yo zkHYE6Ras26^5N}paPPBGZ9(KWd)_w(+Q4sUD5&oc1?%H&3$Pnvk8pvVeIEC5w4G+P kHm<*a(yLy4dX8hx3|!z}64ydYgOG-lv6voyv#+@E1L5&$(f|Me literal 0 HcmV?d00001 diff --git a/app/utils/logger.py b/app/utils/logger.py new file mode 100644 index 0000000..9c0d571 --- /dev/null +++ b/app/utils/logger.py @@ -0,0 +1,13 @@ +import logging + +# Configure logging +logging.basicConfig( + level=logging.INFO, + format="%(asctime)s - %(levelname)s - %(message)s", + handlers=[ + logging.StreamHandler(), # Log to console + logging.FileHandler("app.log", mode='a') # Log to file + ] +) + +logger = logging.getLogger("DetectionService") diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..2db483f --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,11 @@ +services: + digifi-bank-emulator: + build: . + ports: + - "6337:5000" + environment: + - FLASK_APP=app.py + - FLASK_RUN_HOST=0.0.0.0 + volumes: + - .:/app + restart: always diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..232eb1c --- /dev/null +++ b/requirements.txt @@ -0,0 +1,14 @@ +# Flask and Extensions +Flask==2.3.3 +# Flask-SQLAlchemy==3.0.5 +Flask-Marshmallow==0.15.0 +marshmallow==3.19.0 +Flask-Cors==3.0.10 +# marshmallow-sqlalchemy==0.29.0 +# mysqlclient==2.2.0 + + + +# Logging (Python Standard Library, for reference) + +