## En quoi les rapports groupés diffèrent

Par défaut, une requête par message arrive à votre adresse. Pour des volumes élevés,
cela représente des milliers de requêtes — la variante groupée les rassemble en lots.

| | Un par un | Groupés |
|:--|:--|:--|
| Méthode | HTTP GET | HTTP POST |
| Format | Paramètres dans la query string | JSON dans le corps de la requête |
| Contenu | Un seul message | Un tableau d'enregistrements |

Les deux transportent la même chose — les rapports de livraison comme les messages
entrants. La variante un par un est décrite sur
[Confirmations de livraison et SMS entrants](https://www.bulkgate.com/fr/documentation/administration-api-et-tokens/api-confirmations-de-livraison-et-sms-entrants/).

## Comment activer l'envoi groupé

Cochez `Rapports de livraison groupés - requête groupée` dans les paramètres du webhook. L'adresse et les
autres options restent inchangées — seule change la manière dont les données vous
parviennent.

![La fenêtre Delivery reports setting avec l'option d'envoi groupé cochée](docs:api-bulk-delivery-confirmations-and-incoming-sms/bulk-setting_en)

La procédure complète, y compris l'emplacement du réglage, est décrite sur
[Webhooks](https://www.bulkgate.com/fr/documentation/administration-api-et-tokens/webhooks/).

## À quoi ressemble la requête

Elle arrive en POST avec l'en-tête `Content-Type: application/json`. Le corps est
un tableau qui mélange rapports de livraison et messages entrants — vous les
distinguez grâce à `status`.

```http-json
POST /hook/delivery-report HTTP/1.1
Host: example.com
Content-Type: application/json

[
   {
      "status": "1",
      "smsID": "3t2563af3ff285",
      "price": "0.71",
      "date": "1904111007;1904111011"
   },
   {
      "status": "2",
      "smsID": "2t2563af3ff285",
      "price": "0.71",
      "date": "1904111007;1904111011"
   },
   {
      "status": "10",
      "from": "420777777777",
      "message": "texte de la réponse"
   }
]
```

## Paramètres de l'enregistrement

| Paramètre | Valeur | Quand il arrive |
|:--|:--|:--|
| `status` | Le code de statut — voir les [statuts de livraison](#statuts-de-livraison) | Toujours |
| `smsID` | L'identifiant unique du message | Avec un rapport de livraison |
| `price` | Le prix du message en crédits | Avec un rapport de livraison |
| `date` | L'heure d'envoi et l'heure de livraison séparées par un point-virgule, fuseau horaire CET | Avec un rapport de livraison |
| `from` | Le numéro de téléphone de l'expéditeur | Uniquement avec `status` `10` |
| `message` | Le texte du message en UTF-8 | Uniquement avec `status` `10` |

La valeur `date` a la forme `AAMMJJHHMM;AAMMJJHHMM` — ainsi `1904111007;1904111011`
signifie envoyé le 11 avril 2019 à 10 h 07 et livré à 10 h 11.

## Statuts de livraison

| Code | Signification |
|:--|:--|
| `1` | Le message a été livré |
| `2` | Le message est en attente chez l'opérateur — le destinataire est actuellement injoignable et la livraison sera retentée |
| `3` | Le message n'a pas pu être livré — le destinataire est inconnu ou durablement injoignable |
| `10` | Un message entrant ou une réponse, et non un rapport de livraison |
| `13` | Le message a été lu (Viber uniquement) |

> [!NOTE]
> Le statut `2` n'est pas définitif — un autre enregistrement avec le statut final
> arrive plus tard pour le même message. Attendez-vous à voir le même `smsID` plusieurs
> fois, éventuellement même au sein d'un même lot.

## Exemple de traitement

Le script lit le corps de la requête, parcourt le tableau et oriente chaque
enregistrement selon son `status` :

```php
<?php declare(strict_types=1);

const STATUS = [
    1 => 'livré',
    2 => 'en attente chez l’opérateur',
    3 => 'non livré',
    13 => 'lu',
];

function log_line(string $file, string $message): void
{
    file_put_contents(
        __DIR__ . "/$file.log",
        '[' . date('Y-m-d H:i:s') . '] ' . $message . PHP_EOL,
        FILE_APPEND
    );
}

function parse_date(string $value): string
{
    // au format AAMMJJHHMM, par exemple 1904111007
    $date = DateTimeImmutable::createFromFormat('ymdHi', $value);

    return $date ? $date->format('Y-m-d H:i') : '?';
}

$body = file_get_contents('php://input');
$records = json_decode($body ?: '', true);

if (!is_array($records))
{
    http_response_code(400);
    exit;
}

foreach ($records as $record)
{
    $status = (int) ($record['status'] ?? 0);

    if ($status === 10) // un message entrant
    {
        log_line('inbox', sprintf(
            'De : [%s] Message : [%s]',
            $record['from'] ?? '',
            $record['message'] ?? ''
        ));
    }
    else if (isset(STATUS[$status])) // un rapport de livraison
    {
        [$sent, $delivered] = array_pad(explode(';', $record['date'] ?? ''), 2, '');

        log_line('delivery', sprintf(
            'Message [%s] : %s, prix %.4f, envoyé %s, livré %s',
            $record['smsID'] ?? '',
            STATUS[$status],
            (float) ($record['price'] ?? 0),
            parse_date($sent),
            parse_date($delivered)
        ));
    }
}

http_response_code(200);
```

> [!WARNING]
> L'adresse doit répondre rapidement et avec le code de statut `200`. Tout le lot
> attend une seule réponse ; confiez donc les traitements lourds à une file d'attente
> et accusez réception de la requête immédiatement.
