Проблема с ошибкой 500 при работе с подписками

Это похоже на #баг, но я не понимаю, что происходит.

У многих пользователей возникает ошибка 500 при попытке загрузить /s/user/subscriptions/ (при переходе по адресу /my/billing/subscriptions).

NoMethodError (undefined method `[]' for nil) app/controllers/application_controller.rb:427:in `block in with_resolved_locale' app/controllers/application_controller.rb:427:in `with_resolved_locale' l


NoMethodError (undefined method `[]' for nil)
app/controllers/application_controller.rb:427:in `block in with_resolved_locale'
app/controllers/application_controller.rb:427:in `with_resolved_locale'
lib/middleware/omniauth_bypass_middleware.rb:35:in `call'
lib/content_security_policy/middleware.rb:12:in `call'
lib/middleware/anonymous_cache.rb:410:in `call'
lib/middleware/csp_script_nonce_injector.rb:12:in `call'
config/initializers/008-rack-cors.rb:14:in `call'
lib/middleware/default_headers.rb:13:in `call'
config/initializers/100-quiet_logger.rb:20:in `call'
config/initializers/100-silence_logger.rb:29:in `call'
lib/middleware/enforce_hostname.rb:24:in `call'
lib/middleware/processing_request.rb:12:in `call'
lib/middleware/request_tracker.rb:385:in `call'

Я надеялся, что это случайность, так как проблема возникает не у всех пользователей. Однако я не могу найти закономерности.

Я могу выполнить следующее:

 user=User.find_by_username 'xxx'
 DiscourseSubscriptions::Customer.find_by(user_id: user.id)

и это работает.

Не знаю, куда смотреть дальше.

Возможно, я что-то упускаю, но /s/<username>/subscriptions/ — это не маршрут в плагине, верно?

Пока что при посещении /my/billing/subscriptions локально и на продакшн-сайте меня корректно перенаправляет на /u/<username>/billing/subscriptions, и ошибка 500 не возникает.

Есть ли ещё какие-то подсказки, как мне воспроизвести эту проблему?

Спасибо за вашу помощь!

Данные подтягиваются из этого эндпоинта. И на этом сервере у некоторых пользователей возникает ошибка 500 (я, кажется, должен был приложить скриншот). Мне кажется, это происходит уже какое-то время? Они привлекли меня для обновления, которое я выполнил, после чего они сообщили об ошибке. Похоже, они надеялись, что обновление решит проблему (хотя, по крайней мере, обновление её не усугубило?).

Никаких. Мое лучшее предположение — что-то произошло в прошлом, и теперь откуда-то возвращается nil вместо массива? Но я изучил код и не нашёл места, где не проверяется nil перед обращением к массиву.

Я предполагаю, что проблема касается только этого сайта? Вы не видите этого на других сайтах? Да, есть «баг», из-за которого мы не должны возвращать ошибку 500, но, скорее всего, где-то есть «некорректные» данные, например, отсутствует план или что-то подобное. Возможно, он был удалён в Stripe, но не был обновлён в базе данных плагина.

Можете ли вы выполнить эти команды в консоли Rails, чтобы помочь мне отладить проблему?

Stripe.api_key = SiteSetting.discourse_subscriptions_secret_key
user = User.find_by(username: 'xxx')
customer = DiscourseSubscriptions::Customer.find_by(user_id: user.id)
subs = ::Stripe::Subscription.list(customer: stripe_customer_id, status: 'all')[:data]
subs.count # обратите внимание, сколько у них подписок
subscription = subs.first # измените это и выполните следующие строки для каждой подписки
price_id = subscription[:items][:data].first[:price][:id]
plans = ::Stripe::Price.list(expand: ['data.product'], limit: 100)
plan = plans[:data].find { |p| p[:id] == price_id }
plan

Я думаю, что либо plans, либо plan вернёт nil, но это поможет нам сузить круг поиска проблемы. Не могли бы вы сообщить, сколько у них подписок и есть ли у них отсутствующие планы?

Скорее всего, моё исправление кода будет заключаться в том, чтобы просто не возвращать никакие подписки для таких пользователей в случае ошибки, но если у них действительно есть подписка, это не решит проблему. Возможно, я сделаю так, чтобы возвращалась ошибка с сообщением вроде: «Подписка найдена, но при загрузке её деталей возникла ошибка. Пожалуйста, обратитесь за помощью к администратору».

Круто. Спасибо!

Была одна подписка.

Одно исправление в вашем коде:

subs = ::Stripe::Subscription.list(customer: customer.stripe_customer_id, status: 'all')[:data]

У вас 100 тарифов (а вероятно, и больше) — возможно, в этом проблема? (Я пробовал указывать limit: 1000, но это ничего не изменило; полагаю, это ограничение API Stripe?)

Возможно, если тарифов больше 100, происходит сбой? Кстати, пользователи, у которых всё работает, находятся на тарифах из первых 100.

discourse(prod)> plan = plans[:data].find { |p| p[:id] == price_id }
discourse(prod)>
=> nil

РЕДАКТИРОВАНИЕ:

Да. Если я делаю так:

# Получаем ID последнего элемента
last_price_id = plans.data.last.id

# Получаем следующие 100
next_plans = ::Stripe::Price.list(
  expand: ['data.product'],
  limit: 100,
  starting_after: last_price_id
)

то

discourse(prod)> next_plans[:data].find { |p| p[:id] == price_id }

находит то, что нужно.

РЕДАКТИРОВАНИЕ ЕЩЁ РАЗ: next_plans.count == 8. Я предложил им удалить 10 тарифов.

Спасибо за всю информацию, это полезно.

Да, это сработает для исправления без изменения кода. Звучит хорошо, если у них много устаревших планов.

Я также добавлю пагинацию, чтобы мы могли получить их все, если столкнёмся с этим снова.

Вот исправление для этого: