Stripe Payment Service Package

Payment processing service layer for Stripe integration

Overview

@zooly/srv-stripe-payment is a dedicated service package that handles all payment business logic and Stripe integration. It's separate from the general @zooly/app-srv package to ensure payment logic is reusable and testable independently of the Next.js API route framework.

Package Details

  • Package Name: @zooly/srv-stripe-payment
  • Location: packages/srv-stripe-payment
  • Type: Server-side payment service layer

Currency Convention

Key Features

  • Stripe Integration: Centralized Stripe client and API operations
  • Payment Processing: Payment intent creation, completion, and refund handling
  • Revenue Distribution: Multi-party revenue split calculations
  • Payout Management: Automated payout processing and advance payout support
  • Webhook Handling: Stripe webhook event processing
  • Idempotent Operations: Safe handling of duplicate webhook events

Architecture

The package provides a service layer that sits between API routes and the database access layer:

API Routes (Next.js) → @zooly/srv-stripe-payment → @zooly/app-db → Database

Core Modules

Stripe Client & Operations (stripe.ts)

  • Lazy-initialized Stripe client singleton
  • PaymentIntent creation
  • Stripe customer management
  • Stripe Connect transfers
  • Refund processing
  • Webhook signature verification

Product Resolution & Pricing (getProductByPayForId.ts)

  • Resolves product by payFor/payForId to a PaymentProduct
  • Computes price breakdown (Stripe fee, platform fee, talent gross share)
  • IP term products: price from product table (source of truth)
  • MERCH products: price passed as knownAmountCent (cart total calculated externally), sellerAccountId from campaign (defaults to "zooly_acc")

Payment Intent Creation (createPaymentIntent.ts)

  • For authenticated users (IP terms: VOICE_OVER, LIKENESS, IMAGE)
  • Fetches product from DB (source of truth for price and seller)
  • Creates DB payment record and Stripe PaymentIntent
  • Returns client secret for Stripe Elements checkout

Merch Payment Intent Creation (createMerchPaymentIntent.ts)

  • For anonymous merch buyers (no user account)
  • Creates stripe_payment DB record with payFor: "MERCH", payForId: sessionId, buyerUserId: sessionId
  • Creates Stripe PaymentIntent with idempotency key merch-pi-{paymentId} and metadata { stripePaymentId, merchSessionId }
  • Stores internal payment ID (stripePaymentDbId) on the merch session for later retrieval at completion time
  • Returns { stripePaymentId, stripeClientSecret, stripePublishableKey }

Payment Completion (completePayment.ts)

  • Atomic payment completion engine
  • Creates share tracking records for all stakeholders
  • Handles advance payout offsets
  • Generates purchase codes
  • Idempotent webhook handling

Refund Processing (refund.ts)

  • Full refund processing with reversal of all tracking records
  • Advance balance adjustments
  • Two-phase commit pattern (DB-first, then Stripe)

Payout Management

  • payoutDaemon.ts: Daily cron daemon for automated payouts
  • doPayout.ts: Per-account payout processing logic
  • advancePayout.ts: Advance payout mechanism

Logging (paymentLogger.ts)

  • Structured payment event logging
  • Component tagging for audit trails

Dependencies

  • @zooly/app-db - Database access layer
  • @zooly/types - Shared type definitions
  • @zooly/util - Utility functions (Stripe fee calculation)
  • stripe - Stripe SDK

Usage

This package is used by Next.js API routes in apps/zooly-app/app/api/payments/ and apps/zooly-app/app/api/merch/. API routes are thin layers that:

  1. Extract parameters from requests
  2. Verify authentication (for IP term payments) or validate session (for merch)
  3. Delegate to service functions from this package

Merch Integration

The merch payment flow uses:

  • createMerchPaymentIntent() — called by /api/merch/create-payment-intent
  • completePaymentFromClient() — called by /api/merch/order/complete
  • getProductByPayForId("MERCH", sessionId, knownAmountCent) — called internally by completePayment()
  • Webhook fallback via handleStripeWebhook — automatically picks up merch payments from metadata.stripePaymentId

Key Design Principles

  1. Separation of Concerns: Payment logic is isolated from API route framework
  2. Transaction Safety: Critical operations use database transactions
  3. Idempotency: All webhook handlers are idempotent
  4. Two-Phase Commits: Payout processing uses DB-first, then Stripe pattern
  5. No Direct DB Access: Service layer uses access functions from @zooly/app-db, never raw table objects