Skip to main content

Official PayHub SDK for Python.

Project description

payhub — Python SDK

Official PayHub SDK for Python. Sync + async clients, fully typed, zero non-stdlib runtime dependencies beyond httpx.

pip install payhub

PayHub API: v1 · Python: ≥3.9 · License: MIT

1. Authenticate

from payhub import Payhub

client = Payhub(api_key="phk_<id>.<secret>", base_url="https://app.payhub.ly")

AsyncPayhub is the awaitable variant — same shape, same models. Both support context-manager use (with client: / async with client:) for deterministic connection cleanup.

2. Your first payment — Sadad OTP, end to end

from payhub import Payhub, OtpRequired

with Payhub(api_key="phk_…") as client:
    payment = client.payments.create({
        "psp": "sadad",
        "merchant_order_ref": "ord-42",
        "amount_minor": 4500,                  # 4.5 LYD
        "customer": {
            "msisdn": "218910000001",          # mandatory for Sadad
            "birth_year": 1990,                # mandatory for Sadad
        },
    })

    match payment.next_action:
        case OtpRequired(masked_destination=mask):
            print(f"OTP sent to {mask}; ask the customer to type it in.")
        case _:
            ...

    # Customer types the code into your form; you POST it back.
    settled = client.payments.confirm_otp(payment.id, code="123456")
    print(settled.status)  # "succeeded" or "failed"

The SDK auto-mints a UUID4 Idempotency-Key for every create / confirm_otp / refund. Pass idempotency_key=... to use your own (safe across process restarts).

3. Webhook receiver (FastAPI)

⚠️ Pass the raw request bytes, never a parsed body. Re-encoding a JSON object changes whitespace and breaks the HMAC. Use await request.body() to get the raw bytes.

from fastapi import FastAPI, Request, HTTPException
from payhub import WebhookEvent, WebhookSignatureError

app = FastAPI()
WEBHOOK_SECRET = b"…"  # bytes; configure via env

@app.post("/webhooks/payhub")
async def receive(request: Request):
    body = await request.body()                       # raw bytes
    sig = request.headers.get("Hub-Signature")
    if not sig:
        raise HTTPException(400, "missing signature")
    try:
        event = WebhookEvent.verify(WEBHOOK_SECRET, body, sig)
    except WebhookSignatureError:
        raise HTTPException(401, "invalid signature")

    if event.type == "payment.succeeded":
        # mark order paid
        ...
    elif event.type in {"payment.failed", "payment.expired"}:
        # release inventory / notify customer
        ...
    elif event.type == "payment.refunded":
        # update accounting
        ...
    return {"ok": True}

Default replay tolerance is 300 s. Override via WebhookEvent.verify(..., tolerance_seconds=60).

4. Errors

Exception Fires on
AuthenticationError 401 — bad API key, IP not allowlisted
PermissionError 403
NotFoundError 404
ValidationError 422 — customer.msisdn missing for Sadad, etc.
IdempotencyConflict 409 — same Idempotency-Key reused with a different body
RateLimited 429 — exposes .retry_after
GatewayError 502 from a gateway.<psp>.* code
ServerError other 5xx
TimeoutError, ConnectionError, DecodeError network / serialization
WebhookSignatureError base for the three webhook variants below
MalformedHeader Hub-Signature missing t= or v1=
TimestampOutOfTolerance |now − t| > tolerance (carries .skew_seconds)
InvalidSignature HMAC mismatch / non-JSON body

All API errors carry .code, .http_status, .details, .request_id. Log request_id to support tickets — it matches the server-side log line.

Configuration

Payhub(
    api_key="phk_…",
    base_url="https://app.payhub.ly",
    timeout=30.0,
    max_retries=2,                 # idempotent calls only
    http_client=my_httpx_client,   # injection seam (proxies, tests)
    user_agent_suffix="Acme/1.2",
)

Versioning

Independent semver. Compatible with PayHub API v1.

Development

pip install -e ".[test]"
pytest                  # runs the cross-language vectors + unit tests

Tests load ../shared/test-vectors/webhook-signing.json — the canonical spec consumed by every PayHub SDK and by the server's own tests/unit/test_webhook_signing_vectors.py.

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

payhub-1.0.0.tar.gz (32.7 kB view details)

Uploaded Source

Built Distribution

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

payhub-1.0.0-py3-none-any.whl (13.2 kB view details)

Uploaded Python 3

File details

Details for the file payhub-1.0.0.tar.gz.

File metadata

  • Download URL: payhub-1.0.0.tar.gz
  • Upload date:
  • Size: 32.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for payhub-1.0.0.tar.gz
Algorithm Hash digest
SHA256 635a807e84b375ceb3c7c6ef897c1df371ffb36cd09863e37e5ac75f66e8a1ea
MD5 506ff0f1bd2631d2df26c4683939c31b
BLAKE2b-256 fa9cb4e329862659b811da24934b497c25e0141c17465689a050388703f27686

See more details on using hashes here.

Provenance

The following attestation bundles were made for payhub-1.0.0.tar.gz:

Publisher: publish.yml on safwatech/payhub-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file payhub-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: payhub-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 13.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for payhub-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 092d845966b72ab63ca17e44a500bec866e93b51482a020e4d6d5b158bc31f37
MD5 9c59425e36e6a7d618382b6cb94eb962
BLAKE2b-256 8bf78b57cf8a42264a6bb406b886daa37bd68eed3795ae599b21bef6e81d18d2

See more details on using hashes here.

Provenance

The following attestation bundles were made for payhub-1.0.0-py3-none-any.whl:

Publisher: publish.yml on safwatech/payhub-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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