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.0

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.0
File Size Uploaded
sdk_221-0.2.0.tar.gz 152.5 kB Details

Built distribution (wheel)

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

Total release size: 548.6 kB

Release files / sdk_221-0.2.0.tar.gz

Download URL sdk_221-0.2.0.tar.gz
Size 152.5 kB
Tags Source
SHA-256 checksum
How to use checksums
3607ee2691f450f041580d388e5cdb46aa6db98b6489a9ce8def8146450723a5
BLAKE2b-256 checksum
How to use checksums
a85ca090e0d9ef4ac126943b404c664714766cdda4bceced2bf3b356e34801da
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.0-py3-none-any.whl

Download URL sdk_221-0.2.0-py3-none-any.whl
Size 396.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
5ed9889fe8c4c3f88828bd3ad72a1d414ac499bc5882d72f3b5605dd75e1c4ef
BLAKE2b-256 checksum
How to use checksums
5fea4db86d1706d1afa6618a061c8338a92211003e636a5478fe3eff5912d3ff
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

0.2.1

2 release files

This release

0.2.0 This release

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