-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 8134e07
Showing
25 changed files
with
1,001 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
*.pyc | ||
/dist/ | ||
/build/ | ||
/*.egg-info | ||
/.idea/ | ||
.idea/ | ||
paystackpy.egg-info/ | ||
/paystackpy.egg-info/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
MIT License | ||
|
||
Copyright (c) 2019 Confi Yobo | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
# paystackpy | ||
A Paystack API wrapper with python |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
from paystackpy.customers import Customer | ||
from paystackpy.transactions import Transaction | ||
from paystackpy.plans import Plan | ||
from paystackpy.transfers import Transfer | ||
|
||
name = "paystackpy" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
import os | ||
|
||
import requests | ||
import simplejson as json | ||
|
||
from paystackpy.errors import * | ||
import paystackpy.version as version | ||
|
||
|
||
class APIConfig: | ||
""" | ||
API configuration for paystackpy API wrapper | ||
""" | ||
|
||
_CONTENT_TYPE = "application/json" | ||
_API_END_POINT = "https://api.paystack.co" | ||
|
||
def __init__(self, authorization_key=None): | ||
if authorization_key: | ||
self._PAYSTACK_AUTHORIZATION_KEY = authorization_key | ||
else: | ||
self._PAYSTACK_AUTHORIZATION_KEY = os.getenv('PAYSTACK_AUTHORIZATION_KEY', None) | ||
|
||
if not self._PAYSTACK_AUTHORIZATION_KEY: | ||
raise MissingAuthKeyError("Missing paystack Authorization key") | ||
|
||
self.request_headers = { | ||
"Authorization": "Bearer {0}".format(self._PAYSTACK_AUTHORIZATION_KEY), | ||
"Content-Type": "application/json", | ||
"user-agent": "PaystackPy - {0}".format(version.__version__) | ||
} | ||
|
||
def _url(self, path): | ||
return self._API_END_POINT + path | ||
|
||
def _parse_json(self, response_obj): | ||
parsed_response = response_obj.json() | ||
|
||
status = parsed_response.get('status', None) | ||
message = parsed_response.get('message', None) | ||
data = parsed_response.get('data', None) | ||
return response_obj.status_code, status, message, data | ||
|
||
def _handle_request(self, method, url, data=None): | ||
|
||
""" | ||
Generic function to handle all API url calls | ||
Returns a python tuple of status code, status(bool), message, data | ||
""" | ||
method_map = { | ||
'GET': requests.get, | ||
'POST': requests.post, | ||
'PUT': requests.put, | ||
'DELETE': requests.delete | ||
} | ||
|
||
payload = json.dumps(data) if data else data | ||
request = method_map.get(method) | ||
|
||
if not request: | ||
raise InvalidMethodError("Request method not recognised or implemented") | ||
|
||
response = request(url, headers=self.request_headers, data=payload, verify=True) | ||
if response.status_code == 404: | ||
return response.status_code, False, "The object request cannot be found", None | ||
|
||
if response.status_code in [200, 201]: | ||
return self._parse_json(response) | ||
else: | ||
body = response.json() | ||
return {"status_code": response.status_code, "status": body.get('status'), "message": body.get('message'), | ||
"errors": body.get('errors')} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
from paystackpy.apiConfig import APIConfig | ||
|
||
|
||
class Customer(APIConfig): | ||
def create(self, email, first_name=None, last_name=None, phone=None): | ||
""" | ||
Creates a new paystack customer account | ||
args: | ||
email -- Customer's email address | ||
first_name-- Customer's first name (Optional) | ||
last_name-- Customer's last name (Optional) | ||
phone -- optional | ||
""" | ||
url = self._url("/customer/") | ||
payload = { | ||
"first_name": first_name, | ||
"last_name": last_name, | ||
"email": email, | ||
"phone": phone | ||
} | ||
return self._handle_request('POST', url, payload) | ||
|
||
def update(self, user_id, email, first_name=None, last_name=None, phone=None): | ||
""" | ||
Update a customer account given the user id | ||
args: | ||
user_id -- id of the customer | ||
email -- Customer's email address | ||
first_name-- Customer's first name (Optional) | ||
last_name-- Customer's last name (Optional) | ||
phone -- optional | ||
""" | ||
url = self._url("/customer/{}/".format(user_id)) | ||
payload = { | ||
"first_name": first_name, | ||
"last_name": last_name, | ||
"email": email, | ||
"phone": phone | ||
} | ||
return self._handle_request('PUT', url, payload) | ||
|
||
def getall(self, pagination=10): | ||
""" | ||
Gets all the customers we have at paystack in steps of (default) 50 records per page. | ||
We can provide an optional pagination to indicate how many customer records we want to fetch per time | ||
args: | ||
pagination -- Count of data to return per call | ||
""" | ||
url = self._url("/customer/?perPage=" + str(pagination)) | ||
return self._handle_request('GET', url) | ||
|
||
def getone(self, user_id): | ||
""" | ||
Gets the customer with the given user id | ||
args: | ||
user_id -- The customer's user id | ||
""" | ||
url = self._url("/customer/{}/".format(user_id)) | ||
return self._handle_request('GET', url) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
class PaystackPyError(Exception): | ||
""" | ||
Python Paystack Error | ||
""" | ||
pass | ||
|
||
|
||
class MissingAuthKeyError(PaystackPyError): | ||
""" | ||
We can't find the authentication key | ||
""" | ||
pass | ||
|
||
|
||
class InvalidMethodError(PaystackPyError): | ||
""" | ||
Invalid or unrecoginised/unimplemented HTTP request method | ||
""" | ||
pass | ||
|
||
|
||
class InvalidDataError(PaystackPyError): | ||
""" | ||
Invalid input recognised. Saves unecessary trip to server | ||
""" | ||
pass |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
from paystackpy.apiConfig import APIConfig | ||
|
||
|
||
class Misc(APIConfig): | ||
def allbanks(self, perpage=50, page=1): | ||
url = self._url("/bank?perPage={}&page={}".format(perpage, page)) | ||
return self._handle_request('GET', url) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
import paystackpy.utils as utils | ||
from paystackpy.apiConfig import APIConfig | ||
|
||
|
||
class Plan(APIConfig): | ||
def create(self, name, amount, interval, description=None, \ | ||
send_invoices=False, send_sms=False, hosted_page=False, hosted_page_url=None, hosted_page_summary=None, | ||
currency=None): | ||
""" | ||
Creates a new plan. Returns the plan details created | ||
args: | ||
name -- Name of the plan to create | ||
amount -- Amount to attach to this plan | ||
interval -- 'hourly', 'daily', 'weekly', 'monthly', 'annually' | ||
description -- Plan Description (optional) | ||
""" | ||
interval = utils.validate_interval(interval) | ||
amount = utils.validate_amount(amount) | ||
|
||
url = self._url("/plan/") | ||
payload = { | ||
"name": name, | ||
"amount": amount, | ||
"interval": interval, | ||
"currency": currency, | ||
"send_sms": send_sms, | ||
"description": description, | ||
"hosted_page": hosted_page, | ||
"send_invoices": send_invoices, | ||
"hosted_page_url": hosted_page_url, | ||
"hosted_page_summary": hosted_page_summary, | ||
} | ||
return self._handle_request('POST', url, payload) | ||
|
||
def update(self, plan_id, name, amount, interval, description=None, \ | ||
send_invoices=False, send_sms=False, hosted_page=False, hosted_page_url=None, hosted_page_summary=None, | ||
currency=None): | ||
""" | ||
Updates an existing plan given a plan id. Returns the plan details updated. | ||
args: | ||
plan_id -- Plan Id to update | ||
name -- New plan name | ||
amount -- New Amount to attach to this plan | ||
interval -- 'hourly', 'daily', 'weekly', 'monthly', 'annually' | ||
description -- Plan Description (optional) | ||
""" | ||
interval = utils.validate_interval(interval) | ||
amount = utils.validate_amount(amount) | ||
|
||
url = self._url("/plan/{}/".format(plan_id)) | ||
payload = { | ||
"name": name, | ||
"amount": amount, | ||
"interval": interval, | ||
"currency": currency, | ||
"send_sms": send_sms, | ||
"description": description, | ||
"hosted_page": hosted_page, | ||
"send_invoices": send_invoices, | ||
"hosted_page_url": hosted_page_url, | ||
"hosted_page_summary": hosted_page_summary, | ||
} | ||
return self._handle_request('PUT', url, payload) | ||
|
||
def getall(self, pagination=10): | ||
""" | ||
Gets all plans | ||
""" | ||
url = self._url("/plan/?perPage=" + str(pagination)) | ||
return self._handle_request('GET', url) | ||
|
||
def getone(self, plan_id): | ||
""" | ||
Gets one plan with the given plan id | ||
Requires: plan_id | ||
""" | ||
url = self._url("/plan/{}/".format(plan_id)) | ||
return self._handle_request('GET', url) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
import paystackpy.utils as utils | ||
from paystackpy.apiConfig import APIConfig | ||
from paystackpy.errors import InvalidDataError | ||
|
||
|
||
class Transaction(APIConfig): | ||
def getall(self, pagination=10): | ||
""" | ||
Gets all your transactions | ||
args: | ||
pagination -- Count of data to return per call | ||
""" | ||
url = self._url("/transaction/?perPage=" + str(pagination)) | ||
return self._handle_request('GET', url) | ||
|
||
def getone(self, transaction_id): | ||
""" | ||
Gets one customer with the given transaction id | ||
args: | ||
Transaction_id -- transaction we want to get | ||
""" | ||
url = self._url("/transaction/{}/".format(transaction_id)) | ||
return self._handle_request('GET', url) | ||
|
||
def totals(self): | ||
""" | ||
Gets transaction totals | ||
""" | ||
url = self._url("/transaction/totals/") | ||
return self._handle_request('GET', url) | ||
|
||
def initialize(self, email, amount, plan=None, reference=None, channel=None, metadata=None): | ||
""" | ||
Initialize a transaction and returns the response | ||
args: | ||
email -- Customer's email address | ||
amount -- Amount to charge | ||
plan -- optional | ||
Reference -- optional | ||
channel -- channel type to use | ||
metadata -- a list if json data objects/dicts | ||
""" | ||
amount = utils.validate_amount(amount) | ||
|
||
if not email: | ||
raise InvalidDataError("Customer's Email is required for initialization") | ||
|
||
url = self._url("/transaction/initialize") | ||
payload = { | ||
"email": email, | ||
"amount": amount, | ||
"reference": reference, | ||
"plan": plan, | ||
"channels": channel, | ||
"metadata": {"custom_fields": metadata} | ||
} | ||
return self._handle_request('POST', url, payload) | ||
|
||
def charge(self, email, auth_code, amount, reference=None): | ||
""" | ||
Charges a customer and returns the response | ||
args: | ||
auth_code -- Customer's auth code | ||
email -- Customer's email address | ||
amount -- Amount to charge | ||
reference -- optional | ||
""" | ||
amount = utils.validate_amount(amount) | ||
|
||
if not email: | ||
raise InvalidDataError("Customer's Email is required to charge") | ||
|
||
if not auth_code: | ||
raise InvalidDataError("Customer's Auth code is required to charge") | ||
|
||
url = self._url("/transaction/charge_authorization") | ||
payload = { | ||
"authorization_code": auth_code, | ||
"email": email, | ||
"amount": amount, | ||
"reference": reference | ||
} | ||
|
||
return self._handle_request('POST', url, payload) | ||
|
||
def verify(self, reference): | ||
""" | ||
Verifies a transaction using the provided reference number | ||
args: | ||
reference -- reference of the transaction to verify | ||
""" | ||
|
||
reference = str(reference) | ||
url = self._url("/transaction/verify/{}".format(reference)) | ||
return self._handle_request('GET', url) | ||
|
||
|
Oops, something went wrong.