diff --git a/api/blueprints/finstitutions/payments/callback_v2.py b/api/blueprints/finstitutions/payments/callback_v2.py index 6091ed6..a5ebd9b 100644 --- a/api/blueprints/finstitutions/payments/callback_v2.py +++ b/api/blueprints/finstitutions/payments/callback_v2.py @@ -149,6 +149,7 @@ async def safaricom_m_pesa_express_callback( # ┛┗┗┻┗┻ ┗┛┗┛┗ ┛┗┗ client_response = await current_app.safaricom_mpesa_express_controller.handle_payment_callback( + sql_conn = current_app.sql_writer, mongo_data_conn = current_app.data_mongo, inbound_data = inbound_data, inbound_headers = inbound_headers diff --git a/api/blueprints/finstitutions/payments/request_v2.py b/api/blueprints/finstitutions/payments/request_v2.py index f9b7939..c497e78 100644 --- a/api/blueprints/finstitutions/payments/request_v2.py +++ b/api/blueprints/finstitutions/payments/request_v2.py @@ -180,6 +180,7 @@ async def request_payment( # For Safaricom's M-Pesa Express client: if auth_token.client == "safaricomMPesaExpress": response = await current_app.safaricom_mpesa_express_controller.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"]), diff --git a/controllers_v2/finstitutions/payments/all_payments.py b/controllers_v2/finstitutions/payments/all_payments.py index ba90138..15328c1 100644 --- a/controllers_v2/finstitutions/payments/all_payments.py +++ b/controllers_v2/finstitutions/payments/all_payments.py @@ -139,6 +139,7 @@ class AllPaymentsController(PaymentsController): async def request_payment( self, + sql_conn: AsyncMySQL, mongo_data_conn: AsyncMongo, auth_token: CoreAuthTokenModel, user_info: CoreUserInfoModel, @@ -147,6 +148,8 @@ class AllPaymentsController(PaymentsController): """ To request payment from someone through a payment gateway. + :param sql_conn: Only needed when a payment gateway says that the credentials are invalid and the account needs + to be disabled. :param mongo_data_conn: The database connection to use to perform this activity. :param auth_token: The token that has to be used to fetch the data. :param user_info: The info. of your user, so that you can identify who requested the payment. @@ -158,17 +161,21 @@ class AllPaymentsController(PaymentsController): async def handle_payment_callback( self, + sql_conn: AsyncMySQL, mongo_data_conn: AsyncMongo, - inbound_data: dict - ): + inbound_data: dict, + inbound_headers: dict + ) -> PaymentRequestOneResult: """ Whenever the payment gateway sends an update about a requested payment, we use this method to update our records as per the specification of the third-party payment gateway. + :param sql_conn: Only needed when a payment gateway says that the credentials are invalid and the account needs + to be disabled. :param mongo_data_conn: The database connection to use to perform this activity. :param inbound_data: The data sent by the payment gateway in their update. - - :return: ?? + :param inbound_headers: The headers sent by the payment gateway in their update. + :return: A structured response about the process of updating the payment event. """ raise NotImplementedError diff --git a/controllers_v2/finstitutions/payments/base.py b/controllers_v2/finstitutions/payments/base.py index 92fe020..30d38d2 100644 --- a/controllers_v2/finstitutions/payments/base.py +++ b/controllers_v2/finstitutions/payments/base.py @@ -47,7 +47,11 @@ from controllers_v2.core.auth_token import CoreAuthTokenController from models.core.user import CoreUserInfoModel from models.core.auth_token import CoreAuthTokenModel from models.core.payment import CorePaymentModel, PaymentEvent, CustomerDetails -from models.api.finstitutions.payments.request import PGPaymentRequestData, PaymentRequestOneResult +from models.api.finstitutions.payments.request import ( + PGPaymentRequestData, + PaymentRequestOneResult, + PaymentCallbackResult +) # To work with MongoDB: from bson import ObjectId @@ -270,6 +274,34 @@ class PaymentsController(CoreAuthTokenController, ABC): # we return it as our data model: return CorePaymentModel(**record) + async def get_payment_by_client_reference_id( + self, + mongo_data_conn: AsyncMongo, + client_reference_id: str + ) -> CorePaymentModel | None: + + """ + Gets one payment detail if you know its payment id. + :param mongo_data_conn: The instance of the database connector to use for the operation. + :param client_reference_id: The way the client identifies this payment. You need to pass this only on the first + event. Typically, when you initiate the payment request. + :return: The contents of that one payment detail in a structured format. + """ + + # We fetch the whole payload of that one message: + record = await mongo_data_conn.find_one( + collection = self.PAYMENTS_COLLECTION, + filter = {"clientPaymentReferenceId": client_reference_id}, + raise_exception = True + ) + + # If no such message was found: + if record is None: return None + + # If a record was found, + # we return it as our data model: + return CorePaymentModel(**record) + # ┳┳ ┓ ┏┓ # ┃┃┏┓┏┫┏┓╋┏┓ ┃┃┏┓┓┏┏┳┓┏┓┏┓╋┏ # ┗┛┣┛┗┻┗┻┗┗ ┣┛┗┻┗┫┛┗┗┗ ┛┗┗┛ @@ -466,6 +498,7 @@ class PaymentsController(CoreAuthTokenController, ABC): @abstractmethod async def request_payment( self, + sql_conn: AsyncMySQL, mongo_data_conn: AsyncMongo, auth_token: CoreAuthTokenModel, user_info: CoreUserInfoModel, @@ -474,6 +507,8 @@ class PaymentsController(CoreAuthTokenController, ABC): """ To request payment from someone through a payment gateway. + :param sql_conn: Only needed when a payment gateway says that the credentials are invalid and the account needs + to be disabled. :param mongo_data_conn: The database connection to use to perform this activity. :param auth_token: The token that has to be used to fetch the data. :param user_info: The info. of your user, so that you can identify who requested the payment. @@ -486,18 +521,21 @@ class PaymentsController(CoreAuthTokenController, ABC): @abstractmethod async def handle_payment_callback( self, + sql_conn: AsyncMySQL, mongo_data_conn: AsyncMongo, inbound_data: dict, inbound_headers: dict - ) -> None: + ) -> PaymentCallbackResult: """ Whenever the payment gateway sends an update about a requested payment, we use this method to update our records as per the specification of the third-party payment gateway. + :param sql_conn: Only needed when a payment gateway says that the credentials are invalid and the account needs + to be disabled. :param mongo_data_conn: The database connection to use to perform this activity. :param inbound_data: The data sent by the payment gateway in their update. :param inbound_headers: The headers sent by the payment gateway in their update. - :return: ?? + :return: A structured response about the process of updating the payment event. """ pass diff --git a/controllers_v2/finstitutions/payments/safaricom_mpesa_express.py b/controllers_v2/finstitutions/payments/safaricom_mpesa_express.py index 51ddf88..cdf3872 100644 --- a/controllers_v2/finstitutions/payments/safaricom_mpesa_express.py +++ b/controllers_v2/finstitutions/payments/safaricom_mpesa_express.py @@ -168,6 +168,7 @@ class SafaricomMPesaExpressPaymentsController(PaymentsController): async def request_payment( self, + sql_conn: AsyncMySQL, mongo_data_conn: AsyncMongo, auth_token: CoreAuthTokenModel, user_info: CoreUserInfoModel, @@ -176,6 +177,8 @@ class SafaricomMPesaExpressPaymentsController(PaymentsController): """ To request payment from someone through a payment gateway. + :param sql_conn: Only needed when a payment gateway says that the credentials are invalid and the account needs + to be disabled. :param mongo_data_conn: The database connection to use to perform this activity. :param auth_token: The token that has to be used to fetch the data. :param user_info: The info. of your user, so that you can identify who requested the payment. @@ -249,6 +252,7 @@ class SafaricomMPesaExpressPaymentsController(PaymentsController): async def handle_payment_callback( self, + sql_conn: AsyncMySQL, mongo_data_conn: AsyncMongo, inbound_data: dict, inbound_headers: dict @@ -257,6 +261,8 @@ class SafaricomMPesaExpressPaymentsController(PaymentsController): """ Whenever the payment gateway sends an update about a requested payment, we use this method to update our records as per the specification of the third-party payment gateway. + :param sql_conn: Only needed when a payment gateway says that the credentials are invalid and the account needs + to be disabled. :param mongo_data_conn: The database connection to use to perform this activity. :param inbound_data: The data sent by the payment gateway in their update. :param inbound_headers: The headers sent by the payment gateway in their update. @@ -334,6 +340,24 @@ class SafaricomMPesaExpressPaymentsController(PaymentsController): client_reference_id = pg_reference_id ) + # In case the payment collector's credentials have changed, + # we mark his account as disabled: + # if pg_result_code in [2001]: + # payment = await self.get_payment_by_client_reference_id( + # mongo_data_conn = mongo_data_conn, + # client_reference_id = pg_reference_id + # ) + # auth_token = await self.get_token_from_id( + # mongo_data_conn = mongo_data_conn, + # token_id = payment.tokenId + # ) + # auth_token.status = "disabled" + # await self.set_token( + # sql_conn = sql_conn, + # mongo_data_conn = mongo_data_conn, + # auth_token = auth_token + # ) + # Done here: if event_note_success: result.success = True