# Pour aller plus loin : cache, idempotence et webhooks

### Le cache HTTP avec ETag

Pourquoi retélécharger la carte si elle n'a pas changé ? Le serveur associe à chaque version d'une ressource une empreinte, l'**ETag**. Le client la renvoie à la requête suivante, et le serveur répond `304 Not Modified`, sans body, si rien n'a bougé.

```http
GET /v1/cocktails/3

HTTP/1.1 200 OK
ETag: "a1b2c3"
{"id": 3, "name": "Segfault Sunrise", ...}
```

```http
GET /v1/cocktails/3
If-None-Match: "a1b2c3"

HTTP/1.1 304 Not Modified
```

Essayez avec GitHub, qui ne décompte pas les réponses `304` de votre quota :

```bash
curl -i https://api.github.com/users/torvalds            # notez l'etag
curl -i https://api.github.com/users/torvalds -H 'If-None-Match: W/"..."'
```

<p class="callout info">L'ETag sert aussi à éviter les modifications concurrentes : un `PUT` avec `If-Match: "a1b2c3"` échoue en `412 Precondition Failed` si quelqu'un a modifié la ressource entre-temps.</p>

### Les clés d'idempotence

`POST` n'est pas idempotent : si la connexion coupe après l'envoi d'un paiement, le client ne sait pas s'il a été pris en compte. Le renvoyer, c'est risquer de payer deux fois.

La solution, popularisée par Stripe : le client génère un identifiant unique par opération et l'envoie dans un header.

```http
POST /v1/orders
Idempotency-Key: 8e03978e-40d5-43e8-bc93-6894a57f9324
Content-Type: application/json

{"cocktail_id": 3}
```

Le serveur mémorise la clé et la réponse. Si la même clé revient, il renvoie la réponse mémorisée au lieu de recréer une commande.

<p class="callout success">Commander deux fois le même cocktail parce que le Wi-Fi a coupé ? Plus jamais.</p>

### Les webhooks

Plutôt que d'interroger l'API toutes les 5 secondes pour savoir si la commande est prête (*polling*), le client donne une URL, et c'est l'API qui l'appelle quand l'événement se produit.

```http
POST https://mon-appli.example/webhooks/api-ritif
Content-Type: application/json
X-Signature-256: sha256=5d7f1c...

{"event": "order.ready", "order_id": 42, "created_at": "2026-10-14T10:34:30Z"}
```

Problème : n'importe qui peut envoyer une requête à cette URL. L'émetteur **signe** donc le body avec un secret partagé (HMAC), et le récepteur vérifie la signature :

```python
import hashlib, hmac

def is_valid(raw_body: bytes, signature: str, secret: str) -> bool:
    expected = "sha256=" + hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, signature)
```

- La signature se calcule sur le body **brut**, avant tout parsing JSON.
- La comparaison se fait en temps constant (`timingSafeEqual`, `compare_digest`), pour ne pas laisser deviner la signature octet par octet.
- Le récepteur répond vite (`2xx`) et traite l'événement ensuite. Sinon, l'émetteur considère l'envoi comme échoué et réessaie.

<p class="callout info">GitHub, Stripe ou Shopify fonctionnent tous ainsi. GitHub, par exemple, envoie sa signature dans le header `X-Hub-Signature-256`.</p>