Stripe Subscriptions: How to find expired payment methods of your customers?
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.pyimport 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.productionSTRIPE_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.