Hardware FixRecommendedDevice not working? Your driver may be the problemCheck updates for common hardware issues.Fix DriversOctober DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsClean PCRecommendedOne scan can reveal what keeps slowing WindowsLook for cleanup and repair opportunities.Run Scan×
Skip to content
World desk8 min

How to Prevent Duplicate Charges with an Idempotent ASP.NET Core Payment API

A safe payment retry needs more than a request header: persist a scoped operation key, enforce uniqueness, replay established outcomes, and reconcile uncertain processor results.
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

To keep a retry from creating a second payment, give each intended payment a stable operation identity and claim it in durable, shared storage before calling the processor. Enforce that claim with a database uniqueness constraint, bind it to a fingerprint of the payment request, and persist enough state to replay or recover the outcome. The processor’s own idempotency feature is a second layer—not a substitute for your API’s retry contract.

What idempotency guarantees—and what it does not

An operation is idempotent when repeating it produces the same effect as performing it once. The response does not have to be identical: Microsoft’s API guidance notes that a DELETE might first return 204 and later return 404, while the resource remains deleted. A payment-creating POST is not naturally safe to repeat, so the application must add a deduplication mechanism if retries are meant to refer to the same payment.

As an Amazon Associate I earn from qualifying purchases.

This is not exactly-once delivery. A connection can fail after a processor has accepted a charge but before your API or client receives confirmation. Neither side can infer from a timeout alone whether the remote effect happened. The practical goal is narrower and useful: requests carrying the same operation identity must not create a second logical payment, and uncertain outcomes must be reconciled rather than blindly retried as new work.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Choose the identities and contract first

Use a client key for one intended operation

Accept an Idempotency-Key header as part of the payment-creation contract. Clients should generate a random, high-entropy value for each intended payment and retain it when retrying that payment. Stripe recommends a v4 UUID or similarly random string for its API and documents a 255-character maximum; those are Stripe-specific rules, not universal requirements. Your API should publish its own accepted format and maximum length.

Scope the key to the authenticated tenant or customer and the operation. The uniqueness identity should therefore be something like (tenant, operation, key), rather than the raw key alone. This prevents unrelated callers or operation types from colliding. Authenticate and validate the request before claiming the key.

Fingerprint the payment meaning

Associate the key with a stable fingerprint of the fields that determine the payment operation—for example, amount, currency, order or payment-intent identity, and relevant payment options. Normalize those values before fingerprinting so equivalent representations have consistent semantics. Exclude transport-only details such as trace IDs. If the same scoped key arrives with a different fingerprint, reject it with a documented conflict or validation response; never reinterpret the old key as permission to charge a changed amount.

Specify replay and pending behavior

Document what a caller gets for each state: the original logical payment result when processing is complete, a defined pending response or bounded wait while it is in progress, and a clear error for key misuse. Returning a stable payment resource reference is often more useful than trying to reproduce every byte of an earlier HTTP response. Also state how long your API retains an authoritative record and what clients should do after that period.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Claim the operation atomically in ASP.NET Core

ASP.NET Core hosts the endpoint; it does not supply the durable idempotency contract. Keep HTTP handling separate from orchestration, put persistence behind an abstraction, and call the processor through a gateway interface. The key concurrency rule is that only one application instance may become the owner of a scoped key.

  1. Authenticate and validate. Resolve the caller’s tenant or customer, validate required payment fields, and compute the normalized request fingerprint. Do not create a payment record for a request that is invalid under your API’s pre-execution validation rules.
  2. Attempt to reserve the key. In a database transaction, insert an idempotency record containing the scope, key, fingerprint, state such as InProgress, and the identifiers or timestamps needed for recovery. Enforce uniqueness on the scoped key in the database. The constraint—not an in-process lock—is the arbiter when requests reach different ASP.NET Core instances.
  3. Resolve a uniqueness conflict. If the insert loses a race, load the existing record. Compare fingerprints before doing anything else. A mismatch is key misuse; a matching completed record is a replay; a matching in-progress record follows the pending policy. Do not launch a second processor request just because another web request arrived concurrently.
  4. Commit the reservation before external work. Make the ownership record durable, then call the processor outside the database transaction. Holding a database transaction open across a network call can tie up database resources and still cannot make the processor and database one atomic system.
  5. Call the processor with its own idempotency identity. Derive or durably associate a provider key with the local operation. Persist the provider’s operation or payment identifier and the result needed to resolve future retries. Do not assume the provider retains keys for as long as your API does.
  6. Finalize the local operation. Persist a terminal outcome, or a recoverable state if the outcome is uncertain, so a subsequent request can return the established payment resource rather than initiate another charge.

This is an architectural pattern, not database-specific or tested ASP.NET Core code. Select the transaction, uniqueness, and recovery details for the database, processor, and framework version you deploy. Microsoft’s Azure Architecture Center guidance supports the underlying processed-message pattern: record an identity and skip work when a duplicate is encountered.

Recover safely from timeouts and process crashes

The client timed out, but the processor may have succeeded

The caller should retry with the same key and unchanged payment semantics. Your API should find the existing operation and return its result or pending state. A canceled HTTP request is not evidence that the processor did not execute the operation; cancellation can stop local waiting while the remote request has already progressed.

The server crashed after processor acceptance

If the processor accepted a payment but the application crashed before committing local completion, keep the uncertain record. A recovery worker or later request should reconcile through the provider idempotency key and, where available, a queryable provider operation identifier before attempting any new effect. Model this as an explicit state transition—such as from an uncertain or recovery-needed state to a confirmed outcome—instead of deleting the record and allowing a fresh charge.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The processor returned an error

Do not assume every provider treats failed requests alike. Determine whether a failure happened before execution, whether a result is replayed for the same provider key, and how to query or resolve an uncertain operation. Those semantics affect whether the next action is to retry the same identity, correct the request with a new operation, or reconcile status.

Keep processor idempotency separate from your API’s guarantee

A local idempotency store protects the boundary between your client and your API; a processor key protects the separate boundary between your API and the processor. Relying only on the processor leaves your own API without a durable record for replaying its payment resource, controlling concurrent requests across your instances, or defining how long old client retries remain safe.

Concern Your API’s durable record Processor’s idempotency feature
Client-to-API retries Can identify a repeated client operation and return its established resource. Does not by itself define your API’s client-facing replay contract.
Retention and authority You choose and document the retention period and the behavior of old keys. Provider-specific; do not assume its retention matches yours.
Concurrent API instances A shared database uniqueness constraint can decide which request owns the operation. Concurrent-key behavior depends on the selected provider.
Crash between systems Stores local state for recovery and reconciliation. May let you safely repeat or query a provider operation, depending on provider semantics.
Returning an API outcome Can retain a stable payment reference or the response data your contract requires. Replay behavior and the result returned are defined by that provider.

The comparison describes responsibilities, not a claim that every provider offers identical behavior. In particular, consult the selected processor’s current documentation and SDK guidance before relying on a provider key for recovery.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Stripe’s documented behavior is an example, not a standard

Stripe’s API Reference says it saves the first request’s resulting status code and body for a key once endpoint execution begins, including a 500 response, and replays that result on later requests using the key. Stripe says it does not save an idempotent result when validation fails or when a concurrent request conflicts before execution begins. It may prune keys after they are at least 24 hours old; using a pruned key again can start a new request. Stripe also compares parameters associated with a key and errors when a later request differs. These rules apply to Stripe as documented, not to payment processors generally.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The expiry detail is especially important for recovery: your local record should continue to prevent an old client retry from accidentally creating a new payment after the processor has stopped recognizing its key. Stripe documents keys for POST requests; its GET and DELETE requests are idempotent by definition and do not need keys. Verify current provider documentation rather than carrying these exact semantics over to another processor.

Make webhook handling repeat-safe too

Payment creation is not the only place duplicate delivery matters. In a separate webhook-consumer flow, verify authenticity according to the provider’s requirements, persist the provider event identity, and make each business-state transition safe to apply again. A duplicate event should be recognized by its event identity rather than triggering the same fulfillment, ledger action, or notification twice. Microsoft’s processed-message guidance supports this general pattern; event identity, delivery guarantees, and signature requirements must be checked for the chosen provider.

Failure cases to test in your design

  • Two matching requests arrive together: only one reservation succeeds; the other observes the existing operation and follows replay or pending behavior.
  • The key is reused with a changed amount or currency: reject the request instead of mapping it to the prior payment.
  • A request fails validation before operation execution: return the validation error without claiming that a payment was created. Stripe’s handling of this case is provider-specific as described above.
  • A retry arrives while the owner is working: use the documented pending response or bounded wait; never start a parallel charge.
  • The provider operation’s outcome is uncertain: preserve the record and reconcile before initiating another effect.
  • A webhook is delivered again: recognize the stored event identity and avoid repeating the business transition.

These cases expose different boundaries—request validation, database ownership, remote execution, and event delivery. Treating them as one generic “retry” problem obscures which system must decide whether work has already happened.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More from the Wire

  1. World desk4 min
    How to Spot an AI Voice Scam Before Sending MoneyDon’t rely on how a caller sounds. Pause, call back through a known number, and verify the emergency with another trusted person before sending money.
  2. Mountain View desk4 min
    Google’s SynthID Detector: How to Check AI-Generated Images, Video and AudioGoogle’s SynthID Detector looks for an embedded watermark in supported images, video and audio. Here is what its results do—and do not—show.
  3. Redmond desk20 min
    How to create a link to File or Folder in Windows 11Windows 11 gives you several ways to point to a file or folder without moving or duplicating it. You can create a desktop shortcut,…
Recommended PC Tool
Recommended PC Tool
Crashes, No Sound, or Screen Glitches?Free driver scan
Windows Errors? Fix Them Before They SpreadFree repair scan

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.