Add upgrade/downgrade options
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
class Account::Subscriptions::DowngradesController < ApplicationController
|
||||
before_action :ensure_admin
|
||||
|
||||
def create
|
||||
portal_session = Stripe::BillingPortal::Session.create(
|
||||
customer: subscription.stripe_customer_id,
|
||||
return_url: account_settings_url(anchor: "subscription"),
|
||||
flow_data: {
|
||||
type: "subscription_update_confirm",
|
||||
subscription_update_confirm: {
|
||||
subscription: subscription.stripe_subscription_id,
|
||||
items: [ { id: stripe_subscription_item_id, price: Plan.paid.stripe_price_id } ]
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
redirect_to portal_session.url, allow_other_host: true
|
||||
end
|
||||
|
||||
private
|
||||
def subscription
|
||||
@subscription ||= Current.account.subscription
|
||||
end
|
||||
|
||||
def stripe_subscription_item_id
|
||||
Stripe::Subscription.retrieve(subscription.stripe_subscription_id).items.data.first.id
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,28 @@
|
||||
class Account::Subscriptions::UpgradesController < ApplicationController
|
||||
before_action :ensure_admin
|
||||
|
||||
def create
|
||||
portal_session = Stripe::BillingPortal::Session.create(
|
||||
customer: subscription.stripe_customer_id,
|
||||
return_url: account_settings_url(anchor: "subscription"),
|
||||
flow_data: {
|
||||
type: "subscription_update_confirm",
|
||||
subscription_update_confirm: {
|
||||
subscription: subscription.stripe_subscription_id,
|
||||
items: [ { id: stripe_subscription_item_id, price: Plan.paid_with_extra_storage.stripe_price_id } ]
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
redirect_to portal_session.url, allow_other_host: true
|
||||
end
|
||||
|
||||
private
|
||||
def subscription
|
||||
@subscription ||= Current.account.subscription
|
||||
end
|
||||
|
||||
def stripe_subscription_item_id
|
||||
Stripe::Subscription.retrieve(subscription.stripe_subscription_id).items.data.first.id
|
||||
end
|
||||
end
|
||||
@@ -49,7 +49,8 @@ class Stripe::WebhooksController < ApplicationController
|
||||
status: stripe_subscription.status,
|
||||
current_period_end: current_period_end_for(stripe_subscription),
|
||||
cancel_at: stripe_subscription.cancel_at ? Time.at(stripe_subscription.cancel_at) : nil,
|
||||
next_amount_due_in_cents: next_amount_due_for(stripe_subscription)
|
||||
next_amount_due_in_cents: next_amount_due_for(stripe_subscription),
|
||||
plan_key: plan_key_for(stripe_subscription)
|
||||
}
|
||||
|
||||
yield subscription_properties if block_given?
|
||||
@@ -76,4 +77,9 @@ class Stripe::WebhooksController < ApplicationController
|
||||
rescue Stripe::InvalidRequestError
|
||||
nil
|
||||
end
|
||||
|
||||
def plan_key_for(stripe_subscription)
|
||||
price_id = stripe_subscription.items.data.first&.price&.id
|
||||
Plan.all.find { |plan| plan.stripe_price_id == price_id }&.key
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2,6 +2,6 @@ class Account::BillingWaiver < SaasRecord
|
||||
belongs_to :account
|
||||
|
||||
def subscription
|
||||
@subscription ||= Account::Subscription.new(plan_key: Plan.paid_with_extra_storage)
|
||||
@subscription ||= Account::Subscription.new(plan: Plan.paid_with_extra_storage)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -4,4 +4,12 @@
|
||||
<p class="txt-medium margin-none-block-start"><%= subscription_period_end_action(subscription) %> <strong><%= subscription.current_period_end.to_date.to_fs(:long) %></strong></p>
|
||||
<% end %>
|
||||
|
||||
<%= link_to "Manage Billing", account_billing_portal_path, class: "btn", data: { turbo_prefetch: false } %>
|
||||
<div class="flex gap-half justify-center">
|
||||
<%= link_to "Manage Billing", account_billing_portal_path, class: "btn", data: { turbo_prefetch: false } %>
|
||||
|
||||
<% if subscription.plan == Plan.paid %>
|
||||
<%= button_to "Upgrade to Extra Storage", account_subscription_upgrade_path, class: "btn", form: { data: { turbo: false } } %>
|
||||
<% elsif subscription.plan == Plan.paid_with_extra_storage %>
|
||||
<%= button_to "Downgrade to Regular", account_subscription_downgrade_path, class: "btn", form: { data: { turbo: false } } %>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
@@ -10,7 +10,7 @@ module Fizzy
|
||||
Queenbee.host_app = Fizzy
|
||||
|
||||
initializer "fizzy_saas.content_security_policy", before: :load_config_initializers do |app|
|
||||
app.config.x.content_security_policy.form_action = "https://checkout.stripe.com"
|
||||
app.config.x.content_security_policy.form_action = "https://checkout.stripe.com https://billing.stripe.com"
|
||||
end
|
||||
|
||||
initializer "fizzy_saas.assets" do |app|
|
||||
@@ -22,7 +22,12 @@ module Fizzy
|
||||
app.routes.prepend do
|
||||
namespace :account do
|
||||
resource :billing_portal, only: :show
|
||||
resource :subscription
|
||||
resource :subscription do
|
||||
scope module: :subscriptions do
|
||||
resource :upgrade, only: :create
|
||||
resource :downgrade, only: :create
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
namespace :stripe do
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
require "test_helper"
|
||||
require "ostruct"
|
||||
|
||||
class Account::Subscriptions::DowngradesControllerTest < ActionDispatch::IntegrationTest
|
||||
setup do
|
||||
sign_in_as :kevin
|
||||
accounts(:"37s").subscription.update!(
|
||||
stripe_subscription_id: "sub_123",
|
||||
plan_key: Plan.paid_with_extra_storage.key
|
||||
)
|
||||
end
|
||||
|
||||
test "downgrade redirects to stripe billing portal" do
|
||||
stripe_subscription = OpenStruct.new(
|
||||
items: OpenStruct.new(data: [ OpenStruct.new(id: "si_123") ])
|
||||
)
|
||||
portal_session = OpenStruct.new(url: "https://billing.stripe.com/session/abc123")
|
||||
|
||||
Stripe::Subscription.stubs(:retrieve).with("sub_123").returns(stripe_subscription)
|
||||
Stripe::BillingPortal::Session.stubs(:create).returns(portal_session)
|
||||
|
||||
post account_subscription_downgrade_path
|
||||
|
||||
assert_redirected_to "https://billing.stripe.com/session/abc123"
|
||||
end
|
||||
|
||||
test "downgrade requires admin" do
|
||||
logout_and_sign_in_as :david
|
||||
|
||||
post account_subscription_downgrade_path
|
||||
assert_response :forbidden
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,33 @@
|
||||
require "test_helper"
|
||||
require "ostruct"
|
||||
|
||||
class Account::Subscriptions::UpgradesControllerTest < ActionDispatch::IntegrationTest
|
||||
setup do
|
||||
sign_in_as :kevin
|
||||
accounts(:"37s").subscription.update!(
|
||||
stripe_subscription_id: "sub_123",
|
||||
plan_key: Plan.paid.key
|
||||
)
|
||||
end
|
||||
|
||||
test "upgrade redirects to stripe billing portal" do
|
||||
stripe_subscription = OpenStruct.new(
|
||||
items: OpenStruct.new(data: [ OpenStruct.new(id: "si_123") ])
|
||||
)
|
||||
portal_session = OpenStruct.new(url: "https://billing.stripe.com/session/abc123")
|
||||
|
||||
Stripe::Subscription.stubs(:retrieve).with("sub_123").returns(stripe_subscription)
|
||||
Stripe::BillingPortal::Session.stubs(:create).returns(portal_session)
|
||||
|
||||
post account_subscription_upgrade_path
|
||||
|
||||
assert_redirected_to "https://billing.stripe.com/session/abc123"
|
||||
end
|
||||
|
||||
test "upgrade requires admin" do
|
||||
logout_and_sign_in_as :david
|
||||
|
||||
post account_subscription_upgrade_path
|
||||
assert_response :forbidden
|
||||
end
|
||||
end
|
||||
@@ -34,7 +34,7 @@ class Admin::AccountsControllerTest < ActionDispatch::IntegrationTest
|
||||
sign_in_as :david
|
||||
|
||||
untenanted do
|
||||
patch saas.admin_account_path(accounts(:"37s").external_account_id), params: { account: { overridden_card_count: 500 } }
|
||||
patch saas.admin_account_path(accounts(:"37s").external_account_id), params: { account: { card_count: 500 } }
|
||||
assert_redirected_to saas.edit_admin_account_path(accounts(:"37s").external_account_id)
|
||||
end
|
||||
|
||||
|
||||
@@ -76,31 +76,31 @@ class Account::LimitedTest < ActiveSupport::TestCase
|
||||
|
||||
test "detect nearing storage limit" do
|
||||
# Paid plans have large storage limits
|
||||
accounts(:"37s").update_column(:bytes_used, 4.gigabytes)
|
||||
accounts(:"37s").stubs(:bytes_used).returns(4.gigabytes)
|
||||
assert_not accounts(:"37s").nearing_plan_storage_limit?
|
||||
|
||||
# Free plan not near limit
|
||||
accounts(:initech).update_column(:bytes_used, 400.megabytes)
|
||||
accounts(:initech).stubs(:bytes_used).returns(400.megabytes)
|
||||
assert_not accounts(:initech).nearing_plan_storage_limit?
|
||||
|
||||
# Free plan near limit
|
||||
accounts(:initech).update_column(:bytes_used, 600.megabytes)
|
||||
accounts(:initech).stubs(:bytes_used).returns(600.megabytes)
|
||||
assert accounts(:initech).nearing_plan_storage_limit?
|
||||
end
|
||||
|
||||
test "detect exceeding storage limit" do
|
||||
# Free plan under limit
|
||||
accounts(:initech).update_column(:bytes_used, 900.megabytes)
|
||||
accounts(:initech).stubs(:bytes_used).returns(900.megabytes)
|
||||
assert_not accounts(:initech).exceeding_storage_limit?
|
||||
|
||||
# Free plan over limit
|
||||
accounts(:initech).update_column(:bytes_used, 1.1.gigabytes)
|
||||
accounts(:initech).stubs(:bytes_used).returns(1.1.gigabytes)
|
||||
assert accounts(:initech).exceeding_storage_limit?
|
||||
end
|
||||
|
||||
test "override bytes_used limits" do
|
||||
account = accounts(:initech)
|
||||
account.update_column(:bytes_used, 1.1.gigabytes)
|
||||
account.stubs(:bytes_used).returns(1.1.gigabytes)
|
||||
|
||||
assert account.exceeding_storage_limit?
|
||||
assert_equal 1.1.gigabytes, account.billed_bytes_used
|
||||
|
||||
Reference in New Issue
Block a user