Duplicate charge after retry without idempotency

This error occurs when the integration repeats the creation of a charge after a timeout, temporary failure, or inconclusive response, without confirming the result of the previous attempt.

A timeout means the integration did not receive the expected response. It does not confirm that the charge was not created in Asaas.

How to identify

  • there are two or more charges related to the same order;
  • the duplication occurred after a timeout or connection failure;
  • different Asaas IDs are linked to the same internal operation;
  • the system does not record the result of previous attempts;
  • there is no internal identifier or externalReference;
  • more than one process can create the same charge simultaneously.

How to fix

  1. Identify the charges related to the same operation.
  2. Compare customer, amount, due date, and reference.
  3. Confirm which charge should remain active.
  4. Cancel the duplicate charges, when applicable.
  5. Link the correct ID to the internal record.
  6. Analyze the history of the attempts that caused the duplication.
  7. Check the result of the previous attempt before creating again.
  8. Implement a unique identifier for each operation.
  9. Control retries and concurrent executions.

Recommended flow: record the attempt → check the result → reuse the existing ID or create only once → store the result.

Diagnostic flow

1. Identifying and fixing the duplication

%%{init: {"flowchart": {"nodeSpacing": 18,"rankSpacing": 24,"diagramPadding": 4,"padding": 7
}}}%%
flowchart TD
    A["A possible duplicate charge was identified"] --> B{"Is there more than one charge for the same order?"}

    B --> BSim(("Yes"))
    B --> BNao(("No"))

    BNao --> C["Investigate another cause of the mismatch"]

    BSim --> D{"Is the correct ID linked to the internal record?"}

    D --> DSim(("Yes"))
    D --> DNao(("No"))
    D --> DDuvida(("Not sure"))

    DNao --> E["Check the operation history and attempts"]
    DDuvida --> E

    DSim --> F["Confirm which charge should remain active"]
    E --> F

    F --> G{"Are there active duplicate charges?"}

    G --> GSim(("Yes"))
    G --> GNao(("No"))

    GSim --> H["Cancel the duplicate charges"]
    GNao --> I["Keep only the valid charge"]

    H --> I
    I --> J["Record the result and review the retry rule"]
    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. Safe retry after an inconclusive response

%%{init: {"flowchart": {"nodeSpacing": 18,"rankSpacing": 24,"diagramPadding": 4,"padding": 7
}}}%%
flowchart TD
    A["The attempt ended with a timeout or inconclusive response"] --> B{"Is there a unique operation identifier?"}

    B --> BSim(("Yes"))
    B --> BNao(("No"))

    BNao --> C["Generate an internal identifier and set externalReference"]
    C --> D["Record the attempt and its result"]

    BSim --> D

    D --> E{"Is there a charge linked to the identifier?"}

    E --> ESim(("Yes"))
    E --> ENao(("No"))
    E --> EDuvida(("Not sure"))

    ESim --> F["Reuse the existing ID"]

    ENao --> G{"Was there a timeout or temporary failure?"}

    EDuvida --> H["Check history, references, and internal records"]

    G --> GSim(("Yes"))
    G --> GNao(("No"))
    G --> GDuvida(("Not sure"))

    GSim --> H
    GDuvida --> H

    GNao --> I["Handle the error without repeating the creation"]

    H --> J{"Was the charge found?"}

    J --> JSim(("Yes"))
    J --> JNao(("No"))

    JSim --> F

    JNao --> K["Create the charge only once"]
    K --> L["Store the ID and the attempt result"]

    L --> M["Finish the retry with concurrency control"]
    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

The first flow explains how to identify and fix charges that are already duplicated, while the second shows how to perform a retry without wrongly creating a new charge.


How to prevent

Generate a unique identifier for each operation, record all attempts, and check the previous result before repeating a creation. Limit automatic retries and prevent concurrent executions for the same operation.

The externalReference can help link and locate the charge, but it should be used together with internal control of retries and concurrency.

A timeout does not mean the charge was not created. Check the result before trying again.



Did this page help you?