├── .gitignore
├── LICENSE
├── README.md
├── RELEASE
├── examples
├── bank_list.py
└── name_enquiry.py
├── kuda
├── __author__.py
├── __init__.py
├── __version__.py
├── api.py
├── error.py
└── savings.py
├── requirements.txt
└── setup.py
/.gitignore:
--------------------------------------------------------------------------------
1 | main.py
2 | keys.json
3 | test.py
4 | __pycache__
5 | .gitattributes
6 | build
7 | dist
8 | kuda_python.egg-info
9 | deploy.bat
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Manuel InfoSec
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Kuda OpenAPI Python Library
2 | [](https://kuda-python.readthedocs.io/)[](https://black.readthedocs.io/en/stable/)[](https://opensource.org/licenses/MIT)
3 |
4 | This is a lightweight library that works as a connector to [Kuda OpenAPI](https://kudabank.gitbook.io/kudabank/).
5 |
6 | - Customizable base URL.
7 | - Response metadata can be displayed.
8 | - Included examples.
9 |
10 |
11 | ## Upcoming
12 | - Savings endpoints,
13 | - Example cases,
14 | - HTTP proxy.
15 |
16 |
17 | ## Installation
18 |
19 | ```
20 | pip install kuda-python
21 | ```
22 |
23 |
24 | ## Documentation
25 |
26 | **Docs**:
27 | [https://kuda-python.readthedocs.io](https://kuda-python.readthedocs.io) (Coming Soon)
28 |
29 | **Article on Medium:**
30 | https://manuelinfosec.medium.com/python-integration-with-the-kuda-open-banking-api-kuda-python-112606ff989d
31 |
32 |
33 | ## Getting started
34 |
35 | > - Login to your [Kuda dashboard](https://developer.kuda.com) and generate your apiKey.
36 | > - Load your email and apiKey to the Kuda client (JSON or dotenv is recommended).
37 | > - JWT tokens are automatically generated.
38 |
39 |
40 | Usage examples:
41 |
42 | ```python
43 | from kuda import Kuda
44 |
45 | # email and apiKey are generated from your Kuda developer dashboard
46 | Kuda = Kuda(email='', apiKey='', show_request=True, sandbox=False, base_url="")
47 |
48 | # Get bank list
49 | print(Kuda.bank_list())
50 |
51 | # Retrieve main account balance
52 | print(Kuda.get_main_account_balance())
53 |
54 | # Perform name enquiry
55 | print(Kuda.name_enquiry(2005161838, "090267"))
56 |
57 | # Create virtual account
58 | params = {
59 | 'lastName': 'Manuel',
60 | 'firstName': 'Infosec',
61 | 'email': 'manuelinfosec@gmail.cm',
62 | 'phoneNumber': "09131103073",
63 | 'trackingRef': Kuda.get_ref(10) # you can generate your trackingReference some other way you choose.
64 | }
65 |
66 | print(Kuda.create_virtual_account(**params))
67 | ```
68 |
69 |
70 | ### Kuda OpenAPI Test
71 |
72 | [Kuda Test](https://kuda-openapi-uat.kudabank.com/v2) is available too.
73 |
74 | To use Kuda OpenAPI on Test, switch to test on your dashboard and copy apiKey (Live apiKey still remains valid):
75 |
76 | ```python
77 | from kuda import Kuda
78 |
79 | # email and apiKey are generated from your Kuda developer dashboard
80 | Kuda = Kuda(email='', apiKey='', show_request=True, sandbox=False, base_url="")
81 |
82 |
83 | # Get bank list
84 | print(Kuda.bank_list())
85 |
86 | # Retrieve main account balance
87 | print(Kuda.get_main_account_balance())
88 |
89 | # Perform name enquiry
90 | print(Kuda.name_enquiry(2005161838, "999129"))
91 |
92 |
93 | # Create virtual account
94 | params = {
95 | 'lastName': 'Manuel',
96 | 'firstName': 'Infosec',
97 | 'email': 'manuelinfosec@gmail.com',
98 | 'phoneNumber': "+2349131103073",
99 | 'trackingRef': Kuda.get_ref(10) # you can generate your trackingReference some other way you choose.
100 | }
101 |
102 |
103 | print(Kuda.create_virtual_account(**params))
104 | ```
105 |
106 |
107 | ### Base URL
108 |
109 | If not provided, `base_url` defaults to `https://kuda-openapi.kuda.com/v2` if `sandbox=False`, or `https://kuda-openapi-uat.kudabank.com/v2` if `sandbox=True`.
110 |
111 | Changes to the Kuda OpenAPI URL will reflect in future updates. In the case of late update, it is recommended to pass in the `base_url` parameter.
112 |
113 |
114 | ### Request and Response Metadata
115 |
116 | This library provies requests and header data for debugging purposes.
117 |
118 | You can display them by initializing the client with `show_request=True` or `show_header=True` respectively:
119 |
120 | ```python
121 | from kuda import Kuda
122 |
123 | # email and apiKey are generated from your Kuda developer dashboard
124 | Kuda = Kuda(email='', apiKey='', show_request=True, show_header=True, sandbox=False, base_url="")
125 |
126 | # Get bank list
127 | print(Kuda.bank_list())
128 | ```
129 |
130 | returns:
131 |
132 | ```python
133 | {'headers': {'User-Agent': 'python-requests/2.27.1', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': 'Bearer ', 'Content-Length': '77', 'Content-Type': 'application/json'}, 'body': {'data': '', 'password': ''}}
134 | ```
135 |
136 | You can also display full response metadata to help in debugging:
137 |
138 | ```python
139 | Kuda = Kuda(email='', apiKey='', show_request=True, sandbox=False, base_url="")
140 |
141 | # Get bank list
142 | print(Kuda.bank_list())
143 | ```
144 |
145 | returns:
146 |
147 | ```python
148 | {
149 | "data": "{'serviceType': 'BANK_LIST', 'requestRef': '9903712', 'data': {}}"
150 | }
151 | #
152 | ```
153 | > Refer to documentation for respective data types for each fields in the payload
154 |
155 | If `ClientError` is received, it'll display full header meta information.
156 |
157 |
158 | ### Error
159 |
160 | There are 2 types of errors returned from the library:
161 |
162 | - `kuda.error.ClientError`
163 |
164 | - This is thrown when server returns `4XX`, it's an issue from client side.
165 |
166 | - It has 3 properties:
167 |
168 | - `status_code` - HTTP status code, e.g. `-404`
169 |
170 | - `error_message` - Server's error message, e.g. `Something went wrong`
171 |
172 | - `header` - Full response header.
173 |
174 | - `kuda.error.ServerError`
175 |
176 | - This is thrown when server returns `5XX`, it's an issue from server side.
177 |
178 |
179 | ## Contributing
180 |
181 | Contributions are welcome.
182 |
183 | If you've found a bug within this project, please open an issue to discuss what you would like to change.
184 |
185 | If you have any questions, feature requests, or notice any errors with the OpenAPI, please reach out to [Kuda](mailto:openapi@kuda.com), creating a //Skype link and someone will respond.
186 |
187 | ## Authors
188 | [Manuel](https://twitter.com/manuelinfosec)
189 |
190 | ## Acknowledgments
191 | [Kuda Bank Team](https://kudabank.com/)
192 |
--------------------------------------------------------------------------------
/RELEASE:
--------------------------------------------------------------------------------
1 | v1.0.0 (Major):
2 | - First Release on PyPI
3 |
4 | v1.0.1 (Patch):
5 | - Fixed issue with displaying request metadata.
6 |
7 | v1.0.2 (Patch):
8 | - Fixed issue with creating virtual accounts.
9 |
--------------------------------------------------------------------------------
/examples/bank_list.py:
--------------------------------------------------------------------------------
1 | from kuda import Kuda
2 | from kuda.error import ClientError
3 |
4 | email = ""
5 | apiKey = ""
6 |
7 | Kuda = Kuda(email, apiKey,
8 | sandbox=False,
9 | show_request=False,
10 | show_header=False)
11 |
12 | banks = Kuda.bank_list()['data']['Data']['banks']
13 | banks = json.dumps(banks, indent=4)
14 |
15 | print(banks)
--------------------------------------------------------------------------------
/examples/name_enquiry.py:
--------------------------------------------------------------------------------
1 | from kuda import Kuda
2 | from kuda.error import ClientError
3 |
4 | email = ""
5 | apiKey = ""
6 |
7 | Kuda = Kuda(email, apiKey,
8 | sandbox=False,
9 | show_request=False,
10 | show_header=False)
11 |
12 | beneficiaryAccountNumber = ""
13 | beneficiaryBankCode = ""
14 |
15 | name_enquiry = Kuda.name_enquiry(beneficiaryAccountNumber,
16 | beneficiaryBankCode)
17 |
18 |
19 |
20 | name = name_enquiry['data']['Data']['BeneficiaryName']
21 |
--------------------------------------------------------------------------------
/kuda/__author__.py:
--------------------------------------------------------------------------------
1 | __author__ = "Manuel, For Kuda MicroFinance Bank Inc."
--------------------------------------------------------------------------------
/kuda/__init__.py:
--------------------------------------------------------------------------------
1 | import string
2 | import random
3 | import requests
4 | import json
5 | from json import JSONDecodeError
6 | from kuda.error import ClientError, ServerError
7 |
8 | class Kuda():
9 | from kuda.api import bank_list
10 | from kuda.api import name_enquiry
11 | from kuda.api import create_virtual_account
12 | from kuda.api import get_virtual_single_account
13 | from kuda.api import single_fund_transfer
14 | from kuda.api import virtual_fund_transfer
15 | from kuda.api import txn_status_query
16 | from kuda.api import get_main_account_balance
17 | from kuda.api import get_virtual_account_balance
18 | from kuda.api import txn_logs
19 | from kuda.api import main_txn_logs
20 | from kuda.api import filter_main_txn_logs
21 | from kuda.api import virtual_txn_logs
22 | from kuda.api import filter_virtual_txn_logs
23 | from kuda.api import fund_virtual_account
24 | from kuda.api import withdraw_virtual_account
25 |
26 | def __init__(self,
27 | email,
28 | apiKey,
29 | sandbox=False,
30 | show_request=False,
31 | show_header=False,
32 | base_url = "",**kwargs):
33 |
34 | if sandbox and base_url == "":
35 | self.url = "https://kuda-openapi-uat.kudabank.com/v2".replace("\u200b", "")
36 | elif not sandbox and base_url == "":
37 | self.url = "https://kuda-openapi.kuda.com/v2".replace("\u200b", "")
38 | else:
39 | self.url = base_url.replace("\u200b", "")
40 |
41 | if show_request:
42 | self.show_request = True
43 | else:
44 | self.show_request = False
45 |
46 | if show_header:
47 | self.show_header = True
48 | else:
49 | self.show_header = False
50 |
51 | self.token = self.getToken(email, apiKey)
52 |
53 | def getToken(self, email, apiKey):
54 | token = requests.post(f"{self.url}/Account/GetToken",
55 | json={"email" : email, "apiKey":apiKey})
56 | self._handle_exception(token)
57 | return token.text
58 |
59 | def get_ref(self, length: int) -> int:
60 | """Generate a unique reference per request (requestRef)
61 |
62 | Args:
63 | length (int): length of reference
64 |
65 | Returns:
66 | int: random requestRef
67 | """
68 | digits = string.digits
69 | result_int = ''.join(random.choice(digits) for i in range(length))
70 | return int(result_int)
71 |
72 | def koboToNGN(self, kobo):
73 | return kobo/100
74 |
75 | def ngntoKobo(self, naira):
76 | return naira*100
77 |
78 | def sign_request(self, url_path: str, payload={}):
79 |
80 | request = {
81 | "header": {
82 | "Authorization": f"Bearer {self.token}",
83 | },
84 | "body" : {
85 | "data": {
86 | "serviceType": url_path,
87 | "requestRef" : str(self.get_ref(7)),
88 | "data" : payload
89 | }
90 | }
91 | }
92 |
93 | request['body']['data'] = str(request['body']['data'])
94 | if self.show_request:
95 | print(json.dumps(request['body'], indent=4))
96 | return self.send_request(request)
97 |
98 | def send_request(self, request):
99 | # Make request to Kuda API
100 | resp = requests.post(self.url, headers=request['header'], json=request['body'])
101 |
102 | # Handle exceptions in response
103 | self._handle_exception(resp)
104 |
105 | try:
106 | data = resp.json()
107 | except ValueError:
108 | data = resp.text
109 |
110 | result = {}
111 |
112 | # Display header for debugging purpose
113 | if self.show_header:
114 | result['headers'] = resp.request.headers
115 | result['body'] = data
116 | return result
117 |
118 | result['data'] = json.loads(data['data'])
119 | return result
120 |
121 | def _handle_exception(self, response):
122 | # http status code
123 | code = response.status_code
124 | if code < 400:
125 | return
126 | if 400 <= code <= 500:
127 | try:
128 | err = json.loads(response.text)
129 | except JSONDecodeError:
130 | raise ClientError(code, response.text, response.headers)
131 | raise ClientError(code, err, f"Response Headers: {response.headers}")
132 | raise ServerError(code, response.text)
--------------------------------------------------------------------------------
/kuda/__version__.py:
--------------------------------------------------------------------------------
1 | __version__ = "1.0.0"
--------------------------------------------------------------------------------
/kuda/api.py:
--------------------------------------------------------------------------------
1 | def bank_list(self):
2 | """The Bank list covered here are for all Nigerian Financial Institutions supported by NIBSS
3 | https://kudabank.gitbook.io/kudabank/single-fund-transfer/bank-list
4 |
5 | Returns:
6 | dict
7 | """
8 | serviceType = "BANK_LIST"
9 | return self.sign_request("BANK_LIST")
10 |
11 | def name_enquiry(self,
12 | account_number: int,
13 | bank_code: str,
14 | SenderTrackingReference: str = "") -> dict:
15 | """Validate and retrieve information on a NUBAN account number
16 | https://kudabank.gitbook.io/kudabank/single-fund-transfer/name-enquiry
17 |
18 | Args:
19 | account_number (int): Destination Account Number
20 | bank_code (int): Destination Bank Code. Defaults to Kuda bank code
21 |
22 | Returns:
23 | dict
24 | """
25 | serviceType = "NAME_ENQUIRY"
26 |
27 | if not SenderTrackingReference:
28 | SenderTrackingReference = ""
29 | isRequestFromVirtualAccount = "false"
30 | else:
31 | isRequestFromVirtualAccount = "true"
32 |
33 | data = {
34 | "beneficiaryAccountNumber": account_number,
35 | "beneficiaryBankCode": bank_code,
36 | "SenderTrackingReference": SenderTrackingReference,
37 | "isRequestFromVirtualAccount" : isRequestFromVirtualAccount
38 | }
39 |
40 | return self.sign_request(serviceType, data)
41 |
42 | def create_virtual_account(self,
43 | lastName: str,
44 | firstName: str,
45 | email: str,
46 | phoneNumber: str,
47 | trackingRef: int) -> dict:
48 |
49 | """Create a Virtual Account
50 | https://kudabank.gitbook.io/kudabank/virtual-account-creation#create-a-virtual-account
51 |
52 | Args:
53 | lastName (str): User's last name
54 | firstName (str): User's first name
55 | emailAddress (str): User's email address
56 | phoneNumber (str): User's phone number
57 | trackingRef (int): Unique identifier for the account
58 |
59 | Returns:
60 | dict
61 | """
62 | serviceType = "ADMIN_CREATE_VIRTUAL_ACCOUNT"
63 |
64 | data = {
65 | "email": email,
66 | "phoneNumber": phoneNumber,
67 | "lastName": lastName,
68 | "firstName": firstName,
69 | "trackingReference": trackingRef
70 | }
71 |
72 | return self.sign_request(serviceType, data)
73 |
74 | def get_virtual_single_account(self,
75 | trackingRef: int) -> dict:
76 | """Retrieve Virtual Account information
77 | https://kudabank.gitbook.io/kudabank/virtual-account-creation/retrieve-virtual-account
78 |
79 | Args:
80 | trackingRef (int): Unique Identifier for virtual Account
81 |
82 | Returns:
83 | dict
84 | """
85 | serviceType = "ADMIN_RETRIEVE_SINGLE_VIRTUAL_ACCOUNT"
86 |
87 | data = {
88 | "trackingReference": trackingRef
89 | }
90 |
91 | return self.sign_request(serviceType, data)
92 |
93 |
94 | def single_fund_transfer(self,
95 | trackingRef: int,
96 | beneficiaryAccountNumber: int,
97 | beneficiaryBankCode: int,
98 | beneficiaryName: str,
99 | amount: int,
100 | SenderName: str,
101 | narration: str,
102 | accountID: int,
103 | NameEnquirySessionID: str = None) -> dict:
104 | """Bank transfer from a Kuda Account to any bank account
105 | https://kudabank.gitbook.io/kudabank/single-fund-transfer/send-money-from-a-kuda-account
106 |
107 | Args:
108 | trackingRef (int): Request Reference ID
109 | beneficiaryAccountNumber (int): Destination bank account number
110 | beneficiaryBankCode (int): Destination bank code
111 | beneficiaryName (str): Name of the recipient
112 | amount (int): Amount to be transferred (in kobo)
113 | SenderName (str): Name of the person sending money
114 | narration (str): User defined reason for the transaction.
115 | accountID (int): Main Account number
116 | NameEnquirySessionID (str, optional): Session ID generated from the nameEnquiry request. Defaults to None.
117 |
118 | Returns:
119 | dict
120 | """
121 |
122 | serviceType = "SINGLE_FUND_TRANSFER"
123 |
124 | # TODO: Check Request Body
125 | if not NameEnquirySessionID:
126 | accountInfo = self.name_enquiry(beneficiaryAccountNumber, beneficiaryBankCode)
127 | NameEnquirySessionID = accountInfo["data"]["Data"]["SessionID"]
128 | accountName = accountInfo["data"]["Data"]["BeneficiaryName"]
129 | else:
130 | accountInfo = self.name_enquiry(beneficiaryAccountNumber, beneficiaryBankCode)
131 | NameEnquirySessionID = accountInfo["data"]["Data"]["SessionID"]
132 | accountName = accountInfo["data"]["Data"]["BeneficiaryName"]
133 |
134 | data = {
135 | "ClientAccountNumber": accountID,
136 | "beneficiarybankCode": beneficiaryBankCode,
137 | "beneficiaryAccount": beneficiaryAccountNumber,
138 | "beneficiaryName": accountName,
139 | "amount": amount,
140 | "narration": narration,
141 | "nameEnquirySessionID": NameEnquirySessionID,
142 | "trackingReference": trackingRef,
143 | "senderName": SenderName,
144 | }
145 |
146 | return self.sign_request(serviceType, data)
147 |
148 | def virtual_fund_transfer(self,
149 | trackingRef: int,
150 | beneficiaryAccount: int,
151 | amount: int,
152 | narration: str,
153 | beneficiaryBankCode: int,
154 | beneficiaryName: str,
155 | senderName: str,
156 | nameEnquiryId: str = None) -> dict:
157 |
158 | """Bank transfer from a KUDA Virtual Account to any bank account
159 | https://kudabank.gitbook.io/kudabank/single-fund-transfer/virtual-account-fund-transfer
160 |
161 | Args:
162 | trackingRef (int): Unique Identifier for virtual Account
163 | beneficiaryAccount (int): Destination bank account number
164 | beneficiaryBankCode (int): Destination bank code
165 | beneficiaryName (str): Name of the recipient
166 | amount (int): Amount to be transferred (in kobo)
167 | narration (str): User defined reason for the transaction.
168 | SenderName (str): Name of the person sending money
169 | nameEnquiryId (str, optional): Session ID generated from the nameEnquiry request. Defaults to None.
170 |
171 | Returns:
172 | dict
173 | """
174 | serviceType = "VIRTUAL_ACCOUNT_FUND_TRANSFER"
175 |
176 | # TODO: Check Request Body
177 | if not nameEnquiryId:
178 | accountInfo = self.name_enquiry(beneficiaryAccount, beneficiaryBankCode, trackingRef)
179 | nameEnquiryId = accountInfo["data"]["Data"]["SessionID"]
180 | accountName = accountInfo["data"]["Data"]["BeneficiaryName"]
181 | else:
182 | accountInfo = self.name_enquiry(beneficiaryAccount, beneficiaryBankCode, trackingRef)
183 | nameEnquiryId = accountInfo["data"]["Data"]["SessionID"]
184 | accountName = accountInfo["data"]["Data"]["BeneficiaryName"]
185 |
186 | data = {
187 | "trackingReference": trackingRef,
188 | "beneficiarybankCode": beneficiaryBankCode,
189 | "beneficiaryAccount": beneficiaryAccount,
190 | "beneficiaryName": accountName,
191 | "amount": amount,
192 | "narration": narration,
193 | "nameEnquiryId": nameEnquiryId,
194 | "senderName": senderName,
195 | }
196 |
197 | return self.sign_request(serviceType, data)
198 |
199 | def txn_status_query(self,
200 | third_party: bool,
201 | txnRef: int) -> dict:
202 |
203 | """Retrieve information on a the status of a transaction
204 | https://kudabank.gitbook.io/kudabank/single-fund-transfer/transactions-query
205 |
206 | Args:
207 | txnRef (int): Unique identifier for the transfer
208 | third_party (bool, optional): Does it concern a third-party bank? Defaults to False
209 |
210 | Returns:
211 | dict
212 | """
213 |
214 | serviceType = "TRANSACTION_STATUS_QUERY"
215 | if third_party:
216 | isThirdPartyBankTransfer = "true"
217 | else:
218 | isThirdPartyBankTransfer = "true"
219 |
220 | data = {
221 | "isThirdPartyBankTransfer": isThirdPartyBankTransfer,
222 | "transactionRequestReference": txnRef
223 | }
224 | return self.sign_request(serviceType, data)
225 |
226 | def get_main_account_balance(self) -> dict:
227 |
228 | """Check the balance on your KUDA main account
229 | https://kudabank.gitbook.io/kudabank/check-admin-account-balance
230 |
231 | Returns:
232 | dict
233 | """
234 | serviceType = "ADMIN_RETRIEVE_MAIN_ACCOUNT_BALANCE"
235 | return self.sign_request(serviceType)
236 |
237 | def get_virtual_account_balance(self,
238 | trackingRef: int) -> dict:
239 |
240 | """Retrieve Virtual Account Balance
241 | https://kudabank.gitbook.io/kudabank/check-virtual-account-balance
242 |
243 | Args:
244 | trackingRef (int)
245 |
246 | Returns:
247 | dict
248 | """
249 | serviceType = "RETRIEVE_VIRTUAL_ACCOUNT_BALANCE"
250 |
251 | data = {
252 | "trackingReference": trackingRef
253 | }
254 |
255 | return self.sign_request(serviceType, data)
256 |
257 | def txn_logs(self,
258 | RequestReference: int,
259 | ResponseReference: int,
260 | startDate: str,
261 | endDate: str,
262 | txnDate = "",
263 | HasTransactionDateRangeFilter: bool = False,
264 | FetchSuccessfulRecords: bool = True,
265 | pageSize: int = 10,
266 | pageNumber: int = 1) -> dict:
267 |
268 | """Get Transaction Logs
269 |
270 | Args:
271 | Refer to documentaion: https://kudabank.gitbook.io/kudabank/view-transaction-history/get-transaction-logs
272 |
273 | Returns:
274 | dict
275 | """
276 |
277 | serviceType = "RETRIEVE_TRANSACTION_LOGS"
278 |
279 | if HasTransactionDateRangeFilter:
280 | HasTransactionDateRangeFilter = "true"
281 | else:
282 | startDate = txnDate
283 | endDate = txnDate
284 | HasTransactionDateRangeFilter = "false"
285 |
286 | if FetchSuccessfulRecords:
287 | FetchSuccessfulRecords = "true"
288 | else:
289 | FetchSuccessfulRecords = "false"
290 |
291 | data = {
292 | "RequestReference": RequestReference,
293 | "ResponseReference": ResponseReference,
294 | "TransactionDate": txnDate,
295 | "HasTransactionDateRangeFilter": HasTransactionDateRangeFilter,
296 | "StartDate": startDate,
297 | "EndDate": endDate,
298 | "PageSize": pageSize,
299 | "PageNumber": pageNumber,
300 | "FetchSuccessfulRecords": FetchSuccessfulRecords
301 | }
302 | return self.sign_request(serviceType, data)
303 |
304 | def main_txn_logs(self,
305 | pageSize: int = 10, pageNumber: int = 1) -> dict:
306 | """Retrieve a list of all transactions for the currently authenticated user.
307 | https://kudabank.gitbook.io/kudabank/view-transaction-history/kuda-account-transaction-history
308 |
309 | Args:
310 | pageSize (int)
311 | pageNumber (int)
312 | """
313 | serviceType = "ADMIN_MAIN_ACCOUNT_TRANSACTIONS"
314 |
315 | data = {
316 | "pageSize" : pageSize,
317 | "pageNumber" : pageNumber
318 | }
319 |
320 | return self.sign_request(serviceType, data)
321 |
322 | def filter_main_txn_logs(self,
323 | startDate: str,
324 | endDate: str,
325 | pageSize: int = 10,
326 | pageNumber: int = 1) -> dict:
327 | """Retrieve a filtered list of all transactions for the currently authenticated user.
328 | https://kudabank.gitbook.io/kudabank/view-transaction-history/filtered-kuda-account-transaction-history
329 |
330 | Args:
331 | startDate (str): Ex: 2020-10-27T09:58:23.4740446Z
332 | endDate (str): Ex: 2020-12-27T09:58:23.4740446Z
333 | pageSize (int, optional)
334 | pageNumber (int, optional)
335 |
336 | Returns:
337 | dict
338 | """
339 | serviceType = "ADMIN_MAIN_ACCOUNT_FILTERED_TRANSACTIONS"
340 |
341 | data = {
342 | "pageSize": pageSize,
343 | "pageNumber": pageNumber,
344 | "startDate": startDate,
345 | "endDate": endDate
346 | }
347 | return self.sign_request(serviceType, data)
348 |
349 | def virtual_txn_logs(self,
350 | trackingRef: int,
351 | pageSize: int = 10,
352 | pageNumber: int = 1) -> dict:
353 | """Retrieve a list of all transactions for a specified virtual account
354 | https://kudabank.gitbook.io/kudabank/view-transaction-history/virtual-accounttransactionhistory
355 |
356 | Args:
357 | trackingRef (int): Unique Identifier for virtual Account
358 | pageSize (int, optional)
359 | pageNumber (int, optional)
360 |
361 | Returns:
362 | dict
363 | """
364 | serviceType = "ADMIN_VIRTUAL_ACCOUNT_TRANSACTIONS"
365 |
366 | data = {
367 | "trackingReference": trackingRef,
368 | "pageSize" : pageSize,
369 | "pageNumber" : pageNumber,
370 | }
371 |
372 | return self.sign_request(serviceType, data)
373 |
374 | def filter_virtual_txn_logs(self,
375 | trackingRef: int,
376 | startDate: str,
377 | endDate: str,
378 | pageSize: int = 10,
379 | pageNumber: int = 1) -> dict:
380 | """Retrieve a filtered list of all transactions for a specified virtual account
381 | https://kudabank.gitbook.io/kudabank/view-transaction-history/virtual-account-transaction-history
382 |
383 | Args:
384 | trackingRef (int): Unique Identifier for virtual Account
385 | startDate (str): Ex: 2020-10-27T09:58:23.4740446Z
386 | endDate (str): Ex: 2020-12-27T09:58:23.4740446Z
387 | pageSize (int, optional)
388 | pageNumber (int, optional)
389 |
390 | Returns:
391 | dict
392 | """
393 | serviceType = "ADMIN_MAIN_ACCOUNT_FILTERED_TRANSACTIONS"
394 |
395 | data = {
396 | "trackingReference": trackingRef,
397 | "pageSize": pageSize,
398 | "pageNumber": pageNumber,
399 | "startDate": startDate,
400 | "endDate": endDate
401 | }
402 | return self.sign_request(serviceType, data)
403 |
404 | def fund_virtual_account(self,
405 | trackingRef: int,
406 | amount: int,
407 | narration: str) -> dict:
408 |
409 | """Deposit to a virtual account
410 | https://kudabank.gitbook.io/kudabank/add-remove-money-from-a-virtual-account
411 |
412 | Args:
413 | trackingRef (int): Unique Identifier for virtual Account
414 | amount (int): Amount to fund (in kobo)
415 | narration (str, optional): Transaction Description. Defaults to "".
416 |
417 | Returns:
418 | dict:
419 | """
420 | serviceType = "FUND_VIRTUAL_ACCOUNT"
421 |
422 | data = {
423 | "trackingReference": trackingRef,
424 | "amount": amount,
425 | "narration": narration
426 | }
427 | return self.sign_request(serviceType, data)
428 |
429 | def withdraw_virtual_account(self,
430 | trackingRef: int,
431 | amount: int,
432 | narration: str = "") -> dict:
433 | """Withdrawing funds from a virtual account means to transfer funds from a virtual account to an associated KUDA account or to any other Nigerian Bank account.
434 | https://kudabank.gitbook.io/kudabank/add-remove-money-from-a-virtual-account#withdraw-from-virtual-account
435 |
436 | Args:
437 | trackingRef (int): Unique Identifier for virtual Account
438 | amount (int): Amount to be withdrawn (in kobo)
439 | narration (str, optional): Transaction description. Defaults to "".
440 |
441 | Returns:
442 | dict
443 | """
444 | serviceType = "WITHDRAW_VIRTUAL_ACCOUNT"
445 |
446 | data = {
447 | "trackingReference": trackingRef,
448 | "amount": amount,
449 | "narration": narration
450 | }
451 |
452 | return self.sign_request(serviceType, data)
--------------------------------------------------------------------------------
/kuda/error.py:
--------------------------------------------------------------------------------
1 | class Err(Exception):
2 | pass
3 |
4 | class ClientError(Err):
5 | def __init__(self, status_code, error_message, header):
6 | # response status code
7 | self.status_code = status_code
8 | # error message returned from API
9 | self.error_message = error_message
10 | # the whole response header returned from API
11 | self.header = header
12 |
13 | class ServerError(Err):
14 | def __init__(self, status_code, message):
15 | # response status code
16 | self.status_code = status_code
17 | # error message returned from API
18 | self.message = message
19 |
20 | class InsufficientBalance(Err):
21 | def __init__(self, status_code, message):
22 | # response status code
23 | self.status_code = status_code
24 | # error message returned from API
25 | self.error_message = message
--------------------------------------------------------------------------------
/kuda/savings.py:
--------------------------------------------------------------------------------
1 | def get_spend_savings_txns(self,
2 | trackingRef: int,
3 | pageNumber: int,
4 | pageSize: int) -> dict:
5 | serviceType = "RETRIEVE_SPEND_AND_SAVE_TRANSACTIONS"
6 | return
7 |
8 | def create_plain_savings(self,
9 | savingsTrackingRef: int,
10 | name: str,
11 | virtualAccTrackingRef: int) -> dict:
12 | serviceType = "CREATE_PLAIN_SAVINGS"
13 | return
14 |
15 | def get_plain_savings(self,
16 | trackingRef: int) -> dict:
17 | serviceType = "GET_PLAIN_SAVINGS"
18 | return
19 |
20 | def get_all_customer_plain_savings(self,
21 | accountNumber: int) -> dict:
22 | return
23 |
24 | def get_all_plain_savings(self) -> dict:
25 | return
26 |
27 | def update_plain_savings_account(self,
28 | trackingRef: int,
29 | status: int) -> dict:
30 | return
31 |
32 | def plain_savings_txn(self,
33 | trackingRef: int,
34 | amount: int,
35 | transactionType: str,
36 | narration: str = "") -> dict:
37 | serviceType = "PLAIN_SAVINGS_TRANSACTIONS"
38 | return
39 |
40 | def plain_savings_txn_logs(self,
41 | trackingRef: int,
42 | pageSize: int,
43 | pageNumber: int) -> dict:
44 | serviceType = "RETRIEVE_PLAIN_SAVINGS_TRANSACTIONS"
45 | return
46 |
47 | def create_open_flexible_savings(self,
48 | savingsTrackingRef: str,
49 | name: str,
50 | virtualAccTrackingRef: str,
51 | amount: int,
52 | duration: int,
53 | frequency: int,
54 | startNow: str,
55 | startDate: str = True,
56 | isInterestEarning: bool = True) -> dict:
57 | serviceType = "CREATE_OPEN_FLEXIBLE_SAVE"
58 | return
59 |
60 | def create_closed_flexible_save(self,
61 | savingsTrackingRef: str,
62 | name: str,
63 | virtualAccTrackingRef: str,
64 | amount: int,
65 | duration: int,
66 | frequency: int,
67 | startDate: str,
68 | startNow: bool = True,
69 | isInterestEarning: bool = True) -> dict:
70 | serviceType = "CREATE_CLOSED_FLEXIBLE_SAVE"
71 | return
72 |
73 | def get_open_flexible_save(self,
74 | trackingRef: int) -> dict:
75 | serviceType = "GET_OPEN_FLEXIBLE_SAVE"
76 | return
77 |
78 | def get_closed_flexible_savings(self,
79 | trackingRef: int) -> dict:
80 | serviceType = "GET_CLOSED_FLEXIBLE_SAVE"
81 | return
82 |
83 | def get_all_customer_open_flexible_savings(self,
84 | primaryAccountNumber: int) -> dict:
85 | serviceType = "GET_ALL_CUSTOMER_OPEN_FLEXIBLE_SAVE"
86 | return
87 |
88 | def get_all_customer_closed_flexible_savings(self,
89 | primaryAccountNumber: int) -> dict:
90 | serviceType = "GET_ALL_CUSTOMER_CLOSED_FLEXIBLE_SAVE"
91 | return
92 |
93 | def get_all_open_flexible_savings(self) -> dict:
94 | serviceType = "GET_ALL_OPEN_FLEXIBLE_SAVE"
95 | return
96 |
97 | def get_all_closed_flexible_savings(self) -> dict:
98 | serviceType = "GET_ALL_CLOSED_FLEXIBLE_SAVE"
99 | return
100 |
101 | def withdraw_open_flexible_savings(self) -> dict:
102 | serviceType = "COMPLETE_OPEN_FLEXIBLE_SAVE_WITHDRAWAL"
103 | return
104 |
105 | def withdraw_closed_flexible_savings(self) -> dict:
106 | serviceType = "COMPLETE_CLOSED_FLEXIBLE_SAVE_WITHDRAWAL"
107 | return
108 |
109 | def open_flexible_savings_txn_logs(self,
110 | trackingRef: int,
111 | pageSize: int,
112 | pageNumber: int) -> dict:
113 | serviceType = "RETRIEVE_OPEN_FLEXIBLE_SAVINGS_TRANSACTIONS"
114 | return
115 |
116 | def closed_flexible_savings_txn_logs(self,
117 | trackingRef: int,
118 | pageSize: int,
119 | pageNumber: int) -> dict:
120 | serviceType = "RETRIEVE_CLOSED_FLEXIBLE_SAVINGS_TRANSACTIONS"
121 | return
122 |
123 | def create_fixed_savings(self,
124 | savingsTrackingRef: int,
125 | name: str,
126 | virtualAccTrackingRef: int,
127 | amount: int,
128 | duration: int,
129 | startDate: str,
130 | startNow: bool = True,
131 | isInterestEarning: bool = True) -> dict:
132 | serviceType = "CREATE_FIXED_SAVINGS"
133 | return
134 |
135 | def get_fixed_savings(self,
136 | trackingRef: int) -> dict:
137 | serviceType = "GET_FIXED_SAVINGS"
138 | return
139 |
140 | def get_all_customer_fixed_savings(self,
141 | primaryAccountNumber: int) -> dict:
142 | serviceType = "GET_ALL_CUSTOMER_FIXED_SAVINGS "
143 | return
144 |
145 | def get_all_fixed_savings(self) -> dict:
146 | serviceType = "GET_ALL_FIXED_SAVINGS"
147 | return
148 |
149 | def terminate_fixed_deposit(self,
150 | trackingRef: int,
151 | amount: int) -> dict:
152 | """Terminate a fixed deposit
153 |
154 | https://kudabank.gitbook.io/kudabank/savings/fixed-deposit/terminate-a-fixed-deposit
155 |
156 | Args:
157 | trackingRef (int)
158 | amount (int)
159 |
160 | Returns:
161 | dict
162 | """
163 | serviceType = "COMPLETE_FIXED_SAVINGS_WITHDRAWAL"
164 |
165 | data = {
166 | "trackingReference" : trackingRef,
167 | "amount" : amount
168 | }
169 |
170 | return self.sign_request(serviceType, data)
171 |
172 | def fixed_savings_txn_logs(self,
173 | trackingRef: int,
174 | pageSize: int = 10,
175 | pageNumber: int = 1) -> dict:
176 | """View all transactions on a fixed savings account
177 |
178 | https://kudabank.gitbook.io/kudabank/savings/fixed-deposit/view-all-transactions-on-a-fixed-savings-account
179 |
180 | Args:
181 | trackingRef (int)
182 | pageSize (int, optional): Defaults to 10
183 | pageNumber (int, optional): Defualts to 1
184 |
185 | Returns:
186 | dict
187 | """
188 | serviceType = "RETRIEVE_FIXED_SAVINGS_TRANSACTIONS"
189 |
190 | data = {
191 | "trackingReference" : trackingRef,
192 | "pageSize": pageSize,
193 | "pageNumber" : pageNumber
194 | }
195 | return self.sign_request(serviceType, data)
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | requests
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | from setuptools import setup, find_packages
2 |
3 | # For consistent encoding
4 | from codecs import open
5 | from os import path
6 |
7 | HERE = path.abspath(path.dirname(__file__))
8 |
9 | with open(path.join(HERE, 'README.md'), encoding='utf-8') as f:
10 | long_description = f.read()
11 |
12 | setup(
13 | name="kuda-python",
14 | version="1.0.2",
15 | description="Kuda OpenAPI Python Library",
16 | long_description=long_description,
17 | long_description_content_type="text/markdown",
18 | url="https://kuda-python.readthedocs.io/",
19 | author="Chiemezie Njoku",
20 | author_email="njokuchiemezie01@gmail.com",
21 | license="MIT",
22 | classifiers=[
23 | "Intended Audience :: Developers",
24 | "License :: OSI Approved :: MIT License",
25 | "Programming Language :: Python",
26 | "Programming Language :: Python :: 3",
27 | "Programming Language :: Python :: 3.6",
28 | "Programming Language :: Python :: 3.7",
29 | "Programming Language :: Python :: 3.8",
30 | "Programming Language :: Python :: 3.9",
31 | "Operating System :: OS Independent"
32 | ],
33 | packages=["kuda"],
34 | include_package_data=True,
35 | install_requires=["requests"]
36 | )
--------------------------------------------------------------------------------