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
- Identify the charges related to the same operation.
- Compare customer, amount, due date, and reference.
- Confirm which charge should remain active.
- Cancel the duplicate charges, when applicable.
- Link the correct ID to the internal record.
- Analyze the history of the attempts that caused the duplication.
- Check the result of the previous attempt before creating again.
- Implement a unique identifier for each operation.
- 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.
Updated 2 days ago
