Skip to main content

A Typed Python client for the Pesapal API with sync/async support, order submission, IPN management, and optional CLI tools.

Project description

pesapal-client

Release Build status Commit activity License

A Typed Python client for the Pesapal API with sync/async support, order submission, IPN management, and optional CLI tools.


Features

  • Typed models for requests and responses using Pydantic
  • Sync and async API support
  • Order submission and payment status tracking
  • IPN (Instant Payment Notification) management
  • Subscription management
  • Automatic authentication and token refresh
  • Custom exceptions for error handling
  • Utility functions for JWT and JSON parsing
  • Optional CLI tools for quick integration

Installation

pip install pesapal-client

Quick Start

from pesapal_client.client import PesapalClientV3

client = PesapalClientV3(
    consumer_key="your_consumer_key",
    consumer_secret="your_consumer_secret",
    is_sandbox=True,
)

Register IPN(Instant Payment Notification)

The IPN is the url to which pesapal will notify you incase there are any status updates on a recently created payment order. The request method should set via the ipn_nofication_type which can either be a GET or POST

Scenario: Registering an IPN for payment callbacks

from pesapal_client.v3.schemas import IPNRegistrationRequest

ipn_request = IPNRegistrationRequest(
    url="https://yourdomain.com/pesapal/ipn",
    ipn_notification_type="POST"
)

response = client.ipn.register_ipn(ipn_request)
print(response.ipn_id, response.ipn_status_description)

Get Registered IPNs

Pesapal allows registration of multiple IPNs and if incase you need to list them. Here is how you do it.

Scenario: Retrieving all registered IPNs

registered_ipns = client.ipn.get_registered_ipns()
for ipn in registered_ipns:
    print(ipn.url, ipn.created_date)

Initiate a Payment / Submit an Order

Scenario: Customer checks out with a single payment

from uuid import UUID
from pesapal_client.v3.schemas import InitiatePaymentOrderRequest, BillingAddress

payment_request = InitiatePaymentOrderRequest(
    id="ORDER-001",
    currency="UGX",  # ISO 4217 code
    amount=50000.0,
    description="Order #001 - Online Purchase",
    callback_url="https://yourdomain.com/payment/callback",
    notification_id=UUID("YOUR_REGISTERED_IPN_ID"),
    billing_address=BillingAddress(
        email_address="customer@example.com",
        first_name="John",
        last_name="Doe",
        phone_number="+256700000000",
        country_code="UG",
        line_1="123 Kampala Road"
    )
)

response = client.one_time_payment.initiate_payment_order(payment_request)
print("Redirect user to:", response.redirect_url)

Checking Payment Status

Scenario: Checking payment status after customer completes checkout

from pesapal_client.v3.shcemas import PaymentOrderStatusCode
status = client.one_time_payment.get_payment_order_status(
    payment_order_tracking_id="TRACKING_ID_FROM_INITIATE"
)
if status.status_code == PaymentOrderStatusCode.COMPLETED:
    print("✅ Payment successful. Fulfill the order.")
elif status.status_code == PaymentOrderStatusCode.FAILED:
    print("❌ Payment failed. Ask customer to retry.")
elif status.status_code == PaymentOrderStatusCode.REVERSED:
    print("Payment has been refunded")
else:
    print("ℹ️ Payment is still processing...")

Subscriptions

Subscriptions allow recurring payments

from pesapal_client.v3.schemas import InitiateSubscriptionRequest, SubscriptionDetails

subscription_request = InitiateSubscriptionRequest(
    id="SUB-001",
    currency="USD",
    amount=10.0,
    description="Monthly Membership",
    callback_url="https://yourdomain.com/subscription/callback",
    notification_id=UUID("YOUR_REGISTERED_IPN_ID"),
    billing_address=BillingAddress(
        email_address="subscriber@example.com",
        first_name="Alice",
        last_name="Smith",
        phone_number="+14155552671",
        country_code="US",
    ),
    account_number="ACC12345",
    subscription_details=SubscriptionDetails(
        start_date="01-01-2025",
        end_date="01-01-2026",
        frequency="MONTHLY"
    )
)

response = client.subscription.initiate_subscription(subscription_request)
print("Redirect user to:", response.redirect_url)

If the subscription details(they are optional) are left out, then the customer shall be asked to choose the periods when redirected to the payment form page(redirect url)


Refunds

Customer requests for a refund

from pesapal_client.v3.schemas import RefundRequest

refund_request = RefundRequest(
    confirmation_code="CONFIRMATION_CODE_FROM_SUCCESSFUL_TXN",
    amount="1000.00",
    username="customer_identity i.e name or email or phone number",
    remarks="Customer requested refund"
)

response = client.one_time_payment.initiate_refund(refund_request)
if response.status == 200:
    print("✅ Refund initiated:", response.message)
else:
    print("❌ Refund failed:", response.message)

Exception Handling

All API calls may raise a PesapalException if Pesapal returns an error (even if the HTTP status code is 200 OK but the body indicates a failure). You should wrap your calls in a try/except block to handle these gracefully.

Remember to also watch for RequestError exceptions for non Pesapal errors i.e Connection failure, Read Timeout

Example: Handling Errors During Payment Initiation

from pesapal_client.exceptions import PesapalException, RequestError
from pesapal_client.v3.schemas import InitiatePaymentOrderRequest, BillingAddress
from uuid import UUID

try:
    payment_request = InitiatePaymentOrderRequest(
        id="ORDER-ERR-001",
        currency="USD",
        amount=50.0,
        description="Test Order with Error",
        callback_url="https://yourdomain.com/payment/callback",
        notification_id=UUID("YOUR_REGISTERED_IPN_ID"),
        billing_address=BillingAddress(
            email_address="invalid-email",  # <-- This will trigger validation error
            first_name="Jane",
            last_name="Doe"
        )
    )

    response = client.one_time_payment.initiate_payment_order(payment_request)
    print("Redirect user to:", response.redirect_url)

except RequestError as e:
    # Failed to communicate to Pesapal API
    print("Pesapal API Error: ", str(e))
except PesapalException as e:
    # Pesapal API-specific error
    print("❌ Pesapal API Error:", str(e))

except Exception as e:
    # Any other unexpected error
    print("⚠️ Unexpected Error:", str(e))

License

This project is licensed under the MIT License - see the LICENSE file for details.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pesapal_client-0.1.3.tar.gz (10.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pesapal_client-0.1.3-py3-none-any.whl (9.7 kB view details)

Uploaded Python 3

File details

Details for the file pesapal_client-0.1.3.tar.gz.

File metadata

  • Download URL: pesapal_client-0.1.3.tar.gz
  • Upload date:
  • Size: 10.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.7.1 CPython/3.11.13 Linux/6.11.0-1018-azure

File hashes

Hashes for pesapal_client-0.1.3.tar.gz
Algorithm Hash digest
SHA256 a41ffa6bfc01e4e2a851a4a282e5bd82b0482dd051e7f288651998ffb31df718
MD5 2298b21bf57a57e3ae143f62a5692036
BLAKE2b-256 fdc736d785f9d70894d1970acbf8c6cc963ad4e01363cb1a5451392fc09aedb1

See more details on using hashes here.

File details

Details for the file pesapal_client-0.1.3-py3-none-any.whl.

File metadata

  • Download URL: pesapal_client-0.1.3-py3-none-any.whl
  • Upload date:
  • Size: 9.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.7.1 CPython/3.11.13 Linux/6.11.0-1018-azure

File hashes

Hashes for pesapal_client-0.1.3-py3-none-any.whl
Algorithm Hash digest
SHA256 4cf5578efa4d775982760681bcfe351fe9ccac5842defbb186811baac8927b95
MD5 07354d13b7dfce6460a5fe99c104fa2e
BLAKE2b-256 6cb932b453bb9728500300f773a36b22a9e1b118e9c4430551da2c225642e678

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page