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

# Rapprocher ses ventes

> Un rappel peut se perdre de votre côté : serveur arrêté, déploiement. Une tâche de rapprochement garantit que chaque vente finit par être connue.

## 1. Reprendre les ventes en attente

```text Pseudo-code theme={"dark"}
toutes les 5 minutes
  pour chaque vente EN_COURS depuis plus de 15 minutes
    vente ← GET /v1/ventes/{uuid}
    si NON_TROUVEE → renvoyer la même vente, même uuid
    sinon → traiter(vente.statut)
```

```javascript Node.js · fetch icon="https://mintcdn.com/dommi10/MMGXqk_5twdsE4Rl/logos/langages/nodejs.svg?fit=max&auto=format&n=MMGXqk_5twdsE4Rl&q=85&s=60e7eb54ba25d5cb5e9d62e0896b99c7" theme={"dark"}
const BASE_URL = process.env.UMEME_BASE_URL;
const entetes = { Authorization: `Bearer ${process.env.UMEME_CLE}` };

// commandes : votre stockage ; traiter : la fonction qui applique un statut.
export async function reprendre(commandes, traiter) {
  const enAttente = await commandes.enCoursDepuis(15 * 60 * 1000);
  for (const commande of enAttente) {
    const reponse = await fetch(`${BASE_URL}/ventes/${commande.uuid}`, { headers: entetes });
    if (reponse.status === 404) {
      // NON_TROUVEE : la vente n'est jamais arrivée, elle repart avec le même uuid.
      await commandes.renvoyer(commande);
      continue;
    }
    traiter(await reponse.json());
  }
}

// Toutes les 5 minutes.
export function planifier(commandes, traiter) {
  return setInterval(() => reprendre(commandes, traiter), 5 * 60 * 1000);
}
```

```javascript Node.js · Axios icon="https://mintcdn.com/dommi10/MMGXqk_5twdsE4Rl/logos/langages/axios.svg?fit=max&auto=format&n=MMGXqk_5twdsE4Rl&q=85&s=7e8bd7e79f903d3d1bffc290e52fcdc9" theme={"dark"}
import axios from "axios";

const umeme = axios.create({
  baseURL: process.env.UMEME_BASE_URL,
  headers: { Authorization: `Bearer ${process.env.UMEME_CLE}` },
  timeout: 30000,
  validateStatus: (statut) => statut < 500,
});

// commandes : votre stockage ; traiter : la fonction qui applique un statut.
export async function reprendre(commandes, traiter) {
  const enAttente = await commandes.enCoursDepuis(15 * 60 * 1000);
  for (const commande of enAttente) {
    const { status, data } = await umeme.get(`/ventes/${commande.uuid}`);
    if (status === 404) {
      // NON_TROUVEE : la vente n'est jamais arrivée, elle repart avec le même uuid.
      await commandes.renvoyer(commande);
      continue;
    }
    traiter(data);
  }
}

// Toutes les 5 minutes.
export function planifier(commandes, traiter) {
  return setInterval(() => reprendre(commandes, traiter), 5 * 60 * 1000);
}
```

```python Python icon="https://mintcdn.com/dommi10/MMGXqk_5twdsE4Rl/logos/langages/python.svg?fit=max&auto=format&n=MMGXqk_5twdsE4Rl&q=85&s=41e570f5c5c97d07c3c878a6704f2bed" theme={"dark"}
import os
import time

import requests

BASE_URL = os.environ["UMEME_BASE_URL"]
ENTETES = {"Authorization": f"Bearer {os.environ['UMEME_CLE']}"}


# commandes : votre stockage ; traiter : la fonction qui applique un statut.
def reprendre(commandes, traiter):
    for commande in commandes.en_cours_depuis(minutes=15):
        reponse = requests.get(f"{BASE_URL}/ventes/{commande['uuid']}", headers=ENTETES, timeout=30)
        if reponse.status_code == 404:
            # NON_TROUVEE : la vente n'est jamais arrivée, elle repart avec le même uuid.
            commandes.renvoyer(commande)
            continue
        traiter(reponse.json())


# Toutes les 5 minutes.
def planifier(commandes, traiter):
    while True:
        reprendre(commandes, traiter)
        time.sleep(5 * 60)
```

```php PHP icon="https://mintcdn.com/dommi10/MMGXqk_5twdsE4Rl/logos/langages/php.svg?fit=max&auto=format&n=MMGXqk_5twdsE4Rl&q=85&s=706823b5881c1d4e78720e369520cc78" theme={"dark"}
<?php

use GuzzleHttp\Client;

// $commandes : votre stockage ; $traiter : la fonction qui applique un statut.
// À lancer toutes les 5 minutes, par exemple depuis le planificateur de Laravel ou cron.
function reprendre(Commandes $commandes, callable $traiter): void
{
    $umeme = new Client([
        'base_uri' => getenv('UMEME_BASE_URL') . '/',
        'headers' => ['Authorization' => 'Bearer ' . getenv('UMEME_CLE')],
        'timeout' => 30,
        'http_errors' => false,
    ]);
    foreach ($commandes->enCoursDepuis(15) as $commande) {
        $reponse = $umeme->get('ventes/' . $commande['uuid']);
        if ($reponse->getStatusCode() === 404) {
            // NON_TROUVEE : la vente n'est jamais arrivée, elle repart avec le même uuid.
            $commandes->renvoyer($commande);
            continue;
        }
        $traiter(json_decode($reponse->getBody(), true));
    }
}
```

```java Java icon="https://mintcdn.com/dommi10/MMGXqk_5twdsE4Rl/logos/langages/java.svg?fit=max&auto=format&n=MMGXqk_5twdsE4Rl&q=85&s=a99792afd2b2bcd5b3c64f5157b8469e" theme={"dark"}
static final HttpClient CLIENT = HttpClient.newHttpClient();

// commandes : votre stockage ; traiter : la fonction qui applique un statut.
static void reprendre(Commandes commandes, Consumer<String> traiter) throws Exception {
    for (var commande : commandes.enCoursDepuis(Duration.ofMinutes(15))) {
        var requete = HttpRequest.newBuilder(URI.create(System.getenv("UMEME_BASE_URL") + "/ventes/" + commande.uuid()))
            .header("Authorization", "Bearer " + System.getenv("UMEME_CLE"))
            .timeout(Duration.ofSeconds(30))
            .GET()
            .build();
        var reponse = CLIENT.send(requete, HttpResponse.BodyHandlers.ofString());
        if (reponse.statusCode() == 404) {
            // NON_TROUVEE : la vente n'est jamais arrivée, elle repart avec le même uuid.
            commandes.renvoyer(commande);
            continue;
        }
        traiter.accept(reponse.body());
    }
}

// Toutes les 5 minutes.
static void planifier(Commandes commandes, Consumer<String> traiter) {
    Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(() -> {
        try {
            reprendre(commandes, traiter);
        } catch (Exception e) {
            // Le prochain passage reprendra les mêmes ventes.
        }
    }, 0, 5, TimeUnit.MINUTES);
}
```

L'appel de consultation, seul :

```bash cURL icon="https://mintcdn.com/dommi10/MMGXqk_5twdsE4Rl/logos/langages/bash.svg?fit=max&auto=format&n=MMGXqk_5twdsE4Rl&q=85&s=c7cc496df6d82b1c6cdaa5c132b8d4bc" theme={"dark"}
UUID="94a223fe-fb34-47f2-aaa5-b80c4bf8a8eb"

curl "$UMEME_BASE_URL/ventes/$UUID" \
  -H "Authorization: Bearer $UMEME_CLE"
```

```javascript Node.js · fetch icon="https://mintcdn.com/dommi10/MMGXqk_5twdsE4Rl/logos/langages/nodejs.svg?fit=max&auto=format&n=MMGXqk_5twdsE4Rl&q=85&s=60e7eb54ba25d5cb5e9d62e0896b99c7" theme={"dark"}
// Node.js 18 et plus : fetch est intégré.
const baseUrl = process.env.UMEME_BASE_URL;

const uuid = "94a223fe-fb34-47f2-aaa5-b80c4bf8a8eb";

const reponse = await fetch(`${baseUrl}/ventes/${uuid}`, {
  headers: {
    Authorization: `Bearer ${process.env.UMEME_CLE}`,
  },
});
const donnees = await reponse.json();
```

```javascript Node.js · Axios icon="https://mintcdn.com/dommi10/MMGXqk_5twdsE4Rl/logos/langages/axios.svg?fit=max&auto=format&n=MMGXqk_5twdsE4Rl&q=85&s=7e8bd7e79f903d3d1bffc290e52fcdc9" theme={"dark"}
// npm install axios
import axios from "axios";

const umeme = axios.create({
  baseURL: process.env.UMEME_BASE_URL,
  headers: { Authorization: `Bearer ${process.env.UMEME_CLE}` },
  timeout: 30000,
});

const uuid = "94a223fe-fb34-47f2-aaa5-b80c4bf8a8eb";

const { data } = await umeme.get(`/ventes/${uuid}`);
```

```python Python icon="https://mintcdn.com/dommi10/MMGXqk_5twdsE4Rl/logos/langages/python.svg?fit=max&auto=format&n=MMGXqk_5twdsE4Rl&q=85&s=41e570f5c5c97d07c3c878a6704f2bed" theme={"dark"}
import os

import requests

BASE_URL = os.environ["UMEME_BASE_URL"]
ENTETES = {"Authorization": f"Bearer {os.environ['UMEME_CLE']}"}

uuid = "94a223fe-fb34-47f2-aaa5-b80c4bf8a8eb"

reponse = requests.get(
    f"{BASE_URL}/ventes/{uuid}",
    headers=ENTETES,
    timeout=30,
)
donnees = reponse.json()
```

```php PHP icon="https://mintcdn.com/dommi10/MMGXqk_5twdsE4Rl/logos/langages/php.svg?fit=max&auto=format&n=MMGXqk_5twdsE4Rl&q=85&s=706823b5881c1d4e78720e369520cc78" theme={"dark"}
// composer require guzzlehttp/guzzle
$client = new GuzzleHttp\Client([
    'base_uri' => getenv('UMEME_BASE_URL') . '/',
    'headers' => ['Authorization' => 'Bearer ' . getenv('UMEME_CLE')],
    'timeout' => 30,
]);

$uuid = '94a223fe-fb34-47f2-aaa5-b80c4bf8a8eb';

$reponse = $client->get("ventes/{$uuid}");
$donnees = json_decode($reponse->getBody(), true);
```

```java Java icon="https://mintcdn.com/dommi10/MMGXqk_5twdsE4Rl/logos/langages/java.svg?fit=max&auto=format&n=MMGXqk_5twdsE4Rl&q=85&s=a99792afd2b2bcd5b3c64f5157b8469e" theme={"dark"}
// Java 15 et plus : java.net.http et blocs de texte.
var baseUrl = System.getenv("UMEME_BASE_URL");
var client = HttpClient.newHttpClient();

var uuid = "94a223fe-fb34-47f2-aaa5-b80c4bf8a8eb";

var requete = HttpRequest.newBuilder(URI.create(baseUrl + "/ventes/" + uuid))
    .header("Authorization", "Bearer " + System.getenv("UMEME_CLE"))
    .GET()
    .build();

var reponse = client.send(requete, HttpResponse.BodyHandlers.ofString());
```

## 2. Lire la réponse

| Réponse | Ce que vous faites |
| :- | :- |
| `REUSSIE`, `ECHOUEE` | Appliquer le statut, comme pour un rappel |
| `EN_COURS` | Attendre le prochain passage |
| `EN_RECONCILIATION` | Attendre : Umeme vérifie. Ne pas relancer |
| `404 NON_TROUVEE` | La vente n'existe pas : renvoyez-la, avec le même `uuid` |

## 3. Rapprocher le portefeuille

Une fois par jour, comparez vos ventes au relevé `GET /v1/portefeuille/mouvements` : chaque vente acceptée y a un débit, chaque échec un remboursement, et chaque ligne donne le solde après le mouvement. Voir [Recharger et suivre son portefeuille](https://docs.umeme.app/guides/portefeuille.md).

> [!NOTE]
>
> Consultez une même vente au plus toutes les 30 secondes : au-delà, vous risquez `429 TROP_DE_REQUETES`.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.