Impressão fiável de encomendas: idempotência, repetições e impressora offline

Imprimir parece um disparar e esquecer até ao primeiro talão duplicado. Três decisões cobrem quase todos os incidentes em produção: responder primeiro ao webhook, dar chave ao trabalho e definir a política offline.

Uma chamada de impressão cabe num pedido HTTP, o que faz a impressão de encomendas parecer a integração mais simples do roteiro. Continua simples até à noite em que um cliente recebe dois talões por uma encomenda, ou até à noite em que quarenta encomendas nunca chegam à cozinha e ninguém dá por isso antes de fechar.

Resposta curta: três decisões evitam quase todos os incidentes em produção. Responda ao webhook antes de imprimir, dê a cada trabalho uma chave estável para que uma repetição seja detetável, e decida explicitamente o que acontece quando a impressora está offline. Nenhuma é difícil, e as três acrescentam-se mais facilmente agora do que depois do primeiro incidente.

Os três modos de falha

Sintoma Causa real Correção
O mesmo talão sai duas vezes A plataforma repetiu um webhook lento, ou um estado foi reaplicado Uma chave estável por encomenda
Encomendas nunca impressas e ninguém soube A impressora estava offline e a falha foi engolida Uma política offline explícita mais um alerta
O checkout parece lento, ou expira A chamada de impressão corre dentro do pedido que confirma a encomenda Responder primeiro, imprimir depois

Os três estão ligados. Uma chamada de impressão lenta provoca a repetição que provoca o duplicado, por isso corrigir o primeiro elimina a maior parte do segundo.

1. Responder ao webhook antes de imprimir

O Shopify, o WooCommerce, o Stripe e qualquer plataforma séria repetem quando o seu endpoint está lento ou devolve um erro. Se o seu handler imprime de forma síncrona e a API de impressão demora dois segundos, construiu um gerador de repetições.

export default async function handler(req, res) {
  const order = req.body;

  // Confirmar de imediato: a plataforma deixa de repetir.
  res.status(200).end();

  // Depois imprimir, fora do pedido que a plataforma esta a aguardar.
  await printOrder(order).catch((err) => {
    logger.error({ err, orderId: order.id }, 'falha de impressao');
    alertOps(order);
  });
}

A regra é que o orçamento de timeout da plataforma e a latência da sua impressora nunca devem partilhar um prazo. É a mesma razão pela qual os conectores Shopify e WooCommerce não imprimem dentro da transação do webhook.

2. Dar chave a cada trabalho

A idempotência aqui não é um luxo de sistemas distribuídos. Uma encomenda pode legitimamente despoletar o seu handler mais do que uma vez: um callback de gateway que chega duas vezes, um comerciante que reaplica um estado pago à mão, uma repetição de webhook que correu contra a sua confirmação.

Ponha um valor estável em origin, e guarde uma marca de impresso do seu lado:

async function printOrder(order) {
  const key = `encomenda-${order.id}`;
  if (await alreadyPrinted(key)) return;

  await fetch(`https://www.expedy.fr/api/v2/printers/${PRINTER_UID}/print`, {
    method: 'POST',
    headers: {
      Authorization: `${process.env.API_SID}:${process.env.API_TOKEN}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ printer_msg: buildTicket(order), origin: key }),
  });

  await markPrinted(key);
}

Duas coisas ganham o seu lugar aqui. A marca local trava o duplicado antes de chegar à rede, e o origin torna visível no histórico de impressão um duplicado que escapou, o que transforma "acho que imprimiu duas vezes" numa pergunta a que consegue responder.

Escreva a marca na mesma transação que o resto do tratamento da encomenda, se puder. Uma marca escrita depois de uma chamada HTTP bem-sucedida deixa uma janela pequena, e é exatamente dessa janela que saem os talões a dobrar.

3. Escolher a política offline de propósito

Pergunte o que deve acontecer quando a impressora fica sem papel, está desligada, ou está atrás de um router que reiniciou. Só há três respostas sensatas, e a errada é não escolher:

  • Pôr em fila e entregar mais tarde. Correto para etiquetas de envio e documentos de armazém, onde um talão impresso vinte minutos depois ainda serve.
  • Repetir por pouco tempo, depois alertar. Correto para talões de cozinha, onde um talão impresso vinte minutos depois é pior do que inútil porque o serviço já avançou.
  • Alertar de imediato. Correto quando uma pessoa pode agir, e a única opção que transforma uma perda silenciosa num problema conhecido.

Escolha o que escolher, implemente o alerta. A diferença entre um incómodo pequeno e uma má noite não é se a impressora falhou, é se alguém soube.

O que não deve construir

As equipas reconstroem por rotina maquinaria que a API já fornece. Antes de escrever uma fila, um agendador e uma escada de repetições, veja que garantias de entrega já tem: uma API push que retém o trabalho e o entrega quando o equipamento se volta a ligar dispensa a maior parte desse código. Construa as partes que codificam as suas regras de negócio, como que impressora recebe que encomenda, e deixe a fiabilidade do transporte ao transporte.

Testar antes de produção

Três testes apanham a maior parte do que chega a produção:

  1. Repita o mesmo webhook duas vezes. Deve sair exatamente um talão.
  2. Desligue a impressora e envie uma encomenda. A política escolhida deve acontecer de forma visível, alerta incluído.
  3. Encomende um produto com um emoji no nome. As impressoras térmicas falam ESC/POS; translitere antes de enviar, ou vai descobri-lo no pior momento. A referência de paginação documenta o que o formato aceita.

Faça os três contra uma impressora real, não contra um mock. As falhas interessantes são físicas.

Crie uma conta gratuita e ligue os três testes ao seu ramo de integração antes de a primeira encomenda de cliente passar por ele.

A ler também: imprimir encomendas Shopify e encomendas WooCommerce mostram onde ficam estes ganchos em cada plataforma.

FAQ

Porque é que a mesma encomenda às vezes imprime duas vezes?

Quase sempre porque a plataforma repetiu um webhook a que o seu endpoint respondeu devagar, ou porque um estado pago foi aplicado mais do que uma vez. Confirme o webhook antes de imprimir e dê a cada trabalho uma chave estável como o id da encomenda.

Devo imprimir dentro do handler do webhook?

Não. Responda 200 de imediato e depois imprima fora desse pedido. Partilhar um prazo entre o timeout da plataforma e a latência da impressora é o que gera as repetições, e as repetições geram os duplicados.

O que deve acontecer quando a impressora está offline?

Escolha uma política de propósito: pôr em fila e entregar mais tarde, repetir por pouco tempo e depois alertar, ou alertar de imediato. A fila serve para etiquetas de envio, o alerta para talões de cozinha. A única resposta errada é deixar por definir.

Preciso de construir a minha própria fila de impressão?

Normalmente não. Uma API push que retém o trabalho e o entrega quando o equipamento se volta a ligar já faz isso. Escreva o código que codifica as suas regras de negócio, como o encaminhamento para a impressora, não uma segunda camada de transporte.

Como testo a fiabilidade da impressão antes de ir para produção?

Repita um webhook duas vezes esperando um único talão, desligue a impressora e confirme que a política offline e o alerta disparam, e encomende um produto com um emoji no nome. Faça os três contra uma impressora real.