Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,7 @@
test.py
kkiapay.egg-info
dist
build
build
venv
.idea
__pycache__
45 changes: 39 additions & 6 deletions kkiapay/core.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
import json
import requests
from collections import namedtuple

import requests

class Kkiapay:
from kkiapay.exceptions import KKiapayAlogrithmException, KKiapayDestinationTypeException


class Kkiapay:
BASE_URL = "https://api.kkiapay.me"
SANDBOX_URL = "https://api-sandbox.kkiapay.me"
ALGORITHMS = {"1": 'rate', "2": 'roof'}

RATE_FREQUENCIES = {"1": '3d', "2": '1w', "3": '1m'}
default_roof_amount = "50000"
DESTINATION_TYPES = {
"1": 'MOBILE_MONEY',
"2": 'BANK_ACCOUNT',
}

def __init__(self, public_key, private_key, secret, sandbox=False):
self.secret = secret
Expand Down Expand Up @@ -53,7 +63,30 @@ def refund_transaction(self, transaction_id):
),
)

def setup_payout(self, options):
self.url += "/merchant/payouts/schedule"
r = requests.post(self.url, data=options, headers=self.headers)
return r.text
def setup_payout(self, algorithm: str, destination: str, destination_type: str, roof_amount: str = None,
send_notification: bool = True, rate_frequency: str = RATE_FREQUENCIES["1"],
country_code: str = "229"):
options = {
"destination": country_code + destination if destination_type == "1" else destination,
"send_notification": 1 if send_notification else 0,
}

if destination_type in self.DESTINATION_TYPES.keys():
options["destination_type"] = self.DESTINATION_TYPES[destination_type]
else:
raise KKiapayDestinationTypeException(self.DESTINATION_TYPES)

if algorithm in self.ALGORITHMS.keys():
options['algorithm'] = self.ALGORITHMS[algorithm]
else:
raise KKiapayAlogrithmException(self.ALGORITHMS)
if algorithm == "2":
options["roof_amount"] = roof_amount if roof_amount is not None else self.default_roof_amount
else:
options["rate_frequency"] = rate_frequency if rate_frequency in self.RATE_FREQUENCIES.values() else \
self.RATE_FREQUENCIES["1"]
r = requests.post(self.url + '/merchant/payouts/schedule', data=options, headers=self.headers)
return {
'response': r.json(),
'status_code': r.status_code
}
14 changes: 14 additions & 0 deletions kkiapay/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,16 @@
class KkiapayException(Exception):
pass


class KKiapayAlogrithmException(Exception):
def __init__(self, algorithms):
Exception.__init__(self,
"algorithm must be {}".format(
" or ".join(" if it's ".join(algorithm) for algorithm in algorithms.items())))


class KKiapayDestinationTypeException(Exception):
def __init__(self, algorithms):
Exception.__init__(self,
"destination_type must be {}".format(
" or ".join(" if it's ".join(algorithm) for algorithm in algorithms.items())))
49 changes: 48 additions & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,57 @@ Here's the plan for what's coming:
- [x] Sandbox and Live environments
- [x] Verify Transaction
- [x] Refund Transaction - `Only available on Live`
- [ ] Schedule Payout
- [x] Schedule Payout
- [ ] Add better errors and exceptions handling
- [ ] Add tests.


## Schedule Payout

### Example
Below is an example of the function usage:
```python
# Setup Scheduled Payout using 'ROOF' algorithm
schedule_payout = k.setup_payout(algorithm = '2',
roof_amount = '10000',
destination = '61000000',
destination_type = '1',
send_notification = True,
country_code = '229'
)

print(schedule_payout)
#{
# 'response':
# {
# 'merchant': '5d34445784deb700073a0281',
# 'meta_data': '',
# 'algorithm': 'roof',
# 'rate_frequency': '',
# 'roof_amount': '10000',
# 'active': True,
# 'send_notification': True,
# 'destination_type': 'MOBILE_MONEY',
# 'destination': '22961000000',
# 'job_name': '',
# 'account': '5d34445784deb700073a0282'
# },
# 'status_code': 200
#}
```

### Attribute Matrix
| Name | Required | Possible Values | Description |
|:-----------------:|:--------:|:------------------------------------------:|:---------------------------------------------------------------------------------:|
| **algorithm** | **M** | {"1": 'rate', "2": 'roof'} | Specify the algorithm to be used. |
| **destination_type** | **M** | {"1": 'MOBILE_MONEY', "2": 'BANK_ACCOUNT'} | Specify the Destination type |
| **destination** | **M** | '61000000' | Specify the Destination number/account Number |
| **rate_frequency** | M/O | {"1": '3d', "2": '1w', "3": '1m'} | Specify the Rate Frequency. Required in case 'rate' algorithm is used |
| **roof_amount** | M/O | '10000' | Specify the Roof amount. |
| **send_notification** | O | Boolean (True, False) | Specify is a Notification should be sent |
| **country_code** | O | '229' | Specify the Country Code of the destination number is case 'MOBILE MONEY' is used |


## Contributing

Check our [contribution guide](CONTRIBUTING.md).
Expand Down
53 changes: 48 additions & 5 deletions tests/test_kkiapay.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,55 @@ def teardown_module(module):


class TestKkiapay(object):
def test_urls(self):
live = Kkiapay("public_key", "private_key", "secret")
sandbox = Kkiapay("public_key", "private_key", "secret", sandbox=True)
live = Kkiapay("public_key", "private_key", "secret")
sandbox = Kkiapay("public_key", "private_key", "secret", sandbox=True)

assert live.url == Kkiapay.BASE_URL
assert sandbox.url == Kkiapay.SANDBOX_URL
def test_urls(self):
assert self.live.url == Kkiapay.BASE_URL
assert self.sandbox.url == Kkiapay.SANDBOX_URL

def test_requests(self, requests_mock):
pass

def test_setup_payout(self, monkeypatch):
results = {
'response': {
'merchant': '5d34445784deb700073a0281',
'meta_data': '',
'algorithm': 'roof',
'rate_frequency': '',
'roof_amount': '50000',
'active': True,
'send_notification': True,
'destination_type': 'MOBILE_MONEY',
'destination': '22961000000',
'job_name': '',
'account': '5d34445784deb700073a0282'
},
'status_code': 200}

class MockJsonResponse:
status_code = 200

@staticmethod
def json():
return {
'merchant': '5d34445784deb700073a0281',
'meta_data': '',
'algorithm': 'roof',
'rate_frequency': '',
'roof_amount': '50000',
'active': True,
'send_notification': True,
'destination_type': 'MOBILE_MONEY',
'destination': '22961000000',
'job_name': '',
'account': '5d34445784deb700073a0282'
}

def kkiapay_api_mock_return(*args, **kwargs):
return MockJsonResponse()

monkeypatch.setattr(requests, 'post', kkiapay_api_mock_return)
assert self.sandbox.setup_payout(algorithm="2", destination="61000000", destination_type="1") == results
assert self.live.setup_payout(algorithm="1", destination="61000000", destination_type="1") == results