Add 14-day free trial + trial cap for siGit Code Cloud
GTM: let users try cloud before paying (card-required trial converts well), while bounding trial COGS. - StripeService: trial_period_days=14 on checkout subscriptions - Subscription: trialing users get TRIAL_ALLOWANCE (300) cloud requests, well below the paid caps (pro 2000 / team 6000) - Copy: 402 + web CTAs invite the free trial (Start 14-day free trial), billing page shows trial status + trial-end date Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Seto Elkahfi committed
Jun 24, 2026 at 12:06 UTC
650bb5d6f7a6347de8a6254c7623f879c5159170
4 files changed
+33
-11
app/controllers/api/v1/chat_completions_controller.rb
+2
-1
index 2e64c1f..4661557 100644
--- a/app/controllers/api/v1/chat_completions_controller.rb
+++ b/app/controllers/api/v1/chat_completions_controller.rb
@@ -44,7 +44,8 @@ module Api
return if current_user&.entitled_to_cloud?
render json: error_body(
- "siGit Code Cloud requires a Pro subscription. Upgrade in your siGit account to use cloud models."
+ "Start your free siGit Code Cloud trial to use cloud models — 14 days free, then $20/mo. " \
+ "Begin it in your siGit account billing page."
), status: :payment_required
end
app/models/subscription.rb
+8
-2
index 120b5db..5184645 100644
--- a/app/models/subscription.rb
+++ b/app/models/subscription.rb
@@ -13,13 +13,19 @@ class Subscription < ApplicationRecord
# these are the included cloud requests before the cap kicks in.
CLOUD_ALLOWANCE = { "pro" => 2_000, "team" => 6_000 }.freeze
- # Entitled to siGit Code Cloud: on a paid plan in good standing.
+ # During the free trial, cap cloud usage well below the paid allowance so a
+ # trial can't run up an unbounded upstream bill. Enough to feel the product.
+ TRIAL_ALLOWANCE = 300
+
+ # Entitled to siGit Code Cloud: on a paid plan in good standing (incl. trial).
def entitled_to_cloud?
(pro? || team?) && (active? || trialing?)
end
- # Included cloud requests for this plan's billing period.
+ # Included cloud requests for this period. The trial gets a tighter cap.
def cloud_allowance
+ return TRIAL_ALLOWANCE if trialing?
+
CLOUD_ALLOWANCE.fetch(plan, 0)
end
app/services/stripe_service.rb
+8
-1
index 7e84de8..991eeb0 100644
--- a/app/services/stripe_service.rb
+++ b/app/services/stripe_service.rb
@@ -10,6 +10,10 @@ class StripeService
"team" => "sigit_code_team_monthly"
}.freeze
+ # Free-trial length for new subscriptions. Card is collected at checkout and
+ # Stripe auto-converts to paid when the trial ends. Tunable GTM knob.
+ TRIAL_DAYS = 14
+
# A subscription's price lookup_key → our plan.
PLAN_BY_LOOKUP_KEY = {
"sigit_code_pro_monthly" => :pro,
@@ -50,7 +54,10 @@ class StripeService
success_url: success_url,
cancel_url: cancel_url,
metadata: { user_id: user.id.to_s },
- subscription_data: { metadata: { user_id: user.id.to_s } }
+ subscription_data: {
+ metadata: { user_id: user.id.to_s },
+ trial_period_days: TRIAL_DAYS
+ }
}.compact)
session.url
app/views/billing/show.html.erb
+15
-7
index 7b1c9b3..4458f4d 100644
--- a/app/views/billing/show.html.erb
+++ b/app/views/billing/show.html.erb
@@ -24,16 +24,24 @@
<div class="px-6 py-6">
<% if @entitled %>
<p class="text-sm text-gray-300">
- You're on <span class="text-gray-100 font-medium"><%= @plan.to_s.capitalize %></span> — siGit Code Cloud is unlocked.
- <% if @period_end %>
- <span class="text-gray-500">Renews <%= @period_end.strftime("%b %-d, %Y") %>.</span>
+ <% if @status.to_s == "trialing" %>
+ You're on a <span class="text-gray-100 font-medium">free trial</span> of
+ <%= @plan.to_s.capitalize %> — siGit Code Cloud is unlocked.
+ <% if @period_end %>
+ <span class="text-gray-500">Trial ends <%= @period_end.strftime("%b %-d, %Y") %>, then $20/mo.</span>
+ <% end %>
+ <% else %>
+ You're on <span class="text-gray-100 font-medium"><%= @plan.to_s.capitalize %></span> — siGit Code Cloud is unlocked.
+ <% if @period_end %>
+ <span class="text-gray-500">Renews <%= @period_end.strftime("%b %-d, %Y") %>.</span>
+ <% end %>
<% end %>
</p>
<% else %>
<p class="text-sm text-gray-300">
You're on the free <span class="text-gray-100 font-medium">Garage</span> plan. AI runs
- <span class="text-gray-100">on your device</span> — private and free. Upgrade to use
- siGit Code Cloud (the Fast / Balanced / Large tiers) when you need more.
+ <span class="text-gray-100">on your device</span> — private and free. Try siGit Code Cloud
+ (the Fast / Balanced / Large tiers) free for 14 days when you need more.
</p>
<% end %>
</div>
@@ -63,8 +71,8 @@
<% if @entitled %>
<%= button_to "Manage billing", billing_portal_path, method: :post, data: { turbo: false }, class: "btn-primary cursor-pointer" %>
<% else %>
- <%= button_to "Upgrade to Pro", billing_checkout_path(plan: "pro"), method: :post, data: { turbo: false }, class: "btn-primary cursor-pointer" %>
- <%= button_to "Upgrade to Team", billing_checkout_path(plan: "team"), method: :post, data: { turbo: false }, class: "text-sm text-gray-400 hover:text-gray-200 transition-colors bg-transparent border-0 cursor-pointer" %>
+ <%= button_to "Start 14-day free trial", billing_checkout_path(plan: "pro"), method: :post, data: { turbo: false }, class: "btn-primary cursor-pointer" %>
+ <%= button_to "Try Team free", billing_checkout_path(plan: "team"), method: :post, data: { turbo: false }, class: "text-sm text-gray-400 hover:text-gray-200 transition-colors bg-transparent border-0 cursor-pointer" %>
<% end %>
</div>
</div>