├── .github └── workflows │ ├── lint.yml │ └── release.yml ├── .gitignore ├── CONTRIBUTING.md ├── Pipfile ├── Pipfile.lock ├── README.md ├── kkiapay ├── __init__.py ├── api.py ├── base.py └── utils.py ├── requirements.txt ├── setup.py └── tests ├── __init__.py ├── context.py └── test_kkiapay.py /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | run-linters: 7 | name: Run linters 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - name: Check out Git repository 12 | uses: actions/checkout@v2 13 | 14 | - name: Set up Python 15 | uses: actions/setup-python@v1 16 | with: 17 | python-version: 3.9 18 | 19 | - name: Install Python dependencies 20 | run: pip install black flake8 21 | 22 | - name: Run linters 23 | uses: wearerequired/lint-action@v1 24 | with: 25 | black: true 26 | flake8: false 27 | flake8_args: --ignore=E501 28 | auto_fix: true 29 | continue_on_error: false 30 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Publishing to Pypi 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | jobs: 8 | test: 9 | name: Test 10 | runs-on: ubuntu-latest 11 | strategy: 12 | matrix: 13 | python-version: 14 | [ 15 | "3.8", 16 | "3.9", 17 | "3.10" 18 | ] 19 | 20 | steps: 21 | - uses: actions/checkout@v2 22 | 23 | - name: Set up Python ${{ matrix.python-version }} 24 | uses: actions/setup-python@v2 25 | with: 26 | python-version: ${{ matrix.python-version }} 27 | - name: Install Dependencies 28 | run: | 29 | python -m pip install --upgrade pip 30 | pip install -e ".[test]" 31 | - name: Run Tests 32 | run: | 33 | pytest 34 | build-n-publish: 35 | name: Build and publish 36 | needs: [test] 37 | runs-on: ubuntu-latest 38 | steps: 39 | - uses: actions/checkout@master 40 | - name: Set up Python 3.10 41 | uses: actions/setup-python@v3 42 | with: 43 | python-version: "3.10" 44 | - name: Install pypa/build 45 | run: >- 46 | python -m 47 | pip install 48 | build 49 | --user 50 | - name: Build a binary wheel and a source tarball 51 | run: >- 52 | python -m 53 | build 54 | --sdist 55 | --wheel 56 | --outdir dist/ 57 | - name: Publish distribution 📦 to PyPI 58 | if: startsWith(github.ref, 'refs/tags') 59 | uses: pypa/gh-action-pypi-publish@release/v1 60 | with: 61 | password: ${{ secrets.PYPI_API_TOKEN }} 62 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .pytest_cache 2 | .vscode 3 | test.py 4 | kkiapay.egg-info 5 | dist 6 | build 7 | venv 8 | .idea 9 | __pycache__ 10 | .env 11 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | All contributions are welcomed. If it's your first time contributing to open source please don't hesitate and just open a Pull Request. Your suggestion/fix isn't stupid, I'll make sure I explain what's wrong with your PR if it wasn't accepted. 4 | 5 | ## Here are some points to consider: 6 | 7 | - Your PR must be making only a single change, if you want to suggest multiple features or fix multiple issues please open separate PRs. 8 | - If you have an idea that will require a lot of work, make sure you suggest it in a new [issue](https://github.com/PythonBenin/kkiapay-python/issues) first to make sure it's admired before investing time into it. 9 | - If you want to contribute and not sure where to start, check the [road map](https://github.com/PythonBenin/kkiapay-python#road-map). 10 | - Pull Requests that don't have a clear explanation of what it does will be closed. Sorry :) 11 | - Keep your code clean. Clean means you're proud of how it turned out. 12 | 13 | ## How to contribute: 14 | 15 | Clone `kkiapay-python` on your machine. Install all development dependencies using: 16 | 17 | ```bash 18 | pipenv install --dev 19 | ``` 20 | If you haven't used `pipenv` before but are comfortable with virtualenvs, just run `pip install pipenv` in the virtualenv you're already using and invoke the command above from the cloned `kkiapay-python` repo. It will do the correct thing. 21 | 22 | -------------------------------------------------------------------------------- /Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | url = "https://pypi.org/simple" 3 | verify_ssl = true 4 | name = "pypi" 5 | 6 | [dev-packages] 7 | 8 | [packages] 9 | pytest = "*" 10 | black = "*" 11 | requests = "*" 12 | mock = "*" 13 | requests-mock = "*" 14 | setuptools = "*" 15 | wheel = "*" 16 | twine = "*" 17 | 18 | [requires] 19 | python_version = "3.7" 20 | -------------------------------------------------------------------------------- /Pipfile.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "hash": { 4 | "sha256": "828b8ad012f4c8773e6e61e3ac2be0ffcd7540fd7ed175a8355676c8e31c4d3d" 5 | }, 6 | "pipfile-spec": 6, 7 | "requires": { 8 | "python_version": "3.7" 9 | }, 10 | "sources": [ 11 | { 12 | "name": "pypi", 13 | "url": "https://pypi.org/simple", 14 | "verify_ssl": true 15 | } 16 | ] 17 | }, 18 | "default": { 19 | "atomicwrites": { 20 | "hashes": [ 21 | "sha256:03472c30eb2c5d1ba9227e4c2ca66ab8287fbfbbda3888aa93dc2e28fc6811b4", 22 | "sha256:75a9445bac02d8d058d5e1fe689654ba5a6556a1dfd8ce6ec55a0ed79866cfa6" 23 | ], 24 | "version": "==1.3.0" 25 | }, 26 | "attrs": { 27 | "hashes": [ 28 | "sha256:69c0dbf2ed392de1cb5ec704444b08a5ef81680a61cb899dc08127123af36a79", 29 | "sha256:f0b870f674851ecbfbbbd364d6b5cbdff9dcedbc7f3f5e18a6891057f21fe399" 30 | ], 31 | "version": "==19.1.0" 32 | }, 33 | "importlib-metadata": { 34 | "hashes": [ 35 | "sha256:6dfd58dfe281e8d240937776065dd3624ad5469c835248219bd16cf2e12dbeb7", 36 | "sha256:cb6ee23b46173539939964df59d3d72c3e0c1b5d54b84f1d8a7e912fe43612db" 37 | ], 38 | "version": "==0.18" 39 | }, 40 | "more-itertools": { 41 | "hashes": [ 42 | "sha256:3ad685ff8512bf6dc5a8b82ebf73543999b657eded8c11803d9ba6b648986f4d", 43 | "sha256:8bb43d1f51ecef60d81854af61a3a880555a14643691cc4b64a6ee269c78f09a" 44 | ], 45 | "version": "==7.1.0" 46 | }, 47 | "packaging": { 48 | "hashes": [ 49 | "sha256:0c98a5d0be38ed775798ece1b9727178c4469d9c3b4ada66e8e6b7849f8732af", 50 | "sha256:9e1cbf8c12b1f1ce0bb5344b8d7ecf66a6f8a6e91bcb0c84593ed6d3ab5c4ab3" 51 | ], 52 | "version": "==19.0" 53 | }, 54 | "pluggy": { 55 | "hashes": [ 56 | "sha256:0825a152ac059776623854c1543d65a4ad408eb3d33ee114dff91e57ec6ae6fc", 57 | "sha256:b9817417e95936bf75d85d3f8767f7df6cdde751fc40aed3bb3074cbcb77757c" 58 | ], 59 | "version": "==0.12.0" 60 | }, 61 | "py": { 62 | "hashes": [ 63 | "sha256:64f65755aee5b381cea27766a3a147c3f15b9b6b9ac88676de66ba2ae36793fa", 64 | "sha256:dc639b046a6e2cff5bbe40194ad65936d6ba360b52b3c3fe1d08a82dd50b5e53" 65 | ], 66 | "version": "==1.8.0" 67 | }, 68 | "pyparsing": { 69 | "hashes": [ 70 | "sha256:1873c03321fc118f4e9746baf201ff990ceb915f433f23b395f5580d1840cb2a", 71 | "sha256:9b6323ef4ab914af344ba97510e966d64ba91055d6b9afa6b30799340e89cc03" 72 | ], 73 | "version": "==2.4.0" 74 | }, 75 | "pytest": { 76 | "hashes": [ 77 | "sha256:6ef6d06de77ce2961156013e9dff62f1b2688aa04d0dc244299fe7d67e09370d", 78 | "sha256:a736fed91c12681a7b34617c8fcefe39ea04599ca72c608751c31d89579a3f77" 79 | ], 80 | "index": "pypi", 81 | "version": "==5.0.1" 82 | }, 83 | "six": { 84 | "hashes": [ 85 | "sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", 86 | "sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73" 87 | ], 88 | "version": "==1.12.0" 89 | }, 90 | "wcwidth": { 91 | "hashes": [ 92 | "sha256:3df37372226d6e63e1b1e1eda15c594bca98a22d33a23832a90998faa96bc65e", 93 | "sha256:f4ebe71925af7b40a864553f761ed559b43544f8f71746c2d756c7fe788ade7c" 94 | ], 95 | "version": "==0.1.7" 96 | }, 97 | "zipp": { 98 | "hashes": [ 99 | "sha256:4970c3758f4e89a7857a973b1e2a5d75bcdc47794442f2e2dd4fe8e0466e809a", 100 | "sha256:8a5712cfd3bb4248015eb3b0b3c54a5f6ee3f2425963ef2a0125b8bc40aafaec" 101 | ], 102 | "version": "==0.5.2" 103 | } 104 | }, 105 | "develop": {} 106 | } 107 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # kkiapay-python 2 | 3 | - [Introduction](#introduction) 4 | - [Installation](#installation) 5 | - [Usage](#usage) 6 | - [Contributing](#contributing) 7 | - [License](#license) 8 | 9 | ## Introduction 10 | 11 | Community-driven admin KkiaPay Sdk for Python. 12 | 13 | ## Installation 14 | 15 | To get the latest version of KkiaPay Sdk for Python , simply run: 16 | 17 | ```bash 18 | pip install kkiapay 19 | ``` 20 | 21 | ## Usage 22 | 23 | Behold, the power of `kkiapay-python`: 24 | 25 | ### Verify a transaction 26 | 27 | ```python 28 | from kkiapay import Kkiapay 29 | 30 | k = Kkiapay('public_key', 'private_key', 'secret', sandbox=True) 31 | 32 | transaction = k.verify_transaction('LVFNrK1nx') 33 | 34 | print(transaction) 35 | # => { 36 | # "performed_at":"2023-02-20T17:44:47.842Z", 37 | # "received_at":1676915100302, 38 | # "type":"DEBIT", 39 | # "status":"SUCCESS", 40 | # "source":"MOBILE_MONEY", 41 | # "source_common_name":"mtn-benin", 42 | # "amount":100, 43 | # "fees":2, 44 | # "net":0, 45 | # "externalTransactionId":"test", 46 | # "transactionId":"123", 47 | # ... 48 | # } 49 | ``` 50 | 51 | ### Refund a transaction 52 | 53 | ```python 54 | from kkiapay import Kkiapay 55 | 56 | k = Kkiapay('public_key', 'private_key', 'secret', sandbox=True) 57 | 58 | transaction = k.refund_transaction('LVFNrK1nx') 59 | 60 | print(transaction) 61 | # => { 62 | # "code":"SUCCESS", 63 | # "description":"REVERTED", 64 | # "transactionId":"123" 65 | # } 66 | ``` 67 | 68 | ### Schedule Payout 69 | 70 | Schedule payout API is deprecated and no longer supported as of Feb 20th, 2023 from the API and can be done only on the [dashboard](https://app.kkiapay.me/dashboard) until further notice. 71 | 72 | ## Contributing 73 | 74 | Check our [contribution guide](CONTRIBUTING.md). 75 | 76 | ## License 77 | 78 | `kkiapay-python` is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT). 79 | -------------------------------------------------------------------------------- /kkiapay/__init__.py: -------------------------------------------------------------------------------- 1 | from .api import Kkiapay 2 | -------------------------------------------------------------------------------- /kkiapay/api.py: -------------------------------------------------------------------------------- 1 | from .base import KkiapayBase 2 | 3 | 4 | class Kkiapay(KkiapayBase): 5 | def __init__( 6 | self, public_key: str, private_key: str, secret: str, sandbox=False 7 | ) -> None: 8 | r"""Initialize Kkiapay client. 9 | :param public_key: Public key from https://app.kkiapay.me/dashboard/developers/keys. 10 | :param private_key: Provate key from https://app.kkiapay.me/dashboard/developers/keys. 11 | :param secret: Kiapay secret from https://app.kkiapay.me/dashboard/developers/keys. 12 | :param sandbox: A boolean value to specify the environment (Sandbox or Live). 13 | """ 14 | self._initialize_credentials(public_key, private_key, secret, sandbox) 15 | 16 | def verify_transaction(self, transaction_id: str) -> dict: 17 | r"""Verify a transaction. 18 | :param transaction_id: transaction ID 19 | """ 20 | return self._verify_transaction(transaction_id) 21 | 22 | def refund_transaction(self, transaction_id: str) -> dict: 23 | r"""Refund a specific transaction. 24 | :param transaction_id: transaction ID 25 | """ 26 | return self._refund_transaction(transaction_id) 27 | -------------------------------------------------------------------------------- /kkiapay/base.py: -------------------------------------------------------------------------------- 1 | from .utils import make_request 2 | 3 | SANDBOX_BASE_URL = "https://api-sandbox.kkiapay.me" 4 | PRODUCTION_BASE_URL = "https://api.kkiapay.me" 5 | 6 | 7 | class KkiapayBase: 8 | def _initialize_credentials( 9 | self, public_key: str, private_key: str, secret: str, sandbox: bool 10 | ) -> None: 11 | self.public_key = public_key 12 | self.private_key = private_key 13 | self.secret = secret 14 | self.is_sandbox_environment = sandbox 15 | 16 | def _build_request_headers(self) -> dict: 17 | return { 18 | "Accept": "application/json", 19 | "X-SECRET-KEY": self.secret, 20 | "X-API-KEY": self.public_key, 21 | "X-PRIVATE-KEY": self.private_key, 22 | } 23 | 24 | def _build_request_url(self, path: str) -> str: 25 | BASE_URL = ( 26 | SANDBOX_BASE_URL if self.is_sandbox_environment else PRODUCTION_BASE_URL 27 | ) 28 | return f"{BASE_URL}/{path}" 29 | 30 | def _verify_transaction(self, transaction_id: str): 31 | return make_request( 32 | "post", 33 | self._build_request_url("api/v1/transactions/status"), 34 | json={"transactionId": transaction_id}, 35 | headers=self._build_request_headers(), 36 | ) 37 | 38 | def _refund_transaction(self, transaction_id: str): 39 | return make_request( 40 | "post", 41 | self._build_request_url("api/v1/transactions/revert"), 42 | json={"transactionId": transaction_id}, 43 | headers=self._build_request_headers(), 44 | ) 45 | -------------------------------------------------------------------------------- /kkiapay/utils.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from requests.auth import HTTPBasicAuth 3 | 4 | 5 | def make_request(method: str, url: str, data=None, json=None, auth=None, **kwargs): 6 | """Perform a request. 7 | Usage:: 8 | >>> from .utils import make_request 9 | >>> req = make_request('get', 'https://api-sandbox.kkiapay.me') 10 | """ 11 | 12 | try: 13 | r = getattr(requests, method)( 14 | url, 15 | data=data, 16 | json=json, 17 | auth=auth if not auth else HTTPBasicAuth(**auth), 18 | **kwargs, 19 | ) 20 | 21 | return r.json() 22 | except requests.exceptions.RequestException as e: 23 | raise e 24 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | requests==2.31.0 2 | responses==0.22.0 3 | pytest==7.2.1 4 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import os 2 | import setuptools 3 | 4 | with open("requirements.txt") as f: 5 | required = f.read().splitlines() 6 | 7 | with open("README.md", "r") as fh: 8 | long_description = fh.read() 9 | 10 | setuptools.setup( 11 | name="kkiapay", 12 | version="0.0.6", 13 | author="Junior Gantin", 14 | author_email="nioperas06@gmail.com", 15 | description="Community-driven Admin KkiaPay Sdk for Python", 16 | long_description=long_description, 17 | long_description_content_type="text/markdown", 18 | url="https://github.com/PythonBenin/kkiapay-python", 19 | packages=setuptools.find_packages(), 20 | install_requires=required, 21 | classifiers=[ 22 | "Programming Language :: Python :: 3", 23 | "License :: OSI Approved :: MIT License", 24 | "Operating System :: OS Independent", 25 | ], 26 | ) 27 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonBenin/kkiapay-python/2ffe1e8e1aeb490ebbcd34461c14fd7ec5bad75f/tests/__init__.py -------------------------------------------------------------------------------- /tests/context.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import sys 4 | import os 5 | 6 | sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) 7 | 8 | import kkiapay 9 | -------------------------------------------------------------------------------- /tests/test_kkiapay.py: -------------------------------------------------------------------------------- 1 | from .context import kkiapay 2 | from kkiapay import Kkiapay 3 | import responses 4 | 5 | 6 | @responses.activate 7 | def test_verify_transaction(): 8 | responses.add( 9 | responses.POST, 10 | "https://api-sandbox.kkiapay.me/api/v1/transactions/status", 11 | json={ 12 | "performed_at": "2023-02-20T17:44:47.842Z", 13 | "received_at": 1676915100302, 14 | "type": "DEBIT", 15 | "status": "SUCCESS", 16 | "source": "MOBILE_MONEY", 17 | }, 18 | ) 19 | 20 | k = Kkiapay("public_key", "private_key", "secret", True) 21 | 22 | response = k.verify_transaction("123") 23 | assert response["type"] == "DEBIT" 24 | 25 | 26 | @responses.activate 27 | def test_refund_transaction(): 28 | responses.add( 29 | responses.POST, 30 | "https://api-sandbox.kkiapay.me/api/v1/transactions/revert", 31 | json={"code": "SUCCESS", "description": "REVERTED", "transactionId": "123"}, 32 | ) 33 | 34 | k = Kkiapay("public_key", "private_key", "secret", True) 35 | 36 | response = k.refund_transaction("123") 37 | assert response == { 38 | "code": "SUCCESS", 39 | "description": "REVERTED", 40 | "transactionId": "123", 41 | } 42 | --------------------------------------------------------------------------------