Skip to main content

paysawa

Python SDK for the Paysawa mobile money gateway (MTN Mobile Money and Orange Money, XAF). Collect payments, send money, create payment links and verify signed webhooks with a few lines of code. It speaks Paysawa's Campay-style API, so the concepts are the same as Campay's.

pip install paysawa

1. Get your credentials

In your Paysawa dashboard create an app. It gives you three values (you can show them again any time):

Value What it is
App username like pa_1a2b3c4d5e6f7a8b
App password secret, never put it in front-end code or in git
Webhook key verifies that webhooks really come from Paysawa

A test app uses the sandbox (no real money). A live app moves real money.

2. Quick start

from paysawa import Client

client = Client(
    app_username="pa_xxxxxxxxxxxxxxxx",
    app_password="your-app-password",
    webhook_key="whk_xxxxxxxx",
    base_url="https://paysawa.onrender.com/api/",   # or set PAYSAWA_BASE_URL
)

# Ask a customer to pay: they get a prompt on their phone and approve with their PIN.
tx = client.collect(amount=5000, phone="670000001", description="Order 1001",
                    external_reference="order-1001", wait=True)   # wait=True blocks until final
if tx.is_successful:
    print("Paid!", tx.reference)
else:
    print("Not paid:", tx.status)

print(client.get_balance().total_balance)                          # your Paysawa balance
client.withdraw(amount=1000, phone="690000001", description="Refund")   # pay money out
link = client.get_payment_link(amount=2000, description="Order 1002", redirect_url="https://shop.example.com/thanks")
print(link.link)                                                    # send this to your customer

Put the credentials in environment variables instead of code and use Client.from_env(): PAYSAWA_APP_USERNAME, PAYSAWA_APP_PASSWORD, PAYSAWA_WEBHOOK_KEY, PAYSAWA_BASE_URL.

3. Statuses

Every transaction is PENDING, SUCCESSFUL or FAILED (tx.is_pending, tx.is_successful, tx.is_failed, tx.is_final). A collection stays PENDING until the customer approves. Without wait=True, check it later with client.get_transaction(reference) or use client.wait_for_transaction(reference, timeout=120), or rely on webhooks.

4. Webhooks

Set your app's webhook URL in the dashboard. Paysawa calls it with a GET and query parameters plus a signed signature. Always verify it:

# Flask
@app.get("/webhooks/paysawa")
def paysawa_webhook():
    try:
        event = client.verify_webhook(request.args.to_dict())
    except WebhookVerificationError:
        return "invalid", 401
    if event.is_successful:
        mark_order_paid(event.external_reference, event.amount)
    return "ok"
# FastAPI
@app.get("/webhooks/paysawa")
async def paysawa_webhook(request: Request):
    event = client.verify_webhook(dict(request.query_params))   # raises WebhookVerificationError if not authentic
    ...

Django: client.verify_webhook(request.GET.dict()). Reply with any 2xx status; otherwise Paysawa retries. Check event.amount against the order before you ship anything.

5. Async

from paysawa import AsyncClient

async with AsyncClient.from_env() as client:
    tx = await client.collect(5000, "670000001", "Order 1001", "order-1001", wait=True)

6. Errors

All errors inherit from PaysawaError (.message, .status, .code):

Exception When
AuthenticationError wrong app username/password
PermissionDeniedError live mode not enabled, account suspended
InvalidRequestError bad amount or phone number, unknown reference, limits
InsufficientFundsError withdrawal larger than your balance (amount + fee)
RateLimitError too many requests
ServerError / NetworkError Paysawa busy or unreachable
WaitTimeoutError wait_for_transaction gave up (.transaction has the latest state)
WebhookVerificationError the webhook is not authentic: ignore it

Payments (collect, withdraw) are never retried automatically, so a retry can never charge twice. Reads are retried twice. If a payment call fails with a network error, look the payment up by your external_reference before trying again.

7. Sandbox numbers (test apps)

670000001 succeeds, a number ending in 0000 fails, a number ending in 9999 stays pending.

Development

pip install -e ".[dev]"
pytest

Metadata

Release files for paysawa 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for paysawa 0.1.1
File Size Uploaded Provenance
paysawa-0.1.1.tar.gz 15.1 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for paysawa 0.1.1
File Interpreter ABI Platform
paysawa-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 26.9 kB

Release files / paysawa-0.1.1.tar.gz

Download URL paysawa-0.1.1.tar.gz
Size 15.1 kB
Tags Source
SHA-256 checksum
How to use checksums
7f2409a4b4df481c1ca163043c39086e7999fb2025b7b879eb2fbd2033554351
BLAKE2b-256 checksum
How to use checksums
9de9c82c3ee8cd75b32870c368bf2e2342f714f17441abe1e066ffb5770baaac
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.

Transparency log

Release files / paysawa-0.1.1-py3-none-any.whl

Download URL paysawa-0.1.1-py3-none-any.whl
Size 11.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d9bcd6d964c470418c7333dfb56b6528a280e6f0d01f1fbc63c83fdcbceca6ed
BLAKE2b-256 checksum
How to use checksums
33c0d555303f22e68da964a39d2fb69f6e4e80a85cb687d5f9f6f19b8d5bb9e0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.

Transparency log

Security for paysawa 0.1.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for paysawa-0.1.1.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page