> ## Documentation Index
> Fetch the complete documentation index at: https://docs.alphapay.me/llms.txt
> Use this file to discover all available pages before exploring further.

# SDK Python

> Intégrez les paiements AlphaPay côté serveur dans une application Python via le SDK officiel.

Le **SDK Python `alphapay`** enveloppe l'API REST AlphaPay (checkout, softpay, liens de paiement, clients, webhooks...) avec retry automatique (backoff exponentiel + gigue sur 429/5xx/erreur réseau), gestion d'idempotence et exceptions typées par cas d'erreur.

```bash theme={null}
pip install alphapay
```

```bash theme={null}
export ALPHAPAY_SECRET_KEY=sk_test_votre_cle   # sk_live_... en production
```

<Note>
  Chaque exemple ci-dessous est autonome : copiez-le, remplacez `os.environ["ALPHAPAY_SECRET_KEY"]` par votre clé et lancez-le avec `python3`.
</Note>

## Sommaire

1. [Checkout (session hébergée)](#1-checkout-session-hébergée)
2. [Softpay (encaissement direct)](#2-softpay-encaissement-direct)
3. [Lien de paiement](#3-lien-de-paiement)
4. [Solde et grand livre](#4-solde-et-grand-livre)
5. [Clients (CRM)](#5-clients-crm)
6. [Webhooks](#6-webhooks)
7. [Reversements et transferts entre wallets](#7-reversements-et-transferts-entre-wallets)
8. [Clés API et whitelist IP](#8-clés-api-et-whitelist-ip)
9. [Gestion des erreurs](#9-gestion-des-erreurs)

***

## 1. Checkout (session hébergée)

Crée une page de paiement hébergée par AlphaPay et pré-remplie pour un
client précis — l'usage le plus simple pour un e-commerce classique
(redirigez le client vers `checkout_url`).

```python theme={null}
import os
from alphapay import AlphaPayClient

alphapay = AlphaPayClient(os.environ["ALPHAPAY_SECRET_KEY"])

def creer_checkout():
    session = alphapay.checkout_sessions.create(
        amount=5000,
        currency="XOF",
        description="Commande #1042",
        customer_email="client@exemple.com",
        customer_name="Ayaba Client",
        customer_phone="+22900000000",
        return_url="https://boutique.exemple.com/merci",
        metadata={"order_id": "1042"},
        idempotency_key=True,  # génère une clé unique — un retry réseau ne recrée pas de doublon
    )
    print("Redirigez le client vers :", session["checkout_url"])
    print("Slug (à stocker pour retrouver la session) :", session["slug"])
    return session

def suivre_checkout(id):
    session = alphapay.checkout_sessions.get(id)
    print("Statut :", session["status"])  # PENDING, PAID, EXPIRED, CANCELLED

def annuler_checkout(id):
    session = alphapay.checkout_sessions.cancel(id)
    print("Annulée :", session["status"] == "CANCELLED")
```

***

## 2. Softpay (encaissement direct)

Pousse directement une demande de paiement (USSD mobile money) sans page
de checkout à afficher — utile pour une app où vous collectez déjà le
numéro du client.

```python theme={null}
import os
from alphapay import AlphaPayClient, AlphaPayValidationError

alphapay = AlphaPayClient(os.environ["ALPHAPAY_SECRET_KEY"])

def encaisser():
    try:
        payment = alphapay.transactions.payin_initialize(
            amount=2500,
            currency="XOF",
            country="BJ",
            network="mtn_bj",  # cf. GET /networks/ pour la liste à jour
            description="Abonnement mensuel",
            customer={
                "email": "client@exemple.com",
                "first_name": "Ayaba",
                "last_name": "Client",
                "phone": "+22900000000",
            },
            idempotency_key=True,
        )
        print("Paiement initié :", payment["id"], payment["status"])

        # Sondez jusqu'à confirmation (push USSD confirmé/refusé côté client).
        result = alphapay.transactions.payin_verify(payment["id"])
        print("Statut final :", result["status"])
    except AlphaPayValidationError as err:
        print("Champs invalides :", err.field_errors)

# Réseaux à confirmation en 2 temps (ex. Wizall Sénégal, Coris Bénin).
def confirmer_otp(payment_id, otp):
    result = alphapay.transactions.payin_confirm_otp(payment_id, otp)
    print("Confirmé :", result["status"])
```

***

## 3. Lien de paiement

Un lien réutilisable (partageable sur WhatsApp, réseaux sociaux, etc.),
avec ses propres champs personnalisés et suivi publicitaire.

```python theme={null}
import os
from alphapay import AlphaPayClient

alphapay = AlphaPayClient(os.environ["ALPHAPAY_SECRET_KEY"])

def creer_lien():
    link = alphapay.payment_links.create(
        name="Formation en ligne",
        description="Accès à vie à la formation",
        amount_type="FIXED",
        amount=15000,
        currency="XOF",
        facebook_pixel_id=os.environ.get("FACEBOOK_PIXEL_ID"),
        google_ads_id=os.environ.get("GOOGLE_ADS_ID"),
        custom_fields=[
            {"key": "email_formation", "label": "E-mail pour l'accès", "required": True},
        ],
    )
    print("Lien partageable :", link["url"])
    return link

# Ce que voit la page publique du lien — pas d'auth marchand nécessaire.
def consulter_lien_public(slug):
    public_link = alphapay.payment_links.get_public(slug)
    print(f"{public_link['name']} — {public_link.get('amount') or 'montant libre'} {public_link['currency']}")
    print("Utilisable :", public_link["is_usable"], public_link.get("unusable_reason") or "")

# Crée une CheckoutSession one-shot à partir du lien (ex. depuis votre propre
# front public, sans jamais exposer la clé secrète côté client).
def payer_depuis_lien(slug):
    result = alphapay.payment_links.create_public_checkout(
        slug,
        customer={"email": "client@exemple.com", "first_name": "Ayaba", "last_name": "Client"},
        custom_field_values={"email_formation": "ayaba@exemple.com"},
    )
    print("Session créée :", result["checkout_url"])

def lister_liens():
    page = alphapay.payment_links.list(is_active=True)
    for link in page["results"]:
        print(link["name"], link["usage_count"], "utilisations")
```

***

## 4. Solde et grand livre

```python theme={null}
import os
from alphapay import AlphaPayClient, AlphaPayPermissionError

alphapay = AlphaPayClient(os.environ["ALPHAPAY_SECRET_KEY"])

def verifier_soldes():
    page = alphapay.balances.list()
    for balance in page["results"]:
        print(f"{balance['country']} ({balance['currency']}) : {balance['available_amount']} disponible")

def grand_livre():
    try:
        # ⚠️ Dashboard-only — lève AlphaPayPermissionError (403) via une clé API,
        # conservé ici pour documenter la forme réelle de l'endpoint.
        alphapay.balances.ledger_entries(country="BJ")
    except AlphaPayPermissionError:
        print("Grand livre détaillé : consultable uniquement depuis le dashboard.")
```

***

## 5. Clients (CRM)

```python theme={null}
import os
from alphapay import AlphaPayClient

alphapay = AlphaPayClient(os.environ["ALPHAPAY_SECRET_KEY"])

def gerer_client():
    # `country` est l'UUID d'un `geo.Country` côté API — PAS un code ISO2 ("BJ").
    country_id = "00000000-0000-0000-0000-000000000000"

    customer = alphapay.customers.create(
        email="client@exemple.com",
        full_name="Ayaba Client",
        phone="+22900000000",
        country=country_id,
    )

    alphapay.customers.update(customer["id"], phone="+22900000001")

    page = alphapay.customers.transactions(customer["id"], page_size=20)
    print(f"{len(page['results'])} transaction(s) pour ce client.")

    return customer

def lister_clients():
    page = alphapay.customers.list(search="ayaba")
    print([c["email"] for c in page["results"]])
```

***

## 6. Webhooks

Réception et vérification d'un webhook entrant (exemple avec Flask ;
adaptez à votre framework au besoin — le principe ne change pas :
toujours vérifier la signature sur le **corps brut**, avant tout
`json.loads`).

```python theme={null}
import os
from flask import Flask, request
from alphapay import AlphaPayWebhookSignatureError, verify_signature

app = Flask(__name__)
WEBHOOK_SECRET = os.environ["ALPHAPAY_WEBHOOK_SECRET"]

@app.post("/webhooks/alphapay")
def receive_webhook():
    try:
        event = verify_signature(
            payload=request.get_data(),  # corps BRUT, jamais déjà parsé en JSON
            signature=request.headers["X-Webhook-Signature"],
            timestamp=request.headers["X-Webhook-Timestamp"],
            secret=WEBHOOK_SECRET,
        )
    except AlphaPayWebhookSignatureError as err:
        print("Webhook rejeté :", err)
        return "signature invalide", 400

    if event["event"] == "payment.succeeded":
        print("Paiement réussi :", event["data"])
    elif event["event"] == "payment.failed":
        print("Paiement échoué :", event["data"])
    else:
        print("Événement reçu :", event["event"])

    return "ok", 200
```

Consultation en lecture (CRUD d'écriture réservé au dashboard) :

```python theme={null}
import os
from alphapay import AlphaPayClient

alphapay = AlphaPayClient(os.environ["ALPHAPAY_SECRET_KEY"])

def inspecter_webhooks():
    webhooks = alphapay.webhook_endpoints.list()
    for webhook in webhooks["results"]:
        print(webhook["url"], webhook.get("is_active"))

    logs = alphapay.webhook_endpoints.logs.list(status="FAILED")
    print(f"{len(logs['results'])} livraison(s) échouée(s).")
```

***

## 7. Reversements et transferts entre wallets

<Warning>
  Ces deux ressources sont entièrement inaccessibles via clé API (403 `dashboard_only` sur toutes leurs méthodes, y compris en lecture) — elles ne peuvent être pilotées que depuis le dashboard AlphaPay par un compte utilisateur connecté. Elles restent dans le SDK pour documenter la forme réelle des endpoints, pas pour un usage serveur automatisé.
</Warning>

```python theme={null}
import os
from alphapay import AlphaPayClient, AlphaPayPermissionError

alphapay = AlphaPayClient(os.environ["ALPHAPAY_SECRET_KEY"])

def tenter_reversement():
    try:
        alphapay.settlements.create(
            country="BJ", requested_amount=10000, payout_method="mtn_bj", idempotency_key=True,
        )
    except AlphaPayPermissionError:
        print("Reversement : dashboard uniquement, pas via clé API.")

def tenter_transfert_wallet():
    try:
        alphapay.wallet_transfers.create(from_country="BJ", to_country="CI", from_amount=5000)
    except AlphaPayPermissionError:
        print("Transfert entre wallets : dashboard uniquement, pas via clé API.")
```

***

## 8. Clés API et whitelist IP

`api_keys.list/create/get/revoke/delete` sont dashboard-only (403 via clé
API — une clé compromise ne doit pas pouvoir en créer d'autres). Seule
`.ip_whitelist` fonctionne via clé API, et elle est requise pour les
payouts.

```python theme={null}
import os
from alphapay import AlphaPayClient

alphapay = AlphaPayClient(os.environ["ALPHAPAY_SECRET_KEY"])

def gerer_whitelist():
    entry = alphapay.api_keys.ip_whitelist.create(
        ip_address="203.0.113.42",
        label="Serveur de production",
    )

    alphapay.api_keys.ip_whitelist.update(entry["id"], status="ACTIVE")

    page = alphapay.api_keys.ip_whitelist.list()
    print([f"{e['ip_address']} ({e['status']})" for e in page["results"]])
```

***

## 9. Gestion des erreurs

Toutes les erreurs API héritent de `AlphaPayError` — vérifiez le type le
plus spécifique d'abord.

```python theme={null}
import os
from alphapay import (
    AlphaPayClient,
    AlphaPayAuthenticationError,
    AlphaPayConnectionError,
    AlphaPayError,
    AlphaPayIdempotencyError,
    AlphaPayNotFoundError,
    AlphaPayPermissionError,
    AlphaPayRateLimitError,
    AlphaPayServerError,
    AlphaPayValidationError,
)

alphapay = AlphaPayClient(os.environ["ALPHAPAY_SECRET_KEY"])

def appel_securise():
    try:
        return alphapay.checkout_sessions.get("id-inexistant")
    except AlphaPayNotFoundError:
        print("Session introuvable.")
    except AlphaPayValidationError as err:
        print("Erreurs par champ :", err.field_errors)
    except AlphaPayAuthenticationError:
        print("Clé API invalide ou manquante.")
    except AlphaPayPermissionError as err:
        print("Action réservée au dashboard :", err.error_code)  # ex. "dashboard_only"
    except AlphaPayIdempotencyError:
        print("Idempotency-Key déjà utilisée avec un payload différent.")
    except AlphaPayRateLimitError as err:
        print(f"Trop de requêtes, réessayez dans {err.retry_after or 'quelques'}s.")
    except AlphaPayServerError:
        print("Erreur côté AlphaPay — déjà retentée automatiquement.")
    except AlphaPayConnectionError:
        print("Impossible de joindre l'API (réseau/DNS/timeout).")
    except AlphaPayError as err:
        print(f"{err.status} {err.error_code or ''}: {err}")
    return None
```

Le client retente déjà automatiquement (backoff exponentiel + gigue) sur
429/5xx/erreur réseau — configurable via `max_retries` au constructeur du
client. Les erreurs ci-dessus ne surviennent donc qu'après épuisement de
ces tentatives automatiques.
