Prepay Balance System
Relevant source files
The following files were used as context for generating this wiki page:
The Prepay Balance System is a core financial module of the SoulSeer platform that manages user funds in a "top-up" model. Clients add funds to their account balance via Stripe, and these funds are subsequently debited in real-time during live consultations (chat, voice, or video) based on a per-minute rate set by the reader.
This system ensures that service providers are compensated for their time while protecting the platform from credit risk. It includes features for Stripe integration, automated billing during sessions, administrative adjustments, and detailed transaction logging for accounting and transparency.
Sources: README.md:12-14, README.md:92-95
System Architecture and Data Flow
The system operates across the frontend (React), backend (Express), and third-party payment processors (Stripe). The balance is stored in the database as an integer representing cents to avoid floating-point errors.
Fund Addition Flow
The following diagram illustrates the sequence of events when a user adds funds to their account.
Core Components
1. Client-Side Payment Interface
The AddFundsForm component provides a two-step wizard for users to select preset amounts ($10, $25, $50, $100) or enter a custom amount (minimum $5.00). It utilizes Stripe Elements for secure card data handling.
- Preset Amounts: Defined as
[10, 25, 50, 100]. - Minimum Deposit:
$5.00(500 cents). - Appearance: Custom "night" theme matching the SoulSeer celestial aesthetic.
Sources: client/src/components/AddFundsForm.tsx:11-39, shared/src/validators.ts:63-65
2. Backend Payment Processing
The backend utilizes the Stripe SDK to manage transactions. The payments.ts router handles the creation of Payment Intents and the processing of asynchronous webhooks.
| Endpoint | Method | Purpose |
|---|---|---|
/api/payments/create-intent | POST | Generates a Stripe Client Secret for the frontend. |
/api/payments/balance | GET | Retrieves the current user's balance in cents. |
/api/payments/webhook | POST | Handles payment_intent.succeeded events to credit balances. |
/api/payments/transactions | GET | Fetches the user's historical ledger entries. |
| Sources: server/src/routes/payments.ts:103-146 |
3. Data Models
The system relies on two primary entities within the database schema: users (specifically the balance field) and transactions (the immutable ledger).
| Field | Type | Description |
|---|---|---|
balance | Integer | Current user funds in cents. |
type | Enum | One of topup, reading_charge, reader_payout, refund, admin_adjustment. |
amount | Integer | Change in cents (positive for credits, negative for debits). |
balanceBefore | Integer | User balance before the transaction for audit trails. |
balanceAfter | Integer | User balance after the transaction. |
| Sources: client/src/types/index.ts:12-25, client/src/types/index.ts:74-84 |
Transaction Logic and Safety
Idempotency
To prevent duplicate credits if Stripe sends the same webhook multiple times, the server performs an idempotency check by looking up the stripePaymentIntentId in the transactions table before applying funds.
Sources: server/src/routes/payments.ts:61-75
Atomic Transactions
Balance updates and transaction logging are wrapped in database transactions to ensure data consistency.
await db.transaction(async (tx) => {
const [before] = await tx.select().from(users).where(eq(users.id, userId));
await tx.update(users).set({ balance: sql`${users.balance} + ${pi.amount}` });
await tx.insert(transactions).values({ ... });
});
Sources: server/src/routes/payments.ts:80-97
Real-Time Billing during Readings
During a live session, the system calculates accrued costs in real-time. The frontend displays a "Session Bar" indicating elapsed time, current session cost, and the remaining user balance.
- Low Balance Warning: Triggered when a user has less than 2 minutes of balance remaining at the current reader's rate.
- Auto-Termination: If the balance reaches zero, the backend billing service (via
reading:endedevents) terminates the session.
Sources: client/src/pages/reading/ReadingSessionPage.tsx:75-98, client/src/pages/reading/ReadingSessionPage.tsx:431-450
Administrative Adjustments
Administrators have the capability to manually adjust user balances for compensation, corrections, or testing. These actions require the admin role and are logged with a mandatory note for audit purposes.
Sources: server/src/routes/admin.ts:318-356, shared/src/validators.ts:67-71
Summary
The Prepay Balance System provides a robust, ledger-based financial foundation for SoulSeer. By combining Stripe's secure infrastructure with real-time session tracking and immutable transaction history, the platform maintains financial integrity for both clients and readers.