From 100b276c9d475e270d5126cc4f24be3be77e06c9 Mon Sep 17 00:00:00 2001 From: Blake Erickson Date: Thu, 10 Apr 2025 07:35:31 -0600 Subject: [PATCH] DEV: Add pagination to plans request (#272) When pulling up a users subscriptions a 500 might occur if we don't find their plan. If your account happens to have over 100 plans this can be the cause of the 500 error. This change adds pagination when fetching plans form stripe, so that we fetch them all. https://meta.discourse.org/t/problem-with-500-error-with-subscriptions/360808?u=blake --- .../user/subscriptions_controller.rb | 11 ++++- .../user/subscriptions_controller_spec.rb | 44 +++++++++++++++++++ 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/app/controllers/discourse_subscriptions/user/subscriptions_controller.rb b/app/controllers/discourse_subscriptions/user/subscriptions_controller.rb index 299fc19..913207e 100644 --- a/app/controllers/discourse_subscriptions/user/subscriptions_controller.rb +++ b/app/controllers/discourse_subscriptions/user/subscriptions_controller.rb @@ -24,7 +24,14 @@ module DiscourseSubscriptions subscriptions = [] if subscription_ids - plans = ::Stripe::Price.list(expand: ["data.product"], limit: 100) + prices = [] + price_params = { limit: 100, expand: ["data.product"] } + loop do + response = ::Stripe::Price.list(price_params) + prices.concat(response[:data]) + break unless response[:has_more] + price_params[:starting_after] = response[:data].last.id + end all_subscriptions = [] stripe_customer_ids.each do |stripe_customer_id| @@ -35,7 +42,7 @@ module DiscourseSubscriptions subscriptions = all_subscriptions.select { |sub| subscription_ids.include?(sub[:id]) } subscriptions.map! do |subscription| - plan = plans[:data].find { |p| p[:id] == subscription[:items][:data][0][:price][:id] } + plan = prices.find { |p| p[:id] == subscription[:items][:data][0][:price][:id] } subscription.to_h.except!(:plan) subscription.to_h.merge(plan: plan, product: plan[:product].to_h.slice(:id, :name)) end diff --git a/spec/requests/user/subscriptions_controller_spec.rb b/spec/requests/user/subscriptions_controller_spec.rb index bb707d0..d602fd4 100644 --- a/spec/requests/user/subscriptions_controller_spec.rb +++ b/spec/requests/user/subscriptions_controller_spec.rb @@ -5,6 +5,14 @@ require "rails_helper" RSpec.describe DiscourseSubscriptions::User::SubscriptionsController do before { SiteSetting.discourse_subscriptions_enabled = true } + def create_price(id, product) + price = { id: id, product: product } + def price.id + self[:id] + end + price + end + it "is a subclass of ApplicationController" do expect(DiscourseSubscriptions::User::SubscriptionsController < ::ApplicationController).to eq( true, @@ -80,6 +88,42 @@ RSpec.describe DiscourseSubscriptions::User::SubscriptionsController do expect(subscription[:items][:data][0][:plan][:id]).to eq("price_1OrmlvEYXaQnncShNahrpKvA") expect(subscription[:product][:name]).to eq("Exclusive Access") end + + it "aggregates prices from multiple pages using pagination logic" do + subscription_data = { id: "sub_10z", items: { data: [{ price: { id: "price_200" } }] } } + ::Stripe::Subscription + .stubs(:list) + .with(customer: "cus_23456", status: "all") + .returns({ data: [subscription_data] }) + + # Build the first page of 100 prices that do NOT include the desired price. + prices_page_1 = + (1..100).map do |i| + create_price("price_#{i}", { id: "prod_dummy", name: "Dummy Product #{i}" }) + end + + # Second page containing the desired price. + prices_page_2 = [create_price("price_200", { id: "prod_200", name: "Matching Product" })] + + ::Stripe::Price + .expects(:list) + .with(has_entries(limit: 100, expand: ["data.product"])) + .returns({ data: prices_page_1, has_more: true }) + + ::Stripe::Price + .expects(:list) + .with(has_entries(limit: 100, expand: ["data.product"], starting_after: "price_100")) + .returns({ data: prices_page_2, has_more: false }) + + get "/s/user/subscriptions.json" + result = JSON.parse(response.body, symbolize_names: true) + subscription = result.first + + expect(subscription[:id]).to eq("sub_10z") + expect(subscription[:plan][:id]).to eq("price_200") + expect(subscription[:product][:id]).to eq("prod_200") + expect(subscription[:product][:name]).to eq("Matching Product") + end end describe "update" do