#Integrations & Automation

Your endpoint is idempotent. The payment still needs protection.

Zulu Shack illustration for “Your endpoint is idempotent. The payment still needs protection.”: two coins and one order slip

The order table contains one order. The customer can see two charges.

Haibo! That is an uncomfortable disagreement.

An idempotent operation is designed so repeating the same request does not repeat its intended effect. The catch is deciding where that protection applies. A safeguard around your database may leave an external payment call outside the boundary.

Derek Comartin’s February 2026 discussion of hidden idempotency failures is a useful reminder to inspect that boundary.1 For a business taking payments through a custom application, the practical question is how the complete payment operation behaves when requests overlap, connections fail or work resumes.

Consider a hypothetical invoice-payment workflow. The provider accepts the payment, but the application loses the response before recording success. The customer tries again. The next step needs to account for what may already have happened.

If the provider supports idempotency, carry the same intent across

A payment provider’s idempotency mechanism can help prevent repeated requests from creating repeated payments. The application needs a stable key for the intended operation and must use it according to that provider’s documented rules.

Keep a retry tied to the original operation

Generating a fresh key on every retry tells the provider that each request is new. That defeats the purpose.

Instead, create and persist the payment operation’s identity before making the call. Retries of that same operation should use its existing identity and consistent request details. Different legitimate operations need distinct identities.

The exact scope and retention rules matter. Check the chosen API rather than assuming that every provider remembers keys indefinitely or handles changed parameters the same way.

The application also needs to prevent simultaneous local requests from independently creating separate operation records for the same intended payment. A cached response can be useful, but a separate check followed by an unprotected write can leave a race between application instances.

Connecting systems that were never introduced? See our integrations and AI work.

Give uncertainty an explicit state

A timeout tells you that a response did not arrive within the allowed time. It does not establish that no money moved.

Keep that attempt in a state that reflects the uncertainty. Use the provider’s supported retrieval or reconciliation mechanisms to establish the outcome before deciding whether a genuinely new payment attempt is appropriate.

The customer-facing message should match that state. “We are checking your payment” is more helpful than declaring failure and inviting another attempt while the first one may still be successful.

If the provider lacks that protection, examine the limits

A stable reference and a provider lookup may help establish whether a payment already exists. They do not automatically close every race.

Two workers can both check, both see no payment and both proceed. Serializing work for the relevant business operation can reduce that problem, provided every path follows the same coordination rules.

A lock still needs careful failure handling. It can expire or disappear while the remote request is unresolved. Treat it as one part of the design, with a recovery path for uncertain results.

For this workflow, I would ask the integration team to document the actual guarantees available from the provider. Where an automatic retry cannot be made safely, a visible review state may be the better decision.

That is less glamorous than a button that always appears to work, but considerably kinder to the person checking their bank account.

Worth reading next: The integration works. Until someone presses the button twice.

Keep recovery connected to the business record

An outbox can persist the intention to request payment alongside the local order state, then let a worker perform the external call. This helps the application recover pending work.

It does not, by itself, make the remote effect happen exactly once. The worker still needs the provider’s protections, stable identifiers and a way to resolve uncertain outcomes.

The operational record should connect the invoice, payment operation and provider reference. Reconciliation can then identify mismatches, with a controlled correction process when needed.

Before release, test overlapping requests and the awkward interval after provider success but before the local confirmation is saved. Verify the provider-side result as well as the application’s records.

A single order row is reassuring. A single intended charge, correctly recorded and explainable to the customer, is the result you actually want.

Turn reading into results

Like what you’re reading? Let’s put it to work on your brand.