From bc09feafc1a3d293d933cc04fb810730fdfabc3f Mon Sep 17 00:00:00 2001 From: kbe Date: Tue, 16 Sep 2025 23:53:04 +0200 Subject: [PATCH] feat: complete promoter payout system with Stripe Connect onboarding --- .../promoter/payouts_controller.rb | 22 +++++++ app/services/stripe_connect_service.rb | 35 +++++++++++ app/views/promoter/payouts/index.html.erb | 41 ++++++++++++- app/views/promoter/payouts/show.html.erb | 60 ++++++++++++++++++- ...to_stripe_connected_account_id_on_users.rb | 2 +- 5 files changed, 155 insertions(+), 5 deletions(-) create mode 100644 app/services/stripe_connect_service.rb diff --git a/app/controllers/promoter/payouts_controller.rb b/app/controllers/promoter/payouts_controller.rb index 47e3584..0539d80 100644 --- a/app/controllers/promoter/payouts_controller.rb +++ b/app/controllers/promoter/payouts_controller.rb @@ -1,10 +1,32 @@ class Promoter::PayoutsController < ApplicationController + before_action :authenticate_user! + before_action :ensure_promoter + def index + @events = current_user.events.includes(:earnings).order(start_time: :desc) end def show + @event = current_user.events.find(params[:id]) + @earnings = @event.earnings end def create + @event = current_user.events.find(params[:event_id] || params[:id]) + + if @event.can_request_payout? + PayoutService.new.process_event_payout(@event) + redirect_to promoter_payout_path(@event), notice: "Payout requested successfully. It will be processed shortly." + else + redirect_to promoter_payouts_path, alert: "Cannot request payout: #{@event.can_request_payout? ? '' : 'Event not eligible for payout.'}" + end + end + + private + + def ensure_promoter + unless current_user.promoter? + redirect_to dashboard_path, alert: "Access denied. Promoter account required." + end end end diff --git a/app/services/stripe_connect_service.rb b/app/services/stripe_connect_service.rb new file mode 100644 index 0000000..55466f7 --- /dev/null +++ b/app/services/stripe_connect_service.rb @@ -0,0 +1,35 @@ +class StripeConnectService + def self.create_account(user) + return if user.stripe_connected_account_id.present? + + account = Stripe::Account.create( + type: 'express', + country: 'FR', + email: user.email, + capabilities: { + card_payments: {requested: true}, + transfers: {requested: true} + } + ) + + user.update!(stripe_connected_account_id: account.id) + account + end + + def self.onboarding_link(user) + return unless user.stripe_connected_account_id.present? + + account_link = Stripe::AccountLink.create( + account: user.stripe_connected_account_id, + refresh_url: Rails.application.routes.url_helpers.promoter_stripe_refresh_url, + return_url: Rails.application.routes.url_helpers.promoter_stripe_return_url, + type: 'account_onboarding' + ) + + account_link.url + end + + def self.get_account_details(account_id) + Stripe::Account.retrieve(account_id) + end +end \ No newline at end of file diff --git a/app/views/promoter/payouts/index.html.erb b/app/views/promoter/payouts/index.html.erb index 383dcbd..de1ea55 100644 --- a/app/views/promoter/payouts/index.html.erb +++ b/app/views/promoter/payouts/index.html.erb @@ -1,2 +1,39 @@ -

Promoter::Payouts#index

-

Find me in app/views/promoter/payouts/index.html.erb

+
+

My Payouts

+ + <% if @events.any? %> +
+ <% @events.each do |event| %> +
+

<%= event.name %>

+

Date: <%= event.start_time.strftime('%B %d, %Y at %I:%M %p') %>

+

Status: <%= event.payout_status.humanize %>

+ + <% if event.earnings.pending.any? %> +
+

Gross: €<%= (event.total_earnings_cents / 100.0).round(2) %>

+

Fees (10%): €<%= (event.total_fees_cents / 100.0).round(2) %>

+

Net: €<%= (event.net_earnings_cents / 100.0).round(2) %>

+
+ + <% if event.can_request_payout? %> + <%= button_to "Request Payout", promoter_payouts_path(event_id: event.id), + method: :post, + class: "w-full bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline" %> + <% else %> +

Payout not available yet

+ <% end %> + <% else %> +

No pending earnings

+ <% end %> +
+ <% end %> +
+ <% else %> +
+

No events found

+

You haven't created any events yet.

+ <%= link_to "Create Event", new_promoter_event_path, class: "bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded" %> +
+ <% end %> +
diff --git a/app/views/promoter/payouts/show.html.erb b/app/views/promoter/payouts/show.html.erb index daab6a6..47db689 100644 --- a/app/views/promoter/payouts/show.html.erb +++ b/app/views/promoter/payouts/show.html.erb @@ -1,2 +1,58 @@ -

Promoter::Payouts#show

-

Find me in app/views/promoter/payouts/show.html.erb

+
+

<%= @event.name %> - Payout Details

+ +
+

Event Summary

+

Date: <%= @event.start_time.strftime('%B %d, %Y at %I:%M %p') %>

+

Venue: <%= @event.venue_name %>

+

Payout Status: <%= @event.payout_status.humanize %>

+ <% if @event.payout_requested_at %> +

Requested: <%= @event.payout_requested_at.strftime('%B %d, %Y at %I:%M %p') %>

+ <% end %> +
+ + <% if @earnings.any? %> +
+

Earnings Breakdown

+
+ + + + + + + + + + + + <% @earnings.each do |earning| %> + + + + + + + + <% end %> + +
OrderGross AmountFeeNet AmountStatus
#<%= earning.order_id %>€<%= (earning.amount_cents / 100.0).round(2) %>€<%= (earning.fee_cents / 100.0).round(2) %>€<%= (earning.net_amount_cents / 100.0).round(2) %> + + <%= earning.status.humanize %> + +
+
+ +
+

Total Summary

+

Gross Total: €<%= (@earnings.sum(:amount_cents) / 100.0).round(2) %>

+

Total Fees: €<%= (@earnings.sum(:fee_cents) / 100.0).round(2) %>

+

Net Total: €<%= (@earnings.sum(:net_amount_cents) / 100.0).round(2) %>

+
+
+ <% else %> +
+

No earnings recorded for this event.

+
+ <% end %> +
diff --git a/db/migrate/20250916215129_add_index_to_stripe_connected_account_id_on_users.rb b/db/migrate/20250916215129_add_index_to_stripe_connected_account_id_on_users.rb index c2bd3ba..ceb49c8 100644 --- a/db/migrate/20250916215129_add_index_to_stripe_connected_account_id_on_users.rb +++ b/db/migrate/20250916215129_add_index_to_stripe_connected_account_id_on_users.rb @@ -1,6 +1,6 @@ class AddIndexToStripeConnectedAccountIdOnUsers < ActiveRecord::Migration[8.0] def change - add_column :stripe_connected_account_id_on_users, :stripe_connected_account_id, :string + add_index :users, :stripe_connected_account_id, unique: true add_index :stripe_connected_account_id_on_users, :stripe_connected_account_id end end