Stripe Refund Failed: What to Fix in QuickBooks

A failed Stripe refund leaves your books wrong in a way that does not self-correct. Where the money goes, which balance transaction carries it, and the...

Acodei Content Team · 8/9/2026 · 16 min read

A failed Stripe payout is the easy one. The money never left, no QuickBooks record was ever written, and the correct number of entries you need to add is zero. We wrote a whole post about doing nothing.

A failed refund is the mirror image, and it is genuinely worse. By the time the refund fails, QuickBooks already has a Refund Receipt on the books for money the customer never received. Your revenue is reduced, your holding account is reduced, and both of those facts are now wrong. Stripe has the money back. Your books say it is gone.

That gap can sit there for weeks. Stripe says the return of funds "can take up to 30 days from the post date," so the failure that breaks your reconciliation in March can be a refund you issued in February and stopped thinking about.

This post covers what Stripe actually does when a refund fails, which balance transaction it lands in, why the accounting is wrong until you act, and what the fix is. If you want the ordinary case first, how to record Stripe refunds in QuickBooks covers full and partial refunds that work.

Stop reconciling Stripe by hand. Acodei syncs Stripe charges, refunds, fees, and payouts into QuickBooks Online with the balance transaction detail your accountant actually needs. Start a free trial.

What Stripe does when a refund fails

Stripe's own description in the refunds documentation is short and worth reading literally:

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.

Three separate things happen, and only the first one is visible to most people.

The Refund object transitions to failed. It picks up two attributes at the same time: failure_reason, and failure_balance_transaction, which Stripe defines as "the ID of the balance transaction representing the amount returned to your Stripe balance." That second field is the whole accounting answer. It is a pointer straight at the row that undoes the original refund.

Stripe fires a refund.failed event. Stripe calls this rare, and adds the part that matters operationally: "If this occurs, you need to arrange an alternative way to provide your customer with a refund." The failure does not cancel your obligation. It just means the money is sitting in your Stripe balance instead of in the customer's account.

The money reappears in your balance. Not on the failure date you might expect, and not necessarily quickly. Up to 30 days, per Stripe's own documentation.

One more thing belongs in this section because it surprises people. Canceling a refund produces the same shape. Stripe is explicit: "As cancellations are a type of refund failure, the attributes failure_reason and failure_balance_transaction are included on the Refund." If you canceled a refund because the customer changed their mind, your books have exactly the same problem as if it had bounced.

The seven documented failure reasons

Stripe publishes the full failure_reason enum. They are not interchangeable, because two of them are not really failures in the sense you would assume.

failure_reasonWhat Stripe says
charge_for_pending_refund_disputed"A customer disputed the charge while the refund is pending."
declined"Refund declined by our financial partners."
expired_or_canceled_card"Payment method is canceled by a customer or expired by the partner."
insufficient_funds"Refund is pending due to insufficient funds and has crossed the pending refund expiry window."
lost_or_stolen_card"Refund has failed due to loss or theft of the original card."
merchant_request"Refund failed upon the business's request."
unknown"Refund has failed due to an unknown reason."

merchant_request is the cancellation case. charge_for_pending_refund_disputed is the one to read twice: the customer disputed the charge while your refund was still in flight, and Stripe recommends "accepting or challenging the dispute instead of refunding to avoid duplicate reimbursements to the customer." That case leaves your books facing a dispute rather than a failed refund, which is a different accounting path entirely.

insufficient_funds is worth understanding for a reason that has nothing to do with the customer. Refunds draw on your available Stripe balance, not your pending one. If your balance does not cover the refund, Stripe holds card refunds as pending until it does, and refunds for other payment method types fail outright. A business that pays out aggressively and refunds late can manufacture its own refund failures.

For some payment methods you also get a network_decline_code inside the destination_details hash, which carries the reason the financial partner gave.

Which balance transaction the money comes back in

This is where reconciliation actually happens, and where the answer is less tidy than you would like. Stripe uses more than one type depending on how the payment was made and how it failed.

refund_failure. Stripe's definition: "Created when a credit card charge refund fails, and Stripe returns the funds to your balance. This may occur if your customer's bank or card issuer is unable to correctly process a refund (for example, due to a closed bank account or a problem with the card)."

payment_refund. This type covers local payment method refunds generally, but it carries a second job: "if your customer's bank or card issuer is unable to correctly process a refund (e.g., due to a closed bank account or a problem with the card) Stripe returns the funds to your balance. The returned funds are represented as a Balance transaction with the type payment_refund."

adjustment. Stripe lists refund failures first among the common reasons for an adjustment: "the returned funds are represented as a Balance transaction with the type adjustment, where the description indicates the related refund object." The description field is what tells you which refund it belongs to, since the source on an adjustment points at a dispute in the dispute cases rather than at your refund.

payment_failure_refund. Not a failed refund at all, and the name is a trap. This is the reversal of a payment that failed after settling, for ACH, direct debit, and other delayed notification methods. Stripe's description: "You'll see a pending Balance transaction of type payment when the payment is created. Another Balance transaction of type payment_failure_refund appears if the pending payment later fails."

The practical upshot is that "find my failed refunds" is not a single-type query. Stripe's own advice on this, which almost nobody follows, is to stop classifying by type at all and use reporting categories: "To classify transactions for accounting purposes, use the reporting_category field instead of the type field." If you are building a report, that is the field to build it on. For a one-off investigation, the failure_balance_transaction on the Refund object skips the guessing entirely.

Why your books are wrong until you fix it

Strip out the plumbing and the accounting problem is simple.

A Refund Receipt is an asset-reducing document. When you issue a refund, QuickBooks reduces income and takes the cash out of whichever account the refund drew from, which for a Stripe-connected file is your holding account. Both of those entries assume the money left.

When the refund fails, the money did not leave. It came back to Stripe. So for as long as the reversal has not posted:

  • Your holding account is understated by the refund amount. QuickBooks thinks you have less sitting at Stripe than you do.
  • Your income is understated by the same amount, because the refund reduced revenue for a sale that was never actually reversed.
  • Your customer still has not been paid, which is a service problem sitting on top of the accounting one.

The third point is the one that separates this from a cosmetic reconciliation nit. This is not a timing artifact that nets out on its own, the way a Stripe reserve hold and release do. Nothing reverses it unless something reverses it.

The size of the error is also easy to underestimate. The original processing fee does not come back either. Stripe is direct about it: "Stripe's processing fees from the original transaction aren't returned." So a failed refund on a 500 dollar charge leaves your holding account 500 dollars light while the fee expense from the original sale stays exactly where it was, correctly.

How Acodei handles the returned funds

Acodei's documented behavior here is scoped to the adjustment path, and it is worth stating the boundary before the behavior, because the boundary is real.

What is documented. Acodei imports adjustments, but it does not create a standalone QuickBooks transaction for them. It adjusts the original associated entry instead. In real-time mode, that means voiding or correcting the record that was already written. Which mode you are on changes what the correction looks like, and daily summary versus real-time sync covers that split. In daily-summary mode, the adjustment is folded into that day's deposit, where a positive adjustment such as returned refund funds increases the day's net deposit.

For a failed refund specifically, Acodei's documented path is a resync. The instruction is to resync the affected refund or payout, at which point Acodei detects the failed-refund adjustment and voids the previously posted refund entry, so the income is restored. That is the correction the books need, and it is deliberately the same mechanism disputes use: the original record gets undone rather than a second record getting written on top of it. The Stripe balance adjustment glossary entry covers the object itself in more depth.

What is not documented, stated plainly. Acodei's product documentation covers the adjustment route. It does not carry a documented treatment for the refund_failure or payment_failure_refund balance transaction types specifically. If your failed refund arrives under one of those types rather than as an adjustment, check what actually landed in your books before assuming the resync path applies to it, and ask support rather than inferring. We would rather tell you where the documentation ends than guess past it.

One related behavior does help. Resyncing an already-booked refund updates the existing Refund Receipt rather than creating a second one, so a resync is not a way to accidentally double up your refund records.

The reconciliation check that catches this

A failed refund is exactly the kind of drift that a balance comparison catches and a bank-feed match does not. Which of those you get depends on your holding account, and this is the most underrated consequence of that choice.

On a regular asset clearing account, the correct method is balance reconciliation: your QuickBooks clearing account balance should equal your Stripe balance. Because every sale, fee, and payout posts against the clearing account as it happens, the two can be compared daily, and daily is the gold standard. Acodei has automated tracking built for exactly this and only for this mode, fetching the Stripe balance and the QuickBooks holding balance into per-day tracker rows and recording whether they match. A failed refund shows up as a mismatch the day the funds return.

On Undeposited Funds, the method is narrower: match each payout deposit against the bank feed, then reconcile the bank account. Acodei does not support balance-reconciling the Undeposited Funds account itself, and the reason is structural rather than a missing feature. Sales sit in Undeposited Funds until a payout sweeps them, and some items are only added at deposit time, so a point-in-time balance comparison of that account is not meaningful the way it is for a clearing account.

That difference is the underlying argument for a dedicated clearing account on any account complicated enough to have refunds fail. Under Undeposited Funds, a discrepancy inside the balance is invisible between payouts, and the only checkpoint you get is whether each payout landed correctly. The Undeposited Funds entry covers the full trade-off.

There is one useful side effect of Acodei's counting model here. Adjustments are counted in Acodei's transaction totals and included in the balance reconciliation, because Acodei counts fees, adjustments, and payouts as separate events rather than only customer charges. A returned refund is a balance change, so it is a thing the reconciliation can see.

Four cases that look like a failed refund and are not

Most "the refund failed" reports are not failures. Before you go reversing anything, rule these out.

The refund was processed as a reversal. Stripe: "Some refunds, those issued shortly after the original charge, appear in the form of a reversal instead of a refund. In the case of a reversal, the original charge drops off the customer's statement, and a separate credit isn't issued." The customer looks for a credit, finds none, and reports a failure. The money moved correctly. You can confirm it in the API, where a reversal returns destination_details[card][type] = 'reversal'.

It has not been long enough. Stripe submits the request to the customer's bank, and the credit appears "approximately 5-10 business days later, depending upon the bank." Nothing is wrong at day four.

The refund is in requires_action. For payment methods without native refund support, such as Konbini, PromptPay, Boleto, and bank transfers, Stripe has to collect bank details from the customer first and emails them to ask. The status table for these is worth knowing, because it contains a genuine round trip: a refund can reach succeeded, then go back to requires_action when "customer's bank returns the funds back to Stripe," typically because a name did not match or an account number had a typo. Stripe emails the customer again. It only becomes failed if the customer never responds before the expiration threshold.

The refund is pending on your balance. Covered above under insufficient_funds. A card refund that has not gone out because your Stripe balance is short has not failed yet, and topping up your balance releases it.

For a customer who is genuinely convinced the money never arrived, the useful thing to hand them is a reference number. For card refunds Stripe can provide an Acquirer Reference Number, System Trace Audit Number, or Retrieval Reference Number, which their bank can trace. Two caveats: it takes up to seven business days after initiating the refund for the ARN to come back from the banking partners, and an ARN is never available on a reversal, since the original charge was not processed.

What to actually do

A workable order of operations, assuming you have confirmed it is a real failure.

1. Get the failure reason. Pull the Refund object and read failure_reason. If it is charge_for_pending_refund_disputed, stop and handle the dispute instead. If it is merchant_request, this was a cancellation and there is probably nothing to re-send.

2. Find the returning balance transaction. Use failure_balance_transaction from the Refund object. That is the row that put the money back, and you do not have to guess which type it landed in.

3. Reverse the refund entry in QuickBooks. On Acodei, that means resyncing the affected refund or payout so the failed-refund adjustment is detected and the previously posted refund entry is voided, restoring the income. Doing it by hand means deleting or voiding the Refund Receipt, which puts revenue and the holding account back where they were before you issued a refund that never happened.

4. Arrange the refund another way, as Stripe instructs. The customer is still owed the money. Whatever route you use, it needs its own record. If you send a check or a bank transfer outside Stripe, it will not appear in any Stripe sync, so it has to be entered directly in QuickBooks.

5. Re-run the balance comparison. If you are on a clearing account, the QuickBooks holding balance and the Stripe balance should agree again. If they do not, something else is also off, and you have now isolated it.

The reason step 5 belongs in the list is that a failed refund is almost never discovered by looking for one. It is discovered because a balance did not tie out. Getting into the habit of comparing daily on a clearing account, or at absolute minimum monthly, is what turns a 30-day-old surprise into a same-week correction.

Frequently asked questions

What happens to the money when a Stripe refund fails?

The customer's bank returns it to Stripe, and Stripe adds it back to your Stripe account balance. Stripe documents that this can take up to 30 days from the post date. The funds arrive as a balance transaction, and the Refund object's failure_balance_transaction field holds the ID of that exact row.

Does a failed refund reverse itself in QuickBooks?

No. The Refund Receipt you already have is for money the customer never received, and nothing removes it automatically. On Acodei, the documented path is to resync the affected refund or payout so the failed-refund adjustment is detected and the previously posted refund entry is voided, which restores the income. Done by hand, it means voiding or deleting the Refund Receipt yourself.

Why did my refund fail if the customer's card is fine?

The most common documented reasons are a closed bank account, an expired or canceled card, and a lost or stolen card, none of which the customer necessarily thinks of as a problem with their card. There are two reasons that have nothing to do with the customer at all: merchant_request, which is a cancellation, and insufficient_funds, which means your own Stripe balance did not cover the refund before the pending window expired.

Does Stripe give the processing fee back when a refund fails?

No, and it does not give it back on a successful refund either. Stripe states that processing fees from the original transaction are not returned. The fee expense on the original sale stays on your books and is correct, which is why a failed refund understates your holding account by the refund amount rather than by the refund amount plus the fee.

Is a canceled refund the same as a failed one for accounting?

Yes, in shape. Stripe treats cancellations as a type of refund failure and attaches the same failure_reason and failure_balance_transaction attributes to the Refund. The money comes back to your balance, and any refund record already in QuickBooks needs the same reversal it would need after a bounce.

How do I find failed refunds across a whole month?

Do not filter by balance transaction type, because failed refunds land in more than one depending on the payment method and failure mode. Stripe's own guidance is to classify by reporting_category rather than type for accounting purposes. If you have a specific refund in question, failure_balance_transaction on that Refund points directly at the returning row.

Where this leaves you

The failed payout post ends with a reassurance: nothing is wrong, do nothing. This one ends with the opposite. A failed refund is one of the few Stripe events that leaves your general ledger actively wrong in a way that does not self-correct, and it does it quietly, on a delay measured in weeks.

The defense is not vigilance about refunds specifically. It is a holding account you can balance daily, and the discipline to actually compare it. Everything above is a procedure for a day the comparison does not tie.

Get every balance transaction into QuickBooks, not just the charges. Acodei syncs Stripe sales, refunds, fees, adjustments, and payouts so your clearing account can be reconciled against your Stripe balance daily. Start a free trial.

Share

Automate your Stripe to QuickBooks sync

Save hours every month. Acodei automatically syncs your Stripe transactions, invoices, and payouts to QuickBooks Online.

Get more operational finance guides like this one

We will only send high-value product and finance content.