feat(payouts): implement promoter earnings viewing, request flow, and admin Stripe processing with webhooks

Add model methods for accurate net calculations (€0.50 + 1.5% fees), eligibility, refund handling
Update promoter/payouts controller for index (pending events), create (eligibility checks)
Integrate admin processing via Stripe::Transfer, webhook for status sync
Enhance views: index pending cards, events/show preview/form
Add comprehensive tests (models, controllers, service, integration); run migrations
This commit is contained in:
kbe
2025-09-17 02:07:52 +02:00
parent 47f4f50e5b
commit 3c1e17c2af
31 changed files with 1096 additions and 148 deletions

View File

@@ -0,0 +1,58 @@
require "test_helper"
class PayoutFlowTest < ActionDispatch::IntegrationTest
setup do
@promoter = User.create!(email: "promoter@example.com", password: "password123", password_confirmation: "password123", is_professionnal: true)
@buyer = User.create!(email: "buyer@example.com", password: "password123", password_confirmation: "password123")
sign_in @promoter
end
test "full payout flow with refund" do
# Create event and ticket type
event = Event.create!(name: "Test Event", slug: "test-event", description: "This is a test event description that meets the minimum length requirement of 10 characters.", venue_name: "Venue", venue_address: "Address", latitude: 48.0, longitude: 2.0, start_time: 1.day.ago, end_time: 1.hour.ago, user: @promoter, state: :published)
ticket_type = TicketType.create!(event: event, name: "Standard", price_cents: 1000, quantity: 10, sale_start_at: 2.days.ago, sale_end_at: Time.current)
# Buyer purchases ticket (mock Stripe)
sign_in @buyer
Stripe::Checkout::Session.expects(:create).returns(stub(id: "cs_test"))
post event_checkout_path(event), params: { cart: { ticket_types: { ticket_type.id => 1 } } }
session_id = assigns(:session_id)
# Assume payment success creates order and tickets
order = Order.last
ticket = Ticket.last
assert_equal "paid", order.status
assert_equal "active", ticket.status
# Earnings created
earning = Earning.last
assert_not_nil earning
assert_equal 900, earning.amount_cents
# Refund one ticket
sign_in @promoter
ticket.update!(status: "refunded")
earning.reload
assert_equal 0, earning.amount_cents # Recalculated
# Request payout
assert event.can_request_payout?(@promoter)
post promoter_payouts_path, params: { event_id: event.id }
payout = Payout.last
assert_equal :pending, payout.status
# Admin process
admin = User.create!(email: "admin@example.com", password: "password123", password_confirmation: "password123")
admin.add_role :admin
sign_in admin
Stripe::Transfer.expects(:create).returns(stub(id: "tr_success"))
patch admin_payout_path(payout)
payout.reload
assert_equal :completed, payout.status
# Webhook succeeds
post stripe_webhooks_path, params: { type: "payout.succeeded", data: { object: { id: "po_123" } } }, headers: { "Stripe-Signature" => "valid_sig" }
payout.reload
assert_equal :completed, payout.status # Confirmed
end
end