Add option for FOSS contribution payments (#730)

* First commit

* Use subscription flow for monetary contributions

* Removed only part of the SPAN

* Localize Stripe payments message

* More localization of contribution strings

* Missed two billing to payment changes

* Fix tests

* Localization of "Open Demo" strings

* Fix grammar error

* Update for consistency

* Localize CTA

* More localilzation strings
This commit is contained in:
Juan José Mata
2026-01-21 20:45:04 +01:00
committed by GitHub
parent 8e36c8e736
commit 4e425ce4e5
44 changed files with 259 additions and 98 deletions

View File

@@ -5,7 +5,7 @@ class SubscriptionsController < ApplicationController
# Upgrade page for unsubscribed users
def upgrade
if Current.family.subscription&.active?
redirect_to root_path, notice: "You are already subscribed."
redirect_to root_path, notice: "You are already contributing. Thank you!"
else
@plan = params[:plan] || "annual"
render layout: "onboardings"

View File

@@ -1,5 +1,5 @@
class Subscription < ApplicationRecord
TRIAL_DAYS = 14
TRIAL_DAYS = 45
belongs_to :family
@@ -28,11 +28,11 @@ class Subscription < ApplicationRecord
def name
case interval
when "month"
"Monthly Plan"
"Monthly Contribution"
when "year"
"Annual Plan"
"Annual Contribution"
else
"Free trial"
"Open demo"
end
end
end

View File

@@ -91,12 +91,12 @@
<div class="px-4 py-3 space-y-4 bg-container shadow-border-xs rounded-xl">
<div class="flex items-start justify-between">
<div>
<p class="text-sm font-medium text-primary">Free trial</p>
<p class="text-sm text-secondary"><%= Current.family.days_left_in_trial %> days remaining</p>
<p class="text-sm font-medium text-primary"><%= t("layouts.trial.open_demo") %></p>
<p class="text-sm text-secondary"><%= t("layouts.trial.data_deleted_in_days", days: Current.family.days_left_in_trial) %></p>
</div>
<%= render DS::Link.new(
text: "Upgrade",
text: t("layouts.trial.contribute"),
href: upgrade_subscription_path,
) %>
</div>

View File

@@ -17,21 +17,21 @@
<%= image_tag "logo-color.png", class: "w-16 mb-6" %>
<p class="text-xl lg:text-3xl text-primary font-display font-medium">
Try Sure for 14 days.
Try Sure for 45 days
</p>
<h2 class="text-xl lg:text-3xl font-display text-secondary font-medium mb-2">
No credit card required
Data will be deleted then
</h2>
<p class="text-sm text-secondary text-center mb-8">
Starting the trial activates your account for Sure. You won't need to enter payment details.
Starting today you can give the product a good look. <br/>If you like it, self-host or contribute to continue using it here.
</p>
<div class="w-full">
<% if Current.family.can_start_trial? %>
<%= render DS::Button.new(
text: "Try Sure for 14 days",
text: "Try Sure for 45 days",
href: subscription_path,
full_width: true,
data: { turbo: false }
@@ -53,7 +53,7 @@
</div>
<div class="space-y-8">
<h2 class="text-center text-lg lg:text-2xl font-medium text-primary">How your trial will work</h2>
<h2 class="text-center text-lg lg:text-2xl font-medium text-primary">How things work here</h2>
<div class="flex gap-3">
<div class="rounded-xl p-1 bg-gray-400/20 theme-dark:bg-gray-500/20 flex flex-col justify-between items-center text-secondary">
@@ -65,70 +65,19 @@
<div class="space-y-12">
<div class="space-y-1.5 text-sm">
<p class="text-primary font-medium">Today</p>
<p class="text-secondary">You'll get free access to Sure for 14 days</p>
<p class="text-secondary">You'll get free access to Sure for 45 days on our AWS.</p>
</div>
<div class="space-y-1.5 text-sm">
<p class="text-primary font-medium">In 13 days (<%= 13.days.from_now.strftime("%B %d") %>)</p>
<p class="text-secondary">We'll notify you to remind you when your trial will end.</p>
<p class="text-primary font-medium">In 40 days (<%= 40.days.from_now.strftime("%B %d") %>)</p>
<p class="text-secondary">We'll notify you to remind you to export your data.</p>
</div>
<div class="space-y-1.5 text-sm">
<p class="text-primary font-medium">In 14 days (<%= 14.days.from_now.strftime("%B %d") %>)</p>
<p class="text-secondary">Your trial ends &mdash; subscribe to continue using Sure</p>
<p class="text-primary font-medium">In 45 days (<%= 45.days.from_now.strftime("%B %d") %>)</p>
<p class="text-secondary">We delete your data &mdash; contribute to continue using Sure here!</p>
</div>
</div>
</div>
</div>
<div class="space-y-8 max-w-2xl mx-auto">
<h2 class="text-center text-lg lg:text-2xl font-medium text-primary">Here's what's included</h2>
<div class="grid grid-cols-1 lg:grid-cols-3 gap-x-12 gap-y-6 text-secondary">
<div class="flex flex-col gap-4 items-center">
<%= render DS::FilledIcon.new(icon: "landmark", variant: :surface) %>
<p class="text-sm text-primary text-center">More than 10,000 institutions to connect to</p>
</div>
<div class="flex flex-col gap-4 items-center">
<%= render DS::FilledIcon.new(icon: "layers", variant: :surface) %>
<p class="text-sm text-primary text-center">Connect unlimited accounts and account types</p>
</div>
<div class="flex flex-col gap-4 items-center">
<%= render DS::FilledIcon.new(icon: "chart-line", variant: :surface) %>
<p class="text-sm text-primary text-center">Performance and investment returns across portfolio</p>
</div>
<div class="flex flex-col gap-4 items-center">
<%= render DS::FilledIcon.new(icon: "credit-card", variant: :surface) %>
<p class="text-sm text-primary text-center">Comprehensive transaction tracking experience</p>
</div>
<div class="flex flex-col gap-4 items-center">
<%= render "chats/ai_avatar" %>
<p class="text-sm text-primary text-center">Unlimited access and chats with Sure AI</p>
</div>
<div class="flex flex-col gap-4 items-center">
<%= render DS::FilledIcon.new(icon: "keyboard", variant: :surface) %>
<p class="text-sm text-primary text-center">Manual account tracking that works well</p>
</div>
<div class="flex flex-col gap-4 items-center">
<%= render DS::FilledIcon.new(icon: "globe-2", variant: :surface) %>
<p class="text-sm text-primary text-center">Multiple currencies and near global coverage</p>
</div>
<div class="flex flex-col gap-4 items-center">
<%= render DS::FilledIcon.new(icon: "ship", variant: :surface) %>
<p class="text-sm text-primary text-center">Early access to newly released features</p>
</div>
<div class="flex flex-col gap-4 items-center">
<%= render DS::FilledIcon.new(icon: "messages-square", variant: :surface) %>
<p class="text-sm text-primary text-center">Priority human support from team</p>
</div>
</div>
</div>
</div>

View File

@@ -13,22 +13,22 @@
<div class="text-sm space-y-1">
<% if @family.has_active_subscription? %>
<p class="text-primary">
<span>You are currently subscribed to the <span class="font-medium"><%= @family.subscription.name %></span>.</span>
<span>Currently on the <span class="font-medium"><%= @family.subscription.name %></span>.</span>
<% if @family.next_payment_date %>
<span>Your plan renews on <span class="font-medium"><%= @family.next_payment_date.strftime("%B %d, %Y") %></span>.</span>
<span><%= t("views.settings.payments.renewal", date: l(@family.next_payment_date, format: :long)) %></span>
<% end %>
</p>
<% elsif @family.trialing? %>
<p class="text-primary">
You are currently trialing <%= product_name %>
Currently using the open demo of <%= product_name %> <br />
<span class="text-secondary">
(<%= @family.days_left_in_trial %> days remaining)
(Data will be deleted in <%= @family.days_left_in_trial %> days)
</span>
</p>
<% else %>
<p class="text-primary">You are currently <span class="font-medium">not subscribed</span></p>
<p class="text-secondary">Once you subscribe to <%= product_name %>, you'll see your payment settings here.</p>
<p class="text-primary">You are currently <span class="font-medium">not contributing</span></p>
<p class="text-secondary">Contributions to <%= product_name %> will show here.</p>
<% end %>
</div>
</div>
@@ -44,7 +44,7 @@
) %>
<% else %>
<%= render DS::Link.new(
text: "Choose plan",
text: "Choose level",
variant: "primary",
icon: "plus",
icon_position: "right",

View File

@@ -1,6 +1,6 @@
<%# locals: (plan:, form:, checked: false) %>
<% price = plan == "annual" ? 90 : 9 %>
<% price = plan == "annual" ? 25 : 2.50 %>
<% frequency = plan == "annual" ? "/year" : "/month" %>
<div class="relative">
@@ -14,17 +14,13 @@
<div class="mt-auto flex items-end gap-1">
<p class="font-display text-xl lg:text-3xl font-medium text-primary">$<%= price %><%= frequency %></p>
<% if plan == "annual" %>
<span class="text-sm text-secondary mb-1">or <%= Money.new(price.to_f / 52).format %>/week</span>
<% end %>
</div>
<p class="text-sm text-secondary">
<% if plan == "annual" %>
Billed annually, 2 months free
Charged annually
<% else %>
Billed monthly
Charged monthly
<% end %>
</p>
<% end %>

View File

@@ -25,18 +25,18 @@
<%= image_tag "logo-color.png", class: "w-16 mb-6" %>
<% if Current.family.trialing? %>
<p class="text-xl lg:text-3xl text-primary font-display font-medium">Your trial has <%= Current.family.days_left_in_trial %> days remaining</p>
<p class="text-xl lg:text-3xl text-primary font-display font-medium"><%= t('subscriptions.upgrade.trialing', days: Current.family.days_left_in_trial) %></p>
<% else %>
<p class="text-xl lg:text-3xl text-primary font-display font-medium">Your trial is over</p>
<p class="text-xl lg:text-3xl text-primary font-display font-medium"><%= t('subscriptions.upgrade.trial_over') %></p>
<% end %>
<h2 class="text-xl lg:text-3xl font-display font-medium mb-2">
<span class="text-secondary">Unlock</span>
<span class="bg-gradient-to-r from-[#EABE7F] to-[#957049] bg-clip-text text-transparent">Sure</span>
<span class="text-secondary">today</span>
<span class="text-secondary"><%= t('subscriptions.upgrade.header.support') %></span>
<span class="bg-gradient-to-r from-[#EABE7F] to-[#957049] bg-clip-text text-transparent"><%= t('subscriptions.upgrade.header.sure') %></span>
<span class="text-secondary"><%= t('subscriptions.upgrade.header.today') %></span>
</h2>
<p class="text-sm text-secondary mb-8">To continue using Sure pick a plan below.</p>
<p class="text-sm text-secondary mb-8"><%= t('subscriptions.upgrade.cta') %></p>
<%= form_with url: new_subscription_path, method: :get, class: "max-w-xs", data: { turbo: false } do |form| %>
<div class="space-y-4 mb-6">
@@ -46,13 +46,13 @@
<div class="text-center space-y-2">
<%= render DS::Button.new(
text: "Subscribe and unlock Sure",
text: t('subscriptions.upgrade.contribute_and_support_sure'),
variant: "primary",
full_width: true
) %>
<p class="text-xs text-secondary">
In the next step, you'll be redirected to Stripe which handles our payments.
<%= t('subscriptions.upgrade.redirect_to_stripe') %>
</p>
</div>
<% end %>