Introduction to Zooly payment and revenue distribution system
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.
All monetary amounts are in integer minor units, not dollars. Every variable, parameter, column, and API field that represents money must use the MinorUnit suffix to make the unit unambiguous (e.g. amountMinorUnit, platformFeeMinorUnit, totalBrandPriceMinorUnit). Never store or pass currency as floating-point dollars in the payment system.
Multi-currency support: Every table and type that holds a monetary amount also carries a currency field (ISO 4217 code, e.g. "USD", "JPY"). The system supports zero-decimal currencies (e.g. JPY, KRW) where 1 minor unit = 1 whole unit. Use the helpers in @zooly/util — minorUnitToMajor(), majorToMinorUnit(), formatMinorUnitToDisplay() — instead of hardcoding /100 or *100.
payFor: "OFFER", escrow until delivery is accepted)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.
payFor enum + payForId foreign keySee Adding a New Product for more details.
Currently, only a server-side payment flow has been implemented. A client-side payment flow is planned to support the Product-Agnostic Design using a dedicated Stripe context provider.
A payment consists of two linked records: a product record (product-specific) and a stripePayment record (generic Stripe payment details and status).
Creation Flow:
SUCCEEDEDBidirectional Relationship:
payForId/payFor fieldsstripePaymentId fieldAmount Handling:
getProductByPayForId() when neededQuerying Payments:
Total Payment Amount (e.g., 10000 minor units USD = $100.00)
|
|-- Stripe Fee = fixed + 2.9% (fixed = 30 minor units for USD, 0 for zero-decimal currencies like JPY)
|-- Zooly Platform Fee = fixed AI creation cost (e.g., 500 minor units = $5.00 USD)
|-- Talent Share = total - Stripe fee - platform fee
|
|-- If talent has Agent: Agent gets X% of Talent share
|
|-- If has Host Partner: Partner gets 10% of Platform share
|
|-- If has Ambassadors:
| Each ambassador gets SAME 10% of Platform share (AMBASSADOR_SHARE = 0.1)
| Total deducted from platform = 10% * number_of_ambassadors
All amounts use formatMinorUnitToDisplay(amount, currency) for display, which renders the correct symbol and decimal places per currency.