Stripe Failed Payments and Past-Due Subscriptions in SQL

For founders losing revenue to declined cards and want a list they can act on today. You get failed charges by decline code, past-due subscriptions with the amount owed, invoices Stripe is still retrying, and how much of it retries win back.

· AI2SQL

Quick answer

Failed charges in the last 30 days, grouped by decline code. Paste into the Sigma editor.

-- Stripe Sigma (Trino)
-- Failed charges in the last 30 days by decline code
select
  failure_code,
  currency,
  count(*) as failed_charges,
  count(distinct customer_id) as customers,
  sum(amount) / 100.0 as failed_amount
from charges
where status = 'failed'
  and created >= current_date - interval '30' day
group by failure_code, currency
order by failed_charges desc

Schema assumptions

SourceTables usedNotes
Stripe Sigmacharges (id, customer_id, invoice_id, amount, currency, status, failure_code, failure_message, card_brand, created), invoices (id, customer_id, subscription_id, status, amount_due, amount_paid, amount_remaining, attempt_count, next_payment_attempt, created, currency), subscriptions (id, customer_id, status), customers (id, email)Amounts in minor units. Invoice status is draft, open, paid, uncollectible or void.
Stripe Data PipelineSame tables under schema STRIPE
Fivetrancharge, invoice, customer, subscription_history (same *_id columns)Singular table names. Use _fivetran_active rows of subscription_history.
Airbytecharges, invoices, subscriptions (customer, invoice, subscription, epoch created)API-shaped column names.

Copy-ready SQL

1. Past-due subscriptions with the amount owed

Your dunning list: who is behind, by how much, and when Stripe tries again. Change: add 'unpaid' to the status list if your settings leave subscriptions unpaid after retries.

-- Stripe Sigma (Trino)
select
  c.email,
  s.id as subscription_id,
  s.status,
  i.currency,
  sum(i.amount_remaining) / 100.0 as amount_owed,
  max(i.attempt_count) as attempts,
  min(i.next_payment_attempt) as next_retry
from subscriptions s
join invoices i on i.subscription_id = s.id and i.status = 'open'
join customers c on c.id = s.customer_id
where s.status in ('past_due')
group by 1, 2, 3, 4
order by amount_owed desc

Example output (made-up data):

emailsubscription_idstatuscurrencyamount_owedattemptsnext_retry
billing@acme.examplesub_1P…past_dueusd490.0022026-10-14 09:00
jo@startup.examplesub_1Q…past_dueusd49.0012026-10-13 18:30

2. Invoices Stripe is still retrying

Open invoices with at least one failed attempt and a retry scheduled. Change: nothing.

-- Stripe Sigma (Trino)
select
  i.id as invoice_id,
  c.email,
  i.amount_remaining / 100.0 as amount_remaining,
  i.currency,
  i.attempt_count,
  i.next_payment_attempt
from invoices i
join customers c on c.id = i.customer_id
where i.status = 'open'
  and i.attempt_count > 0
  and i.next_payment_attempt is not null
order by i.next_payment_attempt

3. Failed payment rate by month

Share of subscription payment attempts that fail. Change: drop the invoice_id filter to include one-off payments.

-- Stripe Sigma (Trino)
select
  date_trunc('month', created) as month,
  count(*) as attempts,
  count_if(status = 'failed') as failed,
  round(100.0 * count_if(status = 'failed') / count(*), 2) as failed_pct
from charges
where invoice_id is not null
  and created >= date_trunc('month', current_date) - interval '12' month
group by 1
order by 1 desc

Example output (made-up data):

monthattemptsfailedfailed_pct
2026-09-011840975.27
2026-08-011765884.99

4. Revenue recovered by retries

Invoices that failed at least once and were paid later. Change: the 90-day window.

-- Stripe Sigma (Trino)
select
  date_trunc('month', created) as month,
  currency,
  count(*) as recovered_invoices,
  sum(amount_paid) / 100.0 as recovered_amount
from invoices
where status = 'paid'
  and attempt_count > 1
  and created >= current_date - interval '90' day
group by 1, 2
order by 1 desc

5. Failures by card brand and decline code

Spot a brand or issuer that declines more than the rest. Change: the 90-day window.

-- Stripe Sigma (Trino)
select
  card_brand,
  failure_code,
  count(*) as failed_charges
from charges
where status = 'failed'
  and created >= current_date - interval '90' day
group by 1, 2
order by failed_charges desc
limit 20

Example output (made-up data):

card_brandfailure_codefailed_charges
Visacard_declined142
MasterCardinsufficient_funds51
Visaexpired_card33

6. Past-due list from Fivetran tables (Postgres)

The same dunning list on Fivetran’s schema. Change: the stripe schema name.

-- PostgreSQL (Fivetran Stripe schema "stripe")
select
  c.email,
  s.id as subscription_id,
  i.currency,
  sum(i.amount_remaining) / 100.0 as amount_owed,
  max(i.attempt_count) as attempts,
  min(i.next_payment_attempt) as next_retry
from stripe.subscription_history s
join stripe.invoice i on i.subscription_id = s.id and i.status = 'open'
join stripe.customer c on c.id = s.customer_id
where s._fivetran_active
  and s.status = 'past_due'
group by 1, 2, 3
order by amount_owed desc;

7. Failed charges this week from Airbyte tables (Postgres)

Airbyte keeps API names and Unix timestamps. Change: the airbyte_stripe schema name.

-- PostgreSQL (Airbyte Stripe schema "airbyte_stripe")
select
  to_timestamp(created)::date as day,
  failure_code,
  count(*) as failed_charges,
  sum(amount) / 100.0 as failed_amount
from airbyte_stripe.charges
where status = 'failed'
  and livemode
  and to_timestamp(created) >= now() - interval '7 days'
group by 1, 2
order by 1 desc, 3 desc;

Run it on your own database with AI2SQL

If Fivetran or Airbyte syncs your Stripe data into Postgres, MySQL or SQL Server, connect that database read-only and ask in plain English. For Sigma, Snowflake or BigQuery, AI2SQL writes the query in that dialect and you run it there.

Free to try. See pricing for what each plan includes.

TRY IT — FREE, NO SIGNUP
Try one:

Common mistakes

Frequently asked questions

How do I find failed payments in Stripe with SQL? +

In Sigma, query the charges table where status = 'failed'. failure_code gives the machine-readable reason (card_declined, expired_card, insufficient_funds and so on) and failure_message the text the customer saw.

How do I list past-due subscriptions with the amount owed? +

Join subscriptions (status = 'past_due') to their open invoices on subscription_id and sum amount_remaining. Add customers.email to get a contact list, and next_payment_attempt to see when Stripe will retry.

What is the difference between past_due and unpaid in Stripe? +

past_due: the latest invoice failed and Stripe is still retrying. unpaid: retries ended and your settings keep the subscription unpaid instead of cancelling it. Invoices on unpaid subscriptions stay open, so amount_remaining still shows what is owed.

How do I measure how much revenue retries recover? +

Look at paid invoices with attempt_count above 1. Each one failed at least once and was later paid, so the sum of their amount_paid is recovered revenue.

Can I run these on Fivetran or Airbyte data in Postgres? +

Yes. Fivetran uses singular table names (charge, invoice, customer) with the same *_id columns. Airbyte uses plural names, customer and invoice without the _id suffix, and Unix epoch timestamps. Both versions are below.

Related templates

All templates: SQL templates for Stripe, Shopify and Supabase