Metadata-Version: 2.4
Name: spiritpay
Version: 0.1.1
Summary: SDK Python officiel Spirit Pay : paiement par virement Open Banking (checkout, factures, caisse, CRM) et API partenaire pour ERP (encaissements, paiements fournisseurs)
Author-email: Spirit Pay <contact@spiritpay.net>
License-Expression: MIT
Project-URL: Homepage, https://spiritpay.fr
Project-URL: Documentation, https://spiritpay.fr/docs#paiements-fournisseurs
Project-URL: Repository, https://github.com/kifouliw-hash/spiritpay-app/tree/main/sdk/python
Keywords: spiritpay,open-banking,payments,bridge,erp,invoices
Classifier: Programming Language :: Python :: 3
Classifier: Operating System :: OS Independent
Classifier: Intended Audience :: Developers
Classifier: Topic :: Office/Business :: Financial :: Accounting
Classifier: Typing :: Typed
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: license-file

# spiritpay (Python)

SDK Python officiel de **Spirit Pay** : tout ce qu'un serveur Python a besoin pour intégrer le paiement par virement Open Banking, sans réécrire les appels HTTP.

| Vous êtes… | Client | Clé |
|---|---|---|
| **Une entreprise** avec son propre serveur (site e-commerce, devis, caisse, CRM, facturation maison) | `Client` | `sk_test_…` / `sk_live_…` |
| **Un éditeur d'ERP ou une coopérative** qui gère plusieurs entreprises (enDI, Odoo, Dolibarr…) | `PartnerClient` | `sp_test_partner_…` / `sp_live_partner_…` |

- **Aucune dépendance** (bibliothèque standard uniquement), Python ≥ 3.8.
- Une clé de **test** reste en sandbox : aucun euro ne bouge. Les clés se gardent **côté serveur uniquement**.
- Équivalent du SDK Node [`@spiritpay/node`](https://www.npmjs.com/package/@spiritpay/node).
- Les paramètres s'écrivent en `snake_case` (`external_invoice_id`) et sont convertis au format de l'API (`externalInvoiceId`).

```bash
pip install spiritpay
```


---

# Client marchand (`Client`)

```python
import os
from spiritpay import Client

spiritpay = Client(api_key=os.environ["SPIRITPAY_API_KEY"], environment="test")  # sk_test_… / sk_live_…
```

## Checkout e-commerce, devis, bouton de paiement

```python
session = spiritpay.checkout.create(
    line_items=[{"name": "Fauteuil scandinave", "quantity": 1, "unit_price_ht": 100, "vat_rate": 20}],
    customer={"name": "Client SAS", "email": "client@exemple.com"},
    order_ref="WEB-8842",
    success_url="https://boutique.exemple.com/commande/ok",
    cancel_url="https://boutique.exemple.com/commande/annule",
    send_confirmation_email=False,
)
# Redirigez le navigateur du client vers session["bridgeRedirectURL"].
```

Ne vous fiez pas seulement au retour navigateur : confirmez avec le webhook `payment.executed`.

## Facture avec lien de paiement et e-mail

```python
facture = spiritpay.invoices.create(
    amount=1200, invoice_ref="FAC-2026-001", payer_name="Client SAS",
    payer_email="client@exemple.com", send_email=True,
    invoice_amount_ht=1000, invoice_amount_tva=200,
)
print(facture["paymentLink"])
```

## Caisse (POS) : QR dynamique

```python
vente = spiritpay.pos.create(amount=15000, terminal_id="boutique-paris-01", label="Ticket T-8842")  # 15000 = 150,00 €
print(vente["qrCode"], vente["expiresAt"])

statut = spiritpay.pos.get_status(vente["paymentId"])   # statut["status"] == "completed" : encaissé
```

## CRM : solde et échéancier

```python
spiritpay.crm.pay_balance(crm_order_ref="CMD-7", amount=500, payer_email="client@exemple.com", deposit_paid=100)
plan = spiritpay.crm.create_payment_plan(crm_order_ref="CMD-7", total_amount=900, installments=3,
                                         first_due_date="2026-11-01", payer_email="client@exemple.com")
spiritpay.crm.get_plan(plan["planId"])
```

## Webhooks marchand

```python
# raw_body = corps brut de la requête (bytes, non re-sérialisé)
ok = Client.verify_webhook_signature(secret=os.environ["SPIRITPAY_WEBHOOK_SECRET"],
                                     headers=request.headers, raw_body=raw_body)   # en-tête X-Spirit-Pay-Signature
```

---

# Client partenaire (`PartnerClient`)

Pour les ERP et coopératives. Détail ci-dessous.

```python
from spiritpay import PartnerClient

spiritpay = PartnerClient(partner_key=os.environ["SPIRITPAY_PARTNER_KEY"])
```

## Encaisser une facture client

```python
link = spiritpay.invoices.create(
    external_invoice_id="497582",          # id de la facture dans votre ERP (idempotence)
    payer_name="Camille Martin",
    payer_email="client@exemple.com",
    amount=840,                            # euros
    invoice_ref="FC763-2610",
    immediate_only=True,
    issuer_external_id="12",               # enseigne qui émet la facture
    issuer_name="Auprès Fauteuil Remplacer.",  # affichée comme « sous-marchand » chez Spirit Pay
)
# Insérez link["paymentUrl"] dans l'e-mail (bouton « Payer »).

status = spiritpay.invoices.get(link["paymentId"])
```

Sans `issuer_name`, Spirit Pay affiche le nom du compte de règlement. Confirmez le paiement avec le webhook `invoice.paid` :

```python
# raw_body = corps brut de la requête (bytes, non re-sérialisé)
ok = spiritpay.verify_webhook_signature(
    secret=os.environ["SPIRITPAY_WEBHOOK_SECRET"],   # whsec_…
    headers=request.headers,
    raw_body=raw_body,
)
```

## Payer des factures fournisseur

```python
pay = spiritpay.purchases.pay(
    return_url="https://erp.exemple.com/spiritpay/retour",  # HTTPS (http://localhost accepté avec une clé de test)
    invoices=[{
        "external_invoice_id": "497580",
        "supplier_name": "Richard SARL",
        "supplier_iban": "FR76…",
        "supplier_bic": "BNPAFRPP",
        "amount_ttc": 330,
        "invoice_ref": "TEST-2026-0928-01",
        "issuer_external_id": "12",
        "issuer_name": "Auprès Fauteuil Remplacer.",
    }],
)
# Redirigez le navigateur du payeur vers pay["bridgeRedirectURL"].

# Au retour sur return_url (?ref=<batchRef>) : état confirmé auprès de Bridge.
batch = spiritpay.purchases.get_batch(pay["batchRef"])
for invoice in batch["invoices"]:
    if invoice["status"] == "paid":
        ...  # enregistrez le règlement dans votre ERP
```

- **Tout ou rien** : si une facture est refusée, aucune n'est payée. L'exception `SpiritPayError` porte `code == "INVOICES_NOT_PAYABLE"` et `data["problems"]` (une raison par facture).
- **N'enregistrez un règlement que pour `status == "paid"`.** Un lot en cours ou abandonné ne doit rien écrire.
- **Un fournisseur** (même avec plusieurs factures) = un seul virement, toutes banques. **Plusieurs fournisseurs** = paiement groupé (une validation bancaire), seulement si la banque du payeur le permet. Spirit Pay ne bascule pas automatiquement : sinon, faites **un appel par fournisseur**.
- `get_batch` peut prendre jusqu'à ~7 s (Spirit Pay interroge Bridge) : gardez le délai par défaut (30 s).

## Banques compatibles avec le paiement groupé

```python
banks = spiritpay.bulk_banks()["banks"]   # liste publique, aucune clé nécessaire
```

## Erreurs

```python
from spiritpay import SpiritPayError

try:
    spiritpay.purchases.pay(return_url, invoices)
except SpiritPayError as err:
    err.status   # code HTTP, None si réseau indisponible
    err.code     # code stable (INVOICES_NOT_PAYABLE, NETWORK, …)
    err.data     # corps JSON de la réponse
```

Documentation complète : [spiritpay.fr/docs#paiements-fournisseurs](https://spiritpay.fr/docs#paiements-fournisseurs).
