1+ """
2+ Python Package to Easy the Integration with Vodacom Public API
3+ """
4+
15import os
26import json
7+ import sys
38import base64
49import socket
510import requests
@@ -24,7 +29,7 @@ class Mpesa(object):
2429 def __init__ (
2530 self ,
2631 auth_path : Optional [str ] = "keys.json" ,
27- environment : Optional [str ] = "testing " ,
32+ environment : Optional [str ] = "sandbox " ,
2833 ):
2934 """
3035 Mpesa API client for Python
@@ -39,14 +44,24 @@ def __init__(
3944
4045 @property
4146 def authenticate (self ) -> bool :
42- """"""
47+ """
48+ Property to check If the user is has has included auth keys
49+ either manually or through auth json file(keys.json)
50+
51+ >> import pypesa
52+ >> mpesa = pypesa()
53+ >> mpesa.authenticate
54+
55+ Return True if the environment has auth keys
56+ False if auth are not included
57+ """
4358
4459 if self .auth_keys .get ("public_key" ) and self .auth_keys .get ("api_key" ):
4560 self ._encrypted_api_key = self .__generate_encrypted_key ()
4661 return True
4762
4863 elif os .path .isfile (self .auth_path ):
49- print ("loading from file" )
64+ # print("loading from file")
5065 self .auth_keys = self .load_keys (self .auth_path )
5166 if self .auth_keys :
5267 self ._encrypted_api_key = self .__generate_encrypted_key ()
@@ -67,7 +82,17 @@ def authorized_method(self, *args, **kwargs):
6782
6883 @staticmethod
6984 def load_keys (keys_filename : Union [str , Path ]) -> dict :
70- """"""
85+ """
86+ Pypesa internal method to load the auth file
87+
88+ >> import pypesa
89+ >> mpesa = pypesa()
90+ >> pypesa.load_keys()
91+
92+ Return keys Dict if auth file is present
93+
94+ Raise FileNotFoundError if auth file is absent
95+ """
7196 try :
7297
7398 with open (keys_filename , "r" ) as auth :
@@ -83,7 +108,11 @@ def load_keys(keys_filename: Union[str, Path]) -> dict:
83108 raise LoadingKeyError
84109
85110 def __generate_encrypted_key (self , session : Optional [bool ] = False ) -> str :
86- """"""
111+ """
112+
113+ Method use to Encrypt the api key and public key
114+ so as to get a secure RSA Encrypted key
115+ """
87116 try :
88117 pub_key = self .auth_keys .get ("public_key" )
89118 raw_key = self .auth_keys .get ("api_key" )
@@ -95,7 +124,6 @@ def __generate_encrypted_key(self, session: Optional[bool] = False) -> str:
95124 rsa_public_key = RSA .importKey (public_key_string )
96125 raw_cipher = rsa_cipher .new (rsa_public_key )
97126 encrypted_key = raw_cipher .encrypt (raw_key .encode ())
98-
99127 return base64 .b64encode (encrypted_key ).decode ("utf-8" )
100128
101129 except Exception as bug :
@@ -106,10 +134,16 @@ def __generate_encrypted_key(self, session: Optional[bool] = False) -> str:
106134
107135 @property
108136 def path_to_auth (self ) -> str :
137+ """
138+ Return Path to Authentication file
139+ """
109140 return self .auth_path
110141
111142 @path_to_auth .setter
112143 def path_to_auth (self , auth_path : Union [str , Path ]) -> str :
144+ """
145+ Setting new path to the authentication file
146+ """
113147 if isinstance (auth_path , str ):
114148 self .auth_path = auth_path
115149 return self .auth_path
@@ -119,56 +153,102 @@ def path_to_auth(self, auth_path: Union[str, Path]) -> str:
119153
120154 @property
121155 def environment (self ) -> Union [sandbox , production ]:
156+ """
157+ Return the Environment the Pypesa is running
158+
159+ whether its Sandbox | Production
160+ """
122161 return self .urls
123162
124163 @environment .setter
125164 def environment (self , enviro : str ) -> Union [sandbox , production ]:
165+ """
166+ Set new pypesa environment
167+
168+ Eg. changing env to production;
169+
170+ >> import pypesa
171+ >> mpesa = pypesa()
172+ >> mpesa.environment = "production"
173+ <Using Production Urls>
174+ """
126175 if isinstance (enviro , str ):
127- if enviro in ["testing " , "production" ]:
128- if enviro == "testing " :
176+ if enviro in ["sandbox " , "production" ]:
177+ if enviro == "sandbox " :
129178 self .urls = sandbox ()
130179 else :
131180 self .urls = production ()
132181 print (self .urls )
133182 return self .urls
134- return ValueError ("Environment must be either testing or production" )
135- return TypeError (f"environment must be of type string not { type (enviro )} " )
183+ raise ValueError (
184+ "Environment must be either sandbox or production" )
185+ raise TypeError (
186+ f"environment must be of type string not { type (enviro )} " )
136187
137188 @property
138189 def api_key (self ) -> str :
139- return self .auth_keys ["api_key" ]
190+ '''
191+ Return current api key
192+ '''
193+ return self .auth_keys .get ("api_key" )
140194
141195 @api_key .setter
142196 def api_key (self , Api_key : str ) -> str :
197+ '''
198+ Use this propery to explicit set a api_key
199+
200+ >> import pypesa
201+ >> wallet = pypesa()
202+ >> wallet.api_key = " Your api key" #here
203+
204+ '''
143205 if isinstance (Api_key , str ):
144206 self .auth_keys ["api_key" ] = Api_key
145207 return self .auth_keys ["api_key" ]
146- raise TypeError (f"API key must be a of type String not { type (Api_key )} " )
208+ raise TypeError (
209+ f"API key must be a of type String not { type (Api_key )} " )
147210
148211 @property
149212 def public_key (self ) -> str :
150- return self .auth_keys ["public_key" ]
213+ """
214+ Return the current Public key
215+ """
216+ return self .auth_keys .get ("public_key" )
151217
152218 @public_key .setter
153219 def public_key (self , pb_key : str ) -> str :
220+ """
221+ Set a new public key
222+ """
154223 if isinstance (pb_key , str ):
155224 self .auth_keys ["public_key" ] = pb_key
156225 return self .auth_keys ["public_key" ]
157226 raise TypeError (f"Public key must be a string not a { type (pb_key )} " )
158227
159228 @property
160229 def origin_address (self ) -> str :
230+ """
231+ Return the current origin address
232+ """
161233 return self ._origin_ip
162234
163235 @origin_address .setter
164236 def origin_address (self , ip_address : str ) -> str :
237+ """
238+ Set a new origin address
239+ """
165240 if isinstance (ip_address , str ):
166241 self ._origin_ip = ip_address
167242 return self ._origin_ip
168- raise TypeError (f"Address must be of type string not { type (ip_address )} " )
243+ raise TypeError (
244+ f"Address must be of type string not { type (ip_address )} " )
169245
170246 @authenticated
171247 def default_headers (self , auth_key : Optional [str ] = "" ) -> dict :
248+ """
249+ Generate Default header to be used during a Request
250+
251+ """
172252 if not auth_key :
173253 auth_key = self .__generate_encrypted_key (session = True )
174254 return {
@@ -187,9 +267,9 @@ def get_session_id(self) -> str:
187267 session_id = response ["output_SessionID" ]
188268 response_code = response ["output_ResponseCode" ]
189269 description = response ["output_ResponseDesc" ]
190- print (description , " " , response_code )
270+ # print(description, " ", response_code)
191271 if response_code == "INS-989" :
192- print ("Session creation failed!!" )
272+ # print("Session creation failed!!")
193273 raise AuthenticationError
194274 return session_id
195275 except Exception as bug :
@@ -216,7 +296,8 @@ def verify_query(transaction_query: dict, required_fields: set) -> bool:
216296 def customer_to_bussiness (self , transaction_query : dict ) -> dict :
217297 """"""
218298
219- self .verify_query (transaction_query , self .urls .re_customer_to_bussiness )
299+ self .verify_query (transaction_query ,
300+ self .urls .re_customer_to_bussiness )
220301
221302 try :
222303 return requests .post (
@@ -233,7 +314,8 @@ def customer_to_bussiness(self, transaction_query: dict) -> dict:
233314 def bussiness_to_customer (self , transaction_query : dict ) -> dict :
234315 """"""
235316
236- self .verify_query (transaction_query , self .urls .re_bussiness_to_customer )
317+ self .verify_query (transaction_query ,
318+ self .urls .re_bussiness_to_customer )
237319
238320 try :
239321
@@ -251,7 +333,8 @@ def bussiness_to_customer(self, transaction_query: dict) -> dict:
251333 def bussiness_to_bussiness (self , transaction_query : dict ) -> dict :
252334 """"""
253335
254- self .verify_query (transaction_query , self .urls .re_bussiness_to_bussiness )
336+ self .verify_query (transaction_query ,
337+ self .urls .re_bussiness_to_bussiness )
255338
256339 try :
257340 return requests .post (
@@ -328,4 +411,7 @@ def direct_debit_payment(self, transaction_query: dict) -> dict:
328411 verify = True ,
329412 )
330413 except (requests .ConnectTimeout , requests .ConnectionError ):
331- raise MpesaConnectionError
414+ raise MpesaConnectionError
415+
416+
417+ sys .modules [__name__ ] = Mpesa
0 commit comments