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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
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 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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
635a807e84b375ceb3c7c6ef897c1df371ffb36cd09863e37e5ac75f66e8a1ea
|
|
| MD5 |
506ff0f1bd2631d2df26c4683939c31b
|
|
| BLAKE2b-256 |
fa9cb4e329862659b811da24934b497c25e0141c17465689a050388703f27686
|
Provenance
The following attestation bundles were made for payhub-1.0.0.tar.gz:
Publisher:
publish.yml on safwatech/payhub-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
payhub-1.0.0.tar.gz -
Subject digest:
635a807e84b375ceb3c7c6ef897c1df371ffb36cd09863e37e5ac75f66e8a1ea - Sigstore transparency entry: 1476020289
- Sigstore integration time:
-
Permalink:
safwatech/payhub-python@ddf9d0ed67b57275f19b6e807f580dd5e2ab298c -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/safwatech
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@ddf9d0ed67b57275f19b6e807f580dd5e2ab298c -
Trigger Event:
release
-
Statement type:
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
092d845966b72ab63ca17e44a500bec866e93b51482a020e4d6d5b158bc31f37
|
|
| MD5 |
9c59425e36e6a7d618382b6cb94eb962
|
|
| BLAKE2b-256 |
8bf78b57cf8a42264a6bb406b886daa37bd68eed3795ae599b21bef6e81d18d2
|
Provenance
The following attestation bundles were made for payhub-1.0.0-py3-none-any.whl:
Publisher:
publish.yml on safwatech/payhub-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
payhub-1.0.0-py3-none-any.whl -
Subject digest:
092d845966b72ab63ca17e44a500bec866e93b51482a020e4d6d5b158bc31f37 - Sigstore transparency entry: 1476020651
- Sigstore integration time:
-
Permalink:
safwatech/payhub-python@ddf9d0ed67b57275f19b6e807f580dd5e2ab298c -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/safwatech
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@ddf9d0ed67b57275f19b6e807f580dd5e2ab298c -
Trigger Event:
release
-
Statement type: