def test_valid_saf_numbers() -> None: """Test valid safaricom numbers.""" def subscriber(): return random.randint(100000, 999999) gen700_708 = [f"070{i}{subscriber()}" for i in range(0, 8)] gen710_729 = [f"07{i}{subscriber()}" for i in range(10, 29)] gen757_759 = [f"07{i}{subscriber()}" for i in range(57, 59)] gen790_792 = [f"07{i}{subscriber()}" for i in range(90, 92)] safaricom_numbers = gen700_708 + gen710_729 + gen757_759 + gen790_792 for number in safaricom_numbers: _, valid = utils.saf_number_fmt(number) assert valid is True
async def c2b( self, shortcode: str = None, amount: int = None, phone_number: str = None, ) -> dict: """Simulate making payments from client to Safaricom API. Args: shortcode: a paybill number/till number, which you expect to receive payments notifications about. amount: the amount in KSh you are sending to a businees. phone_number: a valid safaricom number you are sending money from. Returns: A dict with the ResponseDescription having a value as shown. { "ConversationID": "AG_20180324_000066530b914eee3f85", "OriginatorCoversationID": "25344-885903-1", "ResponseDescription": "Accept the service request successfully." } Any other value is a failed request. Raises: ValueError: when the phone_number supplied is not a valid phone number. """ url = f"{self.base_url}{self.C2B_URL_PATH}" number, valid = saf_number_fmt(phone_number) if not valid: raise ValueError(f"{phone_number} is not a valid Safaricom number") data = { "ShortCode": shortcode, "CommandID": "CustomerPayBillOnline", "Amount": f"{amount}", "Msisdn": number, "BillRefNumber": number, } headers = await self._get_headers() async with aiohttp.ClientSession(headers=headers) as session: return await self.post(session=session, url=url, data=data)
async def stk_push( self, lipa_na_mpesa_shortcode: str = None, lipa_na_mpesa_passkey: str = None, amount: int = None, party_a: str = None, party_b: str = None, callback_url: str = None, transaction_desc: str = None, transaction_type: str = "CustomerPayBillOnline", ) -> dict: """Make an STK push. Args: lipa_na_mpesa_shortcode: shortcode of the organization initiating the request and expecting the payment. lipa_na_mpesa_passkey: lipa na mpesa pass key. amount: amount being sent from client to business. party_a: debit party of the transaction, hereby the phone number of the customer. party_b: credit party of the transaction, hereby being the shortcode of the organization. callback_url: the endpoint where you want the results of the transaction delivered. transaction_desc: a short description of the transaction. transaction_type: specifies the type of transaction being performed. Raises: ValueError: when the party_b value is not a valid Safaricom number. Returns: a dict with the ResponseCode value as 0. { "MerchantRequestID": "25353-1377561-4", "CheckoutRequestID": "ws_CO_26032018185226297", "ResponseCode": "0", "ResponseDescription": "Success. Request accepted for processing", "CustomerMessage": "Success. Request accepted for processing" } Any other value is a failed request. """ url = f"{self.base_url}{self.STK_URL_PATH}" number, valid = saf_number_fmt(party_a) if not valid: raise ValueError(f"{party_a} is not a valid Safaricom number") password, timestamp = Mpesa.generate_password(lipa_na_mpesa_shortcode, lipa_na_mpesa_passkey) data = { "BusinessShortCode": lipa_na_mpesa_shortcode, "Password": password, "Timestamp": timestamp, "TransactionType": transaction_type, "Amount": f"{amount}", "PartyA": number, "PartyB": lipa_na_mpesa_shortcode, "PhoneNumber": number, "CallBackURL": callback_url, "AccountReference": number, "TransactionDesc": transaction_desc, } headers = await self._get_headers() async with aiohttp.ClientSession(headers=headers) as session: return await self.post(session=session, url=url, data=data)
async def b2c( self, initiator_name: str = None, security_credential: str = None, command_id: str = None, amount: int = None, party_a: str = None, party_b: str = None, remarks: str = None, queue_timeout_url: str = None, result_url: str = None, occassion: str = "", ) -> dict: """Make a payment from MPESA to a client. Args: initiator_name: username of the API operator as assigned on the MPesa Org Portal. security_credential: password of the API operator encrypted using the public key certificate provided. command_id: specifies the type of transaction being performed. There are three allowed values on the API: SalaryPayment, BusinessPayment or PromotionPayment. amount: the amount being sent from a business to a client. party_a: the business shortcode. party_b: the client phone number. remarks: A very short description of the transaction from your end. queue_timeout_url: the callback URL used to send an error callback when the transaction was not able to be processed by MPesa within a stipulated time period. result_url: the callback URL where the results of the transaction will be sent. occassion: A very short description of the transaction from your end. Can be left blank. Returns: A dict with a ResponseCode value of 0: { "ConversationID": "AG_20180326_00005ca7f7c21d608166", "OriginatorConversationID": "12363-1328499-6", "ResponseCode": "0", "ResponseDescription": "Accept the service request successfully." } Any other returned value is a failed request. Raises: ValueError: when the party_b is not a valid Safaricom number or when command_id is not SalaryPayment, BusinessPayment or PromotionPayment. """ url = f"{self.base_url}{self.B2C_URL_PATH}" phone_number, valid = saf_number_fmt(party_b) if not valid: raise ValueError(f"{party_b} is not a valid Safaricom number") if command_id not in [ "SalaryPayment", "BusinessPayment", "PromotionPayment", ]: raise ValueError(f"{command_id} is not a valid CommandID value") data = { "InitiatorName": initiator_name, "SecurityCredential": security_credential, "CommandID": command_id, "Amount": f"{amount}", "PartyA": party_a, "PartyB": phone_number, "Remarks": remarks, "QueueTimeOutURL": queue_timeout_url, "ResultURL": result_url, "Occassion": occassion, } headers = await self._get_headers() async with aiohttp.ClientSession(headers=headers) as session: return await self.post(session=session, url=url, data=data)
def test_invalid_saf_numbers() -> None: """Test invalid Safaricom numbers.""" _, valid = utils.saf_number_fmt("0731100100") assert valid is False