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. Most merchants never see these codes; subscription apps tend to surface only “payment failed.” That's a loss, 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/amount_too_large— the charge tripped a per-transaction or velocity limit on the card. Often clears on a later retry; recurring cases are worth a customer conversation.authentication_required/off_session_rejected— the bank wants the customer present to verify (3-D Secure and similar). A plain retry may not fix it; the customer needs 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 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.)card_declined+ “lost or stolen” detail,pick_up_card-style responses — the card was reported lost or stolen and will never 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.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): 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 behind every failed renewal in your existing subscription app, classifies it into exactly this sort, and shows you the dollar figure per bucket — before you change anything. When you enable actions, each failure gets the treatment its code calls for, not a one-size-fits-all schedule. Join the early-access list →
Moorly is an independent product by Velun Labs and is not affiliated with Shopify, Appstle Inc., or Seal Subscriptions s.r.o. 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.