Skip to content

Project Structure

Murai is a monorepo with five packages. You can install the meta-package for convenience or pick individual packages.

Package overview

PackagenpmPurpose
@murai-wallet/murainpmMeta-package — single install, re-exports everything
@murai-wallet/corenpmWallet, ledger, checkout manager, types, errors
@murai-wallet/gateway-midtransnpmMidtrans Snap adapter
@murai-wallet/gateway-xenditnpmXendit Checkout adapter
@murai-wallet/storage-drizzlenpmDrizzle ORM storage (PostgreSQL)

Architecture diagram

┌──────────────┐ ┌──────────────┐ ┌──────────────────┐
│ Your App │────▶│ Wallet │────▶│ Ledger (core) │
│ │ │ (core) │ │ append-only log │
└──────────────┘ └──────────────┘ └──────────────────┘
│ │
┌──────┴──────┐ ┌──────┴──────┐
│ Checkout │ │ Storage │
│ Manager │ │ Adapter │
└──────┬──────┘ └──────┬──────┘
│ │
┌──────┴──────┐ ┌──────┴──────┐
│ Gateway │ │ Drizzle │
│ Adapter │ │ PostgreSQL │
└─────────────┘ └─────────────┘
Midtrans │ Xendit

Core module breakdown

wallet.ts — Public API

The createWallet factory returns the main interface: getBalance, canSpend, spend, topUp, getTransactions, getCheckouts. This is what most application code interacts with.

ledger.ts — Append-only transaction log

The ledger enforces the core invariants: positive amounts only, idempotency keys on every mutation, and delegation to the storage adapter for atomic balance updates. Ledger entries are never updated or deleted.

checkout.ts — Payment gateway abstraction

The createCheckoutManager factory bridges the gateway adapter with the ledger. It creates checkout sessions and handles webhooks with a 7-step verification flow including dual idempotency guards.

types.ts — Public interfaces

All exported TypeScript interfaces: Wallet, StorageAdapter, PaymentGatewayAdapter, LedgerEntry, CheckoutSession, WebhookResult, and query types.

errors.ts — Typed error hierarchy

Domain errors that extend MuraiError: InsufficientBalanceError, IdempotencyConflictError, WebhookVerificationError, InvalidAmountError, GatewayError. Each has a typed code string for programmatic error handling.

Dependency graph

@murai-wallet/murai (meta-package)
├── @murai-wallet/core
├── @murai-wallet/gateway-midtrans → depends on core
├── @murai-wallet/gateway-xendit → depends on core
└── @murai-wallet/storage-drizzle → depends on core

Gateway and storage adapters depend only on @murai-wallet/core for its types and errors. They have no dependency on each other, so you can use Midtrans without Xendit and vice versa.