Custom Store Backend

How to implement a custom Stripe402Store for backends other than Redis or PostgreSQL.

The Interface

Your store must implement the Stripe402Store interface from @stripe402/core:

interface Stripe402Store {
  getClient(clientId: string): Promise<ClientRecord | null>
  createClient(record: ClientRecord): Promise<void>
  deductBalance(clientId: string, amount: number): Promise<number | null>
  addBalance(clientId: string, amount: number): Promise<number>
  recordTransaction?(transaction: TransactionRecord): Promise<void>
}

Critical Requirement: Atomic deductBalance

The deductBalance method must be atomic. It must check that balance >= amount and perform the deduction in a single, indivisible operation. Without this, concurrent requests can double-spend credits.

Bad (race condition):

// DON'T DO THIS — not atomic
async deductBalance(clientId, amount) {
  const client = await this.getClient(clientId)
  if (client.balance >= amount) {
    // Between this read and the write, another request could deduct too
    await this.updateBalance(clientId, client.balance - amount)
    return client.balance - amount
  }
  return null
}

Good (atomic):

Example: In-Memory Store (for testing)

Note: This in-memory implementation is not safe for concurrent requests in production — JavaScript's single-threaded nature protects against race conditions within a single process, but not across multiple processes or workers. Use a database with proper atomicity for production.

Example: MongoDB Store Skeleton

Checklist for Custom Stores

Last updated