Cobrança duplicada após retry sem idempotência

Esse erro ocorre quando a integração repete a criação de uma cobrança após timeout, falha temporária ou resposta inconclusiva, sem confirmar o resultado da tentativa anterior.

Um timeout significa que a integração não recebeu a resposta esperada. Isso não confirma que a cobrança deixou de ser criada no Asaas.

Como identificar

  • existem duas ou mais cobranças relacionadas ao mesmo pedido;
  • a duplicidade ocorreu após timeout ou falha de conexão;
  • diferentes IDs do Asaas estão vinculados à mesma operação interna;
  • o sistema não registra o resultado das tentativas anteriores;
  • não existe identificador interno ou externalReference;
  • mais de um processo pode criar a mesma cobrança simultaneamente.

Como corrigir

  1. Identifique as cobranças relacionadas à mesma operação.
  2. Compare cliente, valor, vencimento e referência.
  3. Confirme qual cobrança deve permanecer ativa.
  4. Cancele as cobranças duplicadas, quando aplicável.
  5. Relacione o ID correto ao registro interno.
  6. Analise o histórico das tentativas que causaram a duplicidade.
  7. Consulte o resultado da tentativa anterior antes de criar novamente.
  8. Implemente um identificador único para cada operação.
  9. Controle retries e execuções concorrentes.

Fluxo recomendado: registrar a tentativa → consultar o resultado → reutilizar o ID existente ou criar uma única vez → armazenar o resultado.

Fluxo de diagnóstico

1. Identificação e correção da duplicidade

%%{init: {"flowchart": {"nodeSpacing": 18,"rankSpacing": 24,"diagramPadding": 4,"padding": 7
}}}%%
flowchart TD
    A["Foi identificada uma possível cobrança duplicada"] --> B{"Há mais de uma cobrança para o mesmo pedido?"}

    B --> BSim(("Sim"))
    B --> BNao(("Não"))

    BNao --> C["Investigar outra causa da divergência"]

    BSim --> D{"O ID correto está vinculado ao registro interno?"}

    D --> DSim(("Sim"))
    D --> DNao(("Não"))
    D --> DDuvida(("Não sabe"))

    DNao --> E["Consultar o histórico e as tentativas da operação"]
    DDuvida --> E

    DSim --> F["Confirmar qual cobrança deve permanecer ativa"]
    E --> F

    F --> G{"Existem cobranças duplicadas ativas?"}

    G --> GSim(("Sim"))
    G --> GNao(("Não"))

    GSim --> H["Cancelar as cobranças duplicadas"]
    GNao --> I["Manter somente a cobrança válida"]

    H --> I
    I --> J["Registrar o resultado e revisar a regra de retry"]
    C --> J

    classDef inicio fill:#DBEAFE,stroke:#2563EB,color:#1E3A8A,stroke-width:3px
    classDef decisao fill:#FEF3C7,stroke:#D97706,color:#78350F,stroke-width:3px
    classDef correcao fill:#FFEDD5,stroke:#EA580C,color:#7C2D12,stroke-width:2px
    classDef validacao fill:#E0F2FE,stroke:#0284C7,color:#0C4A6E,stroke-width:2px
    classDef sucesso fill:#DCFCE7,stroke:#16A34A,color:#14532D,stroke-width:3px
    classDef analise fill:#FEE2E2,stroke:#DC2626,color:#7F1D1D,stroke-width:3px

    classDef respostaSim fill:#22C55E,stroke:#15803D,color:#FFFFFF,stroke-width:3px
    classDef respostaNao fill:#EF4444,stroke:#B91C1C,color:#FFFFFF,stroke-width:3px
    classDef respostaDuvida fill:#8B5CF6,stroke:#6D28D9,color:#FFFFFF,stroke-width:3px

    class A inicio
    class B,D,G decisao
    class H,I correcao
    class F validacao
    class J sucesso
    class C,E analise

    class BSim,DSim,GSim respostaSim
    class BNao,DNao,GNao respostaNao
    class DDuvida respostaDuvida

    linkStyle default stroke:#94A3B8,stroke-width:2px

    linkStyle 1,5,13 stroke:#22C55E,stroke-width:4px
    linkStyle 2,6,14 stroke:#EF4444,stroke-width:4px
    linkStyle 7 stroke:#8B5CF6,stroke-width:4px

2. Retry seguro após resposta inconclusiva

%%{init: {"flowchart": {"nodeSpacing": 18,"rankSpacing": 24,"diagramPadding": 4,"padding": 7
}}}%%
flowchart TD
    A["A tentativa terminou com timeout ou resposta inconclusiva"] --> B{"Existe identificador único da operação?"}

    B --> BSim(("Sim"))
    B --> BNao(("Não"))

    BNao --> C["Gerar identificador interno e definir externalReference"]
    C --> D["Registrar a tentativa e seu resultado"]

    BSim --> D

    D --> E{"Existe cobrança vinculada ao identificador?"}

    E --> ESim(("Sim"))
    E --> ENao(("Não"))
    E --> EDuvida(("Não sabe"))

    ESim --> F["Reutilizar o ID existente"]

    ENao --> G{"Houve timeout ou falha temporária?"}

    EDuvida --> H["Consultar histórico, referências e registros internos"]

    G --> GSim(("Sim"))
    G --> GNao(("Não"))
    G --> GDuvida(("Não sabe"))

    GSim --> H
    GDuvida --> H

    GNao --> I["Tratar o erro sem repetir a criação"]

    H --> J{"A cobrança foi localizada?"}

    J --> JSim(("Sim"))
    J --> JNao(("Não"))

    JSim --> F

    JNao --> K["Criar a cobrança uma única vez"]
    K --> L["Armazenar o ID e o resultado da tentativa"]

    L --> M["Finalizar o retry com controle de concorrência"]
    F --> M
    I --> M

    classDef inicio fill:#DBEAFE,stroke:#2563EB,color:#1E3A8A,stroke-width:3px
    classDef decisao fill:#FEF3C7,stroke:#D97706,color:#78350F,stroke-width:3px
    classDef correcao fill:#FFEDD5,stroke:#EA580C,color:#7C2D12,stroke-width:2px
    classDef validacao fill:#E0F2FE,stroke:#0284C7,color:#0C4A6E,stroke-width:2px
    classDef sucesso fill:#DCFCE7,stroke:#16A34A,color:#14532D,stroke-width:3px
    classDef analise fill:#FEE2E2,stroke:#DC2626,color:#7F1D1D,stroke-width:3px

    classDef respostaSim fill:#22C55E,stroke:#15803D,color:#FFFFFF,stroke-width:3px
    classDef respostaNao fill:#EF4444,stroke:#B91C1C,color:#FFFFFF,stroke-width:3px
    classDef respostaDuvida fill:#8B5CF6,stroke:#6D28D9,color:#FFFFFF,stroke-width:3px

    class A inicio
    class B,E,G,J decisao
    class C,D,F,K,L correcao
    class H validacao
    class M sucesso
    class I analise

    class BSim,ESim,GSim,JSim respostaSim
    class BNao,ENao,GNao,JNao respostaNao
    class EDuvida,GDuvida respostaDuvida

    linkStyle default stroke:#94A3B8,stroke-width:2px

    linkStyle 1,7,13,20 stroke:#22C55E,stroke-width:4px
    linkStyle 2,8,14,21 stroke:#EF4444,stroke-width:4px
    linkStyle 9,15 stroke:#8B5CF6,stroke-width:4px

O primeiro fluxo orienta como identificar e corrigir cobranças já duplicadas, enquanto o segundo mostra como executar um retry sem criar uma nova cobrança indevidamente.


Como prevenir

Gere um identificador único para cada operação, registre todas as tentativas e consulte o resultado anterior antes de repetir uma criação. Limite retries automáticos e impeça execuções concorrentes para a mesma operação.

O externalReference pode ajudar a relacionar e localizar a cobrança, mas deve ser utilizado junto ao controle interno de retries e concorrência.

Timeout não significa que a cobrança não foi criada. Verifique o resultado antes de tentar novamente.



Did this page help you?