mirror of
https://github.com/astuto/astuto.git
synced 2025-12-15 19:27:52 +01:00
* Do not send emails for stripe custom.subscription.updated webhook event * Do not send "mid" trial period email if tenant trial period has been extended
182 lines
6.0 KiB
Ruby
182 lines
6.0 KiB
Ruby
require 'stripe'
|
|
|
|
class BillingController < ApplicationController
|
|
before_action :check_multi_tenancy
|
|
before_action :authenticate_owner, only: :request_billing_page
|
|
before_action :set_tenant_on_billing_subdomain, only: [:create_checkout_session, :session_status]
|
|
skip_before_action :verify_authenticity_token, only: :webhook
|
|
|
|
def request_billing_page
|
|
tb = Current.tenant.tenant_billing
|
|
tenant_id = tb.slug
|
|
auth_token = tb.generate_auth_token
|
|
|
|
redirect_to get_url_for(
|
|
method(:billing_url),
|
|
disallow_custom_domain: true,
|
|
options: { subdomain: 'billing', tenant_id: tenant_id, auth_token: auth_token }
|
|
)
|
|
end
|
|
|
|
def index
|
|
return unless params[:tenant_id] and params[:auth_token]
|
|
|
|
tb = TenantBilling.unscoped.find_by(slug: params[:tenant_id])
|
|
Current.tenant = tb.tenant
|
|
|
|
if is_current_user_owner? || tb.auth_token == params[:auth_token]
|
|
@page_title = t('billing.title')
|
|
load_tenant_data_for_billing
|
|
|
|
# log in owner on "billing" subdomain
|
|
owner = Current.tenant.owner
|
|
sign_in owner
|
|
tb.invalidate_auth_token
|
|
|
|
# get prices from stripe
|
|
@prices = Stripe::Price.list({
|
|
lookup_keys: [
|
|
Rails.application.stripe_monthly_lookup_key,
|
|
Rails.application.stripe_yearly_lookup_key
|
|
],
|
|
active: true
|
|
}).data
|
|
@prices = @prices.sort_by { |price| price.unit_amount }
|
|
else
|
|
redirect_to get_url_for(method(:root_url))
|
|
end
|
|
end
|
|
|
|
def return
|
|
return unless params[:tenant_id]
|
|
|
|
tb = TenantBilling.unscoped.find_by(slug: params[:tenant_id])
|
|
Current.tenant = tb.tenant
|
|
|
|
if is_current_user_owner?
|
|
@page_title = t('billing.title')
|
|
load_tenant_data_for_billing
|
|
else
|
|
redirect_to get_url_for(method(:root_url))
|
|
end
|
|
end
|
|
|
|
def create_checkout_session
|
|
return_url = get_url_for(
|
|
method(:billing_return_url),
|
|
disallow_custom_domain: true,
|
|
options: { subdomain: 'billing' }
|
|
)
|
|
|
|
session = Stripe::Checkout::Session.create({
|
|
ui_mode: 'embedded',
|
|
line_items: [{
|
|
price: params[:price_id],
|
|
quantity: 1,
|
|
}],
|
|
mode: 'subscription',
|
|
return_url: "#{return_url}?session_id={CHECKOUT_SESSION_ID}&tenant_id=#{params[:tenant_id]}",
|
|
customer: Current.tenant.tenant_billing.customer_id,
|
|
})
|
|
|
|
render json: { clientSecret: session.client_secret }
|
|
end
|
|
|
|
def session_status
|
|
session = Stripe::Checkout::Session.retrieve(params[:session_id])
|
|
render json: { status: session.status, session: session }
|
|
end
|
|
|
|
def webhook
|
|
event = nil
|
|
|
|
begin
|
|
sig_header = request.env['HTTP_STRIPE_SIGNATURE']
|
|
payload = request.body.read
|
|
event = Stripe::Webhook.construct_event(payload, sig_header, Rails.application.stripe_endpoint_secret)
|
|
rescue JSON::ParserError => e
|
|
# Invalid payload
|
|
return head :bad_request
|
|
rescue Stripe::SignatureVerificationError => e
|
|
# Invalid signature
|
|
return head :bad_request
|
|
end
|
|
|
|
if event['type'] == 'invoice.paid'
|
|
Current.tenant = get_tenant_from_customer_id(event.data.object.customer)
|
|
|
|
monthly_lookup_key = Rails.application.stripe_monthly_lookup_key
|
|
yearly_lookup_key = Rails.application.stripe_yearly_lookup_key
|
|
|
|
subscription_type = event.data.object.lines.data.last.price.lookup_key
|
|
return head :bad_request unless subscription_type == monthly_lookup_key || subscription_type == yearly_lookup_key
|
|
|
|
old_subscription_status = Current.tenant.tenant_billing.status
|
|
|
|
subscription_duration = subscription_type == monthly_lookup_key ? 1.month : 1.year
|
|
Current.tenant.tenant_billing.update!(
|
|
status: 'active',
|
|
subscription_ends_at: Time.current + subscription_duration
|
|
)
|
|
|
|
if old_subscription_status == 'trial'
|
|
TenantMailer.subscription_confirmation(tenant: Current.tenant).deliver_later
|
|
end
|
|
elsif event['type'] == 'customer.subscription.updated'
|
|
# This event is triggered when:
|
|
# (1) A subscription is canceled OR a subscription is reactivated after being canceled
|
|
# (2) A subscription is updated (e.g. switching from monthly to yearly plan or vice versa)
|
|
# (3) A subscription is automatically renewed at the end of the billing period (e.g. every month for a monthly subscription)
|
|
# Since it is difficult to distinguish between these cases, we only update the status if the subscription is active or canceled
|
|
# and we do not send any emails notifications.
|
|
|
|
Current.tenant = get_tenant_from_customer_id(event.data.object.customer)
|
|
|
|
if Current.tenant.tenant_billing.status == 'active' || Current.tenant.tenant_billing.status == 'canceled'
|
|
has_canceled = event.data.object.cancel_at_period_end
|
|
Current.tenant.tenant_billing.update!(status: has_canceled ? 'canceled' : 'active')
|
|
end
|
|
end
|
|
|
|
return head :ok
|
|
end
|
|
|
|
private
|
|
|
|
def check_multi_tenancy
|
|
redirect_to root_path unless Rails.application.multi_tenancy?
|
|
end
|
|
|
|
def get_tenant_from_customer_id(customer_id)
|
|
TenantBilling.unscoped.find_by(customer_id: customer_id).tenant
|
|
end
|
|
|
|
def is_current_user_owner?
|
|
user_signed_in? && current_user.tenant_id == Current.tenant.id && current_user.owner?
|
|
end
|
|
|
|
def set_tenant_on_billing_subdomain
|
|
tb = TenantBilling.unscoped.find_by(slug: params[:tenant_id])
|
|
Current.tenant = tb.tenant
|
|
|
|
unless is_current_user_owner?
|
|
render json: {
|
|
error: t('errors.unauthorized')
|
|
}, status: :unauthorized
|
|
return
|
|
end
|
|
end
|
|
|
|
def load_tenant_data_for_billing
|
|
# needed because ApplicationController#load_tenant_data is not called for this action
|
|
@tenant = Current.tenant
|
|
@tenant_setting = @tenant.tenant_setting
|
|
@tenant_billing = @tenant.tenant_billing
|
|
@boards = Board.select(:id, :name, :slug).order(order: :asc)
|
|
@header_full_urls = true
|
|
I18n.locale = @tenant.locale
|
|
|
|
# needed because signing out from 'billing' subdomain cause authenticity token error
|
|
@disable_sign_out = true
|
|
end
|
|
end |