Payment & Billing Architecture
Relevant source files
The following files were used as context for generating this wiki page:
Introduction
The Payment & Billing Architecture of SoulSeer is designed around a prepay balance system. Clients add funds to their account via Stripe, which are then consumed in real-time during live consultations (Chat, Voice, or Video). This system ensures that readers are compensated fairly while protecting the platform from credit risk by verifying available funds before and during sessions.
The architecture spans three primary domains: fund ingestion (Top-ups), real-time usage (Per-minute billing), and disbursements (Reader Payouts). It utilizes Stripe for secure payment processing and Stripe Connect Express for managing reader earnings and payouts.
Sources: README.md:14-25, README.md:104-108
Core Billing Components
The system relies on several key services and routes to manage the financial lifecycle of a user.
| Component | Description |
|---|---|
| Stripe Service | Reusable helpers for creating PaymentIntents, Connect accounts, and Transfers. |
| Billing Service | Server-side logic for real-time per-minute charges and session monitoring. |
| Payment Routes | API endpoints for balance checks, intent creation, and transaction history. |
| Stripe Webhook | Asynchronous handler for payment_intent.succeeded events to credit user balances. |
| Admin Payouts | Manual or automated triggers to move reader balances to their Stripe Connect accounts. |
Sources: server/src/services/stripe-service.ts:1-10, server/src/routes/payments.ts:1-20, server/src/test/billing-service.test.ts:10-25
Fund Ingestion (Top-up Flow)
Clients add funds using the AddFundsForm component, which interacts with the /api/payments/create-intent endpoint. The minimum top-up amount is $5.00 (500 cents).
The webhook handler performs an idempotency check by verifying the stripePaymentIntentId in the transactions table to prevent duplicate credits from Stripe retries.
Sources: server/src/routes/payments.ts:25-85, client/src/components/AddFundsForm.tsx:143-165, server/src/services/stripe-service.ts:14-25
Per-Minute Billing Logic
Consultations are billed per minute based on the reader's set rate. The platform implements a revenue split where the reader receives a 70% floor, and the platform retains the remainder.
Revenue Split Calculation
The split is calculated using integer math to avoid floating-point errors:
- Reader Share:
Math.floor(ratePerMinute * 0.7) - Platform Share:
ratePerMinute - readerShare
Sources: server/src/test/billing-service.test.ts:133-140, README.md:107
Real-Time Session Management
The BillingService monitors active sessions. If a reader goes offline, the service:
- Pauses active/in-progress sessions.
- Broadcasts
reading:partner_disconnectedvia WebSockets. - Cancels pending requests with the reason
reader_offline.
Sources: server/src/test/billing-service.test.ts:100-130
Reader Payouts & Stripe Connect
Readers are onboarded via Stripe Connect Express. Payouts require a minimum balance of $15.00.
The payout process follows a Stripe-first approach: the transfer is attempted before the database balance is zeroed. If the database update fails after a successful Stripe transfer, it is flagged for manual reconciliation to prevent double-payouts or lost records.
Sources: server/src/services/stripe-service.ts:40-60, server/src/routes/admin.ts:310-355
Data Models
The billing system uses two primary tables for financial tracking.
Users Table (Billing Fields)
| Field | Type | Description |
|---|---|---|
balance | Integer | Current account balance in cents. |
stripeAccountId | String | Stripe Connect account ID (for readers). |
pricingChat | Integer | Cents per minute for chat sessions. |
pricingVoice | Integer | Cents per minute for voice sessions. |
pricingVideo | Integer | Cents per minute for video sessions. |
Transactions Table
| Field | Type | Description |
|---|---|---|
userId | Integer | The user associated with the transaction. |
type | Enum | topup, reading_charge, reader_payout, refund, admin_adjustment. |
amount | Integer | Change in balance (positive for credits, negative for debits). |
balanceBefore | Integer | User balance prior to the transaction. |
balanceAfter | Integer | User balance after the transaction. |
stripePaymentIntentId | String | Reference to the Stripe object (PI or Transfer). |
Sources: client/src/types/index.ts:100-115, server/src/test/schema.test.ts:30-65, shared/src/validators.ts:25-30
Admin Financial Controls
Admins have access to manual overrides and oversight tools via the admin-service:
- Balance Adjustments: Manually add or deduct cents from any user balance with a required note.
- Refunds: Revert a completed reading charge, crediting the client's balance and updating the reading's
paymentStatustorefunded. - Transaction Ledger: A platform-wide view of every financial movement.
Sources: server/src/routes/admin.ts:280-305, server/src/routes/admin.ts:380-420
Conclusion
The SoulSeer billing architecture provides a secure, transactional environment for spiritual consultations. By combining a prepay model with real-time session monitoring and Stripe-powered payouts, the system maintains financial integrity while providing flexibility for both clients and readers.