diff --git a/BeemAfrica/Bpay.py b/BeemAfrica/Bpay.py index 3fd9392..e5833eb 100644 --- a/BeemAfrica/Bpay.py +++ b/BeemAfrica/Bpay.py @@ -1,8 +1,11 @@ import sys import requests +import json from BeemAfrica import secured, get_header -BPAY_BALANCE_URL = 'https://apitopup.beem.africa/v1/credit-balance?app_name={}' +BPAY_BALANCE_URL = "https://apitopup.beem.africa/v1/credit-balance?app_name={}" +BPAY_CHECKOUT_URL = "https://checkout.beem.africa/v1/checkout" +BPAY_WHITELIST_URL = "https://checkout.beem.africa/v1/whitelist/add-to-list" class Bpay(object): @@ -10,16 +13,80 @@ def __init__(self) -> None: super().__init__() @secured - def get_balance(self, app_name='USSD'): + def get_balance(self, app_name="USSD"): try: return requests.get( - BPAY_BALANCE_URL.format(app_name), - headers=get_header() + BPAY_BALANCE_URL.format(app_name), headers=get_header() ).json() except (requests.ConnectionError, requests.ConnectTimeout): raise ConnectionError( - 'Connection to the USSD Get balance API Refused, Please check your internet connections') + "Connection to the USSD Get balance API Refused, Please check your internet connections" + ) + + @secured + def checkout( + self, + amount: int = None, + reference_number: str = None, + mobile: str = None, + transaction_id: str = None, + sendSource: bool = True, + ): + """ + QUERY + Field Type Description + amount string + The amount that the customer has to pay, Decimals not allowed + + transaction_id string + Transaction ID to track the payment. Should be UUIDv4. E.g. 96f9cc09-afa0-40cf-928a-d7e2b27b2408 + + reference_number string + Reference number to track the payment. Should be alphanumeric.The prefix pattern should be added when creating collections & checkout products. Example: SAMPLE-12345 + + mobile optional string + Mobile Number with country code prefix e.g. 255701000000 + + sendSource optional boolean + If set to true, response will be a SRC redirect link (recommended if redirect is handled from backend) + If set to false, response will be a redirection with code 302 (recommended if redirect is handled from the front) + """ + params = { + "amount": amount, + "reference_number": reference_number, + "mobile": mobile, + "sendSource": sendSource, + "transaction_id": transaction_id, + } + try: + return requests.get( + BPAY_CHECKOUT_URL, params=params, headers=get_header() + ).json() + + except (requests.ConnectionError, requests.ConnectTimeout): + raise ConnectionError( + "Connection to the BPay checkout, Please check your internet connections" + ) + + @secured + def whitelist_website(self, website: str) -> dict: + """The application / website domain that implements the Checkout library needs to be whitelisted (approved authorized access). + To whitelist a domain, Send a request to the following endpoint with the given parameters: + """ + try: + return requests.post( + BPAY_WHITELIST_URL, + data=json.dumps( + { + "website": website, + } + ), + ).json() + except (requests.ConnectionError, requests.ConnectTimeout): + raise ConnectionError( + "Connection to the BEEM API Refused, Please check your internet connections" + ) sys.modules[__name__] = Bpay() diff --git a/BeemAfrica/__init__.py b/BeemAfrica/__init__.py index 6ddab8d..bd41846 100644 --- a/BeemAfrica/__init__.py +++ b/BeemAfrica/__init__.py @@ -3,22 +3,27 @@ from dataclasses import dataclass -Tokens = '' +Tokens = "" @dataclass class Token(object): - access_key: str = '' - secret_key: str = '' + access_key: str = "" + secret_key: str = "" + beem_secure_token: str = "" class Authorize(object): - def __init__(self, access_key: str = None, secret_key: str = None): + def __init__( + self, + access_key: str = None, + secret_key: str = None, + beem_secure_token: str = None, + ): if not isinstance(access_key, str) or not isinstance(secret_key, str): - raise TypeError( - 'Both access_key and secret key should be of type ') - self.token = Token(access_key, secret_key) - globals()['Tokens'] = self.token + raise TypeError("Both access_key and secret key should be of type ") + self.token = Token(access_key, secret_key, beem_secure_token) + globals()["Tokens"] = self.token @property def access_key(self) -> str: @@ -27,10 +32,21 @@ def access_key(self) -> str: @access_key.setter def access_key(self, access_key: str): if not isinstance(access_key, str): - raise TypeError( - f'access_key should of Type not {type(access_key)}') + raise TypeError(f"access_key should of Type not {type(access_key)}") self.token.access_key = access_key + @property + def beem_secure_token(self) -> str: + return self.token.beem_secure_token + + @beem_secure_token.setter + def beem_secure_token(self, beem_secure_token: str): + if not isinstance(beem_secure_token, str): + raise TypeError( + f"beem_secure_token should of Type not {type(beem_secure_token)}" + ) + self.token.beem_secure_token = beem_secure_token + @property def secret_key(self) -> str: return self.token.secret_key @@ -39,33 +55,44 @@ def secret_key(self) -> str: def secret_key(self, secret_key: str): if not isinstance(secret_key, str): raise TypeError( - f'secret_key should be of Type not {type(secret_key)}') + f"secret_key should be of Type not {type(secret_key)}" + ) self.token.secret_key = secret_key def secured(beem_method): @wraps(beem_method) def verify(*args, **kwargs): - _Tokens = globals()['Tokens'] - if not isinstance(Tokens, Token) or not (_Tokens.access_key and _Tokens.secret_key): + _Tokens = globals()["Tokens"] + if not isinstance(Tokens, Token) or not ( + _Tokens.access_key and _Tokens.secret_key + ): raise ValueError( - f''' + f""" You need to set the value of access_key and secret_key do this to setup !!! >>> from BeemAfrica import Authorize >>> Authorize(access_key, secret_key) - ''') + """ + ) return beem_method(*args, **kwargs) + return verify def get_header(): - token = globals()['Tokens'] + token = globals()["Tokens"] + beem_secure_token = token.beem_secure_token encoded_token = b64encode( - f'{token.access_key}:{token.secret_key}'.encode()).decode() - return { - 'Content-Type': 'application/json', - 'Authorization': f'Basic {encoded_token}' + f"{token.access_key}:{token.secret_key}".encode() + ).decode() + + headers = { + "Content-Type": "application/json", + "Authorization": f"Basic {encoded_token}", } + if beem_secure_token: + headers["beem_secure_token"] = beem_secure_token + return headers diff --git a/README.md b/README.md index 2d44d25..2b9d53a 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,19 @@ # [python-client](#) -A Python library to easy the integration with the Beem Africa SMS Gateway + +A Python library to easy the integration with the Beem Africa SMS Gateway [![Downloads](https://pepy.tech/badge/beem-africa)](https://pepy.tech/project/beem-africa) [![Downloads](https://pepy.tech/badge/beem-africa/month)](https://pepy.tech/project/beem-africa) [![Downloads](https://pepy.tech/badge/beem-africa/week)](https://pepy.tech/project/beem-africa) +## Features to be Implemented -## Features to be Implemented - -- [x] Airtime +- [x] Airtime - [x] OTP -- [x] SMS +- [x] SMS - [ ] Two way SMS - [ ] USSD -- [ ] Bpay - +- [x] Bpay ## Getting started @@ -61,7 +60,7 @@ The above example is that I'm assuming you're using default BeemAfrica sender ID ```python >>> SMS.send_sms( 'You\'re now verified', - '255xxxxxxxxx', + '255xxxxxxxxx', sender_id='new-sender-id' ) @@ -72,9 +71,9 @@ You can also schedule message to be sent after a certain time or at a specific t ```python >>> SMS.send_sms( - 'You have won a 10 Million', - '2557xxxxxxxxx', - sender_id='new-sender-d', + 'You have won a 10 Million', + '2557xxxxxxxxx', + sender_id='new-sender-d', schedule_time='scheduled time' ) {'successful': True, 'request_id': 35918915, 'code': 100, 'message': 'Message Submitted Successfully', 'valid': 1, 'invalid': 0, 'duplicates': 0} @@ -100,16 +99,14 @@ Here how to send OTP with Beem !! ``` To verify the OTP send to user do this !! - -Note: Use pin_id from response you just recieve while sending an OTP and the PIN sent to user phone to verify the OTP, its going to look like this !! +Note: Use pin_id from response you just recieve while sending an OTP and the PIN sent to user phone to verify the OTP, its going to look like this !! ```python >>> OTP.verify(pin_id='4a5c2141-c965-4a9d-aca4-54f58063e831', pin='122496') {'data': {'message': {'code': 117, 'message': 'Valid Pin'}}} ``` - ## AirTime BeemAfrica also provide interface to interact with AirTime allowing you to easily transfer AirTime from BeemAfrica Credit to customer mobile !! @@ -132,7 +129,6 @@ You can also check balance of remaining credit balance by doing this Well these are the only implemented features by now ! - ## Issues Are you facing any issue with the integration of beem-africa libray, please raise an Issue so as we can fix as soon as we can !! @@ -145,7 +141,6 @@ Would you like to contribute to beem-africa python-client, Contributions of any Was this repository useful to you in any means, well then give it a star so as more people can get to know it. - ## Credits -All the credits to [kalebu](https://github.com/kalebu) and all the future contributors \ No newline at end of file +All the credits to [kalebu](https://github.com/kalebu) and all the future contributors diff --git a/setup.py b/setup.py index fdae8a4..4a0142a 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ setup( name="beem-africa", - version="0.1.0", + version="0.2.0", description="A Python library to easy the integration with the Beem Africa SMS Gateway", url="https://github.com/beem-africa/python-client", download_url="https://github.com/beem-africa/python-client/releases/tag/0.1", @@ -19,7 +19,7 @@ "python-tanzania", ], install_requires=[ - 'requests', + "requests", ], include_package_data=True, python_requires=">=3.6",