Idempotency makes repeated processing of the same logical operation leave the intended result once. For payments, correlate receipts, enforce fulfilment uniqueness and preserve uncertain transaction outcomes. Preventing a second onchain transfer requires an enforcing mechanism; a memo or reference alone only identifies the intent.
Name the three identities before writing retry code
The business operation might be activating subscription INV-104. A transaction signature identifies one signed onchain attempt. A receipt identity identifies the specific accepted payment record, which can need an instruction index as well as the signature.
Rebroadcasting identical signed bytes preserves the same transaction identity. Signing a rebuilt message with a new blockhash creates a separate transaction. Both can represent the same intended purchase.
A reference or memo can correlate transactions with the invoice. It does not automatically make the token program reject a second payment for that invoice.
Keep these identities in separate fields. Trying to use one string as invoice key, delivery cursor and settlement proof makes ambiguous outcomes hard to recover.
Deduplicate observation without discarding real money
A webhook, subscription and history backfill can all report the same receipt. Persist a unique key for the actual record and make repeated observation update or reuse it.
Choose a teaching payment at signature S, instruction 2. Two workers discover it. The database permits one receipt row for the chosen cluster/S/2 key, so the amount enters the invoice total once.
If another signature T sends the same amount with the same reference, it is a separate transfer. Do not suppress it merely because the invoice is already paid. Record an overpayment or another explicit exception.
The exact key depends on the event model. A transaction with several legitimate transfers cannot be represented correctly by a dedup policy which discards everything after the first instruction.
Enforce one fulfilment operation in durable storage
Use a database uniqueness rule for the business action, such as one subscription-activation operation per invoice. A process-local flag cannot protect against parallel workers or restarts.
Within a database transaction, validate the invoice state, create the unique fulfilment operation and queue its work. Commit those records together. A worker can then deliver from the committed queue.
If the external service accepts an idempotency key, use the stable fulfilment-operation identity. If it does not, design an equivalent check-and-acknowledgement path appropriate to that service.
Marking an invoice fulfilled before a durable delivery record can lose work after a crash. Performing the external action before saving any operation identity can repeat work after a retry. Place the boundary deliberately.
Commit a durable delivery record before a worker acts
Preserve uncertainty when a send response disappears
Suppose a payout was signed and sent but the response was lost. Save delivery-uncertain state with the original signature and validity information, then query the actual outcome.
A local timeout or expired current blockhash does not establish that the earlier attempt never executed. Adequate status and history coverage are needed to resolve it.
Rebuilding a new attempt without that resolution can create a duplicate economic action. Backend fulfilment uniqueness cannot undo two onchain transfers that both executed.
The transaction retry guide follows this delivery boundary and explains rebroadcasting the same bytes versus creating a replacement.
An enforcing program can consume an operation ID
If the product requires the action itself to execute at most once onchain, a program can derive or locate persistent state for the operation ID and reject a previously consumed operation.
Its successful instruction must validate authorization, perform the intended action and record consumption atomically under the correct account design. Concurrent attempts must conflict through the shared state and checks.
This is a program design to implement and audit, not a feature gained by writing an ID in a memo. An ordinary token transfer does not look up your invoice database.
Account closure, reinitialization, user scoping and ID reuse can undermine the rule. Define how long the consumed-operation evidence must remain valid and who can modify it.
Exercise the failure points between stages
Test two workers processing one receipt, repeated callbacks and an out-of-order callback. The invoice total and fulfilment count should remain correct under the chosen model.
Crash after receipt insertion, after queue commit, after external action and before acknowledgement. Each restart should recover using the same durable operation identity.
Test a distinct second transfer after fulfilment. It must remain in the financial ledger as real received value with an exception path.
For onchain enforcement, test simultaneous attempts with the same operation ID, unauthorized callers and account-recreation paths. State the exact guarantee your implementation enforces instead of claiming an entire distributed workflow has magically become exactly once.
Common questions
Is an invoice reference an idempotency key?
It can identify intent, but only a mechanism which checks and enforces its uniqueness gives a once-only action guarantee.
Can backend uniqueness prevent the customer sending twice?
It can prevent duplicate fulfilment in that backend. Two distinct ordinary onchain transfers can still both execute.
Should I deduplicate by signature alone?
Choose a key matching the unit of processing. Several separate instructions or events in one transaction can need distinct identities.
Sources and documentation
- Solana transactions
- sendTransaction
- getLatestBlockhash
- getSignatureStatuses
- getTransaction
- Solana Pay transfer requests
- Stripe: webhook delivery and verification
Found an error or an outdated detail? Send a correction.
