bursapay-sdk · Python
Official Python SDK for the BursaPay Developer Gateway API.
pip install bursapay-sdk
Version: 0.2.0 · Python 3.8–3.12 · Sync & async · PyPI
Quick start
from bursapay import BursaPay
# Production
bp = BursaPay("sk_live_xxxx")
# Sandbox / test
bp = BursaPay("sk_test_xxxx")
# Local dev server
bp = BursaPay("sk_test_xxxx", base_url="http://localhost:8000/api/v1")
Payments
# Initialize — returns an authorization URL to redirect your customer to
payment = bp.payments.initialize(
amount=5000, # NGN, not kobo
email="customer@example.com",
currency="NGN", # NGN | USD | GBP | KES
metadata={"order_id": "ORD-123"},
)
print(payment["authorization_url"])
# Verify after redirect back
result = bp.payments.verify("BP-XXXX")
print(result["status"]) # "success" | "failed" | "pending"
# Retrieve a single payment
payment = bp.payments.retrieve("BP-XXXX")
# List with optional filters
page = bp.payments.list(status="success", page_size=20)
# Charge a saved authorization code (recurring)
bp.payments.charge(
authorization_code="AUTH_xxx",
email="customer@example.com",
amount=5000,
)
# Charge saved card without redirect (card-on-file / one-click)
bp.payments.charge_saved_card(
customer_reference="cust_abc123",
authorization_code="AUTH_xxx",
amount=5000,
)
# Schedule a future charge (must be >60 s in the future)
bp.payments.initialize(
amount=5000,
email="customer@example.com",
charge_at="2025-12-31T09:00:00Z",
)
# Cancel a scheduled payment
bp.payments.cancel_schedule("BP-XXXX")
# Split payment
bp.payments.initialize(
amount=10000,
email="buyer@example.com",
splits=[
{"subaccount_code": "ACCT_abc123", "share": 0.8},
{"subaccount_code": "ACCT_def456", "share": 0.2},
],
)
# Bulk initialize
bp.payments.bulk_initialize([
{"amount": 1000, "email": "a@b.com", "reference": "BP-001"},
{"amount": 2000, "email": "c@d.com", "reference": "BP-002"},
])
Customers
customer = bp.customers.create(email="jane@example.com", name="Jane Doe")
print(customer["customer_reference"]) # "cust_xxxx"
bp.customers.list()
bp.customers.list(q="jane") # search by email, name, or phone
bp.customers.retrieve("cust_xxxx")
bp.customers.update("cust_xxxx", name="Jane Smith")
bp.customers.payments("cust_xxxx") # payment history
bp.customers.delete("cust_xxxx")
Transfers
transfer = bp.transfers.initiate(
amount=10000,
bank_code="044", # Access Bank
account_number="0123456789",
account_name="John Doe",
narration="Vendor payout",
)
bp.transfers.retrieve(transfer["reference"])
bp.transfers.list(status="completed")
# Bulk payout
bp.transfers.bulk([
{"amount": 5000, "bank_code": "058", "account_number": "0987654321", "account_name": "Vendor A"},
{"amount": 8000, "bank_code": "011", "account_number": "1122334455", "account_name": "Vendor B"},
])
Webhooks
# Register an endpoint
wh = bp.webhooks.create(
url="https://myapp.com/hooks/bursapay/",
events=["payment.success", "payment.failed", "transfer.success"],
)
print(wh["secret"]) # store this to verify incoming payloads
bp.webhooks.list()
bp.webhooks.update(wh["id"], is_active=False)
bp.webhooks.delete(wh["id"])
# Inspect delivery logs
bp.webhooks.logs(wh["id"], status="failed")
bp.webhooks.log_detail(log_id=42)
bp.webhooks.retry(log_id=42)
# All valid event types
bp.webhooks.events()
# Send a test event
bp.webhooks.send_test(wh["id"], event="payment.success")
# Dead-letter queue — deliveries that exhausted all retries
dlq = bp.webhooks.dead_letters()
for entry in dlq["results"]:
if not entry["replayed"]:
bp.webhooks.replay_dead_letter(entry["id"])
Verifying incoming webhook signatures
# Django example
from bursapay import BursaPay
from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt
import json
@csrf_exempt
def bursapay_webhook(request):
is_valid = BursaPay.verify_webhook_signature(
request.body,
request.headers.get("X-BursaPay-Signature", ""),
"your_webhook_secret",
)
if not is_valid:
return HttpResponse(status=401)
event = json.loads(request.body)
if event["event"] == "payment.success":
ref = event["data"]["reference"]
# fulfil order...
return HttpResponse(status=200)
Wallet
bp.wallets.balance()
bp.wallets.ledger() # all entry types
bp.wallets.ledger(entry_type="credit") # filter: credit | debit | fee | settlement | withdrawal | refund
bp.wallets.settlements()
bp.wallets.settlement("STL-xxxx")
Virtual Accounts
# Default bank
va = bp.virtual_accounts.create("cust_xxxx")
# Choose a specific bank: wema-bank | access-bank | titan-paystack | sterling-bank
va = bp.virtual_accounts.create("cust_xxxx", preferred_bank="wema-bank")
print(va["account_number"], va["bank_name"])
bp.virtual_accounts.list()
bp.virtual_accounts.retrieve(va["id"])
Subscriptions
plan = bp.subscriptions.create_plan(
name="Pro Monthly",
amount=5000,
interval="monthly",
)
sub = bp.subscriptions.enroll(
customer_reference="cust_xxxx",
plan_id=plan["id"],
authorization_code="AUTH_xxx",
)
bp.subscriptions.pause(sub["id"])
bp.subscriptions.resume(sub["id"])
bp.subscriptions.cancel(sub["id"])
Invoices
inv = bp.invoices.create(
customer_reference="cust_xxxx",
line_items=[
{"description": "Web design", "quantity": 1, "unit_price": 150000},
{"description": "Hosting (annual)", "quantity": 1, "unit_price": 30000},
],
due_date="2025-12-31",
)
bp.invoices.update(inv["reference"], status="sent") # triggers payment link creation
bp.invoices.list(status="overdue")
bp.invoices.delete(inv["reference"])
Payment Links
link = bp.payment_links.create(
title="Pay for Invoice #42",
amount=180000,
expires_at="2025-12-31T23:59:59Z",
)
print(link["url"])
bp.payment_links.analytics(link["link_code"])
Refunds
refund = bp.refunds.create("BP-XXXX", amount=2500, reason="Customer request")
bp.refunds.retrieve(refund["refund_reference"])
Disputes
# List all disputes
disputes = bp.disputes.list()
disputes = bp.disputes.list(per_page=10, cursor="...")
# Get a single dispute
dispute = bp.disputes.retrieve("DIS-XXXX")
# Submit evidence / update status
bp.disputes.update_evidence(
"DIS-XXXX",
evidence={
"delivery_proof": "https://cdn.example.com/proof.pdf",
"notes": "Item delivered on 2025-01-15",
},
status="under_review",
)
# NOTE: "won" and "lost" statuses are set by Paystack webhook events only —
# the API will reject attempts to set them directly.
Reconciliation
# Requires a live secret key (sk_live_*)
# Rate-limited to 10 requests per hour per developer
result = bp.reconciliation.run("2025-07-31")
print(result["matched"]) # count of matching records
print(result["discrepancies"]) # list of mismatches to investigate
Async support
import asyncio
from bursapay import BursaPay
async def main():
async with BursaPay("sk_test_xxxx").async_client() as bp:
payment = await bp.payments.initialize(amount=5000, email="a@b.com")
print(payment["authorization_url"])
asyncio.run(main())
Error handling
from bursapay import BursaPay
from bursapay.exceptions import (
AuthenticationError,
NotFoundError,
ValidationError,
RateLimitError,
ServerError,
BursaPayError,
)
try:
bp.payments.initialize(amount=5000, email="x@y.com", currency="XYZ")
except ValidationError as e:
print(e.error_code) # "invalid_currency"
print(e.field_errors) # field-level errors dict
print(e.status_code) # 400
except AuthenticationError:
print("Check your API key")
except NotFoundError as e:
print(f"Not found: {e.message}")
except RateLimitError:
print("Rate limited — back off and retry")
except ServerError as e:
print(f"BursaPay server error: {e.status_code}")
except BursaPayError as e:
print(f"Unexpected error: {e}")
Context manager
with BursaPay("sk_test_xxxx") as bp:
result = bp.payments.verify("BP-XXXX")
# connection pool is closed automatically
Environment variables (recommended)
import os
from bursapay import BursaPay
bp = BursaPay(
os.environ["BURSAPAY_SECRET_KEY"],
base_url=os.environ.get("BURSAPAY_BASE_URL"), # omit for production default
)
License
MIT
from bursapay import BursaPay
# Production
bp = BursaPay("sk_live_xxxx")
# Sandbox / test
bp = BursaPay("sk_test_xxxx")
# Local dev server
bp = BursaPay("sk_test_xxxx", base_url="http://localhost:8000/api/v1")
Payments
# Initialize — returns an authorization URL to redirect your customer to
payment = bp.payments.initialize(
amount=5000, # NGN, not kobo
email="customer@example.com",
currency="NGN", # NGN | USD | GBP | KES
metadata={"order_id": "ORD-123"},
)
print(payment["authorization_url"])
# Verify after Paystack redirects back
result = bp.payments.verify("BP-XXXX")
print(result["status"]) # "success" | "failed" | "pending"
# Retrieve a payment
payment = bp.payments.retrieve("BP-XXXX")
# List with optional filters
page = bp.payments.list(status="success", page_size=20)
# Charge a saved authorization code (recurring)
bp.payments.charge(
authorization_code="AUTH_xxx",
email="customer@example.com",
amount=5000,
)
# Schedule a future charge (must be >60 s in the future)
bp.payments.initialize(
amount=5000,
email="customer@example.com",
charge_at="2025-12-31T09:00:00Z",
)
# Cancel a scheduled payment
bp.payments.cancel_schedule("BP-XXXX")
# Split payment
bp.payments.initialize(
amount=10000,
email="buyer@example.com",
splits=[
{"subaccount_code": "ACCT_abc123", "share": 0.8},
{"subaccount_code": "ACCT_def456", "share": 0.2},
],
)
# Bulk initialize
bp.payments.bulk_initialize([
{"amount": 1000, "email": "a@b.com", "reference": "BP-001"},
{"amount": 2000, "email": "c@d.com", "reference": "BP-002"},
])
Customers
customer = bp.customers.create(email="jane@example.com", name="Jane Doe")
print(customer["customer_reference"]) # "CUS-xxxx"
bp.customers.list()
bp.customers.retrieve("CUS-xxxx")
bp.customers.update("CUS-xxxx", name="Jane Smith")
bp.customers.payments("CUS-xxxx")
bp.customers.delete("CUS-xxxx")
Transfers
transfer = bp.transfers.initiate(
amount=10000,
bank_code="044", # Access Bank
account_number="0123456789",
account_name="John Doe",
narration="Vendor payout",
)
bp.transfers.retrieve(transfer["reference"])
bp.transfers.list(status="completed")
# Bulk payout
bp.transfers.bulk([
{"amount": 5000, "bank_code": "058", "account_number": "0987654321", "account_name": "Vendor A"},
{"amount": 8000, "bank_code": "011", "account_number": "1122334455", "account_name": "Vendor B"},
])
Webhooks
# Register an endpoint
wh = bp.webhooks.create(
url="https://myapp.com/hooks/bursapay/",
events=["payment.success", "payment.failed", "transfer.success"],
)
print(wh["secret"]) # store this to verify incoming payloads
bp.webhooks.list()
bp.webhooks.update(wh["id"], is_active=False)
bp.webhooks.delete(wh["id"])
# Inspect delivery logs
bp.webhooks.logs(wh["id"], status="failed")
bp.webhooks.retry(log_id=42)
# All valid event types
bp.webhooks.events()
Verifying incoming webhook signatures
# Django example
from bursapay import BursaPay
from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt
import json
@csrf_exempt
def bursapay_webhook(request):
is_valid = BursaPay.verify_webhook_signature(
request.body,
request.headers.get("X-BursaPay-Signature", ""),
"your_webhook_secret",
)
if not is_valid:
return HttpResponse(status=401)
event = json.loads(request.body)
if event["event"] == "payment.success":
ref = event["data"]["reference"]
# fulfil order...
return HttpResponse(status=200)
Wallet
bp.wallets.balance()
bp.wallets.ledger()
bp.wallets.settlements()
bp.wallets.settlement("STL-xxxx")
Virtual Accounts
va = bp.virtual_accounts.create("CUS-xxxx")
print(va["account_number"], va["bank_name"])
bp.virtual_accounts.list()
bp.virtual_accounts.retrieve(va["id"])
Subscriptions
plan = bp.subscriptions.create_plan(
name="Pro Monthly",
amount=5000,
interval="monthly",
)
sub = bp.subscriptions.enroll(
customer_reference="CUS-xxxx",
plan_id=plan["id"],
authorization_code="AUTH_xxx",
)
bp.subscriptions.pause(sub["id"])
bp.subscriptions.resume(sub["id"])
bp.subscriptions.cancel(sub["id"])
Invoices
inv = bp.invoices.create(
customer_reference="CUS-xxxx",
line_items=[
{"description": "Web design", "quantity": 1, "unit_price": 150000},
{"description": "Hosting (annual)", "quantity": 1, "unit_price": 30000},
],
due_date="2025-12-31",
)
bp.invoices.update(inv["reference"], status="sent") # triggers payment link creation
bp.invoices.list(status="overdue")
bp.invoices.delete(inv["reference"])
Payment Links
link = bp.payment_links.create(
title="Pay for Invoice #42",
amount=180000,
expires_at="2025-12-31T23:59:59Z",
)
print(link["url"])
bp.payment_links.analytics(link["link_code"])
Refunds
refund = bp.refunds.create("BP-XXXX", amount=2500, reason="Customer request")
bp.refunds.retrieve(refund["refund_reference"])
Async support
import asyncio
from bursapay import BursaPay
async def main():
async with BursaPay("sk_test_xxxx").async_client() as bp:
payment = await bp.payments.initialize(amount=5000, email="a@b.com")
print(payment["authorization_url"])
asyncio.run(main())
Error handling
from bursapay import BursaPay
from bursapay.exceptions import (
AuthenticationError,
NotFoundError,
ValidationError,
RateLimitError,
ServerError,
BursaPayError,
)
try:
bp.payments.initialize(amount=5000, email="x@y.com", currency="XYZ")
except ValidationError as e:
print(e.error_code) # "invalid_currency"
print(e.field_errors) # field-level errors dict
except AuthenticationError:
print("Check your API key")
except RateLimitError:
print("Slow down — rate limited")
except ServerError as e:
print(f"BursaPay server error: {e.status_code}")
except BursaPayError as e:
print(f"Unexpected error: {e}")
Context manager
with BursaPay("sk_test_xxxx") as bp:
result = bp.payments.verify("BP-XXXX")
# connection pool is closed automatically
Environment variables (recommended)
import os
from bursapay import BursaPay
bp = BursaPay(
os.environ["BURSAPAY_SECRET_KEY"],
base_url=os.environ.get("BURSAPAY_BASE_URL"), # omit for production default
)
License
MIT
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
bursapay_sdk-0.1.1.tar.gz
(26.4 kB
view details)
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file bursapay_sdk-0.1.1.tar.gz.
File metadata
- Download URL: bursapay_sdk-0.1.1.tar.gz
- Upload date:
- Size: 26.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
Hatch/1.17.1 {"ci":null,"cpu":"AMD64","implementation":{"name":"CPython","version":"3.12.4"},"installer":{"name":"hatch","version":"1.17.1"},"openssl_version":"OpenSSL 3.0.13 30 Jan 2024","python":"3.12.4","system":{"name":"Windows","release":"11"}} HTTPX2/2.9.1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
66e9065dc72bb4ac928128245e7defa515e52de40be8f07c69559ace69ec8c32
|
|
| MD5 |
8d87f90bdf42ac8a6d970a7ae77ed8f8
|
|
| BLAKE2b-256 |
2d8c106928400aae78c49a91edb7c62f647ea7db5accc81c29061fcc4e2f5973
|
File details
Details for the file bursapay_sdk-0.1.1-py3-none-any.whl.
File metadata
- Download URL: bursapay_sdk-0.1.1-py3-none-any.whl
- Upload date:
- Size: 29.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
Hatch/1.17.1 {"ci":null,"cpu":"AMD64","implementation":{"name":"CPython","version":"3.12.4"},"installer":{"name":"hatch","version":"1.17.1"},"openssl_version":"OpenSSL 3.0.13 30 Jan 2024","python":"3.12.4","system":{"name":"Windows","release":"11"}} HTTPX2/2.9.1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
396e677b55a5f04ec5bf3cf3780c7a3c3b43be4e9b2670aee9c8dec1ad056598
|
|
| MD5 |
a82e299e12f3c42bb7ab510e91e2dfd8
|
|
| BLAKE2b-256 |
a6378a1617b3310af1ae1ac5c31d9d017fb2a021a9ef4b3f3d33ebf8173521b0
|