Payment System Introduction

Introduction to Zooly payment and revenue distribution system

Introduction

Zooly is an intellectual property marketplace where content creators (buyers) pay celebrities/talent (sellers) for the right to use their likeness in AI-generated content. The payment system handles the full lifecycle: collecting payments from buyers, distributing revenue among multiple stakeholders, accumulating shares until payout thresholds are met, and transferring funds to talent via Stripe Connect.


Currency Convention

What the Payment System Does

Core Flows

  1. Payment Collection — Charging buyers via Stripe for licenses (Voice, Image, Likeness), merch, and marketplace offers (payFor: "OFFER", escrow until delivery is accepted)
  2. Revenue Distribution — Splitting each payment among 3 or more stakeholders: talent, platform (Zooly), Stripe fees, agents, ambassadors, host partners
  3. Payout Accumulation — Holding earned shares in "open" tracking records until they exceed a per-user minimum threshold (default $100)
  4. Payout Processing — Transferring accumulated funds to talent via Stripe Connect transfers
  5. Advance Payouts — Pre-paying talent before they've earned the amount, then deducting from future earnings
  6. Refunds — Reversing payments with negative tracking records and advance offsets
  7. PPU (Payment Proof Url) — Generating unique PPU codes per completed transaction
  8. Dual-Path Payment Completion — Client endpoint completes payments by verifying with Stripe API; webhook serves as a fallback

Product-Agnostic Design

The payment system is designed to be Product Agnostic. This means that the payment system doesn't know about specific products. Instead, the product details are stored in domain-specific tables.

The payment system is linked to the product via the payFor enum and the payForId foreign key.

The product details are stored in domain-specific tables.

  • Payment system doesn't know about specific products
  • Linked via payFor enum + payForId foreign key
  • Product details live in domain-specific tables

See Adding a New Product for more details.

Client Side - MISSING

Invariant

Revenue Distribution Logic

Standard Payment (AI Creation — Voice, Image, Likeness)

Total Payment Amount (e.g., $100.00 = 10000 cents)
    |
    |-- Stripe Fee = $0.30 + 2.9% = $3.20 (320 cents)
    |-- Zooly Platform Fee = fixed AI creation cost (e.g., $5.00)
    |-- Talent Share = $100.00 - $3.20 - $5.00 = $91.80
    |
    |-- If talent has Agent: Agent gets X% of Talent share
    |   e.g., 15% of $91.80 = $13.77 → Talent gets $78.03
    |
    |-- If has Host Partner: Partner gets 10% of Platform share
    |   e.g., 10% of $5.00 = $0.50 → Platform gets $4.50
    |
    |-- If has Ambassadors:
    |   Each ambassador gets SAME 10% of Platform share (AMBASSADOR_SHARE = 0.1)
    |   Total deducted from platform = 10% * number_of_ambassadors

Architecture Diagram

flowchart TD A[Buyer selects product] --> B[Create PaymentIntent] B --> C[Buyer pays via Stripe] C --> D["Client calls POST /complete (primary)"] D --> D2[Verify with Stripe API] D2 --> E[Complete payment & distribute shares] C --> W["Webhook charge.succeeded (fallback)"] W --> E E --> F[Create tracking records:<br/>Talent, Agent, Platform, etc.] F --> G{Accumulated >= $100?} G -->|No| H[Wait for more payments] G -->|Yes| I[Daily cron: Process payout] I --> J[Stripe Connect transfer] J --> K[Close tracking records] H --> G