PayGate

Python SDK

Official Python SDK for PayGate.

Python SDK

The official Python SDK for PayGate provides a convenient way to access the PayGate API from Python applications.

Requirements: Python 3.8 or later.

Installation

pip install paygate-python
poetry add paygate-python
pipenv install paygate-python

Quick Start

import paygate

client = paygate.Client('sk_test_...')

# Create a payment
payment = client.payments.create(
    amount=5000,
    currency='GHS',
    payment_method='mobile_money',
    provider='mtn',
    phone='0241234567'
)

print(payment.id)

Configuration

API Key

import os
import paygate

# Using environment variable (recommended)
client = paygate.Client(os.environ['PAYGATE_SECRET_KEY'])

# Or pass directly
client = paygate.Client('sk_test_...')

Options

client = paygate.Client(
    api_key='sk_test_...',
    # API version (defaults to latest)
    api_version='2024-01-01',
    # Request timeout in seconds (default: 30)
    timeout=60,
    # Maximum retries for failed requests (default: 3)
    max_retries=5,
    # Custom base URL (for testing)
    base_url='https://api.sandbox.paygate.com.gh'
)

Payments

Create a Payment

payment = client.payments.create(
    amount=5000,
    currency='GHS',
    payment_method='mobile_money',
    provider='mtn',
    phone='0241234567',
    description='Order #1234',
    metadata={
        'order_id': '1234',
        'customer_email': 'customer@example.com'
    }
)

Retrieve a Payment

payment = client.payments.retrieve('pay_abc123')

List Payments

# Get a page of payments
payments = client.payments.list(limit=10, status='succeeded')

# Iterate through all payments
for payment in client.payments.list():
    print(payment.id)

Cancel a Payment

payment = client.payments.cancel('pay_abc123')

Customers

Create a Customer

customer = client.customers.create(
    email='customer@example.com',
    name='John Doe',
    phone='0241234567',
    metadata={'user_id': '123'}
)

Update a Customer

customer = client.customers.update('cus_abc123',
    name='Jane Doe'
)

List Customers

customers = client.customers.list(limit=10)

Subscriptions

Create a Subscription

subscription = client.subscriptions.create(
    customer='cus_abc123',
    plan='plan_xyz789',
    payment_method={
        'type': 'mobile_money',
        'phone': '0241234567',
        'provider': 'mtn'
    }
)

Cancel a Subscription

# Cancel at period end
client.subscriptions.update('sub_abc123',
    cancel_at_period_end=True
)

# Cancel immediately
client.subscriptions.cancel('sub_abc123')

Webhooks

Verify Webhook Signature

from flask import Flask, request
import paygate

app = Flask(__name__)
endpoint_secret = 'whsec_...'

@app.route('/webhooks', methods=['POST'])
def webhook():
    payload = request.data
    signature = request.headers.get('X-PayGate-Signature')

    try:
        event = paygate.Webhook.construct_event(
            payload, signature, endpoint_secret
        )
    except ValueError as e:
        # Invalid payload
        return 'Invalid payload', 400
    except paygate.SignatureVerificationError as e:
        # Invalid signature
        return 'Invalid signature', 400

    # Handle the event
    if event['type'] == 'payment.succeeded':
        payment = event['data']['object']
        print(f"Payment {payment['id']} succeeded!")
    elif event['type'] == 'payment.failed':
        print('Payment failed')

    return {'received': True}

if __name__ == '__main__':
    app.run(port=3000)

Django Example

from django.http import JsonResponse, HttpResponse
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_POST
import paygate

endpoint_secret = 'whsec_...'

@csrf_exempt
@require_POST
def webhook(request):
    payload = request.body
    signature = request.headers.get('X-PayGate-Signature')

    try:
        event = paygate.Webhook.construct_event(
            payload, signature, endpoint_secret
        )
    except (ValueError, paygate.SignatureVerificationError):
        return HttpResponse(status=400)

    if event['type'] == 'payment.succeeded':
        payment = event['data']['object']
        # Handle successful payment
        pass

    return JsonResponse({'received': True})

Error Handling

import paygate
from paygate.errors import (
    PayGateError,
    AuthenticationError,
    InvalidRequestError,
    PaymentError,
    RateLimitError,
    APIError
)

client = paygate.Client('sk_test_...')

try:
    payment = client.payments.create(
        amount=5000,
        currency='GHS',
        payment_method='mobile_money',
        provider='mtn',
        phone='0241234567'
    )
except AuthenticationError as e:
    print('Check your API key')

except InvalidRequestError as e:
    print(f'Invalid parameter: {e.param}')
    print(f'Message: {e.message}')

except PaymentError as e:
    if e.code == 'insufficient_funds':
        print('Please top up your account')
    elif e.code == 'phone_unreachable':
        print('Please check your phone')
    else:
        print(e.message)

except RateLimitError:
    import time
    time.sleep(60)
    # Retry

except APIError as e:
    print(f'PayGate server error: {e.message}')

except PayGateError as e:
    print(f'Error: {e.message}')

Error Types

TypeDescription
AuthenticationErrorInvalid API key
InvalidRequestErrorInvalid parameters
PaymentErrorPayment-specific error
RateLimitErrorToo many requests
APIErrorServer error

Async Support

The SDK supports async/await with aiohttp:

import asyncio
import paygate

async def main():
    client = paygate.AsyncClient('sk_test_...')

    # Create a payment
    payment = await client.payments.create(
        amount=5000,
        currency='GHS',
        payment_method='mobile_money',
        provider='mtn',
        phone='0241234567'
    )

    print(payment.id)

    # List payments
    async for payment in client.payments.list():
        print(payment.id)

asyncio.run(main())

Idempotency

Use idempotency keys to safely retry requests:

payment = client.payments.create(
    amount=5000,
    currency='GHS',
    payment_method='mobile_money',
    provider='mtn',
    phone='0241234567',
    idempotency_key='order_1234_payment'
)

Type Hints

The SDK includes full type hints for IDE support:

from paygate import Client
from paygate.types import Payment, Customer

client = Client('sk_test_...')

# Types are automatically inferred
payment: Payment = client.payments.create(
    amount=5000,
    currency='GHS',
    payment_method='mobile_money',
    provider='mtn',
    phone='0241234567'
)

# IDE autocompletion works
print(payment.status)  # 'pending' | 'processing' | 'succeeded' | 'failed'

Pagination

Handle paginated results:

# Auto-pagination (recommended)
for payment in client.payments.list(status='succeeded'):
    print(payment.id)

# Manual pagination
page = client.payments.list(limit=10)
while True:
    for payment in page.data:
        print(payment.id)

    if not page.has_more:
        break

    page = client.payments.list(
        limit=10,
        starting_after=page.data[-1].id
    )

Invoices

Create an Invoice

invoice = client.invoices.create(
    customer='cus_abc123',
    items=[
        {'description': 'Consulting Services', 'amount': 50000},
        {'description': 'Development Work', 'amount': 150000}
    ],
    due_date='2024-02-15',
    metadata={'project': 'Website Redesign'}
)

Send an Invoice

# Send invoice via email/SMS
client.invoices.send('inv_abc123')

Pay an Invoice

# Mark invoice as paid
client.invoices.pay('inv_abc123',
    payment_method={
        'type': 'mobile_money',
        'phone': '0241234567',
        'provider': 'mtn'
    }
)

List Invoices

invoices = client.invoices.list(status='open', limit=10)
for invoice in invoices:
    print(f"{invoice.id}: {invoice.amount_due / 100} {invoice.currency}")

Coupons

Create a Coupon

# Percentage discount
coupon = client.coupons.create(
    code='SAVE20',
    type='percent',
    percent_off=20,
    duration='repeating',
    duration_in_months=3,
    max_redemptions=100
)

# Fixed amount discount
coupon = client.coupons.create(
    code='FLAT50',
    type='amount',
    amount_off=5000,  # 50 GHS in pesewas
    currency='GHS',
    duration='once'
)

Validate a Coupon

try:
    validation = client.coupons.validate('SAVE20')
    if validation.valid:
        print(f"Discount: {validation.percent_off}%")
except paygate.InvalidRequestError:
    print("Coupon is invalid or expired")

List Coupons

coupons = client.coupons.list(limit=10)
for coupon in coupons:
    print(f"{coupon.code}: {coupon.times_redeemed}/{coupon.max_redemptions}")

QR Codes

Create a Static QR Code

# Create a reusable payment QR code
qr_code = client.qr_codes.create(
    type='static',
    name='Store Counter QR',
    description='Scan to pay at checkout'
)
print(f"QR Image URL: {qr_code.image_url}")

Create a Dynamic QR Code

# Create a QR code for a specific payment
qr_code = client.qr_codes.create(
    type='dynamic',
    amount=5000,
    currency='GHS',
    description='Order #1234',
    expires_in=3600  # 1 hour
)
print(f"QR Image URL: {qr_code.image_url}")

Retrieve QR Code Status

qr_code = client.qr_codes.retrieve('qr_abc123')
print(f"Status: {qr_code.status}")
print(f"Times Used: {qr_code.times_used}")

Reports

Generate a Report

report = client.reports.create(
    type='payments_summary',
    parameters={
        'start_date': '2024-01-01',
        'end_date': '2024-01-31',
        'group_by': 'day'
    },
    format='csv'
)
print(f"Report ID: {report.id}")

Retrieve Report Status

import time

report = client.reports.retrieve('report_abc123')
while report.status == 'pending':
    time.sleep(5)
    report = client.reports.retrieve('report_abc123')

if report.status == 'ready':
    print(f"Download URL: {report.download_url}")

Available Report Types

# Supported report types
report_types = [
    'payments_summary',
    'payouts_summary',
    'settlements',
    'disputes',
    'customers',
    'subscriptions',
    'revenue'
]

Events

List Events

# Get recent events
events = client.events.list(limit=20)
for event in events:
    print(f"{event.type}: {event.id}")

# Filter by type
payment_events = client.events.list(
    type='payment.succeeded',
    limit=10
)

Retrieve an Event

event = client.events.retrieve('evt_abc123')
print(f"Type: {event.type}")
print(f"Data: {event.data}")

Event Types

Common event types include:

  • payment.created
  • payment.succeeded
  • payment.failed
  • payout.created
  • payout.succeeded
  • customer.created
  • subscription.created
  • subscription.cancelled
  • invoice.created
  • invoice.paid
  • dispute.created
  • dispute.closed

Settings

Settlement Schedule

# Get current settlement schedule
schedule = client.settings.get_settlement_schedule()
print(f"Frequency: {schedule.frequency}")
print(f"Day of week: {schedule.day_of_week}")

# Update settlement schedule
schedule = client.settings.update_settlement_schedule(
    frequency='weekly',
    day_of_week='friday'
)

Transaction Limits

# Get current limits
limits = client.settings.get_transaction_limits()
print(f"Min: {limits.min_amount}")
print(f"Max: {limits.max_amount}")
print(f"Daily: {limits.daily_limit}")

# Update limits
limits = client.settings.update_transaction_limits(
    min_amount=100,       # 1 GHS
    max_amount=10000000,  # 100,000 GHS
    daily_limit=50000000  # 500,000 GHS
)

Two-Factor Authentication

Setup 2FA

# Generate 2FA secret and QR code
setup = client.auth.setup_2fa()
print(f"Secret: {setup.secret}")
print(f"QR Code URL: {setup.qr_code_url}")
# Display QR code to user for scanning with authenticator app

Enable 2FA

# Verify and enable 2FA
result = client.auth.enable_2fa(
    code='123456'  # Code from authenticator app
)
if result.enabled:
    print("2FA enabled successfully")
    print(f"Backup codes: {result.backup_codes}")

Verify 2FA Code

# Verify a 2FA code
result = client.auth.verify_2fa(code='123456')
if result.valid:
    print("Code is valid")

Disable 2FA

# Disable 2FA (requires current code)
client.auth.disable_2fa(code='123456')

Regenerate Backup Codes

# Get new backup codes
result = client.auth.regenerate_backup_codes(code='123456')
print(f"New backup codes: {result.backup_codes}")