From d69f8f80c763c2d280b69006ad2fa89e71b6f1e6 Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Fri, 11 Jul 2025 00:37:50 -0700 Subject: [PATCH] more stripe safety --- app/controllers/subscriptions_controller.rb | 22 ++++++++++++--------- app/controllers/users_controller.rb | 5 ++++- app/services/subscription_service.rb | 10 ++++++++-- lib/tasks/data_integrity.rake | 5 ++++- 4 files changed, 29 insertions(+), 13 deletions(-) diff --git a/app/controllers/subscriptions_controller.rb b/app/controllers/subscriptions_controller.rb index 8534aa47..94bb3e5c 100644 --- a/app/controllers/subscriptions_controller.rb +++ b/app/controllers/subscriptions_controller.rb @@ -145,7 +145,6 @@ class SubscriptionsController < ApplicationController end stripe_customer = Stripe::Customer.retrieve current_user.stripe_customer_id - stripe_subscription = stripe_customer.subscriptions.data[0] begin # Delete all existing payment methods to have our new one "replace" them existing_payment_methods = stripe_customer.list_payment_methods(type: 'card') @@ -179,7 +178,10 @@ class SubscriptionsController < ApplicationController def delete_payment_method stripe_customer = Stripe::Customer.retrieve current_user.stripe_customer_id - stripe_subscription = stripe_customer.subscriptions.data[0] + + # Use safe navigation to handle customers without subscriptions + subscriptions = stripe_customer.subscriptions&.data || [] + stripe_subscription = subscriptions.first payment_methods = stripe_customer.list_payment_methods(type: 'card') payment_methods.data.each do |payment_method| @@ -189,14 +191,16 @@ class SubscriptionsController < ApplicationController notice = ['Your payment method has been successfully deleted.'] # Check if user has a non-starter subscription using modern API - current_price_id = stripe_subscription.items.data[0].price.id - if current_price_id != 'starter' - # Cancel the user's at the end of its effective period on Stripe's end, so they don't get rebilled - stripe_subscription.delete(at_period_end: true) + if stripe_subscription&.items&.data&.any? + current_price_id = stripe_subscription.items.data[0].price.id + if current_price_id != 'starter' + # Cancel the user's at the end of its effective period on Stripe's end, so they don't get rebilled + stripe_subscription.delete(at_period_end: true) - active_billing_plan = BillingPlan.find_by(stripe_plan_id: current_price_id) - if active_billing_plan - notice << "Your #{active_billing_plan.name} subscription will end on #{Time.at(stripe_subscription.current_period_end).strftime('%B %d')}." + active_billing_plan = BillingPlan.find_by(stripe_plan_id: current_price_id) + if active_billing_plan + notice << "Your #{active_billing_plan.name} subscription will end on #{Time.at(stripe_subscription.current_period_end).strftime('%B %d')}." + end end end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 4ff030dd..e7ac5d9c 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -57,7 +57,10 @@ class UsersController < ApplicationController # Make sure the user is set to Starter on Stripe so we don't keep charging them stripe_customer = Stripe::Customer.retrieve(current_user.stripe_customer_id) - stripe_subscription = stripe_customer.subscriptions.data[0] + + # Use safe navigation to handle customers without subscriptions + subscriptions = stripe_customer.subscriptions&.data || [] + stripe_subscription = subscriptions.first if stripe_subscription # Update subscription to starter plan using modern API Stripe::Subscription.modify(stripe_subscription.id, { diff --git a/app/services/subscription_service.rb b/app/services/subscription_service.rb index d76611ac..cc06fe58 100644 --- a/app/services/subscription_service.rb +++ b/app/services/subscription_service.rb @@ -8,13 +8,19 @@ class SubscriptionService < Service # Sync with Stripe (todo pipe into StripeService) unless Rails.env.test? stripe_customer = Stripe::Customer.retrieve(user.stripe_customer_id) - stripe_subscription = stripe_customer.subscriptions.data[0] + + # Use safe navigation to handle customers without subscriptions + subscriptions = stripe_customer.subscriptions&.data || [] + stripe_subscription = subscriptions.first if stripe_subscription.nil? # Create a new subscription on Stripe Stripe::Subscription.create(customer: user.stripe_customer_id, price: plan_id) stripe_customer = Stripe::Customer.retrieve(user.stripe_customer_id) - stripe_subscription = stripe_customer.subscriptions.data[0] + + # Use safe navigation to get the newly created subscription + subscriptions = stripe_customer.subscriptions&.data || [] + stripe_subscription = subscriptions.first else # Edit an existing Stripe subscription by modifying its items Stripe::Subscription.modify(stripe_subscription.id, { diff --git a/lib/tasks/data_integrity.rake b/lib/tasks/data_integrity.rake index 7db2c4af..12ad72b4 100644 --- a/lib/tasks/data_integrity.rake +++ b/lib/tasks/data_integrity.rake @@ -23,7 +23,10 @@ namespace :data_integrity do User.where(selected_billing_plan_id: billing_plan_id).find_each do |user| # puts "Checking user ID #{user.id}" stripe_customer = Stripe::Customer.retrieve(user.stripe_customer_id) - stripe_subscription = stripe_customer.subscriptions.data[0] + + # Use safe navigation to handle customers without subscriptions + subscriptions = stripe_customer.subscriptions&.data || [] + stripe_subscription = subscriptions.first # Go through each of the customer's subscription items and make sure their # current billing plan is included as one.