diff --git a/api/blueprints/finstitutions/payments/request_v2.py b/api/blueprints/finstitutions/payments/request_v2.py index c497e78..7693c02 100644 --- a/api/blueprints/finstitutions/payments/request_v2.py +++ b/api/blueprints/finstitutions/payments/request_v2.py @@ -172,6 +172,12 @@ async def request_payment( message = f"No such token key." ) + # Get the user's info: + user_info = CoreUserInfoModel(**kwargs["session_info"]) + + # Add required details to the payment's metadata: + inbound_data.metadata.idUser = user_info.userId + # ┳┓ ┏┓ # ┣┫┏┓┏┓┓┏┏┓┏╋ ┃┃┏┓┓┏┏┳┓┏┓┏┓╋ # ┛┗┗ ┗┫┗┻┗ ┛┗ ┣┛┗┻┗┫┛┗┗┗ ┛┗┗ @@ -183,7 +189,7 @@ async def request_payment( sql_conn = current_app.sql_writer, mongo_data_conn = current_app.data_mongo, auth_token = auth_token, - user_info = CoreUserInfoModel(**kwargs["session_info"]), + user_info = user_info, payment_request = inbound_data ) diff --git a/controllers_v2/finstitutions/payments/base.py b/controllers_v2/finstitutions/payments/base.py index c94d8a3..0b99e44 100644 --- a/controllers_v2/finstitutions/payments/base.py +++ b/controllers_v2/finstitutions/payments/base.py @@ -37,6 +37,7 @@ sys.path.append("..") # My async utils: from utils_v2.string import json +from utils_v2.date_time import date_time from utils_v2.database.async_mysql_v2 import AsyncMySQL from utils_v2.database.async_mongo_v2 import AsyncMongo from utils_v2.cache.async_redis_cache_v2 import AsyncRedisCache @@ -334,15 +335,15 @@ class PaymentsController(CoreAuthTokenController, ABC): sql_conn = sql_conn, proc_name = "accounting_receipt_add", proc_args = ( - payment.metadata["idClient"], - payment.metadata["date"], - payment.metadata["source"], - payment.metadata["amount"], - payment.metadata["reference"], - payment.metadata["inAccount"], - payment.metadata["idUser"], - payment.metadata["documentUrl"], - payment.metadata["notes"] + payment.metadata.idClient, # .................................... p_client_id + date_time.get_current_ist_date_time().strftime("%Y-%m-%d"), # ... p_date + payment.client, # ............................................... p_source + payment.amount, # ............................................... p_amount + payment.metadata.reference, # ................................... p_reference + payment.metadata.inAccount, # ................................... p_in_account + payment.metadata.idUser, # ...................................... p_user_id + payment.metadata.documentUrl, # ................................. p_document_url + payment.metadata.notes # ........................................ p_notes ), retry_count = 1, backoff_seconds = 0.5, diff --git a/controllers_v2/finstitutions/payments/safaricom_mpesa_express.py b/controllers_v2/finstitutions/payments/safaricom_mpesa_express.py index c3f88ac..74fe89d 100644 --- a/controllers_v2/finstitutions/payments/safaricom_mpesa_express.py +++ b/controllers_v2/finstitutions/payments/safaricom_mpesa_express.py @@ -367,11 +367,12 @@ class SafaricomMPesaExpressPaymentsController(PaymentsController): ) # For when the payment is successful: - if pg_result_code in []: + if pg_result_code in [0]: await self.add_receipt( sql_conn = sql_conn, mongo_data_conn = mongo_data_conn, - payment = payment + payment = payment, + session_token = None ) # Done here: diff --git a/models/api/finstitutions/payments/request.py b/models/api/finstitutions/payments/request.py index 6286b9d..ad275a9 100644 --- a/models/api/finstitutions/payments/request.py +++ b/models/api/finstitutions/payments/request.py @@ -41,13 +41,6 @@ from typing import Optional, Literal, Union, List, Any # My utils: from utils_v2.string import regex -from utils_v2.date_time import date_time - -# Models: -from models.core.payment import CorePaymentModel, PaymentMetadata - -# To work with date and time: -import datetime # To work with MongoDB: from bson.objectid import ObjectId @@ -108,6 +101,52 @@ class PGPaymentRequestHeaders(BaseModel): # --------------------------------------------------------------------------------------------------------------------- +class PaymentRequestMetadata(BaseModel): + + """ + These are things that the UI will send you, and you must use them as-is for adding receipts. Their internal workings + and use-cases are not known and are not of this code's direct concern except that you must ensure that these details + get delivered through the mechanism of adding receipts. + """ + + idUser: int | None = Field(frozen = False, default = None, validate_default = True) + idClient: int = Field(frozen = True) + inAccount: int = Field(frozen = True) + reference: str | None = Field(frozen = True, default = None, validate_default = True) + againstReference: str | None = Field(frozen = True, default = None, validate_default = True) + documentUrl: str | None = Field(frozen = False, default = None, validate_default = True) + + # ┏┓ ┏• + # ┃ ┏┓┏┓╋┓┏┓ + # ┗┛┗┛┛┗┛┗┗┫ + # ┛ + + class Config: + extra = "allow" + + # ┓┏ ┓• ┓ • + # ┃┃┏┓┃┓┏┫┏┓╋┓┏┓┏┓ + # ┗┛┗┻┗┗┗┻┗┻┗┗┗┛┛┗ + + @field_validator("reference") + def validate_reference(cls, value): + if not value: value = "On Account" + return value + + @field_validator("againstReference") + def validate_against_reference(cls, value): + if not value: value = None + return value + + @field_validator("documentUrl") + def validate_document_url(cls, value): + if not value: value = None + return value + + +# --------------------------------------------------------------------------------------------------------------------- + + class PGPaymentRequestData(BaseModel): tokenKey: ObjectId = Field( @@ -159,7 +198,7 @@ class PGPaymentRequestData(BaseModel): frozen = True ) - metadata: PaymentMetadata = Field( + metadata: PaymentRequestMetadata = Field( description = "any extra information about this payment", frozen = True ) diff --git a/models/core/payment.py b/models/core/payment.py index 3366409..4896c0f 100644 --- a/models/core/payment.py +++ b/models/core/payment.py @@ -142,8 +142,9 @@ class PaymentMetadata(BaseModel): get delivered through the mechanism of adding receipts. """ - idClient: int = Field(frozen = True) - inAccount: int = Field(frozen = True) + idUser: int | None = Field(frozen = True, default = None, validate_default = True) + idClient: int | None = Field(frozen = True, default = None, validate_default = True) + inAccount: int | None = Field(frozen = True, default = None, validate_default = True) reference: str | None = Field(frozen = True, default = None, validate_default = True) againstReference: str | None = Field(frozen = True, default = None, validate_default = True) documentUrl: str | None = Field(frozen = False, default = None, validate_default = True) @@ -314,7 +315,7 @@ class CorePaymentModel(BaseModel): examples = ["INR", "USD", "KES"] ) - metadata: dict | None = Field( + metadata: PaymentMetadata = Field( description = "any arbitrary amount of data to identify the user and payment details", frozen = True )