Metadata-Version: 2.4
Name: welloo-payment-sdk
Version: 0.1.2
Summary: SDK Python pour authentifier un marchand et créer un paiement via redirection (checkout hébergé) avec le prestataire Welloo.
Author: Welloo
License: MIT
Keywords: payment,sdk,checkout,merchant,welloo,mobile-money
Requires-Python: >=3.9
Description-Content-Type: text/markdown

# welloo-payment-sdk

SDK Python pour :
- créer un paiement et obtenir une **URL de paiement hébergée** vers laquelle rediriger le client,
- transfert Mobile Money via Welloo
- generer un lien paiement associé à un opérateur.
- vérifier la signature d'un webhook de confirmation.

## ⚠️ Sécurité — à lire avant tout

Ce SDK manipule `api_key` et `service_token`, des **credentials secrets**.
**Il ne doit jamais être exécuté dans du code accessible côté client**
(JS embarqué dans une page publique, etc.). Utilise-le uniquement côté
serveur : Flask, Django, FastAPI, script Python, etc. Ne mets jamais ces
valeurs en dur dans ton code ou ta documentation — passe-les toujours par
des variables d'environnement.

Le frontend doit appeler **ton propre backend**, qui utilise ce SDK pour
parler au prestataire de paiement.

```
[Frontend] --(HTTP, sans secret)--> [Ton backend Flask/Django/FastAPI] --(SDK, avec secrets)--> [Welloo]
```

## Procédure d'installation

### 1. Prérequis

- Python >= 3.9

### 2. Installer le package

```bash
pip install welloo-payment-sdk
```

### 3. Configurer les identifiants

Récupère `api_key` et `service_token` auprès de Welloo, puis expose-les en
variables d'environnement (ex: fichier `.env`, jamais commité) :

```bash
API_KEY=...
SERVICE_TOKEN=...
WEBHOOK_SECRET=...
```

### 4. Instancier le SDK

```python
import os

from welloo_payment_sdk import PaymentSDK, PaymentSDKConfig

# Le SDK est instancié UNE FOIS, côté serveur, avec les secrets pris depuis .env
sdk = PaymentSDK(PaymentSDKConfig(
    api_key=os.environ.get("API_KEY", ""),
    service_token=os.environ.get("SERVICE_TOKEN", ""),
    webhook_secret=os.environ.get("WEBHOOK_SECRET") or None,
))
```

`PaymentSDK` lève une `PaymentSDKError` si `api_key` ou `service_token` est
manquant — instancie-le une seule fois au démarrage de l'application, pas à
chaque requête.

## Utilisation du SDK avec `init_payment`

`init_payment` crée une session de paiement auprès de Welloo et retourne
`checkout_url` : l'URL de la page de paiement **hébergée par Welloo**, vers
laquelle rediriger le navigateur du client (il y choisit son opérateur
Mobile Money et son numéro).

```python
@app.get("/api/v1/init-payment")
def init_payment():
    try:
        payment = sdk.init_payment(InitPaymentPayload(
            amount=5,                              # montant en unité mineure du prestataire
            return_url="https://monsite.com/panier",  # lien de retour
            metadata={},
        ))
        return Response(status=302, headers={"Location": payment.checkout_url})
    except PaymentSDKError as e:
        app.logger.error("Impossible de lancer le paiement : %s", e)
        return {"error": "Impossible de lancer le paiement"}, 500
```

### Champs de `InitPaymentPayload`

| Champ | Requis | Description |
| --- | --- | --- |
| `amount` | oui | Montant en unité mineure (ex: centimes) selon le prestataire |
| `return_url` | oui | Cible du bouton "Retour" sur la page de paiement |
| `metadata` | non | Objet libre transmis au prestataire |

`init_payment` retourne un `InitPaymentResult` :

| Champ | Description |
| --- | --- |
| `checkout_url` | URL de la page de paiement hébergée par Welloo — à rediriger |
| `status` | Statut initial de la session (ex: `"active"`, `"pending"`) |

## Utilisation du SDK avec `generate_link_payment`

`generate_link_payment` génère directement un lien de paiement pour un
opérateur donné (endpoint `POST /api/v1/payment-link-sdk`), sans passer par
l'écran de sélection d'opérateur de `init_payment`.

```python
@app.get("/api/v1/generate-link-payment")
def generate_link_payment():
    try:
        link = sdk.generate_link_payment(GenerateLinkPaymentPayload(
            amount=5,
            operator="wave",
            id_service="x",
            return_url="https://www.google.com",
        ))
        return Response(status=302, headers={"Location": link.checkout_url})
    except PaymentSDKError as e:
        app.logger.error("Impossible de générer le lien de paiement : %s", e)
        return {"error": "Impossible de générer le lien de paiement"}, 500
```

> Exemple complet : [`examples/python-backend/app.py`](../../examples/python-backend/app.py).

### Champs de `GenerateLinkPaymentPayload`

| Champ | Description |
| --- | --- |
| `amount` | Montant en unité mineure (ex: centimes) selon le prestataire |
| `operator` | Opérateur Mobile Money ciblé (ex: `"wave"` ou `"welloo"`) |
| `return_url` | URL de retour après paiement |

`generate_link_payment` retourne un `GenerateLinkPaymentResult` avec un seul
champ : `checkout_url` (URL du lien de paiement généré, à rediriger).

## Utilisation du SDK avec `init_transfer`

`init_transfer` initie un transfert et retourne l'URL de paiement hébergée
vers laquelle rediriger le client (endpoint `POST /api/v1/init`).

```python
@app.get("/api/v1/init-transfer")
def init_transfer():
    try:
        transfer = sdk.init_transfer(InitTransferPayload(
            return_url="https://merchant.example.com/success",
        ))
        return Response(status=302, headers={"Location": transfer.checkout_url})
    except PaymentSDKError as e:
        app.logger.error("Impossible d'initier le transfert : %s", e)
        return {"error": "Impossible d'initier le transfert"}, 500
```

### Champs de `InitTransferPayload`

| Champ | Description |
| --- | --- |
| `return_url` | URL de retour après paiement |

`init_transfer` retourne un `InitTransferResult` avec un seul champ :
`checkout_url` (URL vers laquelle rediriger).

> `init_payment` et `generate_link_payment` rejettent un `amount` inférieur
> à 5 (`PaymentSDKError`), avant même d'appeler l'API.

## Utilisation du SDK avec `get_transactions`

`get_transactions` liste les transactions du service authentifié (endpoint
`GET /api/v1/payments`).

```python
@app.get("/api/v1/transactions")
def transactions():
    try:
        return jsonify(sdk.get_transactions())
    except PaymentSDKError as e:
        app.logger.error("Impossible de récupérer les transactions : %s", e)
        return {"error": "Impossible de récupérer les transactions"}, 500
```

`get_transactions` retourne une liste des transactions brutes renvoyées par
Welloo (montant, devise, statut, opérateur, dates, etc.).

## Utilisation du SDK avec `get_solde`

`get_solde` récupère le solde du compte Welloo associé au service
authentifié (endpoint `GET /api/v1/solde`).

```python
@app.get("/api/v1/solde")
def solde():
    try:
        result = sdk.get_solde()
        return jsonify({"currency": result.currency, "solde": result.solde})
    except PaymentSDKError as e:
        app.logger.error("Impossible de récupérer le solde : %s", e)
        return {"error": "Impossible de récupérer le solde"}, 500
```

`get_solde` retourne un `GetSoldeResult` :

| Champ | Description |
| --- | --- |
| `currency` | Devise du solde (ex: `"XOF"`) |
| `solde` | Montant disponible sur le compte |
| `raw` | Réponse brute complète du prestataire |

## Vérifier un webhook

> Contrairement à Express, Flask ne parse pas le corps de la requête tant
> que `request.get_json()` n'est pas appelé — `request.get_data()` renvoie
> donc toujours le corps brut exact, nécessaire au calcul HMAC, sans
> précaution d'ordre de middleware particulière.

```python
@app.post("/api/v1/webhook")
def webhook():
    signature = request.headers.get("X-Webhook-Signature", "")
    raw_body = request.get_data()  # corps brut, nécessaire au calcul HMAC

    if not signature or not sdk.verify_webhook_signature(raw_body, signature):
        return {"error": "Invalid signature"}, 401

    event = request.get_json(silent=True) or {}
    # event["status"] == "paid" -> mettre à jour la commande en base
    return "", 200
```

## API complète

| Méthode | Description |
| --- | --- |
| `init_payment(payload)` | Crée une session de paiement et retourne `checkout_url`. |
| `generate_link_payment(payload)` | Génère un lien de paiement direct pour un opérateur donné. |
| `init_transfer(payload)` | Initie un transfert et retourne `checkout_url`. |
| `get_transactions()` | Liste les transactions du service authentifié. |
| `get_solde()` | Récupère le solde du compte Welloo. |
| `verify_webhook_signature(raw_body, signature)` | Vérifie la signature HMAC-SHA256 d'un webhook. |

## Build

```bash
py -m build
```
