Shopify payment declined codes, explained for subscription merchants
When a subscription renewal fails, somewhere behind that failure is a decline code โ the short reason the payment system recorded for why the charge didn't go through. Shopify shows what it receives from the customer's bank in the order history, and Shopify's subscription billing API records an error code on every failed billing attempt. How much of that reaches you depends on where you look โ some apps show the decline reason on the failed renewal, some show it only in an aggregate report, and some do both. It is worth finding, because the code tells you what would actually recover the money โ and the right responses to different codes are close to opposites.
The one distinction that matters: soft vs. hard declines
- Soft declines are temporary. The card is valid, but this attempt, at this moment, didn't go through โ no money in the account, a cautious bank, a processing hiccup. A well-timed retry can recover these.
- Hard declines are permanent. The card is expired, cancelled, reported lost or stolen, or the account is closed. No retry will ever succeed โ only a customer card update fixes them.
Retrying a hard decline wastes attempts and emails; sending a card-update ask for a soft decline adds friction the customer didn't need. Reading the code correctly is what our retry-schedule guide builds on.
The codes you'll actually see, by family
The names below follow the error codes Shopify's subscription billing API uses for
failed billing attempts (the same reasons appear in slightly different spellings in
order history and in gateway dashboards โ insufficient_funds,
INSUFFICIENT_FUNDS, and “insufficient funds” are the same
decline).
Usually recoverable with a retry (soft)
insufficient_fundsโ the most common soft decline. The account didn't have the money at that moment. Best recovered by a retry that lands after money typically arrives (payday, start of month), not fifteen minutes later.transient_error/processing_error/payment_provider_errorโ a hiccup between processor, network, or bank. Nothing is wrong with the customer or the card; a prompt retry usually clears it, and there's no reason to email anyone about it.transaction_limit_exceededโ the charge tripped a limit on the card. Often clears on a later retry; recurring cases are worth a customer conversation.amount_too_largeis a different problem โ Shopify defines it as the amount exceeding what the payment method allows, which a retry of the same amount will not change.
Needs the customer, not a retry
authentication_required/off_session_rejectedโ the bank wants the customer present to verify (3-D Secure and similar). A retry on its own does not clear it; the customer has to complete verification, so the fix is a link to act on, not silence.
Never recoverable with a retry (hard)
expired_card/expired_payment_method/invalid_expiry_dateโ the card on file is past its expiration. Every retry against it is wasted; the card-update email is the only fix, and it should go out on day one. (Expiry dates are visible in advance โ this is the one failure you can prevent before it happens. On Shopify Payments, Shopify says card networks can also push a reissued card's details onto the stored method automatically โ widely supported in the US, with international support varying โ so where that works the failure never occurs.)card_declinedwhere the order timeline or gateway detail says the card was reported lost or stolen โ Shopify's enum has no distinct code for this, so the signal is in the processor's free-text message, not the code. When you see it, the card will not charge again: card update, immediately.invalid_payment_method/payment_method_not_found/buyer_canceled_payment_methodโ the stored payment method is gone: the customer removed it, or the vaulted record is no longer usable. The subscription needs a fresh payment method to continue.incorrect_number/invalid_number/card_number_incorrectโ the stored card details themselves are wrong. Re-entry by the customer is the only path.
Ambiguous โ the bank won't say (treat carefully)
do_not_honorโ the issuer declined and gave no reason. It's the catch-all: could be balance, could be a risk flag, could be the bank disliking recurring charges that day. Worth one or two well-spaced retries; if it persists, the customer needs to call their bank โ no merchant-side setting fixes a bank that won't say why.call_issuerโ same family, but explicit: the bank wants to hear from the cardholder. Tell the customer that directly; it beats another silent retry.generic_error/card_declinedwith no detail โ no signal either way. Treat like a soft decline for a retry or two, then escalate to the card-update path.fraud_suspectedโ a risk system (bank- or Shopify-side) flagged the charge. Don't blindly retry into a fraud flag; repeated attempts make the flag stickier. If the customer is legitimate, this is a support conversation.
Configuration errors โ yours, not the customer's
merchant_account_error/payment_provider_is_not_enabled/merchant_ruleโ the problem is on the store side: gateway misconfiguration, a disabled provider, or a rule you set. No customer email will fix these; check your payment settings. If a whole day's renewals suddenly fail with the same code, look here first.amount_too_smallโ the renewal was below the gateway's minimum charge, so the card was never asked. Usually a welcome discount that kept applying to every cycle, or line items dropping off until almost nothing was left to bill. No retry and no customer email can fix it โ the amount has to change first.invoice_already_paid/customer_not_found/customer_invalidโ bookkeeping mismatches between the subscription record and the store. Rare, but they masquerade as payment failures while being data problems.
Individual code guides
Each of the codes below has its own deep-dive โ what it means, why it happens on renewals specifically, the exact merchant playbook, and whether retrying helps:
- Soft (retryable):
insufficient_fundsยทtransient_errorยทprocessing_errorยทtransaction_limit_exceeded - Hard (never retryable):
expired_cardยทinvalid_payment_methodยทincorrect_numberยท expiry-related family - Ambiguous (retry with care):
do_not_honorยทcall_issuerยทcard_declinedยทpayment_method_declinedยทfraud_suspectedยทauthentication_requiredยทgeneric_error - Store-side (fix your setup):
amount_too_smallยท configuration-error family ยท data-mismatch family
How to actually see these codes
- Order history: Shopify displays what the bank returned on the declined order โ often generic, sometimes specific.
- Your subscription app: apps differ widely in how much failure detail they surface per renewal. If yours shows only “failed,” the reason still exists underneath โ it's just not being shown to you. Our Appstle and Seal guides cover where each app keeps its failure information.
- The billing API: every failed subscription billing attempt in Shopify carries an error code from the list above โ this is where recovery tooling reads the reason and picks a treatment.
The takeaway
You don't need to memorize fifty codes. You need the sort: retry-able, dead-card,
ambiguous, or my-configuration โ and a different response wired to each. A store
that treats insufficient_funds and expired_card the same
way is guaranteed to leave money in one of those two buckets, and usually both.
Where Moorly fits: Moorly reads the decline reason your subscription app records for each failed renewal and classifies it โ insufficient funds, dead payment method, hard decline, needs authentication, risk-blocked, transient, store-side, or not yet classified โ then totals what the renewals you lost were worth, and shows which classes Moorly would have handled differently. It starts read-only, so you see the picture before you change anything. Join the early-access list โ
Moorly is an independent product by Velun Labs and is not affiliated with Shopify, Appstle Inc., or Seal Subscriptions. Code names and behavior follow Shopify's public API documentation as of August 2026 and may vary by gateway and plan โ always confirm against your own admin.