From cb8e322c1093ae609db7aa7f1a63f56f1641b841 Mon Sep 17 00:00:00 2001 From: khushal Date: Thu, 2 Jan 2025 11:53:13 +0000 Subject: [PATCH] (20250102) Alerts on payment events. --- controllers_v2/core/base.py | 4 +- controllers_v2/finstitutions/payments/base.py | 2 - .../payments/safaricom_mpesa_express.py | 22 ++++++---- models/core/payment.py | 41 +++++++++++++++++++ .../safaricom/controllers/m_pesa_express.py | 2 - 5 files changed, 57 insertions(+), 14 deletions(-) diff --git a/controllers_v2/core/base.py b/controllers_v2/core/base.py index 8311208..16f17cf 100644 --- a/controllers_v2/core/base.py +++ b/controllers_v2/core/base.py @@ -46,7 +46,7 @@ from utils_v2.api.codes import StatusCodes from icecream import IceCreamDebugger # To work with datatypes: -from typing import List +from typing import List, Literal # ***************************************************************************************************************** @@ -141,7 +141,7 @@ class CoreBaseModel: self, message: str, session_token = None, - alert_type = "error" + alert_type: Literal["info", "warn", "error"] = "error" ): """ diff --git a/controllers_v2/finstitutions/payments/base.py b/controllers_v2/finstitutions/payments/base.py index 0b99e44..e18a2fb 100644 --- a/controllers_v2/finstitutions/payments/base.py +++ b/controllers_v2/finstitutions/payments/base.py @@ -351,8 +351,6 @@ class PaymentsController(CoreAuthTokenController, ABC): session_token = session_token ) - print("RECEIPT ADD SQL JSON:", json.to_string(sql_json, default=str)) - # Return the response: if sql_json["status"] == 1: return True else: return False diff --git a/controllers_v2/finstitutions/payments/safaricom_mpesa_express.py b/controllers_v2/finstitutions/payments/safaricom_mpesa_express.py index 74fe89d..e82bc72 100644 --- a/controllers_v2/finstitutions/payments/safaricom_mpesa_express.py +++ b/controllers_v2/finstitutions/payments/safaricom_mpesa_express.py @@ -296,11 +296,11 @@ class SafaricomMPesaExpressPaymentsController(PaymentsController): code_map = { 0: { "status": "settled", - "message": "Payment successful :)" + "message": "Payment successful (0)." }, # ... Success 1037: { "status": "failed", - "message": "The payment gateway could not reach your customer." + "message": "The payment gateway could not reach your customer (1037)." }, # ... DS Timeout. User could not be reached. 1025: { "status": "failed", @@ -312,23 +312,23 @@ class SafaricomMPesaExpressPaymentsController(PaymentsController): }, # ... System error while trying to send the push request. 1032: { "status": "rejected", - "message": "Your customer declined the payment request." + "message": "Your customer declined the payment request (1032)." }, # ... Request Cancelled by the user. 1: { "status": "failed", - "message": "Your customer has insufficient balance." + "message": "Your customer has insufficient balance (1)." }, # ... The user has insufficient balance. 2001: { "status": "failed", - "message": "The payment gateway says your credentials are invalid." + "message": "The payment gateway says your credentials are invalid (2001)." }, # ... Invalid credentials of the initiator. 1019: { "status": "failed", - "message": "The transaction expired before your customer processed it." + "message": "The transaction expired before your customer processed it (1019)." }, # ... Transaction expired. 1001: { "status": "failed", - "message": "Your customer is already in the middle of some transaction on the payment gateway." + "message": "Your customer is already in the middle of some transaction on the payment gateway (1001)." }, # ... The payer is already making some transaction. } @@ -366,9 +366,15 @@ class SafaricomMPesaExpressPaymentsController(PaymentsController): client_reference_id = pg_reference_id ) + # Send an alert for debugging: + await self.send_alert( + payment.to_markdown(include_events = True), + alert_type = "info" + ) + # For when the payment is successful: if pg_result_code in [0]: - await self.add_receipt( + receipt_added = await self.add_receipt( sql_conn = sql_conn, mongo_data_conn = mongo_data_conn, payment = payment, diff --git a/models/core/payment.py b/models/core/payment.py index 4896c0f..16db12e 100644 --- a/models/core/payment.py +++ b/models/core/payment.py @@ -40,6 +40,7 @@ from pydantic import BaseModel, Field, field_validator, PastDatetime, AwareDatet from typing import Optional, Literal, Union, List, Any # My utils: +from utils_v2.string import json from utils_v2.string import regex from utils_v2.date_time import date_time @@ -252,6 +253,19 @@ class PaymentEvent(BaseModel): def parse_date_time(cls, value): return date_time.parse_date_time(input_value = value, timezone = date_time.TIMEZONE_UTC) + # ┏┓ ┏┓ + # ┃ ┓┏┏╋┏┓┏┳┓ ┣ ┓┏┏┓┏┏ + # ┗┛┗┻┛┗┗┛┛┗┗ ┻ ┗┻┛┗┗┛ + + def to_markdown(self): + message = f"Time: `{self.eventTs}`\n" + message += f"Brief: `{self.message}`\n" + message += f"initialized by PG: `{self.initByPG}`\n" + message += "```\n" + message += json.to_string(self.payload, default = str) + message += "\n```" + return message + # --------------------------------------------------------------------------------------------------------------------- @@ -373,6 +387,7 @@ class CorePaymentModel(BaseModel): "customer": self.customer.model_dump(), "ts": self.ts.isoformat(), "lastEventTs": self.lastEventTs, + "lastEventMessage": self.lastEventMessage, "lastPaymentStatus": self.lastPaymentStatus, "amount": self.amount, "currencyCode": self.currencyCode, @@ -435,6 +450,32 @@ class CorePaymentModel(BaseModel): if value is None: value = [] return value + # ┏┓ ┏┓ + # ┃ ┓┏┏╋┏┓┏┳┓ ┣ ┓┏┏┓┏┏ + # ┗┛┗┻┛┗┗┛┛┗┗ ┻ ┗┻┛┗┗┛ + + def to_markdown(self, include_events: bool = False): + + # Build the base message: + message = f"*PAYMENT RECORD*:\n\n" + message += f"Requested `{self.currencyCode} {self.amount}` from the user identified by/as:\n" + message += "```\n" + message += json.to_string(self.customer.model_dump(), default = str) + message += "\n```\n\n" + message += f"Last Status: `{self.lastPaymentStatus}`\n" + message += f"Last Message: `{self.lastEventMessage}`\n\n" + + # Build the events (if asked): + if include_events: + message += "Here are all the events for your reference:\n\n" + for index, event in enumerate(self.events): + message += f"*EVENT {index:0>2}*.\n" + message += event.to_markdown() + message += "\n" + + # Done here: + return message + # ***************************************************************************************************************** # ***** **** diff --git a/utils_v2/payments/safaricom/controllers/m_pesa_express.py b/utils_v2/payments/safaricom/controllers/m_pesa_express.py index 4014fe7..576ccf1 100644 --- a/utils_v2/payments/safaricom/controllers/m_pesa_express.py +++ b/utils_v2/payments/safaricom/controllers/m_pesa_express.py @@ -339,8 +339,6 @@ class SafaricomMPesaExpress: "TransactionDesc": description[:13] if len(description) > 13 else description } - print("HEADERS:", input_headers) - # Make the API call: api_response = await self.__post( url = r"https://api.safaricom.co.ke/mpesa/stkpush/v1/processrequest",