122 lines
4.2 KiB
Python
122 lines
4.2 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Simple script to verify which subscriptions will be caught by the invoice webhook check.
|
|
This shows which subscriptions have both cancel_at_period_end=True and cancellation_reason='billing_model_change'.
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import stripe
|
|
from datetime import datetime
|
|
import logging
|
|
|
|
# Setup logging
|
|
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def main():
|
|
# Get environment variables
|
|
stripe_secret_key = os.getenv('STRIPE_SECRET_KEY')
|
|
if not stripe_secret_key:
|
|
logger.error("STRIPE_SECRET_KEY environment variable not set")
|
|
sys.exit(1)
|
|
|
|
stripe.api_key = stripe_secret_key
|
|
|
|
logger.info("Checking for sunset legacy subscriptions...")
|
|
|
|
# Counters
|
|
total_active = 0
|
|
sunset_legacy = 0
|
|
cancelling_other_reason = 0
|
|
not_cancelling = 0
|
|
|
|
sunset_subscriptions = []
|
|
|
|
try:
|
|
# Get all active subscriptions
|
|
has_more = True
|
|
starting_after = None
|
|
|
|
while has_more:
|
|
params = {
|
|
'status': 'active',
|
|
'limit': 100,
|
|
}
|
|
if starting_after:
|
|
params['starting_after'] = starting_after
|
|
|
|
subscriptions = stripe.Subscription.list(**params)
|
|
|
|
for sub in subscriptions.data:
|
|
total_active += 1
|
|
|
|
cancel_at_period_end = sub.get('cancel_at_period_end', False)
|
|
cancellation_reason = sub.get('metadata', {}).get('cancellation_reason')
|
|
|
|
# This is the exact check from the webhook handler
|
|
if cancel_at_period_end and cancellation_reason == 'billing_model_change':
|
|
sunset_legacy += 1
|
|
|
|
# Get customer email for display
|
|
customer_email = 'unknown'
|
|
try:
|
|
customer = stripe.Customer.retrieve(sub.get('customer'))
|
|
customer_email = customer.get('email', 'unknown')
|
|
except:
|
|
pass
|
|
|
|
# Calculate days until cancellation
|
|
current_period_end_ts = sub.get('current_period_end')
|
|
if current_period_end_ts:
|
|
current_period_end = datetime.fromtimestamp(current_period_end_ts)
|
|
days_until_cancel = (current_period_end - datetime.now()).days
|
|
cancel_date = current_period_end.strftime('%Y-%m-%d')
|
|
else:
|
|
days_until_cancel = -1
|
|
cancel_date = 'Unknown'
|
|
|
|
sunset_subscriptions.append(
|
|
{
|
|
'id': sub.get('id'),
|
|
'customer': customer_email,
|
|
'days_until_cancel': days_until_cancel,
|
|
'cancel_date': cancel_date,
|
|
}
|
|
)
|
|
|
|
elif cancel_at_period_end:
|
|
cancelling_other_reason += 1
|
|
else:
|
|
not_cancelling += 1
|
|
|
|
# Check if there are more pages
|
|
has_more = subscriptions.has_more
|
|
if has_more and subscriptions.data:
|
|
starting_after = subscriptions.data[-1].get('id')
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error fetching subscriptions: {e}")
|
|
sys.exit(1)
|
|
|
|
# Display results
|
|
logger.info("\n=== VERIFICATION RESULTS ===")
|
|
logger.info(f"Total active subscriptions: {total_active}")
|
|
logger.info(f"Sunset legacy (will skip usage charges): {sunset_legacy}")
|
|
logger.info(f"Cancelling for other reasons: {cancelling_other_reason}")
|
|
logger.info(f"Not cancelling: {not_cancelling}")
|
|
|
|
if sunset_subscriptions:
|
|
logger.info("\n=== SUNSET SUBSCRIPTIONS (Usage charges will be skipped) ===")
|
|
for sub in sorted(sunset_subscriptions, key=lambda x: x['days_until_cancel']):
|
|
logger.info(
|
|
f"- {sub['id']} | {sub['customer']} | Cancels in {sub['days_until_cancel']} days ({sub['cancel_date']})"
|
|
)
|
|
|
|
logger.info("\n✅ These subscriptions will NOT receive usage charges on their final invoice.")
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|