feat: Allow SaaS users to manage subscription within the dashboard (#5059)
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
class Enterprise::Api::V1::AccountsController < Api::BaseController
|
||||
before_action :fetch_account
|
||||
before_action :check_authorization
|
||||
|
||||
def subscription
|
||||
Enterprise::CreateStripeCustomerJob.perform_later(@account) if stripe_customer_id.blank?
|
||||
head :no_content
|
||||
end
|
||||
|
||||
def checkout
|
||||
return create_stripe_billing_session(stripe_customer_id) if stripe_customer_id.present?
|
||||
|
||||
render_invalid_billing_details
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def fetch_account
|
||||
@account = current_user.accounts.find(params[:id])
|
||||
@current_account_user = @account.account_users.find_by(user_id: current_user.id)
|
||||
end
|
||||
|
||||
def stripe_customer_id
|
||||
@account.custom_attributes['stripe_customer_id']
|
||||
end
|
||||
|
||||
def render_invalid_billing_details
|
||||
render_could_not_create_error('Please subscribe to a plan before viewing the billing details')
|
||||
end
|
||||
|
||||
def create_stripe_billing_session(customer_id)
|
||||
session = Enterprise::Billing::CreateSessionService.new.create_session(customer_id)
|
||||
render_redirect_url(session.url)
|
||||
end
|
||||
|
||||
def render_redirect_url(redirect_url)
|
||||
render json: { redirect_url: redirect_url }
|
||||
end
|
||||
|
||||
def pundit_user
|
||||
{
|
||||
user: current_user,
|
||||
account: @account,
|
||||
account_user: @current_account_user
|
||||
}
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,21 @@
|
||||
class Enterprise::Api::V1::Webhooks::StripeController < ActionController::API
|
||||
def process_payload
|
||||
# Get the event payload and signature
|
||||
payload = request.body.read
|
||||
sig_header = request.headers['Stripe-Signature']
|
||||
|
||||
# Attempt to verify the signature. If successful, we'll handle the event
|
||||
begin
|
||||
event = Stripe::Webhook.construct_event(payload, sig_header, ENV.fetch('STRIPE_WEBHOOK_SECRET', nil))
|
||||
::Enterprise::Billing::HandleStripeEventService.new.perform(event: event)
|
||||
# If we fail to verify the signature, then something was wrong with the request
|
||||
rescue JSON::ParserError, Stripe::SignatureVerificationError
|
||||
# Invalid payload
|
||||
head :bad_request
|
||||
return
|
||||
end
|
||||
|
||||
# We've successfully processed the event without blowing up
|
||||
head :ok
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,7 @@
|
||||
class Enterprise::CreateStripeCustomerJob < ApplicationJob
|
||||
queue_as :default
|
||||
|
||||
def perform(account)
|
||||
Enterprise::Billing::CreateStripeCustomerService.new(account: account).perform
|
||||
end
|
||||
end
|
||||
@@ -1,13 +1,18 @@
|
||||
module Enterprise::Account
|
||||
def usage_limits
|
||||
{
|
||||
agents: get_limits(:agents).to_i,
|
||||
agents: agent_limits,
|
||||
inboxes: get_limits(:inboxes).to_i
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def agent_limits
|
||||
subscribed_quantity = custom_attributes['subscribed_quantity']
|
||||
subscribed_quantity || get_limits(:agents)
|
||||
end
|
||||
|
||||
def get_limits(limit_name)
|
||||
config_name = "ACCOUNT_#{limit_name.to_s.upcase}_LIMIT"
|
||||
self[:limits][limit_name.to_s] || GlobalConfig.get(config_name)[config_name] || ChatwootApp.max_limit
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
class Enterprise::Billing::CreateSessionService
|
||||
def create_session(customer_id, return_url = ENV.fetch('FRONTEND_URL'))
|
||||
Stripe::BillingPortal::Session.create(
|
||||
{
|
||||
customer: customer_id,
|
||||
return_url: return_url
|
||||
}
|
||||
)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,53 @@
|
||||
class Enterprise::Billing::CreateStripeCustomerService
|
||||
pattr_initialize [:account!]
|
||||
|
||||
DEFAULT_QUANTITY = 2
|
||||
|
||||
def perform
|
||||
customer_id = prepare_customer_id
|
||||
subscription = Stripe::Subscription.create(
|
||||
{
|
||||
customer: customer_id,
|
||||
items: [{ price: price_id, quantity: default_quantity }]
|
||||
}
|
||||
)
|
||||
account.update!(
|
||||
custom_attributes: {
|
||||
stripe_customer_id: customer_id,
|
||||
stripe_price_id: subscription['plan']['id'],
|
||||
stripe_product_id: subscription['plan']['product'],
|
||||
plan_name: default_plan['name'],
|
||||
subscribed_quantity: subscription['quantity']
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def prepare_customer_id
|
||||
customer_id = account.custom_attributes['stripe_customer_id']
|
||||
if customer_id.blank?
|
||||
customer = Stripe::Customer.create({ name: account.name, email: billing_email })
|
||||
customer_id = customer.id
|
||||
end
|
||||
customer_id
|
||||
end
|
||||
|
||||
def default_quantity
|
||||
default_plan['default_quantity'] || DEFAULT_QUANTITY
|
||||
end
|
||||
|
||||
def billing_email
|
||||
account.administrators.first.email
|
||||
end
|
||||
|
||||
def default_plan
|
||||
installation_config = InstallationConfig.find_by(name: 'CHATWOOT_CLOUD_PLANS')
|
||||
@default_plan ||= installation_config.value.first
|
||||
end
|
||||
|
||||
def price_id
|
||||
price_ids = default_plan['price_ids']
|
||||
price_ids.first
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,41 @@
|
||||
class Enterprise::Billing::HandleStripeEventService
|
||||
def perform(event:)
|
||||
ensure_event_context(event)
|
||||
case @event.type
|
||||
when 'customer.subscription.updated'
|
||||
plan = find_plan(subscription['plan']['product'])
|
||||
account.update(
|
||||
custom_attributes: {
|
||||
stripe_customer_id: subscription.customer,
|
||||
stripe_price_id: subscription['plan']['id'],
|
||||
stripe_product_id: subscription['plan']['product'],
|
||||
plan_name: plan['name'],
|
||||
subscribed_quantity: subscription['quantity']
|
||||
}
|
||||
)
|
||||
when 'customer.subscription.deleted'
|
||||
Enterprise::Billing::CreateStripeCustomerService.new(account: account).perform
|
||||
else
|
||||
Rails.logger.debug { "Unhandled event type: #{event.type}" }
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def ensure_event_context(event)
|
||||
@event = event
|
||||
end
|
||||
|
||||
def subscription
|
||||
@subscription ||= @event.data.object
|
||||
end
|
||||
|
||||
def account
|
||||
@account ||= Account.where("custom_attributes->>'stripe_customer_id' = ?", subscription.customer).first
|
||||
end
|
||||
|
||||
def find_plan(plan_id)
|
||||
installation_config = InstallationConfig.find_by(name: 'CHATWOOT_CLOUD_PLANS')
|
||||
installation_config.value.find { |config| config['product_id'].include?(plan_id) }
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user