Short answer
Look the charge up before changing code. If it shows as captured, the capture you wanted has already happened and a second one isn’t needed. Use a different charge ID only if the code picked the wrong charge. Then check what sent the second capture request, such as a repeated webhook delivery or a second capture after a partial capture.
Rather not fix it yourself? App Fix Ticket starts with a fixed-price diagnosis, then a fixed price for the fix before any fix work.
Why the charge is already captured, and the fix
01
The capture already went through
“Inspect the charge to confirm whether the intended capture is complete.” Stripe, Error codes (charge_already_captured)
Retrieve the charge and check whether it was captured. Also check: if it was, the request that failed was a repeat of one that already worked.
02
The same webhook was handled twice
“Webhook endpoints might occasionally receive the same event more than once.” Stripe, Receive Stripe events in your webhook endpoint
Also check: if your code captures when a webhook arrives, a repeated delivery can trigger a second capture. Log the event IDs already processed and skip repeats.
03
A second capture after a partial capture
“If you partially capture a payment, you can’t perform another capture for the difference.” Stripe, Place a hold on a payment method
Also check: after a partial capture, most payments can’t be captured again for the rest. Capture the final amount once, or save the customer’s payment method and create a future payment for the difference, as Stripe suggests.
04
Check the PaymentIntent’s status
“Capture the funds of an existing uncaptured PaymentIntent when its status is requires_capture.” Stripe, Capture a PaymentIntent
Also check: a PaymentIntent can be captured only while its status is requires_capture, which needs capture_method set to manual when it is created.
Checked against the vendor’s docs on : Stripe docs: Error codes; Stripe docs: Place a hold on a payment method; Stripe API: Capture a PaymentIntent; Stripe docs: Webhooks.
Rather hand it over?
Fixed scope, fixed price, paid upfront. The work happens in writing; no call needed.
- from $450App Fix TicketOne reproducible bug in an existing mobile or web app, diagnosed and fixed.See scope and start
- from $450AI-Built App RescueA Lovable, Bolt or Cursor app checked end to end in 3 days, then its worst problems fixed at a fixed price.No issues worth fixing, no charge. If the Launch Check finds nothing rated medium or higher, Simon refunds the full $450. You keep the report either way. This applies to the first 10 Launch Checks bought after October 30, 2026.See scope and start
“Very responsive, attentive to fixing bugs, and completed the project as needed! Thank you!”
Related: Stripe charge_expired_for_capture · Stripe PaymentIntent already succeeded · Stripe idempotency key in use · Stripe charge_already_refunded