# Carrinhos do catálogo

Quando um cliente monta um carrinho com o catálogo de WhatsApp da empresa e o envia, chega para você um `message.received` com `data.message.type: "order"` e o objeto `order` da Meta tal como vem, sem achatar: `catalog_id` (o catálogo), `text` (o comentário que ele escreveu com o carrinho; **vem `""` se não escreveu nada**: o campo está lá, vazio) e `product_items[]`, um por produto, com `product_retailer_id` (o id do produto no catálogo), `quantity`, `item_price` (preço unitário) e `currency`.

**Um produto sem preço** chega com `item_price: 0` e `currency: "USD"`, mesmo que o resto do catálogo esteja em reais, pesos ou outra moeda. Não dá para distingui-lo de um produto grátis: trate o `0` como «a orçar» e ignore `currency` nesse caso.

**Com coexistência os carrinhos chegam do mesmo jeito.** O catálogo é gerenciado no app WhatsApp Business: a Meta não permite administrá-lo por API para este tipo de empresa (o erro dela diz «SMB business type»).

## Exemplos

### cURL

```bash
# Nothing to call: this is what WAIA Connect POSTs to your webhook for a cart.
# {
#   "id": "evt_7f3a1b2c9d4e5f6a7b8c9d0e1f2a3b4c",
#   "type": "message.received",
#   "version": "1",
#   "createdAt": "2026-10-13T01:43:06.000Z",
#   "connection": { "id": "conn_4eede070e5a84d1590bdce2ea1d837bc", "phoneNumber": "+5493510000000", "externalId": "almacen-sur" },
#   "sequence": 12346,
#   "data": {
#     "message": {
#       "from": "5493511234567",
#       "id": "wamid.HBgL…",
#       "timestamp": "1791855786",
#       "type": "order",
#       "order": {
#         "catalog_id": "100200300400500",
#         "text": "¿Me lo mandan el sábado?",
#         "product_items": [
#           { "product_retailer_id": "yerba-1kg", "quantity": 2, "item_price": 4500, "currency": "ARS" },
#           { "product_retailer_id": "mate-calabaza", "quantity": 1, "item_price": 0, "currency": "USD" }
#         ]
#       }
#     },
#     "contacts": [ { "wa_id": "5493511234567", "profile": { "name": "Ana" } } ]
#   }
# }

# List the items of a saved body (item_price 0 = no price in the catalog → to be quoted):
jq -r '.data.message.order.product_items[]
  | "\(.product_retailer_id) x\(.quantity) " + (if .item_price == 0 then "to be quoted" else "\(.item_price) \(.currency)" end)' body.json
```

### Node.js

```javascript
// Inside your webhook handler, after verifying the signature.
const evt = req.body;
if (evt.type === "message.received" && evt.data.message.type === "order") {
  const order = evt.data.message.order;
  const items = order.product_items.map(i => ({
    sku: i.product_retailer_id,
    qty: i.quantity,
    // 0 = the product has no price in the catalog: to be quoted (ignore currency)
    unitPrice: i.item_price === 0 ? null : i.item_price,
    currency: i.item_price === 0 ? null : i.currency
  }));
  const comment = order.text || null; // "" when the customer wrote nothing
  await createOrder(evt.connection.externalId, evt.data.message.from, items, comment); // your code
}
```

### PHP

```php
<?php
// Inside your webhook handler, after verifying the signature.
$evt = json_decode(file_get_contents("php://input"), true);
$msg = $evt["data"]["message"] ?? [];
if ($evt["type"] === "message.received" && ($msg["type"] ?? "") === "order") {
  $items = [];
  foreach ($msg["order"]["product_items"] as $i) {
    $quote = $i["item_price"] == 0; // no price in the catalog: to be quoted (ignore currency)
    $items[] = [
      "sku" => $i["product_retailer_id"],
      "qty" => $i["quantity"],
      "unitPrice" => $quote ? null : $i["item_price"],
      "currency" => $quote ? null : $i["currency"],
    ];
  }
  $comment = $msg["order"]["text"] !== "" ? $msg["order"]["text"] : null; // "" = no comment
  create_order($evt["connection"]["externalId"], $msg["from"], $items, $comment); // your code
}
```

### Python

```python
# Inside your webhook handler, after verifying the signature.
evt = request.get_json()
msg = evt["data"].get("message", {})
if evt["type"] == "message.received" and msg.get("type") == "order":
    order = msg["order"]
    items = [
        {
            "sku": i["product_retailer_id"],
            "qty": i["quantity"],
            # 0 = the product has no price in the catalog: to be quoted (ignore currency)
            "unit_price": None if i["item_price"] == 0 else i["item_price"],
            "currency": None if i["item_price"] == 0 else i["currency"],
        }
        for i in order["product_items"]
    ]
    comment = order["text"] or None  # "" when the customer wrote nothing
    create_order(evt["connection"]["externalId"], msg["from"], items, comment)  # your code
```

## Notas

- **Enviar produtos ou o catálogo por `/v1` não está disponível hoje**: `POST /v1/messages` aceita texto, modelos e mídia.
- Um produto que a empresa envia pelo app do celular chega para você como `message.echo` (`origin: "device"`) com `type: "interactive"` e `interactive.type: "product"`. O resto de `interactive` é montado pela Meta: leia sem dar nenhum campo como certo.
- Uma mensagem que a Meta não entrega pela API chega como `message.received` com `type: "unsupported"` e o `errors[]` da Meta (por exemplo `131051`), que diz por quê. Não traz conteúdo para ler.
- Deduplique como qualquer mensagem: pelo `id` do envelope (`evt_…`) ou por `data.message.id`.
