AltorLabFree Scan →
HomeError CodesSTRIPE_PAYMENT_INTENT_ALREADY_CONFIRMED
stripeshopify

Stripe PaymentIntent Already Confirmed Error

Root cause

Your code attempted to call confirm on a Stripe PaymentIntent that was already in confirmed or succeeded state — typically caused by a webhook handler processing the same event twice, a retry loop that does not check PaymentIntent status before confirming, or a race condition between two concurrent billing processes.

Symptoms

How to fix it

1

Add a status check before every PaymentIntent confirm call

Before calling stripe.paymentIntents.confirm(id), retrieve the PaymentIntent status: stripe.paymentIntents.retrieve(id). Only call confirm if status is requires_confirmation. If status is already succeeded or requires_action, do not call confirm — handle each status appropriately. This guard prevents already-confirmed errors regardless of how many times the code path is triggered.

2

Implement webhook deduplication to prevent double processing

The most common cause of already-confirmed errors is processing the same webhook event twice. Implement event deduplication: when a Stripe event arrives, store its ID in your database before processing. On the next event delivery, check the ID against stored IDs — if already present, return 200 without processing. Stripe guarantees at-least-once delivery, so your handler must be idempotent.

3

Eliminate concurrent billing processes for the same subscription

If two processes can simultaneously trigger billing for the same subscription (e.g., a scheduled job and a manual retry), add a distributed lock: before starting a billing operation for a subscription ID, acquire a lock (Redis, database row lock) that prevents concurrent billing for the same subscription. Release the lock after the billing attempt resolves. This eliminates race conditions that produce already-confirmed errors.

Frequently asked questions

Does a PaymentIntent already-confirmed error mean the customer was double-charged?

Usually no — the already-confirmed error is thrown before a second charge is created. The error means your code tried to confirm again, but Stripe rejected the request because confirmation already happened. Verify by checking the Stripe charge list for the customer: if you see only one charge, no double-billing occurred. If two charges exist, issue a refund immediately.

How do idempotency keys differ from PaymentIntent status checks for preventing this error?

Idempotency keys prevent duplicate API resources from being created (e.g., two charges). PaymentIntent status checks prevent invalid operations on existing resources (e.g., confirming an already-confirmed intent). You need both: idempotency keys ensure you do not create duplicate PaymentIntents, and status checks ensure you only perform valid operations on the PaymentIntents that do exist.

Detect this error automatically

Free cross-stack scan finds all billing errors in 60 seconds.

Run Free Scan →