Skip to content

Stripe API errorStripe: charge_already_refunded

charge_already_refunded is the Stripe error for a refund on a charge that is already fully refunded. No money moves on the second try. Common sources are a refund sent twice: from the Dashboard and your code, a retried webhook, or a double click.

Short answer

Open the charge and check its refunds. If the customer is already refunded, mark the order refunded in your database and stop. Then make refunds safe to repeat: check the charge first, send an idempotency key, and update your records from the refund.created or charge.refunded webhook, comparing amount_refunded with the charge amount before marking the order fully refunded.

Rather not fix it yourself? Stripe Billing Fix and Setup handles it at a fixed price, agreed before work starts.

Why the refund ran twice, and the fix

  1. 01

    Someone refunded it in the Dashboard first

    “Sent when a charge is refunded, including partial refunds.” Stripe, Refunds (charge.refunded event)

    A refund made by hand in the Dashboard doesn’t update your database unless you listen for it. Your code then tries again. Handle refund.created or charge.refunded so both paths agree, and remember charge.refunded also fires for partial refunds.

  2. 02

    The refund request was sent twice

    “The charge you’re attempting to refund has already been refunded.” Stripe, Error codes (charge_already_refunded)

    A double click, a retry after a timeout, or a webhook delivered twice sends the same refund again. Pass an idempotency key for each refund attempt, such as the order ID plus a refund request ID, so a repeat returns the first result. Keys expire after 24 hours.

    await stripe.refunds.create(
      { payment_intent: order.paymentIntentId },
      { idempotencyKey: `refund-${order.id}-${refundRequest.id}` },
    );
  3. 03

    The code refunds the wrong charge

    “Use a different charge ID only if you selected the wrong charge and intended to refund another charge.” Stripe, Error codes (charge_already_refunded)

    A stored charge ID that points at an older payment refunds the wrong one, or one that was already refunded. Look up the charge from the order’s PaymentIntent before refunding.

  4. 04

    Partial refunds add up

    Several partial refunds can reach the full amount. Check amount_refunded on the charge before you refund more, and show staff what is left.

Checked against the vendor’s docs on : Stripe docs: Error codes; Stripe docs: Refunds; Stripe API reference: Idempotent requests.

Rather hand it over?

Fixed scope, fixed price, paid upfront. The work happens in writing; no call needed.

“Working with Simon makes everything run so smoothly and better than I could ask for it. Amazingly communicative, extremely knowledgeable of coding and the backend processes. I strongly recommend.”
A buyer in the United States, January 2026. App development on Fiverr
All 127 Fiverr reviews

Related: Stripe idempotency key in use · Stripe webhook 200 but database not updated · Stripe PaymentIntent already succeeded · Stripe card_not_supported

Questions

More on this error

Was the customer refunded twice?

Not by this call. Stripe refused it, so no second refund was created. Check the charge’s refunds to confirm what the customer received.

Can I refund a disputed payment?

No. Stripe returns a different error for a charge that was charged back. Respond to the dispute instead.

Can you fix it for me?

If one bug is the whole problem, the App Fix Ticket covers it: a written diagnosis first, then the fix. If the app has more going on, the Launch Check is a written check of the whole app. Each issue found is rated critical, high, medium or low, with where it is and how it was confirmed. 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.

A different error?

Describe it and get a written quote

Send the error and what you were doing. You get a written scope, a fixed price and a timeline; Simon replies by email, usually within one business day.