Skip to main content
Você está lendo o contrato atual (v1.2) de order.completed. v1.2 adiciona data.fiscalRepresentation: a numeração fiscal que o ponto de venda obteve antes de injetar o pedido. Viaja sempre: em null quando não se tentou numerar, e preenchido quando sim — numberingStatus diz como terminou. Trazer conteúdo não significa que o comprovante esteja autorizado. Apenas aditivo — nada do que você já lia mudou.O bloco carrega o documento fiscal vigente do pedido: os campos que significam o mesmo em qualquer país no topo, os identificadores do órgão dentro de countryData no vocabulário do seu país, e os documentos anteriores em history. Quando um cancelamento numera, a nota de crédito passa ao topo e a de venda desce para o histórico — com compensates apontando para ela.
order.completed dispara quando um pedido é injetado com sucesso e está pago. Carrega o snapshot V4 do pedido como trigger.data — todos os campos que seu flow precisa para agir sobre o pedido sem voltar a chamar o Fire.

Condição de disparo

O Fire emite order.completed exatamente uma vez por pedido, na primeira vez em que ambos são verdadeiros no momento de injeção:
  • order.status === "COMPLETED"
  • order.paymentStatus === "SUCCEEDED"
Pedidos que ainda estão PENDING de pagamento, ou que falham o pagamento, nunca produzem order.completed. Cancelamentos após completar produzem um evento separado order.cancelled — eles não retraem order.completed.

O que tem em trigger.data

trigger.data é o snapshot V4 do pedido — o mesmo objeto que está persistido em flow_queue.trigger_data e exposto aos templates do seu flow. As chaves top-level, em ordem:

Exemplo — payload real de produção (BR, sanitizado)

O exemplo abaixo vem de uma linha real de flow_queue.trigger_data (tenant sandbox brasileiro, canal KIOSK, serviço dine-in). Os campos PII são substituídos por placeholders; o restante dos campos e formatos é verbatim.
Os valores monetários são strings com o valor inteiro escalado ×10.000 — quatro casas decimais fixas. "229000" é 22,9 BRL, não 229.000. Para ler, divida por 10.000.É a escala com que o FIRE armazena: evita o drift de ponto flutuante ao somar impostos através de várias integrações. Parseie com uma biblioteca decimal, nunca com parseFloat.A exceção é paymentMethods[].totalBill, que a origem às vezes envia como número JSON — trate os dois casos.
Não é a escala de todos os caminhos. A requisição de numeração fiscal leva os valores sem escalar, tal como foram cobrados. Se você tirar um valor deste evento para declará-lo ao órgão, divida antes.

Referência de campos

Identificadores top-level

string
UUID interno do pedido no Fire. Estável através de entregas; use junto com event.id para rastreabilidade.
string | null
Código curto legível mostrado em recibos e telas KDS (ex.: 95K, OC-br-001). null quando o canal não atribui um.
string
Dia de negócio ao qual este pedido pertence, em YYYY-MM-DD. Calculado em hora local da loja, então um pedido feito às 01:00 pode pertencer ao dia de negócio anterior dependendo do corte de fim de dia.
string
O ID de pedido tal como provido pelo canal/agregador na injeção. Use para reconciliar com sistemas upstream (POS, dashboards de agregador).
string | null
Timestamp ISO 8601 UTC de quando o pedido foi originalmente feito. Distinto de event.createdAt, que é quando a execução do flow começou.
string
Sempre "COMPLETED" para este evento.
string
Sempre "SUCCEEDED" para este evento.
boolean
true se pontos de fidelidade foram resgatados neste pedido.
boolean
true se o cliente acumulou pontos de fidelidade.
boolean
true se algum desconto foi aplicado.
string
Nota free-text do cliente para o pedido inteiro. Empty string quando não definido.

data.store

object
Snapshot da loja no momento em que o pedido foi completado.

data.client

object | null
Cliente que fez o pedido. null para pedidos de canal totalmente anônimos. Quando a venda não identifica o comprador —em qualquer país— client trafega populado com o marcador de consumidor final: govIdType: "FINAL_CONSUMER" e govIdNumber em zeros. Não chega traduzido para a regra de cada regime — o NIT genérico da DIAN, por exemplo, é resolvido pelo provedor fiscal.

data.payments

object
Detalhamento de dinheiro.

data.fulfillment

object
Como o pedido é entregue.

data.kds

object
Contexto do kitchen display.

data.device e data.operator

object
{ uid, name, platform, metadata.ip } — dispositivo de origem. Os campos podem ser null para canais não físicos.
object
{ uid, name, session.uid } — staff/caixa que processou o pedido. Todos os campos null para canais self-service (quiosque, web).

data.orderLines

object[]
Produtos pedidos. Totalmente camelCase (transformado pelo builder V4).

data.marketing, data.metadata, data.channel

object | null
Loyalty + cupons. null na maioria dos países hoje; reservado para uso futuro.
object
Bag free-form para extras a nível de pedido. Frequentemente {}.
object
{ uid, code, metadata }. Exemplos de code: KIOSK, APP, IFOOD, RAPPI.

Dados fiscais

Os dados fiscais são incluídos apenas quando a loja tem emissão fiscal habilitada (store.storeFiscalConfig.enabled === true). Para países sem fiscal ou lojas sem configuração, todas as três localizações abaixo estão ausentes ou em null.
order.completed carrega informação fiscal em três localizações distintas. Cada uma serve um propósito diferente:

1. data.store.storeFiscalConfig — identidade do emissor e config do provedor

Identifica a entidade legal que emite o documento e como autenticar com o provedor fiscal. Credenciais NÃO estão aqui intencionalmente — o nó fiscal as busca por provedor/account.

2. data.payments.metadata.fiscal — agregados fiscais a nível de pedido

Totais agregados estilo SEFAZ, prontos para envio ao provedor fiscal (seu provedor fiscal no Brasil). Os valores são strings escaladas ×10.000, igual ao resto do evento.

3. data.orderLines[n].metadata.fiscal — classificação fiscal por linha

Códigos fiscais por produto. Usados pelo provedor fiscal para classificar cada linha no documento.
Além disso, metadata por imposto vive dentro de cada taxes[n].metadata (em payments.totals[].taxes[], orderLines[].price.totalPrice[].taxes[] e orderLines[].lineTotals[].taxes[]) com códigos como cst, cBenef, cClassTrib, reducao, rateNominal, rateEffective.

Variações por país

O exemplo acima é de uma loja brasileira — o caso mais complexo. O formato V4 é idêntico em todos os países, incluindo o detalhamento granular de taxes[]: cada país popula payments.totals[].taxes[] e os taxes por linha com seus impostos locais no mesmo formato { base, name, rate, amount, metadata }. O que muda por país:
  • Nomes de imposto — BR usa ICMS, PIS, COFINS, IBS_UF, IBS_MUN, CBS; outros países carregam seus impostos locais (ex. IVA) com a mesma estrutura.
  • Códigos de metadata — BR carrega códigos SEFAZ (cst, cBenef, cClassTrib, reducao, rateNominal, rateEffective); outros países seus próprios códigos.
  • Emissão fiscal SEFAZ — apenas Brasil. payments.metadata.fiscal, orderLines[].metadata.fiscal (ncm/cfop/csosn) e os eventos order.invoiced / order.reversed só aplicam ao BR. Os demais países ainda carregam seu taxes[], mas esses blocos SEFAZ estão ausentes.
As lojas brasileiras com storeFiscalConfig.enabled === true carregam o payload fiscal completo — veja a seção Dados fiscais acima. Marcadores de país:
  • store.locationInfo.country.code: "BR" · name: "Brasil" · timezone: "America/Sao_Paulo"
  • store.locationInfo.currencyCode: "BRL"
  • store.storeFiscalConfig.govIdType: "CNPJ" (14 dígitos)
  • store.storeFiscalConfig.secondaryGovIdType: "INSCRICAO_ESTADUAL"
  • payments.totals[].currencyCode: "BRL", paymentMethods[].currencyCode: "BRL", orderLines[].selectedCurrency: "BRL"
  • Populados: payments.metadata.fiscal (vBC / vNF / vICMS / vPIS / vCOFINS / vTotTrib …), orderLines[].metadata.fiscal (ncm / cfop / csosn), lineTotals[].taxes[] (ICMS, PIS, COFINS, IBS_*)

Tabela de referência rápida

À medida que mais países tiverem um pipeline fiscal dedicado, seus eventos fiscais chegarão como fiscal.*.{cc} (ex.: fiscal.authorized.co, fiscal.authorized.ec). Até lá, apenas order.completed e order.cancelled disparam para lojas não-BR — os blocos fiscais permanecem null / vazios.

Handler de exemplo

data.policy e data.lastKnown

Todos os eventos de pedido carregam estes dois blocos, não só este. Foram adicionados junto com o ciclo de pagamento diferido e são adicionados na v1.1, aditivos: consumidores existentes continuam funcionando sem alteração.
O detalhe campo a campo está em order.opened, o evento onde estes blocos mais importam.

Erros comuns

  • Decimais como strings × 10000. payments.totals[0].total === "229000" significa 22.9 BRL. Use uma biblioteca decimal; nunca com parseFloat.
  • O casing é misto em payments.totals[] e partes de paymentMethods[]. Leia tanto currencyCode quanto currencyCode defensivamente. O builder V4 transforma a maior parte do snapshot mas passa os objetos de payment sem alteração.
  • fulfillment.delivery pode estar presente mesmo para serviços non-delivery com zeros placeholder. Sempre ramifique em fulfillment.service.code.
  • client pode ser um placeholder “FINAL_CONSUMER” populado em qualquer país — não é null. Trate govIdType === "FINAL_CONSUMER" como anônimo para analítica.
  • event.id é o ID de execução do flow, não o ID do pedido. Use event.id para idempotência (muda por entrega), e orderId como chave de negócio.
  • Routing multi-tenant. Use store.account.uid, store.vendor.uid e store.code para rotear ao tenant correto no seu sistema, mesmo que o Fire já dê escopo ao flow do lado dele.

Eventos relacionados

order.cancelled

Dispara quando este pedido é cancelado depois.

order.invoiced

Apenas Brasil — dispara quando a SEFAZ autoriza o documento fiscal do pedido.

data.fiscalRepresentation

A numeração fiscal que o ponto de venda obteve antes de injetar o pedido: cobra, pede os identificadores, imprime o comprovante e só então injeta. Por isso viaja no pedido e não em um evento fiscal separado — quando o pedido nasce, isso já aconteceu.
A presença deste bloco NÃO significa que o comprovante esteja autorizado. São os números impressos no caixa; o veredito do órgão está em lastKnown.fiscal.status. Um ticket que diga “autorizado” só porque o bloco está presente declara algo que pode não ter acontecido.
A chave viaja sempre. Chega em null quando não se tentou numerar — agregadores, países sem representação fiscal, ou comércios com a numeração desativada — e traz o bloco quando houve tentativa. Trazer o bloco significa que se tentou numerar, não que foi numerado: numberingStatus diz como a tentativa terminou, e failure por quê quando não terminou bem. Ramifique pelo valor, não pela presença da chave:
O veredicto do órgão não o altera. O que o cliente levou impresso não muda porque o órgão depois autorize ou rejeite — para isso existe lastKnown.fiscal, que é o que de fato se move. O que o substitui é um documento novo. O bloco carrega o documento fiscal vigente do pedido. Enquanto houve apenas um, era sempre a nota de venda; quando um cancelamento produz uma nota de crédito, é ela que fica no topo — documentType diz qual é — e a nota de venda desce para history, inteira e com seus próprios identificadores do órgão. Não se perde: se move. compensates aponta para ela pelo número, então a relação fica explícita.

Quando a numeração falha

Uma venda pode ser cobrada e ficar sem comprovante fiscal. Esse caso também viaja, e precisa ser tratado: os identificadores vêm null e o motivo em failure.
Ramifique por failure.scope:
  • TECHNICAL — imprima “em trâmite” e siga. Pode se resolver sozinho.
  • FUNCTIONAL — há um dado errado e repetir não resolve. Precisa correção. O que muda é lastKnown.fiscal.
lastKnown.fiscal.sourceEvent agora informa a procedência real. Antes era deduzida do status, e um processing semeado na injeção era reportado como fiscal.callback sem que nenhum callback tivesse ocorrido. Esse caso agora diz order.injected. Se você ramifica por este campo, contemple o valor novo.