Tempos de espera, repetições e idempotência: como não cobrar duas vezes ao cliente

PHP e Programação·· 12 visualizações· 4 min de leitura

A resposta curta: quando um pedido a uma API esgota o tempo de espera, não sabe se ele chegou. Pode ter falhado antes de sair, ou ter sido feito do outro lado e a resposta ter-se perdido no caminho. Repetir às cegas é como se cobra duas vezes um cliente ou se manda o mesmo SMS em duplicado. A defesa tem um nome, idempotência: fazer com que repetir o mesmo pedido tenha o mesmo efeito que fazê-lo uma vez. Para programadores que ligam o site a pagamentos, SMS ou outras APIs que criam coisas.

Quando repetir, e quando não

O que aconteceu Repetir? Como
O nome não resolve ou a ligação foi recusada (erro 6 ou 7 do cURL) Sim O pedido não chegou a sair. Veja os erros 28, 7 e 6 do cURL.
Tempo esgotado (erro 28) ou ligação cortada a meio Só com cuidado Não sabe se foi feito. Consulte primeiro o estado, ou repita com a mesma chave de idempotência.
429 Too Many Requests Sim, mais tarde Espere o que o cabeçalho Retry-After indicar. Veja o erro 429.
500, 502, 503 ou 504 Normalmente sim Com esperas cada vez maiores e, se o pedido cria ou cobra alguma coisa, com chave de idempotência.
400, 401, 403, 404 ou 422 Não O pedido está errado ou não é permitido. Repetir dá o mesmo erro: corrija primeiro.
409 Conflict Depende Em muitas APIs quer dizer «já existe» ou «já está em curso». Consulte antes de fazer mais alguma coisa.
200 ou 201 Não Está feito. Guarde a resposta.

Tornar uma cobrança segura contra repetições

1 Dê a cada operação um identificador seu, criado antes do primeiro envio e guardado com a encomenda: a referência da encomenda, por exemplo, ou um identificador único gerado no momento.
2 Se a API aceita chave de idempotência, envie-a, e envie sempre a mesma nas repetições. Quando ela reconhece a chave, devolve o resultado da primeira vez em vez de cobrar outra vez. O nome do campo muda de serviço para serviço (um cabeçalho Idempotency-Key em várias APIs, uma «referência externa» noutras): curl -i -X POST https://api.example.com/v1/charges \
-H "Authorization: Bearer YOUR_KEY" \
-H "Idempotency-Key: order-1042-charge" \
-H "Content-Type: application/json" \
-d '{"order": "1042", "amount": "..."}'
3 Se a API não aceita, consulte antes de repetir: pergunte pelo estado com a sua referência e só repita se tiver a certeza de que não ficou feito.
4 Repita com esperas crescentes e um máximo de tentativas, nunca num ciclo apertado. Um pouco de acaso em cada espera evita que muitos pedidos voltem todos ao mesmo tempo. Defina tempos de espera explícitos no cURL (CURLOPT_CONNECTTIMEOUT e CURLOPT_TIMEOUT) em vez de ficar com os de omissão.
5 Do seu lado, faça a base de dados recusar duplicados. Uma restrição de unicidade na coluna do identificador da transacção ou do evento recusa o segundo registo, mesmo que dois pedidos cheguem ao mesmo tempo. Faça uma cópia antes de alterar uma tabela: ALTER TABLE payments ADD UNIQUE (transaction_id);
6 O mesmo para os webhooks que recebe: o operador pode enviar o mesmo evento mais do que uma vez, sobretudo se o seu site demorou a responder. Guarde o identificador do evento e ignore os repetidos, como em receber um webhook em PHP.
O botão «Pagar» também repete. O cliente carrega duas vezes, ou volta atrás e reenvia o formulário. Desactive o botão depois do primeiro clique, mas não confie só nisso: no servidor, use a mesma referência da encomenda, para que o segundo pedido dê com o primeiro em vez de criar outra cobrança.
Registe cada tentativa com a sua referência, a hora, o código de estado e o erro do cURL. Quando um cliente disser «cobraram-me duas vezes», é esse registo que mostra se foram duas cobranças ou uma repetição que a API ignorou. Ver também chamar uma API a partir do PHP.

Os pedidos do seu site a uma API esgotam o tempo com frequência? Diga-nos o endereço da API e a hora de um caso, e vemos se a saída da hospedagem tem alguma coisa a ver.

Abrir pedido de suporte

VEJA TAMBÉM

Erro cURL 28, 7 e 6 no PHP: tempo esgotado, ligação recusada e não resolve o nome

Erro 429 (Too Many Requests): quem está a pedir demais, e o que fazer

Chamar uma API web a partir do PHP com o cURL e ler a resposta em JSON

Receber um webhook em PHP e verificar a assinatura

URL de callback ou URL de retorno: porque o cliente voltar ao site não prova o pagamento

Síncrono ou assíncrono: porque a confirmação de um pagamento ou de um SMS chega depois

PRODUTO RECOMENDADO

Alojamento de sites com cPanel

Domínio e SSL incluídos, cópias diárias e o painel que já conhece. desde 321,75 MT/mês (plano de 3 anos, com cupão)

Ver planos
Esta resposta foi útil? A sua resposta ajuda-nos a melhorar este artigo.