Skip to main content

SDK Python 221

Client de l'API 221 généré depuis l'OpenAPI par openapi-python-client (EF-221-02), vérification des signatures de webhook et données de référence hors ligne (EF-221-03). Distribution sdk-221, module sdk221. Site et documentation : https://221.orvlabs.com/documentation. Le code généré est rebâti dans le dépôt principal (generate.sh) ; ce dépôt publie le résultat.

Module Contenu Origine
sdk221.generated Client, AuthenticatedClient, api.<tag>.<opération>, models Généré (httpx + attrs), jamais modifié à la main
sdk221 client(api_key, base_url), verify_webhook(...) Écrit à la main
sdk221.offline holidays, holiday, operator_for, banks, bank, places, place, snapshot Instantané versionné de server/data.json

Installation

uv add sdk-221     # ou, dans un environnement virtuel : pip install sdk-221

Pas encore publié sur PyPI : la commande fonctionnera à la publication.

Développement

cd packages/sdk-python
./generate.sh      # Node et uv ; contrat Go (make openapi, sans serveur) : OpenAPI, instantané, client
uv run pytest      # tests/test_api.py appelle l'API Go locale (make dev)

Clés API

Une clé a la forme sk_221_pay_test_… (mode test) ou sk_221_pay_live_… (mode réel). Les clés déjà émises avec 221pay_test_, 221pay_live_ ou sk_221_ restent acceptées.

import os
from sdk221 import client

api = client(os.environ["API_KEY"])  # Authorization: Bearer <clé> ; base_url= pour une autre adresse

Les routes de données fonctionnent aussi sans clé (client()), avec un quota bas par adresse IP.

Créer un paiement

Idempotency-Key est propre à chaque paiement : renvoyer la même valeur ne crée pas de doublon. Le contrat décrit le corps des routes de paiement comme du binaire et ne liste que la réponse 200, alors que la création répond 201 : on passe par le client httpx du SDK, qui porte déjà la clé.

response = api.get_httpx_client().post(
    "/v1/payments",
    headers={"Idempotency-Key": "commande-1042"},
    json={
        "amount": "10000",
        "currency": "XOF",
        "rail": "sn_wave",
        "merchant_reference": "commande-1042",
        "return_url": "https://boutique.example/merci",
    },
)
response.raise_for_status()
payment = response.json()

Vérifier un webhook

Lisez le corps brut avant tout décodage JSON ; refusez la requête si la fonction renvoie False (signature fausse, ou horodatage de plus de 5 minutes, max_age_seconds pour changer la durée).

from sdk221 import verify_webhook

# FastAPI : raw_body = await request.body()
if not verify_webhook(WEBHOOK_SECRET, request.headers.get("X-221-Timestamp", ""), raw_body, request.headers.get("X-221-Signature", "")):
    raise HTTPException(status_code=401)

Dédupliquez sur l'identifiant de l'événement : une livraison peut être rejouée.

Premier appel, sans clé

from sdk221 import client
from sdk221.generated.api.jours_feries import list_jours_feries

api = client()  # https://apps.orvlabs.com par défaut
jours = list_jours_feries.sync(client=api, year="2026")
print(jours.data[0]["name"]["fr"])  # « Jour de l'an »

Chaque opération existe en sync, sync_detailed, asyncio et asyncio_detailed.

Erreurs et quotas

sync_detailed renvoie le statut, les en-têtes et parsed : le modèle attendu pour 200, un Error (code, message, request_id, fields) pour les erreurs décrites, None sinon. Testez code (en majuscules, par exemple QUOTA_EXCEEDED), pas le statut.

from sdk221.generated.models import Error

response = list_jours_feries.sync_detailed(client=api, year="2026")
if response.status_code == 429:
    wait = int(response.headers["Retry-After"])  # secondes ; ou utilisez une clé
elif isinstance(response.parsed, Error):
    raise RuntimeError(f"{response.parsed.code}: {response.parsed.message}")

Ne relancez pas un 429 en boucle : attendez Retry-After secondes.

Hors ligne

from sdk221 import offline

offline.holidays(2026)                   # jours fériés (date_status : confirmee ou previsionnelle) ; ValueError hors des années embarquées
offline.operator_for("771234567")        # opérateur du préfixe d'origine (portabilité possible)
offline.bank("K 0010 A")                 # par id ou code banque BCEAO
offline.places(level="region", q="thies")
offline.place("reg_om6lpn2l")            # avec ancêtres et enfants, comme l'API
offline.snapshot["built_at"]             # date des données embarquées

Les fonctions renvoient les mêmes objets JSON que l'API. Validation et formatage des numéros et des montants (modules telephonie et monnaie) : dans le SDK TypeScript seulement pour l'instant.

Exemple

uv run python examples/premier_appel.py   # API_KEY et API_URL facultatifs

normalize() dans offline.py recopie celle de server/internal/api/data.go ; tests/test_api.py compare les recherches hors ligne aux réponses de l'API (le quota anonyme est de 10 appels par minute).

Metadata

Release files for sdk-221 0.2.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 sdk-221 0.2.1
File Size Uploaded
sdk_221-0.2.1.tar.gz 163.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for sdk-221 0.2.1
File Interpreter ABI Platform
sdk_221-0.2.1-py3-none-any.whl Python 3 none any Details

Total release size: 584.8 kB

Release files / sdk_221-0.2.1.tar.gz

Download URL sdk_221-0.2.1.tar.gz
Size 163.5 kB
Tags Source
SHA-256 checksum
How to use checksums
15931d90741368428325a17e84e2ded08e72e90ba70d585ce48cca8f88d9a3ed
BLAKE2b-256 checksum
How to use checksums
60de6613f2dfa84e8614aef897440f4a73e46fe31ce6604ca13aee44d6568f20
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.5 {"installer":{"name":"uv","version":"0.11.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / sdk_221-0.2.1-py3-none-any.whl

Download URL sdk_221-0.2.1-py3-none-any.whl
Size 421.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
5165890c56588e9954145218bcd40419beb5d88238a54e5713277ddd2e3fa3ed
BLAKE2b-256 checksum
How to use checksums
9c73f40ef986d0644fe9e46976165e7afe515663f7a3be8d279177dc2c3d770c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.5 {"installer":{"name":"uv","version":"0.11.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release history Release notifications | RSS feed

This release

0.2.1 This release

2 release files

0.2.0

2 release files

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