Glossary

Stripe Refund

A Stripe refund is a separate object that returns some or all of a charge to the original payment method, carrying its own status, its own balance transaction, and its own ability to fail.

Also called: refund, re_, refund object, money back

Definition

A refund is not an edit to the charge. It is a new object, with its own id beginning `re_`, its own timestamps, and its own lifecycle that runs after the charge is finished. The charge gets a couple of summary fields updated, but the refund itself lives somewhere else and can do things the charge knows nothing about.

That matters because of what Stripe documents in the `status` field: "Status of the refund. This can be `pending`, `requires_action`, `succeeded`, `failed`, or `canceled`." Five values. Most people are working with a mental model that has two, issued and not issued, and the missing three are exactly where reconciliation goes wrong.

So the sentence to carry away is this: issuing a refund is not the same as the customer receiving money. Between those two events sits a balance check, a bank, and a card issuer, any of which can hold the refund up or send it back. A refund that reads `succeeded` has cleared all three. A refund your Dashboard shows you issued last Tuesday may not have.

The object next to it in the chain is the [balance transaction](/glossary/stripe-balance-transaction), which is where the money actually moves, and the object before it is the [charge](/glossary/stripe-charge), which records what was collected in the first place. A [credit note](/glossary/stripe-credit-note) is a different instrument again: it adjusts an invoice rather than returning funds to a payment method.

Key points

  • +A refund is its own object with its own id, beginning `re_`. It is not a field on the charge.
  • +Stripe documents five statuses: `pending`, `requires_action`, `succeeded`, `failed`, or `canceled`.
  • +Refunds are funded from your available Stripe balance, not clawed back from the original charge.
  • +Stripe documents that its "processing fees from the original transaction aren’t returned", so a refunded sale still costs you the fee.
  • +`reason` is the label you chose. `failure_reason` is what went wrong. They are different fields with different authors.
  • +A failed refund creates a second balance transaction that puts the money back on your balance.
  • +Acodei books a refund as a QuickBooks refund receipt against the customer, drawing from the holding account.

Five statuses, and only one of them means the customer has the money

Stripe lists the values plainly: "Status of the refund. This can be `pending`, `requires_action`, `succeeded`, `failed`, or `canceled`." Three of those five describe a refund that exists in your account but has not landed with the customer, and each has a different cause.

`pending` has its own explanatory field. Stripe documents `pending_reason` as: "Provides the reason for why the refund is pending. Possible values are: `processing`, `insufficient_funds`, or `charge_pending`." Those are three genuinely different situations. `processing` is ordinary waiting. `insufficient_funds` means the refund is queued against a balance that cannot cover it yet, which is a cash-position problem rather than a payments problem. `charge_pending` means the original money has not finished settling, so there is nothing to send back yet.

`requires_action` is the one most people have never seen, and Stripe points at the field that explains it: `next_action` is documented as what applies "If the refund has a status of `requires_action`", describing "what the refund needs to continue processing." For payment methods without native refund support, that action can involve the customer directly. Stripe documents `instructions_email` as the place to "provide an email address for the customer to receive refund instructions." A refund in this state is waiting on a human, and no amount of resyncing will move it.

The practical consequence for a month-end close is that a refund list filtered on "exists" overstates what has actually gone back to customers. Filter on `succeeded` when you are reconciling, and treat everything else as a queue to work through.

A refund is funded from your balance, not clawed back from the charge

This is the most common structural misunderstanding, and it explains several behaviours that otherwise look like bugs.

Stripe is explicit about the funding source: "Refunds use your available Stripe balance (not including pending amounts)." The original charge is not reopened and money is not retrieved from it. A refund is a fresh outbound movement, paid for out of whatever you happen to be holding at that moment.

Which is why an empty balance changes the outcome rather than merely delaying it, and Stripe splits the behaviour by payment method: "If your available balance doesn’t cover the amount of the refund, Stripe holds the refund as pending for card transactions (refunds for other payment method types will fail) until your Stripe balance becomes sufficient." A card refund waits. A refund on another method does not wait, it fails. If you process a batch of refunds on a low-volume day, that distinction decides whether you have a queue or a pile of failures.

Two related constraints follow from the same design. Stripe documents that "Refunds can only be sent back to the original payment method used in a charge. You can’t send a refund to a different destination, such as another card or bank account." And on partial refunds: "You can issue more than one refund against a charge, but you can’t refund a total greater than the original charge amount."

One more piece of the economics is easy to miss and lands directly in your books. Stripe states that its "processing fees from the original transaction aren’t returned." A refunded sale is therefore not a neutral event. The revenue reverses, the fee does not, and the difference is a real cost that stays on your profit and loss.

`reason` is your label, `failure_reason` is the outcome

Two fields on the refund object sound similar and are constantly conflated. They are not the same kind of fact, and only one of them tells you anything about whether the refund worked.

`reason` is a categorisation you supply. Stripe documents it as: "Reason for the refund, which is either user-provided (`duplicate`, `fraudulent`, or `requested_by_customer`) or generated by Stripe internally (`expired_uncaptured_charge`)." Three of those four values are whatever the person clicking Refund selected in the Dashboard. It is useful for your own reporting and it is worth applying consistently, but it is a note about intent, not a system determination.

`failure_reason` is the opposite. Stripe documents it as: "Provides the reason for the refund failure. Possible values are: `lost_or_stolen_card`, `expired_or_canceled_card`, `charge_for_pending_refund_disputed`, `insufficient_funds`, `declined`, `merchant_request`, or `unknown`." Every one of those is an outcome reported back by a bank or an issuer, not something you chose.

The distinction has a practical edge. If you are building a report on why money goes back to customers, `reason` is your field, and its quality depends entirely on your team applying it consistently. If you are investigating why a customer says they never got their money, `reason` will tell you nothing at all and `failure_reason` is the only field worth reading.

A failed refund leaves two balance transactions, not zero

Every refund points at its own money record. Stripe documents `balance_transaction` on the refund as the "Balance transaction that describes the impact on your account balance." That is the debit: funds leaving you and heading for the customer.

When a refund fails, that debit is not deleted. A second record is created to undo it. Stripe documents `failure_balance_transaction` as: "After the refund fails, this balance transaction describes the adjustment made on your account balance that reverses the initial balance transaction." So a failed refund is visible in your balance history twice, once going out and once coming back, and the net is zero only when you read both.

The timing is the part that catches people. Stripe describes the mechanics this way: "A refund can fail if the customer’s bank or card issuer can’t process it. For example, a closed bank account or a problem with the card can cause a refund to fail. When this happens, the bank returns the refunded amount to us and we add it back to your Stripe account balance. This process can take up to 30 days from the post date." Thirty days is long enough to cross a close, which means a refund can be recorded in one period and reversed in the next while nothing at all was wrong with your bookkeeping. [When a Stripe refund fails and the money comes back](/blog/stripe-refund-failed-quickbooks) works through that case end to end.

One last fact that sits at the boundary with disputes and is worth knowing before you decide how quickly to refund a complaining customer: Stripe states that "Disputes and chargebacks aren’t possible on credit card charges that are fully refunded."

What a refund becomes in QuickBooks

Acodei syncs refunds from the `charge.refunded` event, and the QuickBooks record it produces is a refund receipt created against the customer, drawing from the resolved [holding account](/glossary/holding-account) for that Stripe account and currency, with line items mirroring what was refunded. The [QuickBooks refund receipt](/glossary/quickbooks-refund-receipt) entry covers that record on its own terms.

Full versus partial is not a setting you choose. It is detected by comparing the refund amount to the original charge amount, and the two cases build their lines differently.

Resyncing a refund that has already been booked updates the existing QuickBooks entity rather than creating a second one, so re-running a sync to chase a discrepancy does not leave you with duplicate refund receipts.

Tax on refunds is genuinely involved, and the treatment depends on your account’s tax mapping rather than on anything about the refund itself. That belongs to [Stripe refund tax in QuickBooks](/blog/stripe-refund-tax-quickbooks) rather than to this page.

Two cases take a different path entirely. Refunds of invoice payments, and refunds that arrive as dispute or adjustment activity, can reverse the original records instead of producing a new refund receipt.

Want to see this on your own Stripe data?

Start a free trial

Frequently asked questions

What is a Stripe refund?

A refund is a separate Stripe object, with an id beginning `re_`, that returns some or all of a charge to the original payment method it was paid with. It is not an edit to the charge. It has its own status, its own balance transaction, and it can fail on its own after being created.

What are the possible statuses of a Stripe refund?

Stripe documents the field as: "Status of the refund. This can be `pending`, `requires_action`, `succeeded`, `failed`, or `canceled`." Only `succeeded` means the money has gone back. When a refund is `pending`, the `pending_reason` field distinguishes ordinary `processing` from `insufficient_funds` and `charge_pending`.

Do I get the Stripe fee back when I refund a payment?

No. Stripe states that its "processing fees from the original transaction aren’t returned." The sale reverses but the processing cost does not, so a refunded transaction leaves a real expense on your books. Refunding may also incur a fee of its own depending on your account and payment method.

Why is my Stripe refund pending?

Most often because the money is not there yet. Stripe documents that refunds "use your available Stripe balance (not including pending amounts)", and that if the balance does not cover the refund, it is held as pending for card transactions until the balance becomes sufficient. Refunds on other payment method types fail in that situation rather than waiting.

What is the difference between `reason` and `failure_reason` on a refund?

`reason` is a label you or your team selected, documented by Stripe as "either user-provided (`duplicate`, `fraudulent`, or `requested_by_customer`) or generated by Stripe internally (`expired_uncaptured_charge`)". `failure_reason` is what a bank or issuer reported back when the refund did not go through, with values such as `expired_or_canceled_card` and `insufficient_funds`. Only the second tells you whether the refund worked.

What does Acodei create in QuickBooks when I issue a refund?

A refund receipt against the customer, drawing from the resolved holding account for that Stripe account and currency, with lines mirroring what was refunded. Whether the refund is full or partial is detected by comparing the refund amount to the original charge amount. Resyncing an already-booked refund updates the existing record instead of adding a second one.

What customers say about running Stripe through Acodei

Stripe Verified Partner BadgeQuickBooks Intuit Badge
If you're testing out all the different Stripe/QuickBooks integration apps right now, let me save you some time. This one is the best one by far.
RyanOwner at Indie Music Academy
Works well and is really helpful for massive transactions. The support is really fast and helpful. 100% recommended.
AndresCo-founder and CEO at Kanguro Collections and Reinsurance

Ready to try Acodei?

Connect Stripe to QuickBooks Online in minutes and let the fees, refunds, and payouts land where your accountant expects them.