This repository was archived by the owner on Jul 27, 2025. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Merged
Billing #1269
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
d2efece
Change env SELF_HOSTING_ENABLED to SELF_HOSTED
Shpigford ca42e08
Initial Stripe implementation
Shpigford f33918f
Fix portal link
Shpigford 49267d3
Use webhook signatures
Shpigford 91e3f69
Migrated to new Stripe gem conventions
Shpigford d18381e
Added faraday-multipart gem to resolve middleware notice
Shpigford 7d96aaf
Merge branch 'main' into billing
Shpigford 60ab843
Merge branch 'main' into billing
Shpigford e910ada
Merge fix
Shpigford f3a2091
Merge fix
Shpigford 935a9dd
Temporary upgrade prompt for early access
Shpigford 1a3a317
Lint fix
Shpigford 43b12e0
i18n fixes
Shpigford b1c39f5
Remove catch-all rescue
Shpigford 4a17cb7
Merge branch 'main' into billing
Shpigford 633baf5
Update .env.example
Shpigford File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| class Settings::BillingsController < SettingsController | ||
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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, | ||
Shpigford marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| 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 | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| module Settings::BillingHelper | ||
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| module SubscriptionHelper | ||
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| module WebhooksHelper | ||
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| require "test_helper" | ||
|
|
||
| class Settings::BillingsControllerTest < ActionDispatch::IntegrationTest | ||
| # test "the truth" do | ||
| # assert true | ||
| # end | ||
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| require "test_helper" | ||
|
|
||
| class SubscriptionsControllerTest < ActionDispatch::IntegrationTest | ||
| # test "the truth" do | ||
| # assert true | ||
| # end | ||
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| require "test_helper" | ||
|
|
||
| class WebhooksControllerTest < ActionDispatch::IntegrationTest | ||
| # test "the truth" do | ||
| # assert true | ||
| # end | ||
| end |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.