mirror of
https://github.com/maybe-finance/maybe.git
synced 2025-07-19 21:29:38 +02:00
Billing (#1269)
* Change env SELF_HOSTING_ENABLED to SELF_HOSTED * Initial Stripe implementation * Fix portal link * Use webhook signatures * Migrated to new Stripe gem conventions Also updated resource routing * Added faraday-multipart gem to resolve middleware notice * Merge fix * Merge fix * Temporary upgrade prompt for early access * Lint fix * i18n fixes * Remove catch-all rescue * Update .env.example
This commit is contained in:
parent
41dff228e8
commit
31f3ff6a16
27 changed files with 225 additions and 12 deletions
12
.env.example
12
.env.example
|
@ -36,8 +36,8 @@ SENTRY_DSN=
|
||||||
# This is useful for controlling who can sign up for your Maybe instance.
|
# This is useful for controlling who can sign up for your Maybe instance.
|
||||||
REQUIRE_INVITE_CODE=false
|
REQUIRE_INVITE_CODE=false
|
||||||
|
|
||||||
# Enables self hosting features
|
# Enables self hosting features (should be set to true for most folks)
|
||||||
SELF_HOSTING_ENABLED=false
|
SELF_HOSTED=true
|
||||||
|
|
||||||
# The hosting platform used to deploy the app (e.g. "render")
|
# The hosting platform used to deploy the app (e.g. "render")
|
||||||
# `localhost` (or unset) is used for local development and testing
|
# `localhost` (or unset) is used for local development and testing
|
||||||
|
@ -94,3 +94,11 @@ GITHUB_REPO_BRANCH=main
|
||||||
# CLOUDFLARE_ACCESS_KEY_ID=
|
# CLOUDFLARE_ACCESS_KEY_ID=
|
||||||
# CLOUDFLARE_SECRET_ACCESS_KEY=
|
# CLOUDFLARE_SECRET_ACCESS_KEY=
|
||||||
# CLOUDFLARE_BUCKET=
|
# CLOUDFLARE_BUCKET=
|
||||||
|
|
||||||
|
# ======================================================================================================
|
||||||
|
# Billing Module - responsible for handling billing
|
||||||
|
# ======================================================================================================
|
||||||
|
#
|
||||||
|
STRIPE_PUBLISHABLE_KEY=
|
||||||
|
STRIPE_SECRET_KEY=
|
||||||
|
STRIPE_WEBHOOK_SECRET=
|
1
Gemfile
1
Gemfile
|
@ -45,6 +45,7 @@ gem "rails-settings-cached"
|
||||||
gem "tzinfo-data", platforms: %i[windows jruby]
|
gem "tzinfo-data", platforms: %i[windows jruby]
|
||||||
gem "csv"
|
gem "csv"
|
||||||
gem "redcarpet"
|
gem "redcarpet"
|
||||||
|
gem "stripe"
|
||||||
gem "intercom-rails"
|
gem "intercom-rails"
|
||||||
|
|
||||||
group :development, :test do
|
group :development, :test do
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
GIT
|
GIT
|
||||||
remote: https://github.com/maybe-finance/lucide-rails.git
|
remote: https://github.com/maybe-finance/lucide-rails.git
|
||||||
revision: 79d989593ee4ac6c50106ec5e4d2bd4ec8f5af87
|
revision: 272e5fb8418ea458da3995d6abe0ba0ceee9c9f0
|
||||||
specs:
|
specs:
|
||||||
lucide-rails (0.2.0)
|
lucide-rails (0.2.0)
|
||||||
railties (>= 4.1.0)
|
railties (>= 4.1.0)
|
||||||
|
@ -414,6 +414,7 @@ GEM
|
||||||
stimulus-rails (1.3.4)
|
stimulus-rails (1.3.4)
|
||||||
railties (>= 6.0.0)
|
railties (>= 6.0.0)
|
||||||
stringio (3.1.1)
|
stringio (3.1.1)
|
||||||
|
stripe (13.0.0)
|
||||||
tailwindcss-rails (2.7.7)
|
tailwindcss-rails (2.7.7)
|
||||||
railties (>= 7.0.0)
|
railties (>= 7.0.0)
|
||||||
tailwindcss-rails (2.7.7-aarch64-linux)
|
tailwindcss-rails (2.7.7-aarch64-linux)
|
||||||
|
@ -506,6 +507,7 @@ DEPENDENCIES
|
||||||
simplecov
|
simplecov
|
||||||
stackprof
|
stackprof
|
||||||
stimulus-rails
|
stimulus-rails
|
||||||
|
stripe
|
||||||
tailwindcss-rails
|
tailwindcss-rails
|
||||||
turbo-rails
|
turbo-rails
|
||||||
tzinfo-data
|
tzinfo-data
|
||||||
|
|
2
app/controllers/settings/billings_controller.rb
Normal file
2
app/controllers/settings/billings_controller.rb
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
class Settings::BillingsController < SettingsController
|
||||||
|
end
|
36
app/controllers/subscriptions_controller.rb
Normal file
36
app/controllers/subscriptions_controller.rb
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
class SubscriptionsController < ApplicationController
|
||||||
|
def new
|
||||||
|
client = Stripe::StripeClient.new(ENV["STRIPE_SECRET_KEY"])
|
||||||
|
|
||||||
|
if Current.family.stripe_customer_id.blank?
|
||||||
|
customer = client.v1.customers.create(
|
||||||
|
email: Current.family.primary_user.email,
|
||||||
|
metadata: { family_id: Current.family.id }
|
||||||
|
)
|
||||||
|
Current.family.update(stripe_customer_id: customer.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
session = client.v1.checkout.sessions.create({
|
||||||
|
customer: Current.family.stripe_customer_id,
|
||||||
|
line_items: [ {
|
||||||
|
price: ENV["STRIPE_PLAN_ID"],
|
||||||
|
quantity: 1
|
||||||
|
} ],
|
||||||
|
mode: "subscription",
|
||||||
|
success_url: settings_billing_url,
|
||||||
|
cancel_url: settings_billing_url
|
||||||
|
})
|
||||||
|
|
||||||
|
redirect_to session.url, allow_other_host: true, status: :see_other
|
||||||
|
end
|
||||||
|
|
||||||
|
def show
|
||||||
|
client = Stripe::StripeClient.new(ENV["STRIPE_SECRET_KEY"])
|
||||||
|
|
||||||
|
portal_session = client.v1.billing_portal.sessions.create(
|
||||||
|
customer: Current.family.stripe_customer_id,
|
||||||
|
return_url: settings_billing_url
|
||||||
|
)
|
||||||
|
redirect_to portal_session.url, allow_other_host: true, status: :see_other
|
||||||
|
end
|
||||||
|
end
|
61
app/controllers/webhooks_controller.rb
Normal file
61
app/controllers/webhooks_controller.rb
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
class WebhooksController < ApplicationController
|
||||||
|
skip_before_action :verify_authenticity_token, only: [ :stripe ]
|
||||||
|
skip_authentication
|
||||||
|
|
||||||
|
def stripe
|
||||||
|
webhook_body = request.body.read
|
||||||
|
sig_header = request.env["HTTP_STRIPE_SIGNATURE"]
|
||||||
|
client = Stripe::StripeClient.new(ENV["STRIPE_SECRET_KEY"])
|
||||||
|
|
||||||
|
begin
|
||||||
|
thin_event = client.parse_thin_event(webhook_body, sig_header, ENV["STRIPE_WEBHOOK_SECRET"])
|
||||||
|
|
||||||
|
event = client.v1.events.retrieve(thin_event.id)
|
||||||
|
|
||||||
|
case event.type
|
||||||
|
when /^customer\.subscription\./
|
||||||
|
handle_subscription_event(event)
|
||||||
|
when "customer.created", "customer.updated", "customer.deleted"
|
||||||
|
handle_customer_event(event)
|
||||||
|
else
|
||||||
|
Rails.logger.info "Unhandled event type: #{event.type}"
|
||||||
|
end
|
||||||
|
|
||||||
|
rescue JSON::ParserError
|
||||||
|
render json: { error: "Invalid payload" }, status: :bad_request
|
||||||
|
return
|
||||||
|
rescue Stripe::SignatureVerificationError
|
||||||
|
render json: { error: "Invalid signature" }, status: :bad_request
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
render json: { received: true }, status: :ok
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def handle_subscription_event(event)
|
||||||
|
subscription = event.data.object
|
||||||
|
family = Family.find_by(stripe_customer_id: subscription.customer)
|
||||||
|
|
||||||
|
if family
|
||||||
|
family.update(
|
||||||
|
stripe_plan_id: subscription.plan.id,
|
||||||
|
stripe_subscription_status: subscription.status
|
||||||
|
)
|
||||||
|
else
|
||||||
|
Rails.logger.error "Family not found for Stripe customer ID: #{subscription.customer}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_customer_event(event)
|
||||||
|
customer = event.data.object
|
||||||
|
family = Family.find_by(stripe_customer_id: customer.id)
|
||||||
|
|
||||||
|
if family
|
||||||
|
family.update(stripe_customer_id: customer.id)
|
||||||
|
else
|
||||||
|
Rails.logger.error "Family not found for Stripe customer ID: #{customer.id}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
2
app/helpers/settings/billing_helper.rb
Normal file
2
app/helpers/settings/billing_helper.rb
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
module Settings::BillingHelper
|
||||||
|
end
|
|
@ -3,6 +3,7 @@ module SettingsHelper
|
||||||
{ name: I18n.t("settings.nav.profile_label"), path: :settings_profile_path },
|
{ name: I18n.t("settings.nav.profile_label"), path: :settings_profile_path },
|
||||||
{ name: I18n.t("settings.nav.preferences_label"), path: :settings_preferences_path },
|
{ name: I18n.t("settings.nav.preferences_label"), path: :settings_preferences_path },
|
||||||
{ name: I18n.t("settings.nav.self_hosting_label"), path: :settings_hosting_path, condition: :self_hosted? },
|
{ name: I18n.t("settings.nav.self_hosting_label"), path: :settings_hosting_path, condition: :self_hosted? },
|
||||||
|
{ name: I18n.t("settings.nav.billing_label"), path: :settings_billing_path },
|
||||||
{ name: I18n.t("settings.nav.accounts_label"), path: :accounts_path },
|
{ name: I18n.t("settings.nav.accounts_label"), path: :accounts_path },
|
||||||
{ name: I18n.t("settings.nav.tags_label"), path: :tags_path },
|
{ name: I18n.t("settings.nav.tags_label"), path: :tags_path },
|
||||||
{ name: I18n.t("settings.nav.categories_label"), path: :categories_path },
|
{ name: I18n.t("settings.nav.categories_label"), path: :categories_path },
|
||||||
|
|
2
app/helpers/subscription_helper.rb
Normal file
2
app/helpers/subscription_helper.rb
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
module SubscriptionHelper
|
||||||
|
end
|
2
app/helpers/webhooks_helper.rb
Normal file
2
app/helpers/webhooks_helper.rb
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
module WebhooksHelper
|
||||||
|
end
|
|
@ -127,4 +127,12 @@ class Family < ApplicationRecord
|
||||||
def synth_usage
|
def synth_usage
|
||||||
self.class.synth_provider&.usage
|
self.class.synth_provider&.usage
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def subscribed?
|
||||||
|
stripe_subscription_status.present? && stripe_subscription_status == "active"
|
||||||
|
end
|
||||||
|
|
||||||
|
def primary_user
|
||||||
|
users.order(:created_at).first
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -22,7 +22,9 @@
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<% if @accounts.empty? %>
|
<% if !Current.family.subscribed? && !self_hosted? %>
|
||||||
|
<%= render "shared/subscribe_prompt" %>
|
||||||
|
<% elsif @accounts.empty? %>
|
||||||
<%= render "shared/no_account_empty_state" %>
|
<%= render "shared/no_account_empty_state" %>
|
||||||
<% else %>
|
<% else %>
|
||||||
<section class="flex gap-4">
|
<section class="flex gap-4">
|
||||||
|
|
|
@ -26,6 +26,9 @@
|
||||||
<%= sidebar_link_to t(".self_hosting_label"), settings_hosting_path, icon: "database" %>
|
<%= sidebar_link_to t(".self_hosting_label"), settings_hosting_path, icon: "database" %>
|
||||||
</li>
|
</li>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
<li>
|
||||||
|
<%= sidebar_link_to t(".billing_label"), settings_billing_path, icon: "circle-dollar-sign" %>
|
||||||
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<%= sidebar_link_to t(".accounts_label"), accounts_path, icon: "layers" %>
|
<%= sidebar_link_to t(".accounts_label"), accounts_path, icon: "layers" %>
|
||||||
</li>
|
</li>
|
||||||
|
|
16
app/views/settings/billings/show.html.erb
Normal file
16
app/views/settings/billings/show.html.erb
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<% content_for :sidebar do %>
|
||||||
|
<%= render "settings/nav" %>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<div class="space-y-4">
|
||||||
|
<h1 class="text-gray-900 text-xl font-medium mb-4"><%= t(".page_title") %></h1>
|
||||||
|
<%= settings_section title: t(".subscription_title"), subtitle: t(".subscription_subtitle") do %>
|
||||||
|
<% if Current.family.stripe_plan_id.blank? %>
|
||||||
|
<%= link_to t(".subscribe_button"), new_subscription_path, class: "w-fit flex text-white text-sm font-medium items-center gap-1 bg-gray-900 rounded-lg p-2", data: { turbo: false } %>
|
||||||
|
<% else %>
|
||||||
|
<%= link_to t(".manage_subscription_button"), subscription_path, class: "w-fit flex text-white text-sm font-medium items-center gap-1 bg-gray-900 rounded-lg p-2", data: { turbo: false } %>
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<%= settings_nav_footer %>
|
||||||
|
</div>
|
16
app/views/shared/_subscribe_prompt.html.erb
Normal file
16
app/views/shared/_subscribe_prompt.html.erb
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<div class="flex justify-center items-center h-[800px]">
|
||||||
|
<div class="text-center flex flex-col gap-4 items-center max-w-[300px]">
|
||||||
|
<%= lucide_icon "circle-fading-arrow-up", class: "w-8 h-8 text-green-500" %>
|
||||||
|
|
||||||
|
<div class="space-y-1 text-sm">
|
||||||
|
<p class="text-gray-900 font-medium"><%= t(".title") %></p>
|
||||||
|
<p class="text-gray-500"><%= t(".subtitle") %></p>
|
||||||
|
<p class="text-gray-400 text-xs"><%= t(".guarantee") %></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<%= link_to new_subscription_path, class: "btn btn--primary flex items-center gap-1" do %>
|
||||||
|
<%= lucide_icon("credit-card", class: "w-5 h-5") %>
|
||||||
|
<span><%= t(".subscribe") %></span>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -29,6 +29,6 @@ module Maybe
|
||||||
# TODO: This is here for incremental adoption of localization. This can be removed when all translations are implemented.
|
# TODO: This is here for incremental adoption of localization. This can be removed when all translations are implemented.
|
||||||
config.i18n.fallbacks = true
|
config.i18n.fallbacks = true
|
||||||
|
|
||||||
config.app_mode = (ENV["SELF_HOSTING_ENABLED"] == "true" ? "self_hosted" : "managed").inquiry
|
config.app_mode = (ENV["SELF_HOSTED"] == "true" ? "self_hosted" : "managed").inquiry
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,8 +1,16 @@
|
||||||
---
|
---
|
||||||
en:
|
en:
|
||||||
settings:
|
settings:
|
||||||
|
billings:
|
||||||
|
show:
|
||||||
|
manage_subscription_button: Manage subscription
|
||||||
|
page_title: Billing
|
||||||
|
subscribe_button: Subscribe
|
||||||
|
subscription_subtitle: Manage your subscription and billing details
|
||||||
|
subscription_title: Manage subscription
|
||||||
nav:
|
nav:
|
||||||
accounts_label: Accounts
|
accounts_label: Accounts
|
||||||
|
billing_label: Billing
|
||||||
categories_label: Categories
|
categories_label: Categories
|
||||||
feedback_label: Feedback
|
feedback_label: Feedback
|
||||||
general_section_title: General
|
general_section_title: General
|
||||||
|
|
|
@ -13,6 +13,12 @@ en:
|
||||||
no_account_subtitle: Since no accounts have been added, there's no data to display.
|
no_account_subtitle: Since no accounts have been added, there's no data to display.
|
||||||
Add your first accounts to start viewing dashboard data.
|
Add your first accounts to start viewing dashboard data.
|
||||||
no_account_title: No accounts yet
|
no_account_title: No accounts yet
|
||||||
|
subscribe_prompt:
|
||||||
|
guarantee: We're reasonable people here. If you're not happy or something doesn't
|
||||||
|
work, we'll gladly refund you.
|
||||||
|
subscribe: Upgrade your account
|
||||||
|
subtitle: To continue using Maybe, please subscribe!
|
||||||
|
title: Upgrade
|
||||||
upgrade_notification:
|
upgrade_notification:
|
||||||
app_upgraded: The app has been upgraded to %{version}.
|
app_upgraded: The app has been upgraded to %{version}.
|
||||||
dismiss: Dismiss
|
dismiss: Dismiss
|
||||||
|
|
|
@ -13,8 +13,11 @@ Rails.application.routes.draw do
|
||||||
resource :profile, only: %i[show update destroy]
|
resource :profile, only: %i[show update destroy]
|
||||||
resource :preferences, only: %i[show update]
|
resource :preferences, only: %i[show update]
|
||||||
resource :hosting, only: %i[show update]
|
resource :hosting, only: %i[show update]
|
||||||
|
resource :billing, only: :show
|
||||||
end
|
end
|
||||||
|
|
||||||
|
resource :subscription, only: %i[new show]
|
||||||
|
|
||||||
resources :tags, except: %i[show destroy] do
|
resources :tags, except: %i[show destroy] do
|
||||||
resources :deletions, only: %i[new create], module: :tag
|
resources :deletions, only: %i[new create], module: :tag
|
||||||
end
|
end
|
||||||
|
@ -104,6 +107,9 @@ Rails.application.routes.draw do
|
||||||
|
|
||||||
resources :currencies, only: %i[show]
|
resources :currencies, only: %i[show]
|
||||||
|
|
||||||
|
# Stripe webhook endpoint
|
||||||
|
post "webhooks/stripe", to: "webhooks#stripe"
|
||||||
|
|
||||||
# Reveal health status on /up that returns 200 if the app boots with no exceptions, otherwise 500.
|
# Reveal health status on /up that returns 200 if the app boots with no exceptions, otherwise 500.
|
||||||
# Can be used by load balancers and uptime monitors to verify that the app is live.
|
# Can be used by load balancers and uptime monitors to verify that the app is live.
|
||||||
get "up" => "rails/health#show", as: :rails_health_check
|
get "up" => "rails/health#show", as: :rails_health_check
|
||||||
|
|
7
db/migrate/20241007211438_add_billing_to_families.rb
Normal file
7
db/migrate/20241007211438_add_billing_to_families.rb
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
class AddBillingToFamilies < ActiveRecord::Migration[7.2]
|
||||||
|
def change
|
||||||
|
add_column :families, :stripe_plan_id, :string
|
||||||
|
add_column :families, :stripe_customer_id, :string
|
||||||
|
add_column :families, :stripe_subscription_status, :string, default: "incomplete"
|
||||||
|
end
|
||||||
|
end
|
7
db/schema.rb
generated
7
db/schema.rb
generated
|
@ -10,7 +10,7 @@
|
||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema[7.2].define(version: 2024_10_03_163448) do
|
ActiveRecord::Schema[7.2].define(version: 2024_10_07_211438) do
|
||||||
# These are extensions that must be enabled in order to support this database
|
# These are extensions that must be enabled in order to support this database
|
||||||
enable_extension "pgcrypto"
|
enable_extension "pgcrypto"
|
||||||
enable_extension "plpgsql"
|
enable_extension "plpgsql"
|
||||||
|
@ -120,7 +120,7 @@ ActiveRecord::Schema[7.2].define(version: 2024_10_03_163448) do
|
||||||
t.boolean "is_active", default: true, null: false
|
t.boolean "is_active", default: true, null: false
|
||||||
t.date "last_sync_date"
|
t.date "last_sync_date"
|
||||||
t.uuid "institution_id"
|
t.uuid "institution_id"
|
||||||
t.virtual "classification", type: :string, as: "\nCASE\n WHEN ((accountable_type)::text = ANY ((ARRAY['Loan'::character varying, 'CreditCard'::character varying, 'OtherLiability'::character varying])::text[])) THEN 'liability'::text\n ELSE 'asset'::text\nEND", stored: true
|
t.virtual "classification", type: :string, as: "\nCASE\n WHEN ((accountable_type)::text = ANY (ARRAY[('Loan'::character varying)::text, ('CreditCard'::character varying)::text, ('OtherLiability'::character varying)::text])) THEN 'liability'::text\n ELSE 'asset'::text\nEND", stored: true
|
||||||
t.uuid "import_id"
|
t.uuid "import_id"
|
||||||
t.index ["accountable_type"], name: "index_accounts_on_accountable_type"
|
t.index ["accountable_type"], name: "index_accounts_on_accountable_type"
|
||||||
t.index ["family_id"], name: "index_accounts_on_family_id"
|
t.index ["family_id"], name: "index_accounts_on_family_id"
|
||||||
|
@ -215,6 +215,9 @@ ActiveRecord::Schema[7.2].define(version: 2024_10_03_163448) do
|
||||||
t.string "currency", default: "USD"
|
t.string "currency", default: "USD"
|
||||||
t.datetime "last_synced_at"
|
t.datetime "last_synced_at"
|
||||||
t.string "locale", default: "en"
|
t.string "locale", default: "en"
|
||||||
|
t.string "stripe_plan_id"
|
||||||
|
t.string "stripe_customer_id"
|
||||||
|
t.string "stripe_subscription_status", default: "incomplete"
|
||||||
end
|
end
|
||||||
|
|
||||||
create_table "good_job_batches", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
|
create_table "good_job_batches", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
|
||||||
|
|
|
@ -40,7 +40,7 @@ services:
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
SELF_HOSTING_ENABLED: "true"
|
SELF_HOSTED: "true"
|
||||||
RAILS_FORCE_SSL: "false"
|
RAILS_FORCE_SSL: "false"
|
||||||
RAILS_ASSUME_SSL: "false"
|
RAILS_ASSUME_SSL: "false"
|
||||||
GOOD_JOB_EXECUTION_MODE: async
|
GOOD_JOB_EXECUTION_MODE: async
|
||||||
|
|
|
@ -21,7 +21,7 @@ services:
|
||||||
name: maybe
|
name: maybe
|
||||||
property: connectionString
|
property: connectionString
|
||||||
|
|
||||||
- key: SELF_HOSTING_ENABLED
|
- key: SELF_HOSTED
|
||||||
value: true
|
value: true
|
||||||
- key: HOSTING_PLATFORM
|
- key: HOSTING_PLATFORM
|
||||||
value: render
|
value: render
|
||||||
|
|
7
test/controllers/settings/billings_controller_test.rb
Normal file
7
test/controllers/settings/billings_controller_test.rb
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
require "test_helper"
|
||||||
|
|
||||||
|
class Settings::BillingsControllerTest < ActionDispatch::IntegrationTest
|
||||||
|
# test "the truth" do
|
||||||
|
# assert true
|
||||||
|
# end
|
||||||
|
end
|
7
test/controllers/subscriptions_controller_test.rb
Normal file
7
test/controllers/subscriptions_controller_test.rb
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
require "test_helper"
|
||||||
|
|
||||||
|
class SubscriptionsControllerTest < ActionDispatch::IntegrationTest
|
||||||
|
# test "the truth" do
|
||||||
|
# assert true
|
||||||
|
# end
|
||||||
|
end
|
7
test/controllers/webhooks_controller_test.rb
Normal file
7
test/controllers/webhooks_controller_test.rb
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
require "test_helper"
|
||||||
|
|
||||||
|
class WebhooksControllerTest < ActionDispatch::IntegrationTest
|
||||||
|
# test "the truth" do
|
||||||
|
# assert true
|
||||||
|
# end
|
||||||
|
end
|
|
@ -7,7 +7,7 @@ end
|
||||||
|
|
||||||
require_relative "../config/environment"
|
require_relative "../config/environment"
|
||||||
|
|
||||||
ENV["SELF_HOSTING_ENABLED"] = "false"
|
ENV["SELF_HOSTED"] = "false"
|
||||||
ENV["UPGRADES_ENABLED"] = "false"
|
ENV["UPGRADES_ENABLED"] = "false"
|
||||||
ENV["RAILS_ENV"] ||= "test"
|
ENV["RAILS_ENV"] ||= "test"
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue