Stripe Subscriptions: How to find expired payment methods of your customers?

Category: Development

You need to know which customers of your Stripe subscriptions have expired payment methods? Or you'd like to know which subscriptions are not properly configured with a default payment method? Then you can use the following script.

Introduction

When using Stripe's subscription model, you potentially face some questions which can't be answered directly in their dashboard:

  • Which customers have an expired credit card assigned to the subscription?
  • Which customers do not have a payment method assigned to their subscription?
  • Which customers do not have the most current payment method defined as their default?
  • Which customer's credit card cannot be charged because no standard is defined for the assigned payment methods?

With a simple python script, these questions can be answered: It shows all the relevant information and displays a green, yellow or red status for several conditions.

Setup

To use the following script, you need:

  • Python 3.10
  • Pipenv
  • A Stripe API Key

To create a Stripe API Key, log in to your Stripe account and visit "Developers" -> "API keys". Create a "restricted key" with the following read privileges: Customers, Credit notes, Payment intents, Subscriptions.

First, create a file called 'Pipfile' with the following contents:

Pipfile
[[source]] url = "https://pypi.org/simple" verify_ssl = true name = "pypi" [packages] python-dotenv = "*" pendulum = "*" rich = "*" stripe = "*" [dev-packages] [requires] python_version = "3.10"

Install the packages:

pipenv install

Python Script

Then create a python script. We call it 'check_subscriptions.py':

check_subscriptions.py
import os import stripe import pendulum from dotenv import load_dotenv if os.environ.get("PY_ENV") == "production": load_dotenv(".env.production") else: load_dotenv() from rich.console import Console from rich.table import Table from rich.progress import Progress from rich import box stripe.api_key = os.environ.get("STRIPE_KEY") default_limit = 10 subscriptions = stripe.Subscription.search( limit=default_limit, query='status:"active" OR status:"past_due"', expand=["total_count", "data.customer"] ) total_count = subscriptions.total_count print(f"Subscriptions: {total_count}") table = Table(title="Subscriptions", box=box.MINIMAL, title_style="bold blue", header_style="blue", row_styles=["dim", ""]) table.add_column("Subscription", no_wrap=True) table.add_column("Status", no_wrap=True) table.add_column("Customer", no_wrap=True) table.add_column("Cards (added)", justify="right") table.add_column("Status", justify="center") console = Console() with Progress(console=console) as progress: task = progress.add_task("Getting data", total=total_count) for subscription in subscriptions.auto_paging_iter(): has_default = False default_is_last = False default_created = -1 default_expired = -1 card_timestamps = [] card_text = "" status_dots = "" progress.update(task, advance=1) customer = subscription.customer cards = stripe.Customer.list_payment_methods(customer=customer.id, type="card") default_card = customer.invoice_settings.default_payment_method for idx, card in enumerate(cards): if card.id == default_card: has_default = True default_str = "[green]*[/] " default_created = card.created default_expired = pendulum.datetime( card.card.exp_year, card.card.exp_month, 1, 0, 0, 0).timestamp() else: default_str = "" card_created = pendulum.from_timestamp(card.created).format("YY-MM-DD") card_timestamps.append(card.created) card_text += f"{default_str} {card.card.last4}, {card.card.exp_month:02d} / {card.card.exp_year} ({card_created})" if idx < len(cards) - 1: card_text += "\n" if default_created == max(card_timestamps): default_is_last = True if subscription.status == "active": subscription_str = "[black on green]active[/]" else: subscription_str = f"[black on yellow]{subscription.status}[/]" if has_default is False: status_dots = "[red not dim]⬤ [/]" elif default_expired < pendulum.today().timestamp(): status_dots = "[red not dim]⬤ [/]" elif default_is_last is False: status_dots = "[yellow not dim]⬤ [/]" else: status_dots = "[green dim]⬤ [/]" table.add_row(subscription.id, subscription_str, customer.email, card_text, status_dots) console.print(table)

Finally, create a file called '.env.production' with your Stripe key:

.env.production
STRIPE_KEY=rk_live_YOUR_KEY

With the following command, the data will be gathered and displayed:

PY_ENV=production pipenv run python check_subscriptions.py

The output will show you all active and past due subscriptions, which customer it belongs to and the assigned credit cards.

The status column will display the following states:

  • Red dot: Credit card expired or no credit card assigned as default payment method.
  • Yellow: The default payment method is not the most current credit card.
  • Green: Everything looks good.

If you'd like to know, which subscription renewals might fail due to an expiring credit card, checkout out my other article.

I hope this script helps some other Stripe users like it helps us. If you've any comment, just drop me an email.