commit e77edb9b45a77208ada9a4664b74033749e0d8eb Author: CHIEFSOFT\ameye Date: Thu Mar 20 20:59:29 2025 -0400 first commit diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..e81c242 Binary files /dev/null and b/.DS_Store differ 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 0000000..2a7bdd5 Binary files /dev/null and b/app/__pycache__/__init__.cpython-39.pyc differ diff --git a/app/__pycache__/config.cpython-39.pyc b/app/__pycache__/config.cpython-39.pyc new file mode 100644 index 0000000..af0b39e Binary files /dev/null and b/app/__pycache__/config.cpython-39.pyc differ 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 0000000..92cc8b7 Binary files /dev/null and b/app/blueprints/__pycache__/__init__.cpython-39.pyc differ 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 0000000..d199290 Binary files /dev/null and b/app/blueprints/__pycache__/bulk_sms.cpython-39.pyc differ 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 0000000..6a873b2 Binary files /dev/null and b/app/blueprints/__pycache__/collect_loan.cpython-39.pyc differ diff --git a/app/blueprints/__pycache__/customer_consent.cpython-39.pyc b/app/blueprints/__pycache__/customer_consent.cpython-39.pyc new file mode 100644 index 0000000..b2ce75f Binary files /dev/null and b/app/blueprints/__pycache__/customer_consent.cpython-39.pyc differ diff --git a/app/blueprints/__pycache__/disbursement.cpython-39.pyc b/app/blueprints/__pycache__/disbursement.cpython-39.pyc new file mode 100644 index 0000000..2e21d52 Binary files /dev/null and b/app/blueprints/__pycache__/disbursement.cpython-39.pyc differ diff --git a/app/blueprints/__pycache__/eligibility_check.cpython-39.pyc b/app/blueprints/__pycache__/eligibility_check.cpython-39.pyc new file mode 100644 index 0000000..fb7afb7 Binary files /dev/null and b/app/blueprints/__pycache__/eligibility_check.cpython-39.pyc differ diff --git a/app/blueprints/__pycache__/lien_check.cpython-39.pyc b/app/blueprints/__pycache__/lien_check.cpython-39.pyc new file mode 100644 index 0000000..d36bdad Binary files /dev/null and b/app/blueprints/__pycache__/lien_check.cpython-39.pyc differ 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 0000000..61a8027 Binary files /dev/null and b/app/blueprints/__pycache__/loan_information.cpython-39.pyc differ diff --git a/app/blueprints/__pycache__/new_transaction_check.cpython-39.pyc b/app/blueprints/__pycache__/new_transaction_check.cpython-39.pyc new file mode 100644 index 0000000..9c3809f Binary files /dev/null and b/app/blueprints/__pycache__/new_transaction_check.cpython-39.pyc differ diff --git a/app/blueprints/__pycache__/notification_callback.cpython-39.pyc b/app/blueprints/__pycache__/notification_callback.cpython-39.pyc new file mode 100644 index 0000000..ab39e23 Binary files /dev/null and b/app/blueprints/__pycache__/notification_callback.cpython-39.pyc differ 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 0000000..ef8c783 Binary files /dev/null and b/app/blueprints/__pycache__/penal_charge.cpython-39.pyc differ 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 0000000..ab5f7a7 Binary files /dev/null and b/app/blueprints/__pycache__/provide_loan.cpython-39.pyc differ diff --git a/app/blueprints/__pycache__/rac_check.cpython-39.pyc b/app/blueprints/__pycache__/rac_check.cpython-39.pyc new file mode 100644 index 0000000..7ae117c Binary files /dev/null and b/app/blueprints/__pycache__/rac_check.cpython-39.pyc differ diff --git a/app/blueprints/__pycache__/repayment.cpython-39.pyc b/app/blueprints/__pycache__/repayment.cpython-39.pyc new file mode 100644 index 0000000..6011b2e Binary files /dev/null and b/app/blueprints/__pycache__/repayment.cpython-39.pyc differ 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 0000000..20451d7 Binary files /dev/null and b/app/blueprints/__pycache__/revoke_enable_consent.cpython-39.pyc differ 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 0000000..5edb8dd Binary files /dev/null and b/app/blueprints/__pycache__/select_offer.cpython-39.pyc differ diff --git a/app/blueprints/__pycache__/sms.cpython-39.pyc b/app/blueprints/__pycache__/sms.cpython-39.pyc new file mode 100644 index 0000000..1626b95 Binary files /dev/null and b/app/blueprints/__pycache__/sms.cpython-39.pyc differ 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 0000000..8c27e41 Binary files /dev/null and b/app/blueprints/__pycache__/token_validation.cpython-39.pyc differ 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 0000000..ddd910a Binary files /dev/null and b/app/blueprints/__pycache__/transaction_verify.cpython-39.pyc differ diff --git a/app/blueprints/bulk_sms.py b/app/blueprints/bulk_sms.py new file mode 100644 index 0000000..b06063b --- /dev/null +++ b/app/blueprints/bulk_sms.py @@ -0,0 +1,55 @@ +from marshmallow import ValidationError +from app.utils.logger import logger +from app.helpers.response_helper import ResponseHelper +from app.schemas.bulk_sms import BulkSMSSchema + +class BulkSMSService: + @staticmethod + def process_request(data): + """ + Process the Bulk SMS request. + + Args: + data (dict): The request data. + + Returns: + dict: A standardized response. + """ + try: + logger.info("Processing BulkSMS request") + + # Validate input data using BulkSMSSchema + schema = BulkSMSSchema() + validated_data = schema.load(data) # Raises ValidationError if invalid + + # Simulated Bulk SMS sending logic + response_data = { + "data": "", + "statusCode": 200, + "isSuccessful": True, + "errorMessage": None + } + + + # return ResponseHelper.success( + # data=response_data, + # message="Bulk SMS sent successfully" + # ) + + return response_data + + except ValidationError as err: + logger.error(f"Validation Error: {err.messages}") + return ResponseHelper.error( + message="Invalid input data", + status_code=400, + error=err.messages + ) + + except Exception as e: + logger.error(f"An error occurred: {str(e)}", exc_info=True) + return ResponseHelper.error( + message="An internal error occurred", + status_code=500, + error=str(e) + ) diff --git a/app/blueprints/collect_loan.py b/app/blueprints/collect_loan.py new file mode 100644 index 0000000..3912d57 --- /dev/null +++ b/app/blueprints/collect_loan.py @@ -0,0 +1,63 @@ +from flask import request +from marshmallow import ValidationError +from app.utils.logger import logger +from app.helpers.response_helper import ResponseHelper +from app.schemas.collect_loan import CollectLoanSchema + +class CollectLoanService: + @staticmethod + def process_request(data): + """ + Process the CollectLoan request. + + Args: + data (dict): The request data. + + Returns: + dict: A standardized response. + """ + try: + logger.info("Processing CollectLoan request") + + # Validate input data using CollectLoanSchema + schema = CollectLoanSchema() + validated_data = schema.load(data) # Raises ValidationError if invalid + + # Simulated processing logic + response_data = { + "transactionId": "T002", + "debtId": "273194670", + "customerId": "CN621868", + "accountId": "2017821799", + "productId": "101", + "collectAmount": 60000.00, + "penalCharge": 0, + "lienAmount": 20000, + "countryId": "01", + "comment": "Testing CollectionLoanRequest", + "resultCode": "00", + "resultDescription": "Loan Collection Successful" + } + + + # return ResponseHelper.success( + # data=response_data, + # message="Loan collection completed successfully" + # ) + return response_data + + except ValidationError as err: + logger.error(f"Validation Error: {err.messages}") + return ResponseHelper.error( + message="Invalid input data", + status_code=400, + error=err.messages + ) + + except Exception as e: + logger.error(f"An error occurred: {str(e)}", exc_info=True) + return ResponseHelper.error( + message="An internal error occurred", + status_code=500, + error=str(e) + ) diff --git a/app/blueprints/customer_consent.py b/app/blueprints/customer_consent.py new file mode 100644 index 0000000..6f33f47 --- /dev/null +++ b/app/blueprints/customer_consent.py @@ -0,0 +1,55 @@ +from flask import request +from marshmallow import ValidationError +from app.utils.logger import logger +from app.helpers.response_helper import ResponseHelper +from app.schemas.customer_consent import CustomerConsentSchema + + +class CustomerConsentService: + @staticmethod + def process_request(data): + """ + Process the CustomerConsent request. + + Args: + data (dict): The request data. + + Returns: + dict: A standardized response. + """ + try: + logger.info("Processing CustomerConsent request") + + # Validate input data using the CustomerConsent schema + schema = CustomerConsentSchema() + validated_data = schema.load(data) # Raises ValidationError if invalid + + # Simulated processing logic + response_data = { + "resultCode": "00", + "resultDescription": "Request is received" + } + + + # return ResponseHelper.success( + # data=response_data, + # message="Customer consent processed successfully" + # ) + + return response_data + + except ValidationError as err: + logger.error(f"Validation Error: {err.messages}") + return ResponseHelper.error( + message="Invalid input data", + status_code=400, + error=err.messages + ) + + except Exception as e: + logger.error(f"An error occurred: {str(e)}", exc_info=True) + return ResponseHelper.error( + message="An internal error occurred", + status_code=500, + error=str(e) + ) diff --git a/app/blueprints/disbursement.py b/app/blueprints/disbursement.py new file mode 100644 index 0000000..b2ad0db --- /dev/null +++ b/app/blueprints/disbursement.py @@ -0,0 +1,66 @@ +from flask import request +from marshmallow import ValidationError +from app.utils.logger import logger +from app.helpers.response_helper import ResponseHelper +from app.schemas.disbursement import DisbursementSchema + +class DisbursementService: + @staticmethod + def process_request(data): + """ + Process the Disbursement request. + + Args: + data (dict): The request data. + + Returns: + dict: A standardized response. + """ + try: + logger.info("Processing Disbursement request") + + # Validate input data using DisbursementSchema + schema = DisbursementSchema() + validated_data = schema.load(data) # Raises ValidationError if invalid + + # Simulated processing logic + response_data = { + "transactionId": "T001", + "TransactionId": "Tr201712RK9232P115", + "debtId": "273194670", + "customerId": "CN621868", + "accountId": "2017821799", + "productId": "101", + "provideAmount": 100000.0, + "collectAmountInterest": 5000, + "collectAmountMgtFee": 1000, + "collectAmountInsurance": 1000, + "collectAmountVAT": 75, + "countryId": "01", + "resultCode": "00", + "resultDescription": "Loan Request Completed Successfully!" + } + + + # return ResponseHelper.success( + # data=response_data, + # message="Disbursement completed successfully" + # ) + + return response_data + + except ValidationError as err: + logger.error(f"Validation Error: {err.messages}") + return ResponseHelper.error( + message="Invalid input data", + status_code=400, + error=err.messages + ) + + except Exception as e: + logger.error(f"An error occurred: {str(e)}", exc_info=True) + return ResponseHelper.error( + message="An internal error occurred", + status_code=500, + error=str(e) + ) diff --git a/app/blueprints/eligibility_check.py b/app/blueprints/eligibility_check.py new file mode 100644 index 0000000..3b78d74 --- /dev/null +++ b/app/blueprints/eligibility_check.py @@ -0,0 +1,77 @@ +from flask import session +from app.utils.logger import logger +from app.helpers.response_helper import ResponseHelper +from app.schemas.eligibility_check import EligibilityCheckSchema +from marshmallow import ValidationError + +class EligibilityCheckService: + @staticmethod + def process_request(data): + """ + Process the EligibilityCheck request. + + Args: + data (dict): The request data. + + Returns: + dict: A standardized response. + """ + try: + logger.info("Processing EligibilityCheck request") + + # Validate input data using Schema + schema = EligibilityCheckSchema() + validated_data = schema.load(data) # Raises an error if invalid + + # Example: Validate data, perform calculations, etc. + if not data: + return ResponseHelper.error("Invalid input data", status_code=400) + + # Simulate processing + response_data = { + "customerId": "CN621868", + "transactionId": "Tr201712RK9232P115", + "msisdn": "3451342", + "eligibleOffers": [ + { + "minamount": 5000, + "maxamount": 20000, + "productId": 101, + "offerid": 101, + "Tenor": 30 + }, + { + "minamount": 20000, + "maxamount": 50000, + "productId": 102, + "offerid": 102, + "Tenor": 60 + } + ], + "resultCode": "00", + "resultDescription": "Successful" + } + + + # Return a success response + # return ResponseHelper.success( + # data=response_data, + # message="Eligibility check completed successfully" + # ) + + return response_data + except ValidationError as err: + logger.error(f"Validation Error: {err.messages}") + return ResponseHelper.error( + message="Invalid input data", + status_code=400, + error=err.messages + ) + + except Exception as e: + logger.error(f"An error occurred during EligibilityCheck processing: {str(e)}", exc_info=True) + return ResponseHelper.error( + message="An internal error occurred", + status_code=500, + error=str(e) + ) \ No newline at end of file diff --git a/app/blueprints/lien_check.py b/app/blueprints/lien_check.py new file mode 100644 index 0000000..b3f2b3e --- /dev/null +++ b/app/blueprints/lien_check.py @@ -0,0 +1,54 @@ +from marshmallow import ValidationError +from app.utils.logger import logger +from app.helpers.response_helper import ResponseHelper +from app.schemas.lien_check import LienCheckSchema + +class LienCheckService: + @staticmethod + def process_request(data): + """ + Process the LienCheck request. + + Args: + data (dict): The request data. + + Returns: + dict: A standardized response. + """ + try: + logger.info("Processing LienCheck request") + + # Validate input data using LienCheckSchema + schema = LienCheckSchema() + validated_data = schema.load(data) # Raises ValidationError if invalid + + # Simulated lien check logic + response_data = { + "lienAmount": 20000.0, + "resultCode": "00", + "resultDescription": "Successful" + } + + + # return ResponseHelper.success( + # data=response_data, + # message="Lien check completed successfully" + # ) + + return response_data + + except ValidationError as err: + logger.error(f"Validation Error: {err.messages}") + return ResponseHelper.error( + message="Invalid input data", + status_code=400, + error=err.messages + ) + + except Exception as e: + logger.error(f"An error occurred: {str(e)}", exc_info=True) + return ResponseHelper.error( + message="An internal error occurred", + status_code=500, + error=str(e) + ) diff --git a/app/blueprints/loan_information.py b/app/blueprints/loan_information.py new file mode 100644 index 0000000..d9197f9 --- /dev/null +++ b/app/blueprints/loan_information.py @@ -0,0 +1,67 @@ +from flask import request +from marshmallow import ValidationError +from app.utils.logger import logger +from app.helpers.response_helper import ResponseHelper +from app.schemas.loan_information import LoanInformationSchema + +class LoanInformationService: + @staticmethod + def process_request(data): + """ + Process the Loan Information request. + + Args: + data (dict): The request data. + + Returns: + dict: A standardized response. + """ + try: + logger.info("Processing LoanInformation request") + + # Validate input data using the imported schema + schema = LoanInformationSchema() + validated_data = schema.load(data) # Raises ValidationError if invalid + + # Simulated processing logic + response_data = { + "customerId": "CN621868", + "loans": [ + { + "debtId": "123456789", + "loanDate": "2019-10-18 14:26:21.063", + "dueDate": "2019-11-20 14:26:21.063", + "currentLoanAmount": 8500.0, + "initialLoanAmount": 10000.0, + "defaultFee": 0.0, + "continuousFee": 0.0, + "productId": "101" + } + ], + "resultCode": "00", + "resultDescription": "Successful" + } + + + # return ResponseHelper.success( + # data=response_data, + # message="Loan information retrieved successfully" + # ) + + return response_data + + except ValidationError as err: + logger.error(f"Validation Error: {err.messages}") + return ResponseHelper.error( + message="Invalid input data", + status_code=400, + error=err.messages + ) + + except Exception as e: + logger.error(f"An error occurred: {str(e)}", exc_info=True) + return ResponseHelper.error( + message="An internal error occurred", + status_code=500, + error=str(e) + ) diff --git a/app/blueprints/new_transaction_check.py b/app/blueprints/new_transaction_check.py new file mode 100644 index 0000000..60461f9 --- /dev/null +++ b/app/blueprints/new_transaction_check.py @@ -0,0 +1,61 @@ +from marshmallow import ValidationError +from app.utils.logger import logger +from app.helpers.response_helper import ResponseHelper +from app.schemas.new_transaction_check import NewTransactionCheckSchema + +class NewTransactionCheckService: + @staticmethod + def process_request(data): + """ + Process the NewTransactionCheck request. + + Args: + data (dict): The request data. + + Returns: + dict: A standardized response. + """ + try: + logger.info("Processing NewTransactionCheck request") + + # Validate input data using NewTransactionCheckSchema + schema = NewTransactionCheckSchema() + validated_data = schema.load(data) # Raises ValidationError if invalid + + # Simulated transaction check logic + response_data = { + "transactionId": "24110114545374721", + "data": { + "transactionId": "241101", + "providedAmount": 1000.00, + "collectedAmount": 0.00, + "resultCode": "00", + "resultDescription": "Loan Provision is successful" + }, + "resultCode": "00", + "resultDescription": "SUCCESS" + } + + + # return ResponseHelper.success( + # data=response_data, + # message="New transaction check completed successfully" + # ) + + return response_data + + except ValidationError as err: + logger.error(f"Validation Error: {err.messages}") + return ResponseHelper.error( + message="Invalid input data", + status_code=400, + error=err.messages + ) + + except Exception as e: + logger.error(f"An error occurred: {str(e)}", exc_info=True) + return ResponseHelper.error( + message="An internal error occurred", + status_code=500, + error=str(e) + ) diff --git a/app/blueprints/notification_callback.py b/app/blueprints/notification_callback.py new file mode 100644 index 0000000..72522fa --- /dev/null +++ b/app/blueprints/notification_callback.py @@ -0,0 +1,54 @@ +from flask import request +from marshmallow import ValidationError +from app.utils.logger import logger +from app.helpers.response_helper import ResponseHelper +from app.schemas.notification_callback import NotificationCallbackSchema + +class NotificationCallbackService: + @staticmethod + def process_request(data): + """ + Process the NotificationCallback request. + + Args: + data (dict): The request data. + + Returns: + dict: A standardized response. + """ + try: + logger.info("Processing NotificationCallback request") + + # Validate input data using the NotificationCallback schema + schema = NotificationCallbackSchema() + validated_data = schema.load(data) # Raises ValidationError if invalid + + # Simulated processing logic + response_data = { + "resultCode": "00", + "resultDescription": "Successful" + } + + + # return ResponseHelper.success( + # data=response_data, + # message="Notification callback processed successfully" + # ) + + return response_data + + except ValidationError as err: + logger.error(f"Validation Error: {err.messages}") + return ResponseHelper.error( + message="Invalid input data", + status_code=400, + error=err.messages + ) + + except Exception as e: + logger.error(f"An error occurred: {str(e)}", exc_info=True) + return ResponseHelper.error( + message="An internal error occurred", + status_code=500, + error=str(e) + ) diff --git a/app/blueprints/penal_charge.py b/app/blueprints/penal_charge.py new file mode 100644 index 0000000..5cc9df4 --- /dev/null +++ b/app/blueprints/penal_charge.py @@ -0,0 +1,55 @@ +from flask import request +from marshmallow import ValidationError +from app.utils.logger import logger +from app.helpers.response_helper import ResponseHelper +from app.schemas.penal_charge import PenalChargeSchema + + +class PenalChargeService: + @staticmethod + def process_request(data): + """ + Process the PenalCharge request. + + Args: + data (dict): The request data. + + Returns: + dict: A standardized response. + """ + try: + logger.info("Processing PenalCharge request") + + # Validate input data using PenalChargeSchema + schema = PenalChargeSchema() + validated_data = schema.load(data) # Raises ValidationError if invalid + + # Simulated processing logic + response_data = { + "resultCode": "00", + "resultDescription": "Penal charge debited successfully" + } + + + # return ResponseHelper.success( + # data=response_data, + # message="Penal charge applied successfully" + # ) + + return response_data + + except ValidationError as err: + logger.error(f"Validation Error: {err.messages}") + return ResponseHelper.error( + message="Invalid input data", + status_code=400, + error=err.messages + ) + + except Exception as e: + logger.error(f"An error occurred: {str(e)}", exc_info=True) + return ResponseHelper.error( + message="An internal error occurred", + status_code=500, + error=str(e) + ) diff --git a/app/blueprints/provide_loan.py b/app/blueprints/provide_loan.py new file mode 100644 index 0000000..3c84d45 --- /dev/null +++ b/app/blueprints/provide_loan.py @@ -0,0 +1,59 @@ +from flask import request +from marshmallow import ValidationError +from app.utils.logger import logger +from app.helpers.response_helper import ResponseHelper +from app.schemas.provide_loan import ProvideLoanSchema + +class ProvideLoanService: + @staticmethod + def process_request(data): + """ + Process the ProvideLoan request. + + Args: + data (dict): The request data. + + Returns: + dict: A standardized response. + """ + try: + logger.info("Processing ProvideLoan request") + + # Validate input data using the imported schema + schema = ProvideLoanSchema() + validated_data = schema.load(data) # Raises ValidationError if invalid + + # Business logic - providing a loan + response_data ={ + "requestId": "202111170001371256908", + "transactionId": "Tr201712RK9232P115", + "customerId": "CN621868", + "accountId": "ACN8263457", + "msisdn": "3451342", + "resultCode": "00", + "resultDescription": "Successful" + } + + + # return ResponseHelper.success( + # data=response_data, + # message="Loan successfully provided" + # ) + + return response_data + + except ValidationError as err: + logger.error(f"Validation Error: {err.messages}") + return ResponseHelper.error( + message="Invalid input data", + status_code=400, + error=err.messages + ) + + except Exception as e: + logger.error(f"An error occurred: {str(e)}", exc_info=True) + return ResponseHelper.error( + message="An internal error occurred", + status_code=500, + error=str(e) + ) diff --git a/app/blueprints/rac_check.py b/app/blueprints/rac_check.py new file mode 100644 index 0000000..7a1397e --- /dev/null +++ b/app/blueprints/rac_check.py @@ -0,0 +1,67 @@ +from flask import request +from marshmallow import ValidationError +from app.utils.logger import logger +from app.helpers.response_helper import ResponseHelper +from app.schemas.rac_check import RACCheckSchema + +class RACCheckService: + @staticmethod + def process_request(data): + """ + Process the RACCheck request. + + Args: + data (dict): The request data. + + Returns: + dict: A standardized response. + """ + try: + logger.info("Processing RACCheck request") + + # Validate input data using RACCheckSchema + schema = RACCheckSchema() + validated_data = schema.load(data) # Raises ValidationError if invalid + + # Simulated processing logic + response_data = { + "resultCode": "00", + "RACResponse": { + "SalaryAccount": "1", + "BVN": "1", + "BVNAttachedToAccount": "1", + "CRMS": "1", + "CRC": "1", + "AccountStatus": "1", + "Lien": "1", + "NoBouncedCheck": "1", + "Whitelist": "1", + "NoPastDueSalaryLoan": "1", + "NoPastDueOtherLoan": "1" + }, + "resultDescription": "RAC Check Successful" + } + + + # return ResponseHelper.success( + # data=response_data, + # message="RAC check completed successfully" + # ) + + return response_data + + except ValidationError as err: + logger.error(f"Validation Error: {err.messages}") + return ResponseHelper.error( + message="Invalid input data", + status_code=400, + error=err.messages + ) + + except Exception as e: + logger.error(f"An error occurred: {str(e)}", exc_info=True) + return ResponseHelper.error( + message="An internal error occurred", + status_code=500, + error=str(e) + ) diff --git a/app/blueprints/repayment.py b/app/blueprints/repayment.py new file mode 100644 index 0000000..fbedf68 --- /dev/null +++ b/app/blueprints/repayment.py @@ -0,0 +1,54 @@ +from flask import request +from marshmallow import ValidationError +from app.utils.logger import logger +from app.helpers.response_helper import ResponseHelper +from app.schemas.repayment import RepaymentSchema + +class RepaymentService: + @staticmethod + def process_request(data): + """ + Process the Repayment request. + + Args: + data (dict): The request data. + + Returns: + dict: A standardized response. + """ + try: + logger.info("Processing Repayment request") + + # Validate input data using the Repayment schema + schema = RepaymentSchema() + validated_data = schema.load(data) # Raises ValidationError if invalid + + # Simulated processing logic + response_data = { + "repayment_id": "67890", + "status": "Paid", + "amount": validated_data.get("amount", 0), # Example: Use validated field + } + + # return ResponseHelper.success( + # data=response_data, + # message="Repayment processed successfully" + # ) + + return response_data + + except ValidationError as err: + logger.error(f"Validation Error: {err.messages}") + return ResponseHelper.error( + message="Invalid input data", + status_code=400, + error=err.messages + ) + + except Exception as e: + logger.error(f"An error occurred: {str(e)}", exc_info=True) + return ResponseHelper.error( + message="An internal error occurred", + status_code=500, + error=str(e) + ) diff --git a/app/blueprints/revoke_enable_consent.py b/app/blueprints/revoke_enable_consent.py new file mode 100644 index 0000000..4379d7a --- /dev/null +++ b/app/blueprints/revoke_enable_consent.py @@ -0,0 +1,58 @@ +from flask import request +from marshmallow import ValidationError +from app.utils.logger import logger +from app.helpers.response_helper import ResponseHelper +from app.schemas.revoke_enable_consent import RevokeEnableConsentSchema + + +class RevokeEnableConsentService: + @staticmethod + def process_request(data): + """ + Process the RevokeEnableConsent request. + + Args: + data (dict): The request data. + + Returns: + dict: A standardized response. + """ + try: + logger.info("Processing RevokeEnableConsent request") + + # Validate input data using RevokeEnableConsentSchema + schema = RevokeEnableConsentSchema() + validated_data = schema.load(data) # Raises ValidationError if invalid + + # Simulated processing logic + response_data = { + "type": "RevokeEnableConsentResponse", + "customerId": "CN621868", + "accountId": "2017821799", + "resultCode": "00", + "resultDescription": "Success" + } + + + # return ResponseHelper.success( + # data=response_data, + # message="Consent revocation processed successfully" + # ) + + return response_data + + except ValidationError as err: + logger.error(f"Validation Error: {err.messages}") + return ResponseHelper.error( + message="Invalid input data", + status_code=400, + error=err.messages + ) + + except Exception as e: + logger.error(f"An error occurred: {str(e)}", exc_info=True) + return ResponseHelper.error( + message="An internal error occurred", + status_code=500, + error=str(e) + ) diff --git a/app/blueprints/select_offer.py b/app/blueprints/select_offer.py new file mode 100644 index 0000000..5a21f16 --- /dev/null +++ b/app/blueprints/select_offer.py @@ -0,0 +1,109 @@ +from flask import request +from marshmallow import ValidationError +from app.utils.logger import logger +from app.helpers.response_helper import ResponseHelper +from app.schemas.select_offer import SelectOfferSchema + +class SelectOfferService: + @staticmethod + def process_request(data): + """ + Process the SelectOffer request. + + Args: + data (dict): The request data. + + Returns: + dict: A standardized response. + """ + try: + logger.info("Processing SelectOffer request") + + # Validate input data using the imported schema + schema = SelectOfferSchema() + validated_data = schema.load(data) # Raises ValidationError if invalid + + # Business logic - selecting an offer + response_data = { + "outstandingDebtAmount": 0, + "requestId": "202111170001371256908", + "transactionId": "1231231321232", + "customerId": "1256907", + "accountId": "5948306019", + "offers": [ + { + "offerId": "14451", + "productId": "2030", + "amount": 10000.0, + "upfrontPayment": 1000.0, + "interestRate": 3.0, + "managementRate": 1.0, + "managementFee": 1.0, + "insuranceRate": 1.0, + "insuranceFee": 100.0, + "vatRate": 7.5, + "vatAmount": 100.0, + "recommendedRepaymentDates": ["2022-11-30"], + "installmentAmount": 11000.0, + "totalRepaymentAmount": 11000.0 + }, + { + "offerId": "16645", + "productId": "2060", + "amount": 10000.0, + "upfrontPayment": 0.0, + "interestRate": 3.0, + "managementRate": 1.0, + "managementFee": 1.0, + "insuranceRate": 1.0, + "insuranceFee": 100.0, + "vatRate": 7.5, + "vatAmount": 100.0, + "recommendedRepaymentDates": ["2022-11-30", "2023-12-30"], + "installmentAmount": 5761.9, + "totalRepaymentAmount": 11523.8 + }, + { + "offerId": "122212", + "productId": "2090", + "amount": 10000.0, + "upfrontPayment": 0.0, + "interestRate": 10.0, + "managementRate": 1.0, + "managementFee": 1.0, + "insuranceRate": 1.0, + "insuranceFee": 100.0, + "vatRate": 7.5, + "vatAmount": 100.0, + "recommendedRepaymentDates": ["2022-11-30", "2022-12-30", "2023-01-29"], + "installmentAmount": 4021.15, + "totalRepaymentAmount": 12063.45 + } + ], + "resultCode": "00", + "resultDescription": "Successful" + } + + + # return ResponseHelper.success( + # data=response_data, + # message="Offer selection completed successfully" + # ) + + return response_data + + except ValidationError as err: + logger.error(f"Validation Error: {err.messages}") + return ResponseHelper.error( + message="Invalid input data", + status_code=400, + error=err.messages + ) + + except Exception as e: + logger.error(f"An error occurred: {str(e)}", exc_info=True) + return ResponseHelper.error( + message="An internal error occurred", + status_code=500, + error=str(e) + ) diff --git a/app/blueprints/sms.py b/app/blueprints/sms.py new file mode 100644 index 0000000..08ce9a3 --- /dev/null +++ b/app/blueprints/sms.py @@ -0,0 +1,56 @@ +from marshmallow import ValidationError +from app.utils.logger import logger +from app.helpers.response_helper import ResponseHelper +from app.schemas.sms import SMSSchema + + +class SMSService: + @staticmethod + def process_request(data): + """ + Process the SMS request. + + Args: + data (dict): The request data. + + Returns: + dict: A standardized response. + """ + try: + logger.info("Processing SMS request") + + # Validate input data using SMSSchema + schema = SMSSchema() + validated_data = schema.load(data) # Raises ValidationError if invalid + + # Simulated SMS sending logic + response_data = { + "data": "", + "statusCode": 200, + "isSuccessful": True, + "errorMessage": None + } + + + # return ResponseHelper.success( + # data=response_data, + # message="SMS sent successfully" + # ) + + return response_data + + except ValidationError as err: + logger.error(f"Validation Error: {err.messages}") + return ResponseHelper.error( + message="Invalid input data", + status_code=400, + error=err.messages + ) + + except Exception as e: + logger.error(f"An error occurred: {str(e)}", exc_info=True) + return ResponseHelper.error( + message="An internal error occurred", + status_code=500, + error=str(e) + ) diff --git a/app/blueprints/token_validation.py b/app/blueprints/token_validation.py new file mode 100644 index 0000000..88fc77d --- /dev/null +++ b/app/blueprints/token_validation.py @@ -0,0 +1,58 @@ +from flask import request +from marshmallow import ValidationError +from app.utils.logger import logger +from app.helpers.response_helper import ResponseHelper +from app.schemas.token_validation import TokenValidationSchema + + +class TokenValidationService: + @staticmethod + def process_request(data): + """ + Process the TokenValidation request. + + Args: + data (dict): The request data. + + Returns: + dict: A standardized response. + """ + try: + logger.info("Processing TokenValidation request") + + # Validate input data using TokenValidationSchema + schema = TokenValidationSchema() + validated_data = schema.load(data) # Raises ValidationError if invalid + + # Simulated token validation logic + response_data = { + "Authenticated": True, + "AuthenticatedMessage": "The user Oluwole Olusoga has successfully authenticated!", + "ResponseCode": "00", + "ResponseMessage": "Successful", + "RequestId": "SMB1234567" + } + + + # return ResponseHelper.success( + # data=response_data, + # message="Token validation completed successfully" + # ) + + return response_data + + except ValidationError as err: + logger.error(f"Validation Error: {err.messages}") + return ResponseHelper.error( + message="Invalid input data", + status_code=400, + error=err.messages + ) + + except Exception as e: + logger.error(f"An error occurred: {str(e)}", exc_info=True) + return ResponseHelper.error( + message="An internal error occurred", + status_code=500, + error=str(e) + ) diff --git a/app/blueprints/transaction_verify.py b/app/blueprints/transaction_verify.py new file mode 100644 index 0000000..7aea3a0 --- /dev/null +++ b/app/blueprints/transaction_verify.py @@ -0,0 +1,61 @@ +from flask import request +from marshmallow import ValidationError +from app.utils.logger import logger +from app.helpers.response_helper import ResponseHelper +from app.schemas.transaction_verify import TransactionVerifySchema + + +class TransactionVerifyService: + @staticmethod + def process_request(data): + """ + Process the TransactionVerify request. + + Args: + data (dict): The request data. + + Returns: + dict: A standardized response. + """ + try: + logger.info("Processing TransactionVerify request") + + # Validate input data using TransactionVerifySchema + schema = TransactionVerifySchema() + validated_data = schema.load(data) # Raises ValidationError if invalid + + # Simulated processing logic + response_data = { + "type": "TransactionCheckResponse", + "nativeId": "FBN20191031104405CN621868", + "customerId": "CN621868", + "accountId": "2017821799", + "providedAmount": 0.0, + "collectedAmount": 7.50, + "resultCode": "00", + "resultDescription": "Collect Status retrieved successfully." + } + + + # return ResponseHelper.success( + # data=response_data, + # message="Transaction verification completed successfully" + # ) + + return response_data + + except ValidationError as err: + logger.error(f"Validation Error: {err.messages}") + return ResponseHelper.error( + message="Invalid input data", + status_code=400, + error=err.messages + ) + + except Exception as e: + logger.error(f"An error occurred: {str(e)}", exc_info=True) + return ResponseHelper.error( + message="An internal error occurred", + status_code=500, + error=str(e) + ) diff --git a/app/config.py b/app/config.py new file mode 100644 index 0000000..5e16347 --- /dev/null +++ b/app/config.py @@ -0,0 +1,10 @@ +import os + +class Config: + """Base configuration for Flask app""" + + # SQLALCHEMY_DATABASE_URI = "mysql://root:password@localhost/flask_app" + # SQLALCHEMY_TRACK_MODIFICATIONS = False + # SECRET_KEY = os.environ.get("SECRET_KEY", "your_secret_key") + + DEBUG = True \ No newline at end of file diff --git a/app/helpers/__pycache__/response_helper.cpython-39.pyc b/app/helpers/__pycache__/response_helper.cpython-39.pyc new file mode 100644 index 0000000..180c8b7 Binary files /dev/null and b/app/helpers/__pycache__/response_helper.cpython-39.pyc differ diff --git a/app/helpers/response_helper.py b/app/helpers/response_helper.py new file mode 100644 index 0000000..47faeaf --- /dev/null +++ b/app/helpers/response_helper.py @@ -0,0 +1,213 @@ +from flask import jsonify +from typing import List, Dict, Union, Optional, Any + + +class ResponseHelper: + """ + A helper class for building standardized JSON responses in Flask. + """ + + @staticmethod + def build_response( + status: bool, + message: str, + data: Optional[Union[Dict, List, str]] = None, + status_code: int = 200, + error: Optional[Union[Dict, str]] = None, + ) -> 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 0000000..563400e Binary files /dev/null and b/app/routes/__pycache__/__init__.cpython-39.pyc differ diff --git a/app/routes/__pycache__/routes.cpython-39.pyc b/app/routes/__pycache__/routes.cpython-39.pyc new file mode 100644 index 0000000..7f0db77 Binary files /dev/null and b/app/routes/__pycache__/routes.cpython-39.pyc differ 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 0000000..5b171dc Binary files /dev/null and b/app/schemas/__pycache__/__init__.cpython-39.pyc differ 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 0000000..2a414f0 Binary files /dev/null and b/app/schemas/__pycache__/bulk_sms.cpython-39.pyc differ 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 0000000..f6a902d Binary files /dev/null and b/app/schemas/__pycache__/collect_loan.cpython-39.pyc differ 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 0000000..23f6f42 Binary files /dev/null and b/app/schemas/__pycache__/customer_consent.cpython-39.pyc differ diff --git a/app/schemas/__pycache__/disbursement.cpython-39.pyc b/app/schemas/__pycache__/disbursement.cpython-39.pyc new file mode 100644 index 0000000..6cbc98d Binary files /dev/null and b/app/schemas/__pycache__/disbursement.cpython-39.pyc differ 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 0000000..0cf83e6 Binary files /dev/null and b/app/schemas/__pycache__/eligibility_check.cpython-39.pyc differ 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 0000000..89440e6 Binary files /dev/null and b/app/schemas/__pycache__/lien_check.cpython-39.pyc differ 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 0000000..cbba475 Binary files /dev/null and b/app/schemas/__pycache__/loan_information.cpython-39.pyc differ 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 0000000..8fd2c20 Binary files /dev/null and b/app/schemas/__pycache__/new_transaction_check.cpython-39.pyc differ 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 0000000..39516cd Binary files /dev/null and b/app/schemas/__pycache__/notification_callback.cpython-39.pyc differ 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 0000000..3ea2b3a Binary files /dev/null and b/app/schemas/__pycache__/penal_charge.cpython-39.pyc differ 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 0000000..7e6f066 Binary files /dev/null and b/app/schemas/__pycache__/provide_loan.cpython-39.pyc differ 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 0000000..151cb6d Binary files /dev/null and b/app/schemas/__pycache__/rac_check.cpython-39.pyc differ diff --git a/app/schemas/__pycache__/repayment.cpython-39.pyc b/app/schemas/__pycache__/repayment.cpython-39.pyc new file mode 100644 index 0000000..400b10d Binary files /dev/null and b/app/schemas/__pycache__/repayment.cpython-39.pyc differ 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 0000000..dd05cce Binary files /dev/null and b/app/schemas/__pycache__/revoke_enable_consent.cpython-39.pyc differ 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 0000000..691fe8b Binary files /dev/null and b/app/schemas/__pycache__/select_offer.cpython-39.pyc differ diff --git a/app/schemas/__pycache__/sms.cpython-39.pyc b/app/schemas/__pycache__/sms.cpython-39.pyc new file mode 100644 index 0000000..1358690 Binary files /dev/null and b/app/schemas/__pycache__/sms.cpython-39.pyc differ diff --git a/app/schemas/__pycache__/token_validation.cpython-39.pyc b/app/schemas/__pycache__/token_validation.cpython-39.pyc new file mode 100644 index 0000000..69311d4 Binary files /dev/null and b/app/schemas/__pycache__/token_validation.cpython-39.pyc differ 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 0000000..fa915ac Binary files /dev/null and b/app/schemas/__pycache__/transaction_verify.cpython-39.pyc differ 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 0000000..7651329 Binary files /dev/null and b/app/utils/__pycache__/logger.cpython-39.pyc differ 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) + +