pfaffman
(Jay Pfaffman)
2025 年4 月 7 日 21:28
1
这看起来像是 Contribute > Bug ,但我不知道到底出了什么问题。
很多用户在尝试加载 /s/user/subscriptions/(访问 /my/billing/subscriptions 时)时会遇到 500 错误。
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)
这样是可以的。
我不确定接下来该从哪里着手排查。
blake
(Blake Erickson)
2025 年4 月 8 日 20:55
4
也许我遗漏了什么,但是 /s/<username>/subscriptions/ 并不是插件中的路由,对吧?
到目前为止,我在本地和生产站点上访问 /my/billing/subscriptions 时,它都会正确重定向到 /u/<username>/billing/subscriptions,并且我没有收到 500 错误。
还有其他关于我如何重现此问题的线索吗?
pfaffman
(Jay Pfaffman)
2025 年4 月 8 日 21:13
5
感谢您的帮助!
import EmberObject from "@ember/object";
import { ajax } from "discourse/lib/ajax";
import discourseComputed from "discourse/lib/decorators";
import { i18n } from "discourse-i18n";
import Plan from "discourse/plugins/discourse-subscriptions/discourse/models/plan";
export default class UserSubscription extends EmberObject {
static findAll() {
return ajax("/s/user/subscriptions", { method: "get" }).then((result) =>
result.map((subscription) => {
subscription.plan = Plan.create(subscription.plan);
return UserSubscription.create(subscription);
})
);
}
@discourseComputed("status")
canceled(status) {
return status === "canceled";
它会从该端点拉取。在此服务器上,对于某些用户,我会收到 500 错误(我认为我应该附上截图)。我认为这种情况已经发生了一段时间了?他们让我加入升级,我照做了,然后他们报告了这个错误,但似乎他们希望升级能够解决它(至少升级没有破坏它?)。
blake:
还有其他关于我如何重现此问题的线索吗?
零。我最好的猜测是,过去某个时候发生了某些事情,现在它从某个地方得到了一个 nil 而不是一个数组?但我已经查看了代码,没有看到任何地方在访问数组之前没有检查 nil。
blake
(Blake Erickson)
2025 年4 月 9 日 16:53
6
我猜这只是针对这个网站?你在其他网站上没有看到这个吗?是的,有一个“bug”,我们不应该返回 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
我猜计划或 plan 会返回 nil,但这将帮助我们缩小问题的范围。你能告诉我他们有多少订阅以及是否有任何计划丢失吗?
很可能是一个代码修复,我将只为这些用户返回任何订阅(如果有错误),但如果他们实际上有一个订阅,那并没有真正解决问题。也许我会让它返回一个错误,比如“已找到订阅,但加载其计划详细信息时出错,请联系管理员寻求帮助”。
pfaffman
(Jay Pfaffman)
2025 年4 月 9 日 18:34
7
好的。谢谢!
有一个订阅。
您的代码有一个修复:
subs = ::Stripe::Subscription.list(customer: customer.stripe_customer_id, status: 'all')[:data]
有 100 个计划(所以可能不止这些)——这可能是问题所在吗?(我尝试了 limit: 1000 但没有改变任何东西;我猜那是 Stripe API 的限制?)
所以也许当计划数 > 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 个计划。
blake
(Blake Erickson)
2025 年4 月 9 日 20:11
8
感谢所有信息,这很有帮助。
是的,这可以解决非代码修复的问题。如果他们有很多过时的计划,听起来不错。
我也会添加一些分页,以便我们以后遇到这种情况时可以全部获取。