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>