## En qué se diferencian los informes masivos

De forma predeterminada, a su dirección llega una solicitud por cada mensaje. Con grandes volúmenes
eso supone miles de solicitudes; la variante masiva las agrupa en lotes.

| | Uno a uno | En bloque |
|:--|:--|:--|
| Método | HTTP GET | HTTP POST |
| Formato | Parámetros en la query string | JSON en el cuerpo de la solicitud |
| Contenido | Un único mensaje | Un array de registros |

Ambas transmiten lo mismo: tanto informes de entrega como mensajes entrantes. La
variante uno a uno se describe en
[Confirmaciones de entrega y SMS entrantes](https://www.bulkgate.com/es/documentacion/administracion-de-la-api/api-confirmaciones-de-entrega-y-sms-entrantes/).

## Cómo activar el envío masivo

Marque `DLRs a granel - solicitud a granel` en la configuración del webhook. La dirección y las
demás opciones siguen igual; solo cambia la forma en que le llegan los datos.

![La configuración de Informes de entrega con la opción de envío masivo marcada](docs:api-bulk-delivery-confirmations-and-incoming-sms/bulk-setting_en)

El procedimiento completo, incluido dónde encontrar esta configuración, se describe en
[Webhooks](https://www.bulkgate.com/es/documentacion/administracion-de-la-api/webhooks/).

## Qué aspecto tiene la solicitud

Llega por POST con la cabecera `Content-Type: application/json`. El cuerpo es
un array que mezcla informes de entrega y mensajes entrantes; se distinguen por
`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": "texto de la respuesta"
   }
]
```

## Parámetros del registro

| Parámetro | Valor | Cuándo llega |
|:--|:--|:--|
| `status` | El código de estado; consulte los [estados de entrega](#estados-de-entrega) | Siempre |
| `smsID` | El ID único del mensaje | Con un informe de entrega |
| `price` | El precio del mensaje en créditos | Con un informe de entrega |
| `date` | La hora de envío y la de entrega separadas por punto y coma, zona horaria CET | Con un informe de entrega |
| `from` | El número de teléfono del remitente | Solo con `status` `10` |
| `message` | El texto del mensaje en UTF-8 | Solo con `status` `10` |

El valor `date` tiene la forma `YYMMDDHHMM;YYMMDDHHMM`, así que `1904111007;1904111011`
significa enviado el 11 de abril de 2019 a las 10:07 y entregado a las 10:11.

## Estados de entrega

| Código | Significado |
|:--|:--|
| `1` | El mensaje se ha entregado |
| `2` | El mensaje está en cola en el operador: el destinatario no está disponible en este momento y se volverá a intentar la entrega |
| `3` | No se ha podido entregar el mensaje: el destinatario es desconocido o no está disponible de forma permanente |
| `10` | Un mensaje entrante o una respuesta, no un informe de entrega |
| `13` | El mensaje se ha leído (solo Viber) |

> [!NOTE]
> El estado `2` no es definitivo: más adelante llega para el mismo mensaje otro registro
> con el estado final. Cuente con recibir el mismo `smsID` más de una vez, posiblemente
> incluso dentro del mismo lote.

## Ejemplo de procesamiento

El script lee el cuerpo de la solicitud, recorre el array y trata cada registro según
`status`:

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

const STATUS = [
    1 => 'entregado',
    2 => 'en cola del operador',
    3 => 'no entregado',
    13 => 'leído',
];

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
{
    // la forma YYMMDDHHMM, por ejemplo 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 mensaje entrante
    {
        log_line('inbox', sprintf(
            'De: [%s] Mensaje: [%s]',
            $record['from'] ?? '',
            $record['message'] ?? ''
        ));
    }
    else if (isset(STATUS[$status])) // un informe de entrega
    {
        [$sent, $delivered] = array_pad(explode(';', $record['date'] ?? ''), 2, '');

        log_line('delivery', sprintf(
            'Mensaje [%s]: %s, precio %.4f, enviado %s, entregado %s',
            $record['smsID'] ?? '',
            STATUS[$status],
            (float) ($record['price'] ?? 0),
            parse_date($sent),
            parse_date($delivered)
        ));
    }
}

http_response_code(200);
```

> [!WARNING]
> La dirección debe responder rápido y con el código de estado `200`. Todo el
> lote espera una única respuesta, así que envíe el procesamiento pesado a una cola y
> confirme la solicitud de inmediato.
