Pular para conteúdo

Receber e processar pedidos

O Gubee expõe pedidos via um modelo híbrido pull + push:

  1. Pull — filas HTTP por status, paginadas em HATEOAS (hal+json).
  2. Push — webhooks HTTP enviados quando um pedido muda de status.

A recomendação é usar os dois: webhook como camada reativa de baixa latência, e poll periódico como rede de segurança para recuperar webhooks perdidos.

Pré-requisitos

  • Catálogo sincronizado — produtos precisam existir para que os itens do pedido referenciem SKUs válidos.
  • Estoque e preço atualizados — o pedido só "cai na fila" quando o pagamento é confirmado pelo marketplace.

Lifecycle do pedido

                         ┌──────────┐
                         │ CREATED  │ ← pedido criado no marketplace
                         └────┬─────┘
                              │ marketplace confirma pagamento
                         ┌──────────┐
              ┌──────────│   PAID   │
              │          └────┬─────┘
              │               │ PUT /orders/invoiced/{orderId}
              │               ▼
              │          ┌──────────┐
              │          │ INVOICED │ ← NF emitida
              │          └────┬─────┘
              │               │ PUT /orders/shipped/{orderId}
              │               ▼
              │          ┌──────────┐
              │          │ SHIPPED  │ ← etiqueta gerada, transporte iniciado
              │          └────┬─────┘
              │               │ PUT /orders/delivered/{orderId}/{shipmentCode}
              │               ▼
              │          ┌──────────┐
              │          │ DELIVERED│
              │          └──────────┘
              │ marketplace cancela
         ┌──────────┐
         │ CANCELED │ ← pode sair de CREATED, PAID, INVOICED, SHIPPED, DELIVERED
         └──────────┘

                       ┌──────────┐
         entrega ─────►│ RETURNED │ ← PUT /orders/returned/{orderId}?marketplaceStatus=
                       └──────────┘

Filas disponíveis para consumo via GET:

Status Quando entra na fila
CREATED Pedido criado no marketplace (ainda não pago).
PAID/PAYED Pagamento confirmado. Ambos aceitos (aliases).
INVOICED NF emitida e confirmada via PUT.
SHIPPED Pedido despachado via PUT.
DELIVERED Pedido entregue ao destinatário via PUT.
CANCELED Pedido cancelado (manual ou pelo marketplace).
REJECTED Update rejeitado pelo marketplace (30 dias de histórico).

Consumo via fila (pull)

GET /integration/orders/queue/{status} retorna pedidos paginados em PagedModel<EntityModel<OrderApiDTO>> (HATEOAS). Pedidos permanecem na fila até serem explicitamente removidos via DELETE — idempotentes no GET.

Request

curl 'https://api.gubee.com.br/integration/orders/queue/PAID?page=0&size=50&sort=createdDt,asc' \
  -H 'Authorization: Bearer $TOKEN' \
  -H 'Accept: application/hal+json'

Response

{
  "_embedded": {
    "orderApiDTOList": [
      {
        "id": "MLB1234567890",
        "status": "PAID",
        "totalAmount": 4999.90,
        "currency": "BRL",
        "createdDt": "2026-07-07T10:30:00Z",
        "paidDt": "2026-07-07T10:31:15Z",
        "customer": {
          "name": "Maria Silva",
          "email": "maria.silva@example.com",
          "documents": [{ "type": "CPF", "value": "12345678900" }],
          "phones": [{ "type": "MOBILE", "number": "11987654321", "countryCode": "55" }],
          "address": {
            "street": "Avenida Paulista", "number": "1578",
            "district": "Bela Vista", "city": "São Paulo",
            "state": "SP", "zipCode": "01310-200", "country": "Brasil"
          }
        },
        "items": [
          {
            "skuId": "sku-uuid-1001-blk",
            "sku": "PHONE-1001-BLK",
            "name": "Samsung Galaxy S24 256GB Preto",
            "qty": 1,
            "unitPrice": 4999.90,
            "totalPrice": 4999.90
          }
        ],
        "shipments": [],
        "_links": {
          "self": { "href": "https://api.gubee.com.br/integration/orders/MLB1234567890" }
        }
      }
    ]
  },
  "_links": {
    "first": { "href": "...?page=0&size=50" },
    "self":  { "href": "...?page=0&size=50" },
    "next":  { "href": "...?page=1&size=50" },
    "last":  { "href": "...?page=3&size=50" }
  },
  "page": {
    "size": 50, "totalElements": 187, "totalPages": 4, "number": 0
  }
}

HATEOAS

O campo _links contém URIs prontas para navegação (first, self, next, last). Clientes HATEOAS descobrem próximas páginas sem construir URLs.

Query params

Parâmetro Descrição
page Página (0-indexed).
size Itens por página (1-100). Default 20.
sort campo,asc ou campo,desc. Ex.: createdDt,asc.

ACK — remover pedido da fila

DELETE /integration/orders/queue/{status}/{orderId} marca o pedido como consumido. Idempotente — chamar de novo retorna 200 (ou 404 se já removido).

Semântica at-least-once: o mesmo pedido pode aparecer mais de uma vez na fila se você não der ACK. Processe de forma idempotente (use o orderId como chave de deduplicação).

curl -X DELETE 'https://api.gubee.com.br/integration/orders/queue/PAID/MLB1234567890' \
  -H 'Authorization: Bearer $TOKEN'

Fluxo recomendado por pedido

GET /queue/PAID
  └── para cada order:
      1. Verifica idempotência no ERP (já processado?)
      2. Salva no ERP (transação local)
      3. DELETE /queue/PAID/{orderId} (ack)
      4. Prossegue para próxima transição (invoice, ship, etc.)

Nunca inverta 2 e 3: ACK antes de persistir localmente gera perda de pedido se o ERP cair entre os dois passos.

Fila de rejeições (REJECTED)

Updates que o marketplace rejeitou (geralmente shipped, delivered, canceled) ficam acessíveis por 30 dias:

curl 'https://api.gubee.com.br/integration/orders/queue/rejected?page=0&size=50' \
  -H 'Authorization: Bearer $TOKEN'

Response (RejectedOrderDTO):

{
  "_embedded": {
    "rejectedOrderDTOList": [
      {
        "orderId": "MLB1234567890",
        "status": "SHIPPED",
        "rejectedDt": "2026-07-07T11:00:00Z",
        "reason": "invalid tracking code format",
        "attemptedPayload": { "...": "..." }
      }
    ]
  },
  "page": { "size": 50, "totalElements": 3, "totalPages": 1, "number": 0 }
}

Tratamento

  1. Leia o reason para corrigir o payload.
  2. Re-envie o PUT correspondente (ex.: PUT /orders/shipped/{orderId}) com o payload corrigido.
  3. Dê ACK em /queue/rejected/{orderId} após corrigir.

Transições de lifecycle (PUT)

Cada transição avança o pedido para o próximo estado. A tentativa de pular estados (ex.: CREATED direto para SHIPPED) é rejeitada.

Faturar — PUT /integration/orders/invoiced/{orderId}

Aplica-se a pedido PAID. Anexa a NF e move para INVOICED.

curl -X PUT 'https://api.gubee.com.br/integration/orders/invoiced/MLB1234567890' \
  -H 'Authorization: Bearer $TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "line": "NFe",
    "issueDate": "2026-07-07T12:00:00",
    "danfeXml": "<nfe>...</nfe>",
    "danfeLink": "https://storage.gubee.com.br/danfe/MLB1234567890.pdf",
    "key": "NFe35260707891234000111150010000012341123456789",
    "number": "123451"
  }'

InvoiceOrderApiDTO:

Campo Tipo Obrigatório Notas
line string sim Modelo: NFe, NFSe.
issueDate LocalDateTime sim Data de emissão.
danfeXml string não XML da NF.
danfeLink string não URL de download da DANFE.
key string sim* Chave de acesso (44 dígitos).
number string sim* Número da NF.

*Se você enviar um valor com 44+ caracteres em key ou number, o backend identifica automaticamente qual é a chave e qual é o número. São intercambiáveis na prática.

Despachar — PUT /integration/orders/shipped/{orderId}

Aplica-se a pedido INVOICED.

curl -X PUT 'https://api.gubee.com.br/integration/orders/shipped/MLB1234567890' \
  -H 'Authorization: Bearer $TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "code": "SHIPPED",
    "invoiceKey": "NFe35260707891234000111150010000012341123456789",
    "transport": {
      "name": "Correios",
      "trackingCode": "ON123456789BR",
      "method": "SEDEX",
      "type": "CARRIER"
    },
    "items": [
      {
        "skuId": "sku-uuid-1001-blk",
        "qty": 1
      }
    ],
    "tracks": [
      { "number": "ON123456789BR", "url": "https://rastreio.correios.com.br/ON123456789BR" }
    ],
    "estimatedDeliveryDt": "2026-07-12T18:00:00"
  }'

ShipmentOrderApiDTO:

Campo Tipo Obrigatório Notas
code string sim Código interno do shipment (uuid do grupo).
invoiceKey string não* Obrigatório se houver múltiplas NFs.
transport TransportOrderApiDTO sim Transportadora + tracking.
items Set<ItemOrderDTO> sim Itens incluídos neste shipment.
tracks Set<TrackingOrderDTO> não Tracking numbers individuais.
estimatedDeliveryDt LocalDateTime sim Estimativa de entrega.

*Se invoiceKey for omitido e houver apenas 1 NF no pedido, o backend usa automaticamente a chave da única NF existente.

Entregar — PUT /integration/orders/delivered/{orderId}/{shipmentCode}

Aplica-se a pedido SHIPPED. {shipmentCode} é o code usado no shipment.

curl -X PUT 'https://api.gubee.com.br/integration/orders/delivered/MLB1234567890/SHIPPED' \
  -H 'Authorization: Bearer $TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "deliveredDt": "2026-07-12T14:30:00Z"
  }'

Cancelar — PUT /integration/orders/cancel/{orderId}

Pode partir de CREATED, PAID, INVOICED, SHIPPED ou DELIVERED. Nem todos marketplaces aceitam cancelamento em qualquer estágio.

curl -X PUT 'https://api.gubee.com.br/integration/orders/cancel/MLB1234567890' \
  -H 'Authorization: Bearer $TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "cancelDt": "2026-07-07T15:00:00Z"
  }'

Devolver — PUT /integration/orders/returned/{orderId}?marketplaceStatus=...

Marca o pedido como devolvido. O parâmetro marketplaceStatus carrega o motivo/status reportado pelo marketplace.

curl -X PUT 'https://api.gubee.com.br/integration/orders/returned/MLB1234567890?marketplaceStatus=RETURNED' \
  -H 'Authorization: Bearer $TOKEN'

Marcar como pago (sandbox) — PUT /integration/orders/paid/{orderId}?marketplaceStatus=...

Sandbox-only. Em produção, o status PAID só vem via webhook do marketplace.

curl -X PUT 'https://api.gubee.com.br/integration/orders/paid/MLB1234567890?marketplaceStatus=PAID' \
  -H 'Authorization: Bearer $TOKEN' \
  -H 'Content-Type: application/json' \
  -d '[]'

Body opcional: Set<PaymentApiDTO> com detalhes do pagamento.

Push via webhooks

Para baixa latência, configure webhooks em Eventos de webhook. O Gubee faz POST HTTP para a URL cadastrada quando o pedido muda de status.

Recuperação de webhooks perdidos

Se seu endpoint ficar indisponível, o Gubee armazena as notificações perdidas por 7 dias. Recupere via:

curl 'https://api.gubee.com.br/integration/notifications/missed/{urlName}?page=0&size=100' \
  -H 'Authorization: Bearer $TOKEN'

urlName é o nome cadastrado no painel de webhooks. Após processar cada notificação perdida, marque como lida:

curl -X DELETE 'https://api.gubee.com.br/integration/notifications/missed/{notificationId}' \
  -H 'Authorization: Bearer $TOKEN'

Estratégia de polling

worker ──► GET /queue/PAID (page=0, size=50)
            ├── se vazio: sleep 30s, repetir
            ├── se cheio:
            │   ├── para cada order:
            │   │   ├── persiste local
            │   │   ├── DELETE /queue/PAID/{orderId}
            │   │   └── dispara próxima transição (invoice async)
            │   └── se page.next existe: continua imediatamente
            └── escala workers horizontalmente particionando por seller

Intervalo recomendado

Volume diário (por seller) Intervalo de poll
< 100 pedidos 2 minutos
100-1000 30 segundos
1000-10000 10 segundos
> 10000 5 segundos + múltiplos workers por status

Anti-patterns

  • Poll agressivo (< 5s) sem volume real: gera rate limit e aquecimento desnecessário.
  • ACK antes de persistir: perda de pedido em crash.
  • Processamento síncrono dentro do loop de poll: bloqueia o worker. Use fila interna (uma fila interna (RabbitMQ, SQS, Redis Streams, etc)) para desacoplar consumo de processamento.

Idempotência

Operação Idempotente por
GET /queue/{status} Sempre (pode repetir).
DELETE /queue/.../{id} Sim — segundo DELETE = 404 esperado.
PUT /orders/invoiced/.. orderId — re-envio do mesmo payload atualiza.
PUT /orders/shipped/.. orderId + code (uuid do shipment).
PUT /orders/cancel/.. orderId — cancelamento de pedido já cancelado é no-op.

Tratamento de erros comuns

Status Causa Ação
400 Transição inválida (ex.: CREATEDSHIPPED) Siga o lifecycle — passe por PAID e INVOICED antes.
404 Pedido não encontrado ou status não aplicável Re-leia o pedido via GET /orders/{orderId}.
409 Conflito de estado (raro) Re-leia e reconcilie.
422 Payload inválido (ex.: key mal formatada) Corrija e refaça.

Próximos passos