Skip to content

Commit

Permalink
Initial Commit
Browse files Browse the repository at this point in the history
  • Loading branch information
ConfidenceYobo committed Jun 10, 2019
0 parents commit 8134e07
Show file tree
Hide file tree
Showing 25 changed files with 1,001 additions and 0 deletions.
8 changes: 8 additions & 0 deletions .gitignore
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/
21 changes: 21 additions & 0 deletions LICENSE
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.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# paystackpy
A Paystack API wrapper with python
6 changes: 6 additions & 0 deletions build/lib/paystackpy/__init__.py
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"
72 changes: 72 additions & 0 deletions build/lib/paystackpy/apiConfig.py
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')}
63 changes: 63 additions & 0 deletions build/lib/paystackpy/customers.py
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)
26 changes: 26 additions & 0 deletions build/lib/paystackpy/errors.py
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
7 changes: 7 additions & 0 deletions build/lib/paystackpy/misc.py
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)
81 changes: 81 additions & 0 deletions build/lib/paystackpy/plans.py
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)
102 changes: 102 additions & 0 deletions build/lib/paystackpy/transactions.py
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)


Loading

0 comments on commit 8134e07

Please sign in to comment.