Merge branch 'testing' of DigiFi/FirstCore into master

This commit is contained in:
2025-04-24 12:53:52 +00:00
committed by Gogs
20 changed files with 787 additions and 274 deletions
+36 -9
View File
@@ -1,7 +1,8 @@
from flask import Blueprint, request, jsonify, send_from_directory
from flask import Blueprint, request, jsonify
from app.api.services import RepaymentService
from app.api.services.repayment_service import RepaymentService
from app.api.services.loan_charge_service import LoanChargeService
from app.api.services.loan_service import LoanService
from app.api.services.transaction_service import TransactionService
from app.api.services.auth_service import AuthService
@@ -138,11 +139,37 @@ def get_transactions():
response = TransactionService.process_request(filters)
return response
# Repayment Endpoint
@api.route("/Repayment", methods=["POST"])
# @jwt_required()
def repayment():
data = request.get_json()
# logger.info(f"Repayment request received: {data}")
response = RepaymentService.process_request(data)
return response
@api.route('/repayments', methods=['GET'])
# @token_required
def get_all_repayments():
# Extract query parameters for filtering
filters = {
'loan_id': request.args.get('loan_id'),
'customer_id': request.args.get('customer_id'),
'product_id': request.args.get('product_id'),
'start_date': request.args.get('start_date'),
'end_date': request.args.get('end_date'),
'page': request.args.get('page', 1),
'limit': request.args.get('limit', 20)
}
# logger.info(f"Get repayments request received with filters: {filters}")
response = RepaymentService.get_all_repayments(filters)
return jsonify(response)
@api.route('/loan-charges', methods=['GET'])
# @token_required
def get_all_loan_charges():
# Extract query parameters for filtering
filters = {
'loan_id': request.args.get('loan_id'),
'code': request.args.get('code'),
'start_date': request.args.get('start_date'),
'end_date': request.args.get('end_date'),
'due_before': request.args.get('due_before'),
'due_after': request.args.get('due_after'),
'page': request.args.get('page', 1),
'limit': request.args.get('limit', 20)
}
# logger.info(f"Get loan charges request received with filters: {filters}")
response = LoanChargeService.get_all_loan_charges(filters)
return jsonify(response)
-12
View File
@@ -1,12 +0,0 @@
from marshmallow import Schema, fields
# Repayment Schema
class RepaymentSchema(Schema):
type = fields.Str(required=False)
msisdn = fields.Str(required=False) #optional
debtId = fields.Str(required=True)
productId = fields.Str(required=True)
transactionId = fields.Str(required=True)
accountId = fields.Str(required=True)
customerId = fields.Str(required=True)
channel = fields.Str(required=True)
+1
View File
@@ -6,3 +6,4 @@ from app.api.services.loan_service import LoanService
from app.api.services.auth_service import AuthService
from app.api.services.dashboard_service import DashboardService
from app.api.services.repayment_service import RepaymentService
from app.api.services.loan_charge_service import LoanChargeService
+1 -1
View File
@@ -2,7 +2,7 @@ from flask import request, jsonify
from app.api.services.base_service import BaseService
from marshmallow import ValidationError
from app.utils.logger import logger
from app.api.schemas.customer_consent import CustomerConsentSchema
from app.api.schemas.customer_consent_schema import CustomerConsentSchema
from app.api.services.base_service import BaseService
from app.api.enums import TransactionType
from app.extensions import db
+107
View File
@@ -0,0 +1,107 @@
import logging
from datetime import datetime
from flask import jsonify
from app.models.loan_charge import LoanCharge
# Configure logging
logger = logging.getLogger(__name__)
class LoanChargeService:
"""
Service class for handling loan charge-related operations.
"""
@staticmethod
def get_all_loan_charges(filters=None):
"""
Get all loan charges with optional filtering.
Args:
filters (dict, optional): Filters for the loan charges query.
Returns:
dict: A standardized response with loan charges data.
"""
try:
if filters is None:
filters = {}
# Extract filters
loan_id = filters.get('loan_id')
code = filters.get('code')
start_date = filters.get('start_date')
end_date = filters.get('end_date')
due_before = filters.get('due_before')
due_after = filters.get('due_after')
# Extract pagination parameters
page = int(filters.get('page', 1))
limit = int(filters.get('limit', 20))
# Ensure page and limit are valid
if page < 1:
page = 1
if limit < 1 or limit > 100:
limit = 20
# Convert string dates to datetime objects if provided
if start_date and isinstance(start_date, str):
start_date = datetime.fromisoformat(start_date.replace('Z', '+00:00'))
if end_date and isinstance(end_date, str):
end_date = datetime.fromisoformat(end_date.replace('Z', '+00:00'))
if due_before and isinstance(due_before, str):
due_before = datetime.fromisoformat(due_before.replace('Z', '+00:00'))
if due_after and isinstance(due_after, str):
due_after = datetime.fromisoformat(due_after.replace('Z', '+00:00'))
# Get loan charges with optional filters and pagination
loan_charges, total_count = LoanCharge.get_all_loan_charges(
loan_id=loan_id,
code=code,
start_date=start_date,
end_date=end_date,
due_before=due_before,
due_after=due_after,
page=page,
limit=limit
)
# Convert loan charges to dictionary format
loan_charges_data = []
for charge in loan_charges:
loan_charges_data.append({
'loan_id': charge.loan_id,
'code': charge.code,
'amount': charge.amount,
'percent': charge.percent,
'description': charge.description,
'due': charge.due,
'transaction_id': charge.transaction_id,
'due_date': charge.due_date.isoformat() if charge.due_date else None,
'created_at': charge.created_at.isoformat() if charge.created_at else None,
'updated_at': charge.updated_at.isoformat() if charge.updated_at else None
})
# Calculate total pages
total_pages = (total_count + limit - 1) // limit
response_data = {
'loan_charges': loan_charges_data,
'count': len(loan_charges_data),
'pagination': {
'total_count': total_count,
'total_pages': total_pages,
'current_page': page,
'limit': limit,
'has_next': page < total_pages,
'has_prev': page > 1
}
}
return response_data
except Exception as e:
logger.error(f"An error occurred: {str(e)}", exc_info=True)
return jsonify({
"message": "Internal Server Error"
}), 500
+1 -1
View File
@@ -3,7 +3,7 @@ from marshmallow import ValidationError
from app.api.enums.loan_status import LoanStatus
from app.models import Customer
from app.utils.logger import logger
from app.api.schemas.loan_status import LoanStatusSchema
from app.api.schemas.loan_status_schema import LoanStatusSchema
from app.api.services.base_service import BaseService
from app.api.enums import TransactionType
from app.extensions import db
+73 -91
View File
@@ -1,115 +1,97 @@
from flask import request, jsonify
from marshmallow import ValidationError
from app.api.enums.loan_status import LoanStatus
from app.models.customer import Customer
from app.models.loan import Loan
import logging
from datetime import datetime
from flask import jsonify
from app.models.repayment import Repayment
from app.utils.logger import logger
from app.api.schemas.repayment import RepaymentSchema
from app.api.services.base_service import BaseService
from app.api.enums import TransactionType
from threading import Thread
from app.extensions import db
class RepaymentService(BaseService):
TRANSACTION_TYPE = TransactionType.REPAYMENT
# Configure logging
logger = logging.getLogger(__name__)
class RepaymentService:
"""
Service class for handling repayment-related operations.
"""
@staticmethod
def process_request(data):
def get_all_repayments(filters=None):
"""
Process the Repayment request.
Get all repayments with optional filtering.
Args:
data (dict): The request data.
filters (dict, optional): Filters for the repayments query.
Returns:
dict: A standardized response.
dict: A standardized response with repayments data.
"""
try:
with db.session.begin():
validated_data = RepaymentService.validate_data(data, RepaymentSchema())
customer_id = validated_data.get('customerId')
request_id = validated_data.get('requestId')
loan_id = validated_data.get('debtId')
product_id = validated_data.get('productId')
account_id = validated_data.get('accountId')
customer = Customer.get_customer(customer_id)
if(RepaymentService.validate_account_ownership(account_id = account_id, customer_id = customer_id)):
if filters is None:
filters = {}
# Save the repayment details
repayment = Repayment.create_repayment(
customer_id = customer_id,
loan_id = loan_id,
product_id = product_id
# Extract filters
loan_id = filters.get('loan_id')
customer_id = filters.get('customer_id')
product_id = filters.get('product_id')
start_date = filters.get('start_date')
end_date = filters.get('end_date')
)
# Extract pagination parameters
page = int(filters.get('page', 1))
limit = int(filters.get('limit', 20))
if not repayment:
logger.error(f"Failed to save repayment details")
return jsonify({
"message": "Failed to save repayment details."
}), 400
#Update Loan status
Loan.update_status(loan_id = loan_id, status = LoanStatus.REPAID)
transaction = RepaymentService.log_transaction(validated_data = validated_data)
# Ensure page and limit are valid
if page < 1:
page = 1
if limit < 1 or limit > 100:
limit = 20
if not transaction:
logger.error(f"Failed to log transaction")
return jsonify({
"message": "Failed to log transaction."
}), 400
else:
return jsonify({
"message": "Invalid Customer or Account"
}), 400
# Convert string dates to datetime objects if provided
if start_date and isinstance(start_date, str):
start_date = datetime.fromisoformat(start_date.replace('Z', '+00:00'))
if end_date and isinstance(end_date, str):
end_date = datetime.fromisoformat(end_date.replace('Z', '+00:00'))
# Get repayments with optional filters and pagination
repayments, total_count = Repayment.get_all_repayments(
loan_id=loan_id,
customer_id=customer_id,
product_id=product_id,
start_date=start_date,
end_date=end_date,
page=page,
limit=limit
)
# Simulated processing logic
response_data = {
"customerId": customer_id,
"productId": product_id,
"debtId": loan_id,
"resultCode": "00",
"resultDescription": "Successful"
# Convert repayments to dictionary format
repayments_data = []
for repayment in repayments:
repayments_data.append({
'loan_id': repayment.loan_id,
'customer_id': repayment.customer_id,
'product_id': repayment.product_id,
'transaction_id': repayment.transaction_id,
'created_at': repayment.created_at.isoformat() if repayment.created_at else None,
'updated_at': repayment.updated_at.isoformat() if repayment.updated_at else None
})
# Calculate total pages
total_pages = (total_count + limit - 1) // limit
response_data = {
'repayments': repayments_data,
'count': len(repayments_data),
'pagination': {
'total_count': total_count,
'total_pages': total_pages,
'current_page': page,
'limit': limit,
'has_next': page < total_pages,
'has_prev': page > 1
}
}
# return ResponseHelper.success(
# data=response_data,
# message="Repayment processed successfully"
# )
# Call Kafka in a background thread
thread = Thread(target=RepaymentService.async_send_to_kafka, args=(response_data, request_id, "LOAN_REPAYMENT"))
thread.start()
db.session.commit()
return response_data
except ValidationError as err:
logger.error(f"Validation Error: {getattr(err, 'messages', str(err))}")
db.session.rollback()
return jsonify({
"message": "Validation exception"
}) , 422
except ValueError as err:
logger.error(f"{getattr(err, 'messages', str(err))}")
db.session.rollback()
return jsonify({
"message": str(err)
}) , 400
return response_data
except Exception as e:
logger.error(f"An error occurred: {str(e)}", exc_info=True)
db.session.rollback()
return jsonify({
"message": "Internal Server Error"
}) , 500
}), 500
+3 -1
View File
@@ -3,5 +3,7 @@ from .account import Account
from .loan import Loan
from .transaction import Transaction
from .user import User
from .repayment import Repayment
from .loan_charge import LoanCharge
__all__ = ['Customer', 'Account', 'Loan', 'Transaction', User]
__all__ = ['Customer', 'Account', 'Loan', 'Transaction', 'User', 'Repayment', 'LoanCharge']
+90
View File
@@ -0,0 +1,90 @@
from datetime import datetime, timezone
from app.extensions import db
from sqlalchemy.exc import IntegrityError
class LoanCharge(db.Model):
__tablename__ = 'loan_charges'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
loan_id = db.Column(db.Integer, nullable=False)
code = db.Column(db.String(50), nullable=False)
amount = db.Column(db.Float, default=0.00)
percent = db.Column(db.Float)
description = db.Column(db.String(255))
due = db.Column(db.Integer)
created_at = db.Column(db.DateTime, default=datetime.now(timezone.utc))
updated_at = db.Column(db.DateTime, default=datetime.now(timezone.utc), onupdate=datetime.now(timezone.utc))
transaction_id = db.Column(db.String(50))
due_date = db.Column(db.DateTime)
@classmethod
def get_all_loan_charges(cls, loan_id=None, code=None, start_date=None, end_date=None,
due_before=None, due_after=None, page=1, limit=20):
"""
Get all loan charges with optional filtering
Args:
loan_id (int, optional): Filter by loan ID
code (str, optional): Filter by charge code
start_date (datetime, optional): Filter by start date (created_at)
end_date (datetime, optional): Filter by end date (created_at)
due_before (datetime, optional): Filter charges due before this date
due_after (datetime, optional): Filter charges due after this date
page (int, optional): Page number for pagination
limit (int, optional): Number of items per page
Returns:
tuple: (list of LoanCharge objects, total count)
"""
query = cls.query
# Apply filters if provided
if loan_id:
query = query.filter(cls.loan_id == loan_id)
if code:
query = query.filter(cls.code == code)
if start_date:
query = query.filter(cls.created_at >= start_date)
if end_date:
query = query.filter(cls.created_at <= end_date)
if due_before:
query = query.filter(cls.due_date <= due_before)
if due_after:
query = query.filter(cls.due_date >= due_after)
# Order by created_at descending (newest first)
query = query.order_by(cls.created_at.desc())
# Get total count before pagination
total_count = query.count()
# Apply pagination
offset = (page - 1) * limit
query = query.limit(limit).offset(offset)
return query.all(), total_count
def to_dict(self):
"""
Convert the LoanCharge object to a dictionary format for JSON serialization.
"""
return {
'loan_id': self.loan_id,
'code': self.code,
'amount': self.amount,
'percent': self.percent,
'description': self.description,
'due': self.due,
'transaction_id': self.transaction_id,
'due_date': self.due_date.isoformat() if self.due_date else None,
'created_at': self.created_at.isoformat() if self.created_at else None,
'updated_at': self.updated_at.isoformat() if self.updated_at else None
}
def __repr__(self):
return f'<LoanCharge {self.id}>'
+58 -31
View File
@@ -1,51 +1,78 @@
from datetime import datetime, timezone
from app.api.enums.loan_status import LoanStatus
from app.extensions import db
from app.models.customer import Customer
from app.models.loan import Loan
from sqlalchemy.exc import IntegrityError
class Repayment(db.Model):
__tablename__ = 'repayments'
id = db.Column(
db.Integer,
primary_key=True,
autoincrement=True,
)
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
loan_id = db.Column(db.String(50), nullable=False)
customer_id = db.Column(db.String(50), nullable=False)
product_id = db.Column(db.String(20), nullable=True)
product_id = db.Column(db.String(50), nullable=False)
created_at = db.Column(db.DateTime, default=datetime.now(timezone.utc))
updated_at = db.Column(db.DateTime, default=datetime.now(timezone.utc), onupdate=datetime.now(timezone.utc))
transaction_id = db.Column(db.String(50))
@classmethod
def create_repayment(cls, customer_id, loan_id, product_id):
def get_all_repayments(cls, loan_id=None, customer_id=None, product_id=None,
start_date=None, end_date=None, page=1, limit=20):
"""
Get all repayments with optional filtering
# Check customer exists
if not Customer.is_valid_customer(customer_id):
raise ValueError("Invalid customer")
Args:
loan_id (str, optional): Filter by loan ID
customer_id (str, optional): Filter by customer ID
product_id (str, optional): Filter by product ID
start_date (datetime, optional): Filter by start date (created_at)
end_date (datetime, optional): Filter by end date (created_at)
page (int, optional): Page number for pagination
limit (int, optional): Number of items per page
# Check loan exists
loan = Loan.get_customer_loan(loan_id = loan_id, customer_id = customer_id)
# Check that the loan is active
if loan.status != LoanStatus.ACTIVE:
raise ValueError(f"Repayment cannot be processed. Loan status: ({loan.status})")
Returns:
tuple: (list of Repayment objects, total count)
"""
query = cls.query
repayment = cls(
customer_id=customer_id,
loan_id=loan_id,
product_id=product_id,
)
# Apply filters if provided
if loan_id:
query = query.filter(cls.loan_id == loan_id)
try:
db.session.add(repayment)
except IntegrityError as err:
raise ValueError(f"Database integrity error: {err}")
if customer_id:
query = query.filter(cls.customer_id == customer_id)
return repayment
if product_id:
query = query.filter(cls.product_id == product_id)
if start_date:
query = query.filter(cls.created_at >= start_date)
if end_date:
query = query.filter(cls.created_at <= end_date)
# Order by created_at descending (newest first)
query = query.order_by(cls.created_at.desc())
# Get total count before pagination
total_count = query.count()
# Apply pagination
offset = (page - 1) * limit
query = query.limit(limit).offset(offset)
return query.all(), total_count
def to_dict(self):
"""
Convert the Repayment object to a dictionary format for JSON serialization.
"""
return {
'loan_id': self.loan_id,
'customer_id': self.customer_id,
'product_id': self.product_id,
'transaction_id': self.transaction_id,
'created_at': self.created_at.isoformat() if self.created_at else None,
'updated_at': self.updated_at.isoformat() if self.updated_at else None
}
def __repr__(self):
return f'<Repayment {self.id}>'
return f'<Repayment {self.id}>'
+19 -8
View File
@@ -65,8 +65,16 @@
}
},
{
"name": "Repayment",
"description": "Repayment Request.",
"name": "Repayments",
"description": "Get all repayments with optional filtering.",
"externalDocs": {
"description": "Find out more",
"url": "https://www.simbrellang.net"
}
},
{
"name": "Loan Charges",
"description": "Get all loan charges with optional filtering.",
"externalDocs": {
"description": "Find out more",
"url": "https://www.simbrellang.net"
@@ -89,8 +97,11 @@
"/transactions": {
"$ref": "../swagger/paths/Transactions.json"
},
"/Repayment": {
"$ref": "../swagger/paths/Repayment.json"
"/repayments": {
"$ref": "../swagger/paths/Repayments.json"
},
"/loan-charges": {
"$ref": "../swagger/paths/LoanCharges.json"
}
},
"components": {
@@ -119,11 +130,11 @@
"TransactionsResponse": {
"$ref": "../swagger/schemas/TransactionsResponse.json"
},
"RepaymentRequest": {
"$ref": "../swagger/schemas/RepaymentRequest.json"
"RepaymentsResponse": {
"$ref": "../swagger/schemas/RepaymentsResponse.json"
},
"RepaymentResponse": {
"$ref": "../swagger/schemas/RepaymentResponse.json"
"LoanChargesResponse": {
"$ref": "../swagger/schemas/LoanChargesResponse.json"
}
},
"securitySchemes": {
+117
View File
@@ -0,0 +1,117 @@
{
"get": {
"tags": ["Loan Charges"],
"summary": "Get all loan charges with optional filtering",
"description": "Retrieve loan charges with various filter options including loan ID, charge code, etc.",
"operationId": "getLoanCharges",
"parameters": [
{
"name": "loan_id",
"in": "query",
"description": "Filter by loan ID",
"required": false,
"schema": {
"type": "integer"
},
"example": 7463
},
{
"name": "code",
"in": "query",
"description": "Filter by charge code",
"required": false,
"schema": {
"type": "string"
},
"example": "INTEREST"
},
{
"name": "start_date",
"in": "query",
"description": "Filter by start date (ISO format)",
"required": false,
"schema": {
"type": "string",
"format": "date-time"
},
"example": "2023-01-01T00:00:00Z"
},
{
"name": "end_date",
"in": "query",
"description": "Filter by end date (ISO format)",
"required": false,
"schema": {
"type": "string",
"format": "date-time"
},
"example": "2023-12-31T23:59:59Z"
},
{
"name": "due_before",
"in": "query",
"description": "Filter charges due before this date (ISO format)",
"required": false,
"schema": {
"type": "string",
"format": "date-time"
},
"example": "2023-12-31T23:59:59Z"
},
{
"name": "due_after",
"in": "query",
"description": "Filter charges due after this date (ISO format)",
"required": false,
"schema": {
"type": "string",
"format": "date-time"
},
"example": "2023-01-01T00:00:00Z"
},
{
"name": "page",
"in": "query",
"description": "Page number for pagination",
"required": false,
"schema": {
"type": "integer",
"default": 1,
"minimum": 1
},
"example": 1
},
{
"name": "limit",
"in": "query",
"description": "Number of items per page (max 100)",
"required": false,
"schema": {
"type": "integer",
"default": 20,
"minimum": 1,
"maximum": 100
},
"example": 20
}
],
"responses": {
"200": {
"description": "Successful operation",
"content": {
"application/json": {
"schema": {
"$ref": "../schemas/LoanChargesResponse.json"
}
}
}
},
"400": {
"description": "Invalid request"
},
"500": {
"description": "Internal server error"
}
}
}
}
-56
View File
@@ -1,56 +0,0 @@
{
"post": {
"tags": [
"Repayment"
],
"summary": "Repayment Request",
"description": "Repayment Request",
"operationId": "Repayment",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "../schemas/RepaymentRequest.json"
}
},
"application/xml": {
"schema": {
"$ref": "../schemas/RepaymentRequest.json"
}
},
"application/x-www-form-urlencoded": {
"schema": {
"$ref": "../schemas/RepaymentRequest.json"
}
}
}
},
"responses": {
"200": {
"description": "Repayment Successful",
"content": {
"application/json": {
"schema": {
"$ref": "../schemas/RepaymentResponse.json"
}
},
"application/xml": {
"schema": {
"$ref": "../schemas/RepaymentResponse.json"
}
}
}
},
"400": {
"description": "Invalid request"
},
"422": {
"description": "Validation exception"
},
"500": {
"description": "Internal server error"
}
}
}
}
+105
View File
@@ -0,0 +1,105 @@
{
"get": {
"tags": ["Repayments"],
"summary": "Get all repayments with optional filtering",
"description": "Retrieve repayments with various filter options including loan ID, customer ID, product ID, etc.",
"operationId": "getRepayments",
"parameters": [
{
"name": "loan_id",
"in": "query",
"description": "Filter by loan ID",
"required": false,
"schema": {
"type": "string"
},
"example": "10"
},
{
"name": "customer_id",
"in": "query",
"description": "Filter by customer ID",
"required": false,
"schema": {
"type": "string"
},
"example": "CID0000025585"
},
{
"name": "product_id",
"in": "query",
"description": "Filter by product ID",
"required": false,
"schema": {
"type": "string"
},
"example": "101"
},
{
"name": "start_date",
"in": "query",
"description": "Filter by start date (ISO format)",
"required": false,
"schema": {
"type": "string",
"format": "date-time"
},
"example": "2023-01-01T00:00:00Z"
},
{
"name": "end_date",
"in": "query",
"description": "Filter by end date (ISO format)",
"required": false,
"schema": {
"type": "string",
"format": "date-time"
},
"example": "2023-12-31T23:59:59Z"
},
{
"name": "page",
"in": "query",
"description": "Page number for pagination",
"required": false,
"schema": {
"type": "integer",
"default": 1,
"minimum": 1
},
"example": 1
},
{
"name": "limit",
"in": "query",
"description": "Number of items per page (max 100)",
"required": false,
"schema": {
"type": "integer",
"default": 20,
"minimum": 1,
"maximum": 100
},
"example": 20
}
],
"responses": {
"200": {
"description": "Successful operation",
"content": {
"application/json": {
"schema": {
"$ref": "../schemas/RepaymentsResponse.json"
}
}
}
},
"400": {
"description": "Invalid request"
},
"500": {
"description": "Internal server error"
}
}
}
}
@@ -0,0 +1,100 @@
{
"type": "object",
"properties": {
"loan_charges": {
"type": "array",
"items": {
"type": "object",
"properties": {
"loan_id": {
"type": "integer",
"example": 7463
},
"code": {
"type": "string",
"example": "INTEREST"
},
"amount": {
"type": "number",
"format": "float",
"example": 0.00
},
"percent": {
"type": "number",
"format": "float",
"example": 1.1
},
"description": {
"type": "string",
"example": "This is fee 9000"
},
"due": {
"type": "integer",
"example": 0
},
"transaction_ {
"type": "integer",
"example": 0
},
"transaction_id": {
"type": "string",
"example": "TRX123456",
"nullable": true
},
"due_date": {
"type": "string",
"format": "date-time",
"example": "2025-04-16T20:03:31.998445Z",
"nullable": true
},
"created_at": {
"type": "string",
"format": "date-time",
"example": "2025-04-16T20:03:31.998085Z"
},
"updated_at": {
"type": "string",
"format": "date-time",
"example": "2025-04-16T20:03:31.998445Z"
}
}
}
},
"count": {
"type": "integer",
"example": 1
},
"pagination": {
"type": "object",
"properties": {
"total_count": {
"type": "integer",
"example": 100
},
"total_pages": {
"type": "integer",
"example": 5
},
"current_page": {
"type": "integer",
"example": 1
},
"limit": {
"type": "integer",
"example": 20
},
"has_next": {
"type": "boolean",
"example": true
},
"has_prev": {
"type": "boolean",
"example": false
}
}
}
},
"xml": {
"name": "LoanChargesResponse"
}
}
-36
View File
@@ -1,36 +0,0 @@
{
"type": "object",
"properties": {
"msisdn": {
"type": "string",
"example": "3451342"
},
"debtId": {
"type": "string",
"example": "10"
},
"productId": {
"type": "string",
"example": "101"
},
"transactionId": {
"type": "string",
"example": "20171209232115"
},
"customerId": {
"type": "string",
"example": "CID0000025585"
},
"channel": {
"type": "string",
"example": "USSD"
},
"accountId": {
"type": "string",
"example": "ACN8263457"
}
},
"xml": {
"name": "RepaymentRequest"
}
}
@@ -1,28 +0,0 @@
{
"type": "object",
"properties": {
"customerId": {
"type": "string",
"example": "CN621868"
},
"productId": {
"type": "string",
"example": "101"
},
"debtId": {
"type": "string",
"example": "273194670"
},
"resultCode": {
"type": "string",
"example": "00"
},
"resultDescription": {
"type": "string",
"example": "Successful"
}
},
"xml": {
"name": "RepaymentResponse"
}
}
@@ -0,0 +1,76 @@
{
"type": "object",
"properties": {
"repayments": {
"type": "array",
"items": {
"type": "object",
"properties": {
"loan_id": {
"type": "string",
"example": "10"
},
"customer_id": {
"type": "string",
"example": "CID0000025585"
},
"product_id": {
"type": "string",
"example": "101"
},
"transaction_id": {
"type": "string",
"example": "TRX123456",
"nullable": true
},
"created_at": {
"type": "string",
"format": "date-time",
"example": "2025-04-10T16:45:47.879552Z"
},
"updated_at": {
"type": "string",
"format": "date-time",
"example": "2025-04-10T16:45:47.879642Z"
}
}
}
},
"count": {
"type": "integer",
"example": 1
},
"pagination": {
"type": "object",
"properties": {
"total_count": {
"type": "integer",
"example": 100
},
"total_pages": {
"type": "integer",
"example": 5
},
"current_page": {
"type": "integer",
"example": 1
},
"limit": {
"type": "integer",
"example": 20
},
"has_next": {
"type": "boolean",
"example": true
},
"has_prev": {
"type": "boolean",
"example": false
}
}
}
},
"xml": {
"name": "RepaymentsResponse"
}
}