Hide contribution payments from demo user(s) (#738)

* Hide payment contribution options from demo and manually created users

Demo data users and manually created users don't have stripe_customer_id
set on their family, so they should not see payment/contribution options.

Changes:
- Add can_manage_subscription? method to Family::Subscribeable that checks
  for presence of stripe_customer_id
- Guard Settings::PaymentsController to return 403 for users without
  stripe_customer_id
- Guard SubscriptionsController#show action (Stripe portal redirect) for
  users without stripe_customer_id
- Update settings navigation to hide the payment link when
  stripe_customer_id is not present
- Add tests for the new behavior

* Fix broken test

---------

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Juan José Mata
2026-01-23 12:35:49 +01:00
committed by GitHub
parent 5ba051c8cf
commit e0fb585bda
7 changed files with 71 additions and 8 deletions

View File

@@ -1,6 +1,8 @@
class Settings::PaymentsController < ApplicationController
layout "settings"
guard_feature unless: -> { Current.family.can_manage_subscription? }
def show
@family = Current.family
end

View File

@@ -1,6 +1,9 @@
class SubscriptionsController < ApplicationController
# Disables subscriptions for self hosted instances
guard_feature if: -> { self_hosted? }
before_action :guard_self_hosted, if: -> { self_hosted? }
# Disables Stripe portal for users without stripe_customer_id (demo users, manually created users)
guard_feature unless: -> { Current.family.can_manage_subscription? }, only: :show
# Upgrade page for unsubscribed users
def upgrade
@@ -58,6 +61,10 @@ class SubscriptionsController < ApplicationController
end
private
def guard_self_hosted
render plain: "Feature disabled: subscriptions are not available in self-hosted mode", status: :forbidden
end
def stripe
@stripe ||= Provider::Registry.get_provider(:stripe)
end

View File

@@ -41,6 +41,10 @@ module Family::Subscribeable
subscription&.active?
end
def can_manage_subscription?
stripe_customer_id.present?
end
def needs_subscription?
subscription.nil? && !self_hoster?
end

View File

@@ -8,7 +8,7 @@ nav_sections = [
{ label: t(".preferences_label"), path: settings_preferences_path, icon: "bolt" },
{ label: t(".profile_label"), path: settings_profile_path, icon: "circle-user" },
{ label: t(".security_label"), path: settings_security_path, icon: "shield-check" },
{ label: t(".payment_label"), path: settings_payment_path, icon: "circle-dollar-sign", if: !self_hosted? }
{ label: t(".payment_label"), path: settings_payment_path, icon: "circle-dollar-sign", if: !self_hosted? && Current.family.can_manage_subscription? }
]
},
{

View File

@@ -1,7 +1,22 @@
require "test_helper"
class Settings::PaymentsControllerTest < ActionDispatch::IntegrationTest
# test "the truth" do
# assert true
# end
setup do
sign_in @user = users(:empty)
@family = @user.family
end
test "returns forbidden when family has no stripe_customer_id" do
assert_nil @family.stripe_customer_id
get settings_payment_path
assert_response :forbidden
end
test "shows payment settings when family has stripe_customer_id" do
@family.update!(stripe_customer_id: "cus_test123")
get settings_payment_path
assert_response :success
end
end

View File

@@ -11,9 +11,10 @@ class SubscriptionsControllerTest < ActionDispatch::IntegrationTest
end
test "disabled for self hosted users" do
Rails.application.config.app_mode.stubs(:self_hosted?).returns(true)
post subscription_path
assert_response :forbidden
with_self_hosting do
post subscription_path
assert_response :forbidden
end
end
# Trial subscriptions are managed internally and do NOT go through Stripe
@@ -73,4 +74,23 @@ class SubscriptionsControllerTest < ActionDispatch::IntegrationTest
assert @family.subscription.active?
assert_equal "Welcome to Sure! Your contribution is appreciated.", flash[:notice]
end
test "show action returns forbidden when family has no stripe_customer_id" do
assert_nil @family.stripe_customer_id
get subscription_path
assert_response :forbidden
end
test "show action redirects to stripe portal when family has stripe_customer_id" do
@family.update!(stripe_customer_id: "cus_test123")
@mock_stripe.expects(:create_payment_portal_session_url).with(
customer_id: "cus_test123",
return_url: settings_payment_url
).returns("https://billing.stripe.com/session/test")
get subscription_path
assert_redirected_to "https://billing.stripe.com/session/test"
end
end

View File

@@ -10,4 +10,19 @@ class Family::SubscribeableTest < ActiveSupport::TestCase
@family.subscription.update!(trial_ends_at: 1.day.ago, status: "trialing")
assert_not @family.trialing?
end
test "can_manage_subscription? returns true when stripe_customer_id is present" do
@family.update!(stripe_customer_id: "cus_test123")
assert @family.can_manage_subscription?
end
test "can_manage_subscription? returns false when stripe_customer_id is nil" do
@family.update!(stripe_customer_id: nil)
assert_not @family.can_manage_subscription?
end
test "can_manage_subscription? returns false when stripe_customer_id is blank" do
@family.update!(stripe_customer_id: "")
assert_not @family.can_manage_subscription?
end
end