PHPackages                             timefrontiers/php-wallet - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. timefrontiers/php-wallet

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

timefrontiers/php-wallet
========================

Blockchain-like immutable wallet and transaction system with file-based ledger

v1.0(1mo ago)09MITPHPPHP &gt;=8.1

Since Apr 16Pushed 1mo agoCompare

[ Source](https://github.com/timefrontiers/php-wallet)[ Packagist](https://packagist.org/packages/timefrontiers/php-wallet)[ Docs](https://github.com/timefrontiers/php-wallet)[ RSS](/packages/timefrontiers-php-wallet/feed)WikiDiscussions master Synced 1w ago

READMEChangelog (1)Dependencies (1)Versions (2)Used By (0)

TimeFrontiers PHP Wallet
========================

[](#timefrontiers-php-wallet)

Blockchain-like immutable wallet and transaction system with file-based ledger.

Features
--------

[](#features)

- **Immutable Ledger**: Append-only file-based ledger as source of truth
- **Rolling Files**: Monthly ledger archives prevent file bloat
- **Checkpointing**: Periodic snapshots for fast recovery
- **Integrity Verification**: Checksums detect tampering
- **Batch Transactions**: Efficient bulk transfers
- **Payment Integration**: Interface-based external payment verification
- **Flexible Database**: Pass credentials at initialization

Installation
------------

[](#installation)

```
composer require timefrontiers/php-wallet
```

Quick Start
-----------

[](#quick-start)

```
use TimeFrontiers\Wallet\{Config, Wallet, Transaction};

// Database credentials
$db_cred = [
  'server'   => 'localhost',
  'username' => 'wallet_user',
  'password' => 'secure_password',
  'database' => 'myapp_wallet',  // Your database name (with any prefix)
];

// Configure ledger path
Config::setLedgerPath('/var/data/.txhive/ledgers');

// Create/load wallets with credentials
$alice = new Wallet('USR123456', 'NGN', $db_cred);
$bob = new Wallet('USR789012', 'NGN', $db_cred);

// Check balance
echo $alice->balance(); // 5000.00

// Transfer
$hashes = Transaction::transfer($alice, $bob, 1000.00, 'Payment for services');

// Batch transfer
$result = Transaction::batch($alice)
  ->credit($bob, 500, 'Bonus')
  ->credit('219555555555555', 200, 'Refund')
  ->execute();
```

Database Setup
--------------

[](#database-setup)

### 1. Create Your Database

[](#1-create-your-database)

Create your database manually with any required prefix:

```
CREATE DATABASE myprefix_wallet;
```

### 2. Run the Schema

[](#2-run-the-schema)

The schema file is included in the package at `schema/schema.sql`:

```
mysql -u root -p myprefix_wallet  'localhost',
  'username' => 'db_user',
  'password' => 'db_pass',
  'database' => 'myprefix_wallet',
];

$wallet = new Wallet('USR123456', 'NGN', $db_cred);
```

### Option 2: Pass PDO Instance

[](#option-2-pass-pdo-instance)

```
$pdo = new PDO('mysql:host=localhost;dbname=myprefix_wallet', 'user', 'pass');

$wallet = new Wallet('USR123456', 'NGN', $pdo);
```

### Option 3: Set Shared Connection

[](#option-3-set-shared-connection)

```
// Set once at application startup
Wallet::setDatabase([
  'server'   => 'localhost',
  'username' => 'db_user',
  'password' => 'db_pass',
  'database' => 'myprefix_wallet',
]);

// Now credentials are optional
$wallet = new Wallet('USR123456', 'NGN');
```

### Using Wallet's Connection

[](#using-wallets-connection)

Each wallet instance has its own db connection:

```
$wallet = new Wallet('USR123456', 'NGN', $db_cred);

// Access the connection
$db = $wallet->db();

// Transaction methods use wallet's connection automatically
Transaction::transfer($from, $to, 100, 'Payment');
```

Configuration
-------------

[](#configuration)

```
use TimeFrontiers\Wallet\Config;

// Ledger storage path
Config::setLedgerPath('/var/data/.txhive/ledgers');

// Decimal precision (default: 8)
Config::setPrecision(8);

// Wallet address format (default: prefix=219, length=15)
Config::setAddressPrefix('219');
Config::setAddressLength(15);

// Batch ID format (default: prefix=127, length=15)
Config::setBatchPrefix('127');
Config::setBatchLength(15);

// Strict integrity mode (fail on ledger/DB mismatch)
Config::setStrictIntegrity(true);

// Payment verifier
Config::setPaymentVerifier(new MyPaymentVerifier());

// Exchange rates
Config::setExchangeProvider(new FixedRateProvider([
  'NGN:DWL' => 1.0,
  'USD:NGN' => 1550.0,
]));
```

Code Generation
---------------

[](#code-generation)

Codes are generated with configurable prefix and length:

```
// Wallet address: 219 + 12 random digits = 219123456789012
Config::setAddressPrefix('219');
Config::setAddressLength(15);
$address = Config::generateAddress();

// Batch ID: 127 + 12 random digits = 127987654321098
Config::setBatchPrefix('127');
Config::setBatchLength(15);
$batch = Config::generateBatch();

// Generic code generation
$code = Config::generateCode('PRE', 18); // PRE + 15 digits

// Validation
Config::isValidAddress('219123456789012'); // true
Config::isValidBatch('127987654321098');   // true
```

Wallet Operations
-----------------

[](#wallet-operations)

### Create/Load Wallet

[](#createload-wallet)

```
// By user + currency (creates if not exists)
$wallet = new Wallet('USR123456', 'NGN', $db_cred);

// By address (must exist)
$wallet = new Wallet('219123456789012', '', $db_cred);

// Check existence
Wallet::exists('219123456789012', $pdo);

// Find user's wallets
$wallets = Wallet::findByUser('USR123456', $pdo);
```

### Balance

[](#balance)

```
// Get balance (from ledger - source of truth)
$balance = $wallet->balance();

// Verify against database (throws IntegrityException on mismatch)
$balance = $wallet->balance(verify: true);

// Check sufficient funds
$wallet->hasSufficientBalance(1000.00); // bool
```

Transactions
------------

[](#transactions)

### Transfer Between Wallets

[](#transfer-between-wallets)

```
$hashes = Transaction::transfer($from, $to, 1000.00, 'Payment');
// Returns: ['credit_hash', 'debit_hash']
```

### Credit from External Payment

[](#credit-from-external-payment)

```
use TimeFrontiers\Wallet\Payment\MockPaymentVerifier;

$verifier = new MockPaymentVerifier();
$verifier->addPayment('PAY123456', 5000.00, 'NGN', 'paid');

$hash = Transaction::creditFromPayment(
  $wallet,
  'PAY123456',
  5000.00,
  'Top-up via Stripe',
  $verifier
);
```

### Batch Transfers

[](#batch-transfers)

```
$result = Transaction::batch($source_wallet)
  ->credit('219111111111111', 100.00, 'Bonus')
  ->credit('219222222222222', 50.00, 'Refund')
  ->credit('219333333333333', 25.00, 'Cashback')
  ->execute();

// Result
$result->batchId();        // '127123456789012'
$result->totalAmount();    // 175.00
$result->creditCount();    // 3
$result->hashes();         // All tx hashes
$result->creditHashes();   // Credit hashes only
$result->debitHash();      // Single debit hash
```

### Bulk Credits

[](#bulk-credits)

```
$result = Transaction::batch($source)
  ->creditMany([
    ['address' => '219111111111111', 'amount' => 100, 'narration' => 'Bonus'],
    ['address' => '219222222222222', 'amount' => 50, 'narration' => 'Refund'],
  ])
  ->execute();
```

### Query Transactions

[](#query-transactions)

```
// Find by hash
$tx = Transaction::find('abc123...', $pdo);

// Find by address
$txs = Transaction::findByAddress('219123456789012', type: 'credit', limit: 50, db: $pdo);

// Find by batch
$txs = Transaction::findByBatch('127123456789012', $pdo);
```

Ledger System
-------------

[](#ledger-system)

### File Structure

[](#file-structure)

```
/.txhive/ledgers/USR123456/
  ├── 219123456789012.ledger           # Current active
  ├── 219123456789012.2024-01.ledger   # January archive
  ├── 219123456789012.2024-02.ledger   # February archive
  └── 219123456789012.checksum         # Integrity hash

```

### Direct Ledger Access

[](#direct-ledger-access)

```
$ledger = $wallet->ledger();

// Read operations
$ledger->balance();
$ledger->count();
$ledger->count('credit');
$ledger->totals();
$ledger->first();
$ledger->last();
$ledger->getTransaction('abc123');
$ledger->getTransactions('credit');

// Verification
$ledger->verify();

// Archives
$ledger->archives();
```

### Recovery

[](#recovery)

```
// Rebuild ledger from database
$transactions = Transaction::findByAddress($address, db: $pdo);
$wallet->ledger()->rebuild($transactions);
```

Payment Integration
-------------------

[](#payment-integration)

### Implement Custom Verifier

[](#implement-custom-verifier)

```
use TimeFrontiers\Wallet\Payment\PaymentVerifierInterface;

class StripePaymentVerifier implements PaymentVerifierInterface {

  public function verify(string $reference, float $amount, string $currency): bool {
    $payment = \Stripe\PaymentIntent::retrieve($reference);

    return $payment->status === 'succeeded'
        && $payment->amount >= $amount * 100
        && strtoupper($payment->currency) === $currency;
  }

  public function availableBalance(string $reference): float {
    // Return available balance
  }

  public function markSpent(string $reference, float $amount, string $tx_hash): bool {
    // Record the claim
  }

  public function getPayment(string $reference): ?array {
    // Return payment details
  }

  public function isValidReference(string $reference): bool {
    return preg_match('/^pi_[a-zA-Z0-9]+$/', $reference);
  }
}
```

Error Handling
--------------

[](#error-handling)

```
use TimeFrontiers\Wallet\Exception\{
  WalletException,
  WalletNotFoundException,
  InsufficientBalanceException,
  IntegrityException,
  PaymentVerificationException,
  TransactionException
};

try {
  Transaction::transfer($from, $to, 1000000, 'Big payment');
} catch (InsufficientBalanceException $e) {
  echo "Need {$e->required()}, have {$e->available()}";
  echo "Shortfall: {$e->shortfall()}";
} catch (IntegrityException $e) {
  echo "Ledger: {$e->ledgerBalance()}, DB: {$e->dbBalance()}";
} catch (PaymentVerificationException $e) {
  echo "Payment {$e->reference()} failed";
}
```

Security
--------

[](#security)

### Why File Ledger is More Secure

[](#why-file-ledger-is-more-secure)

AspectFile LedgerDatabaseAttack SurfaceServer access onlySQL injection, leaked credsRemote TamperingRequires server accessMultiple vectorsAccess ControlOS permissionsApp + DB usersDetectionChecksumsRequires audit logs### Best Practices

[](#best-practices)

1. **Store ledgers outside web root**: `/var/data/.txhive/`
2. **Restrict file permissions**: `chmod 600` on ledger files
3. **Use strict integrity mode**: Fail on mismatch
4. **Regular backups**: Archive ledger files
5. **Monitor alerts table**: Process pending notifications

License
-------

[](#license)

[MIT License](LICENSE)

###  Health Score

38

—

LowBetter than 83% of packages

Maintenance89

Actively maintained with recent releases

Popularity6

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity42

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

54d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/dfcd9a5645cb2deb0771b9102be193d63b495564b1e06c0b85cb31bf028d0887?d=identicon)[ikechukwuokalia](/maintainers/ikechukwuokalia)

---

Top Contributors

[![ikechukwuokalia](https://avatars.githubusercontent.com/u/4541227?v=4)](https://github.com/ikechukwuokalia "ikechukwuokalia (3 commits)")

---

Tags

phpimmutablewallettransactionfintechblockchainledger

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/timefrontiers-php-wallet/health.svg)

```
[![Health](https://phpackages.com/badges/timefrontiers-php-wallet/health.svg)](https://phpackages.com/packages/timefrontiers-php-wallet)
```

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
