Zuschauer-Monetarisierung (Viewer Donations & Tipping)

ZurΓΌck zur MOC - Map of Content


Status: πŸ”œ GEPLANT (Tournament Phase)

Viewer-Monetarisierung ist geplant für das Breakout Tournament und spÀter für regelmÀßige Sessions.


Konzept: Viewer Donations wΓ€hrend Live-Events

Beschreibung

WΓ€hrend des Breakout Tournaments (und spΓ€ter in Sessions) kΓΆnnen Zuschauer ihre Lieblingsartists, Jury-Creator oder die Plattform direkt unterstΓΌtzen mit Micro-Payments.

Γ„hnlich wie: Twitch Tips, YouTube Super Chat, Patreon, Ko-fi Donations

Psychologie


Tiering-Modell (πŸ”œ GEPLANT)

€1 Tier: "Applause"
β”œβ”€ What: Digitale Animation (πŸ’š Heart, Clap, Fire, etc.)
β”œβ”€ Duration: Instant
β”œβ”€ Visibility: Alle Zuschauer sehen Animation
└─ Use Case: "Schnell einen KΓΌnstler unterstΓΌtzen"

€5 Tier: "Support"
β”œβ”€ What: Username-Badge (z.B., "[Supporter] John")
β”œβ”€ Duration: 1 Minute sichtbar im Chat
β”œβ”€ Visibility: Nur Chat-Nutzer sehen Badge
└─ Use Case: "Mein Name sollte sichtbar sein"

€25 Tier: "VIP Supporter"
β”œβ”€ What: Shout-out von Host
β”œβ”€ Duration: WΓ€hrend Song des Artists
β”œβ”€ Visibility: Alle Viewers im Stream
└─ Use Case: "Ich mΓΆchte dem Artist Hallo sagen!"
└─ Message: Custom text (up to 50 chars)

€100+ Tier: "Platinum Fan"
β”œβ”€ What: Name-Drop vor Jury-Panel
β”œβ”€ Duration: 10-sekunden Segment
β”œβ”€ Visibility: Alle Viewers sehen dich mit deinem Namen
β”œβ”€ Message: Custom intro (up to 100 chars)
└─ Use Case: "Ich bin der GRΓ–SSTE Fan!"

Alternative: Simpler Tier-Model (fΓΌr Phase 1)

€5: Quick Support (Heart Animation)
€25: Named Support (Shout-out)
€100+: Premium Supporter (Name + Custom Message)

Technische Implementierung (πŸ”œ GEPLANT)

Payment Flow

1. Viewer opens Donation Modal in Stream
2. Selects Tier (€5, €25, €100)
3. Optional: Writes custom message
4. Clicks "Donate"
5. Stripe Payment Intent created (embedded on stream page)
6. Viewer enters payment (Stripe hosted form or Apple Pay/Google Pay)
7. Payment completed β†’ Webhook received
8. Supabase: Insert into `donations` table
9. Real-time Realtime Channel broadcasts to all viewers
10. Donation Wall updated (Live)
11. Host/Jury notified
12. Revenue Split calculated & stored

Database Schema (πŸ”œ GEPLANT)

CREATE TABLE donations (
  id BIGSERIAL PRIMARY KEY,
  session_id UUID REFERENCES sessions(id),
  viewer_id UUID, -- Anonymous or Auth
  target_type ENUM ('artist', 'creator', 'platform'), -- who gets tip?
  target_id UUID, -- artist_id or creator_id
  amount_eur_cents INT,
  tier ENUM ('applause', 'support', 'vip', 'platinum'),
  message TEXT,
  payment_id VARCHAR (Stripe), -- stripe_payment_intent_id
  status ENUM ('pending', 'succeeded', 'failed'),
  created_at TIMESTAMP DEFAULT NOW()
);

CREATE TABLE donation_splits (
  id BIGSERIAL PRIMARY KEY,
  donation_id BIGINT REFERENCES donations(id),
  recipient_type ENUM ('artist', 'jury_creator', 'platform'),
  recipient_id UUID,
  amount_eur_cents INT,
  percentage INT, -- 60%, 20%, 20%
  status ENUM ('pending', 'paid', 'failed')
);

Netlify Function (πŸ”œ GEPLANT)

// netlify/functions/processDonation.mts

export async function handler(event: HandlerEvent) {
  if (event.httpMethod !== 'POST') {
    return { statusCode: 405 };
  }

  const {
    sessionId,
    viewerId,
    targetType,
    targetId,
    amountEurCents,
    tier,
    message,
    stripePaymentIntentId,
  } = JSON.parse(event.body!);

  try {
    // 1. Create Stripe Charge (if not already authorized)
    const charge = await stripe.charges.create({
      amount: amountEurCents,
      currency: 'eur',
      payment_method: stripePaymentIntentId,
    });

    if (charge.status !== 'succeeded') {
      return { statusCode: 400, body: 'Payment failed' };
    }

    // 2. Insert Donation Record
    const { data: donation, error: insertError } = await supabase
      .from('donations')
      .insert([
        {
          session_id: sessionId,
          viewer_id: viewerId,
          target_type: targetType,
          target_id: targetId,
          amount_eur_cents: amountEurCents,
          tier,
          message,
          payment_id: charge.id,
          status: 'succeeded',
        },
      ])
      .select()
      .single();

    if (insertError) throw insertError;

    // 3. Calculate Split & Insert Donation_Splits
    const artistShare = Math.floor(amountEurCents * 0.6);
    const juryShare = Math.floor(amountEurCents * 0.2);
    const platformShare = amountEurCents - artistShare - juryShare;

    await supabase.from('donation_splits').insert([
      {
        donation_id: donation.id,
        recipient_type: 'artist',
        recipient_id: targetId,
        amount_eur_cents: artistShare,
        percentage: 60,
        status: 'pending',
      },
      {
        donation_id: donation.id,
        recipient_type: 'jury_creator',
        recipient_id: targetId, // Host Creator
        amount_eur_cents: juryShare,
        percentage: 20,
        status: 'pending',
      },
      {
        donation_id: donation.id,
        recipient_type: 'platform',
        amount_eur_cents: platformShare,
        percentage: 20,
        status: 'pending',
      },
    ]);

    // 4. Real-time Notification to Stream Viewers
    supabase.realtime.broadcast({
      event: 'new_donation',
      payload: {
        donation: {
          id: donation.id,
          tier,
          message,
          amount: amountEurCents,
          viewerName: viewerId,
        },
      },
    });

    return {
      statusCode: 200,
      body: JSON.stringify({
        success: true,
        donationId: donation.id,
      }),
    };
  } catch (error) {
    console.error(error);
    return {
      statusCode: 500,
      body: JSON.stringify({ error: String(error) }),
    };
  }
}

Viewer Experience (πŸ”œ GEPLANT)

During Tournament Stream

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  TOURNAMENT LIVESTREAM                  β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                          β”‚
β”‚  [Artist performing... Music plays]     β”‚
β”‚                                          β”‚
β”‚  β”Œβ”€ Donation Wall (Right Sidebar) ──┐   β”‚
β”‚  β”‚ πŸ†• Platinum Fan: Max             β”‚   β”‚
β”‚  β”‚    "Go Artist_Name! πŸ”₯"          β”‚   β”‚
β”‚  β”‚                                   β”‚   β”‚
β”‚  β”‚ VIP Supporter: Lisa              β”‚   β”‚
β”‚  β”‚ [Support Button] €5  €25  €100+  β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚                                          β”‚
β”‚  [Jury Panel scoring...]                β”‚
β”‚  [Chat Reactions...]                    β”‚
β”‚                                          β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Donation Modal

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Support [Artist Name]          β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                β”‚
β”‚ ❀️ €5 - Applause              β”‚
β”‚    Quick animation visible    β”‚
β”‚                                β”‚
β”‚ πŸ‘ €25 - VIP Supporter         β”‚
β”‚    Your name + shout-out       β”‚
β”‚    Message: [Text field]       β”‚
β”‚                                β”‚
β”‚ 🎁 €100+ - Platinum Fan        β”‚
β”‚    Premium shout-out           β”‚
β”‚    Custom message (100 chars)  β”‚
β”‚    [Text field]                β”‚
β”‚                                β”‚
β”‚ [Donate] [Cancel]              β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Revenue Impact & Projections (πŸ”œ GEPLANT)

Assumptions for Tournament Day

Calculation Example (1,000 concurrent viewers)

Viewers:                1,000
Donation Rate (2%):     Γ— 20 donors
Average Tier:           €15 per donor
─────────────────────────────────
Revenue:                €300 per ~30min round

Per Tournament (4 rounds):
Total Donations:        €300 Γ— 4 = €1,200

Artist share (60%):     €720
Jury Creators (20%):    €240 (split among 5 = €48 each)
Platform (20%):         €240
Stripe fees (2.9%):     -€35
Net Platform:           €205

Scaling Projection (Season 1+)

Event Expected Viewers Donation Rate Expected Revenue
Tournament Week 1 500–1,000 1–2% €100–300
Tournament Week 2 1,000–2,000 2–3% €300–900
Regular Session 100–300 0,5–1% €10–30
Post-Tournament 200–500 1–2% €30–100

Annual Projection (if 4Γ— tournaments + 100 sessions):


Gamification Elements (πŸ”œ GEPLANT)

Leaderboard

TOP SUPPORTERS THIS TOURNAMENT

1. πŸ† Max (€200 total)
   β”œβ”€ 4 € 100 donations
   └─ "Go Artist_Name! πŸ”₯"

2. πŸ₯ˆ Lisa (€150 total)
   β”œβ”€ 1 € 100 + 2 € 25
   └─ "Amazing performance!"

3. πŸ₯‰ Alex (€100 total)
   β”œβ”€ 4 € 25 donations
   └─ Various messages

Badges & Rewards (πŸ”œ GEPLANT, fΓΌr spΓ€ter)

πŸ’Ž Platinum Supporter Badge
   "You've donated €100+ to breakout artists"
   
❀️ Heart of the Community
   "Most donations in a single tournament"
   
🎡 Artist Champion
   "Biggest supporter of [Artist Name]"

Anti-Fraud & Moderation (πŸ”œ GEPLANT)

Donation Validation:
β”œβ”€ Stripe Verification (Always require valid payment method)
β”œβ”€ Rate Limiting (Max 10 donations per viewer per session)
β”œβ”€ Message Filtering (No spam, profanity, hateful content)
β”œβ”€ Chargebacks: Hold disputed donations in escrow
└─ Suspicious Patterns: Alert admin if $X donated instantly

Message Moderation:
β”œβ”€ Auto-filter profanity using API
β”œβ”€ Report Inappropriate Messages
β”œβ”€ Admin Approval for >€50 messages (optional)
└─ Blacklist users who abuse system

Fallback & Issues (πŸ”œ GEPLANT)

What if Stripe payment fails?

1. Donation initiated but Stripe times out
2. Show "Payment pending" state to viewer
3. Retry automatically (3 attempts)
4. If failed: Show error, option to retry manually
5. If success on retry: Show success + animation
6. If all fail: Refund & show "Try again later"

What if Real-time connection drops?

1. Donation still processes (backend is independent)
2. Real-time broadcast queues messages
3. When reconnected: Bulk broadcast pending donations
4. Viewers see "Loading donations..." β†’ loads all
5. No donations lost, may be delayed 5–10 secs

Future Enhancements (Post-Season 1)

πŸ”œ Later:
β”œβ”€ Donation Goals ("Raise €1,000 for Artist Fund")
β”œβ”€ Recurring Monthly Support ("Sponsor this Artist")
β”œβ”€ Donation Matching ("Double your impact!")
β”œβ”€ Crowdfunding for Album Production
β”œβ”€ Affiliate Revenue Sharing with Artists
└─ NFT Collectibles ("You donated to make this artist!")

Verweise