byjg/wallets 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

byjg/wallets

最新稳定版本:6.0.0

Composer 安装命令:

composer require byjg/wallets

包简介

A robust PHP library for managing digital wallets and financial transactions with full audit trails, transaction chain integrity, and support for reserved funds.

README 文档

README

Sponsor Build Status Opensource ByJG GitHub source GitHub license GitHub release

A robust PHP library for managing digital wallets and financial transactions with full audit trails, transaction chain integrity, and support for reserved funds.

Features

  • Multiple wallets per user - Each user can have multiple wallets for different currencies
  • Multiple currencies - Support for any currency type (fiat, crypto, points, etc.)
  • Reserved funds - Pre-authorize and block funds for pending transactions
  • Transaction chain integrity - Immutable transaction history with UUID linking and checksums
  • Atomic operations - All balance updates are atomic and transactional
  • Flexible scale - Support for different decimal places (cents, satoshis, whole units)
  • Overdraft support - Optional negative balances with configurable minimum values
  • Audit trail - Complete transaction history with balance snapshots
  • Idempotent operations - UUID-based transaction deduplication
  • Extensible - Easily extend wallets and transactions with custom fields

Installation

composer require byjg/wallets

Quick Start

use ByJG\Wallets\Service\WalletService;
use ByJG\Wallets\Service\WalletTypeService;
use ByJG\Wallets\Service\TransactionService;
use ByJG\Wallets\Entity\WalletTypeEntity;
use ByJG\Wallets\Repository\WalletRepository;
use ByJG\Wallets\Repository\WalletTypeRepository;
use ByJG\Wallets\Repository\TransactionRepository;
use ByJG\Wallets\DTO\TransactionDTO;
use ByJG\AnyDataset\Db\Factory;

// Initialize database connection
$dbDriver = Factory::getDbInstance('mysql://user:pass@localhost/dbname');

// Initialize repositories
$walletTypeRepo = new WalletTypeRepository($dbDriver);
$transactionRepo = new TransactionRepository($dbDriver);
$walletRepo = new WalletRepository($dbDriver);

// Initialize services
$walletTypeService = new WalletTypeService($walletTypeRepo);
$transactionService = new TransactionService($transactionRepo, $walletRepo);
$walletService = new WalletService($walletRepo, $walletTypeService, $transactionService);

// Create a wallet type
$walletType = new WalletTypeEntity();
$walletType->setWalletTypeId('USD');
$walletType->setName('US Dollar');
$walletTypeService->update($walletType);

// Create a wallet with $100.00 initial balance (10000 cents)
$walletId = $walletService->createWallet('USD', 'user-123', 10000, 2);

// Add funds: $50.00
$transaction = $transactionService->addFunds(
    TransactionDTO::create($walletId, 5000)
        ->setDescription('Deposit from bank account')
);

// Withdraw funds: $30.00
$transaction = $transactionService->withdrawFunds(
    TransactionDTO::create($walletId, 3000)
        ->setDescription('Purchase payment')
);

// Reserve funds for pending withdrawal: $20.00
$reserve = $transactionService->reserveFundsForWithdraw(
    TransactionDTO::create($walletId, 2000)
        ->setDescription('Pre-authorization')
);

// Accept the reservation
$transactionService->acceptFundsById($reserve->getTransactionId());

// Get wallet balance
$wallet = $walletService->getById($walletId);
echo "Available: " . ($wallet->getAvailable() / 100) . " USD\n";

Documentation

Full documentation is available at https://opensource.byjg.com/docs/php/wallets

Core Concepts

Integer Storage with Scale

All monetary amounts are stored as integers (BIGINT) representing the smallest currency unit:

// USD with scale=2 (cents)
$walletId = $walletService->createWallet('USD', 'user-123', 10000, 2);
// 10000 represents $100.00

// Bitcoin with scale=8 (satoshis)
$btcWalletId = $walletService->createWallet('BTC', 'user-123', 100000000, 8);
// 100000000 represents 1.00000000 BTC

// Loyalty points with scale=0 (whole units)
$pointsWalletId = $walletService->createWallet('POINTS', 'user-123', 1000, 0);
// 1000 represents 1000 points

Balance Components

Each wallet maintains three balance components:

  • balance - Total funds (reserved + available)
  • reserved - Funds held for pending transactions
  • available - Funds available for immediate use
$wallet = $walletService->getById($walletId);
echo "Balance: " . $wallet->getBalance();      // 10000 (total)
echo "Reserved: " . $wallet->getReserved();    // 2000 (blocked)
echo "Available: " . $wallet->getAvailable();  // 8000 (usable)

Transaction Types

Type Code Description
Balance B Initial balance or reset
Deposit D Add funds immediately
Withdraw W Remove funds immediately
Deposit Blocked DB Reserve for incoming funds
Withdraw Blocked WB Reserve funds for withdrawal
Reject R Reverse a reserved transaction

Transaction Chain Integrity

Every transaction links to the previous via uuid and previousuuid, creating an immutable audit trail:

$tx1 = $transactionService->addFunds($dto1);  // previousuuid = null
$tx2 = $transactionService->addFunds($dto2);  // previousuuid = $tx1->uuid
$tx3 = $transactionService->withdrawFunds($dto3);  // previousuuid = $tx2->uuid

Each transaction includes a SHA-256 checksum for data integrity verification.

Use Cases

E-commerce Platform

// Reserve funds when order is placed
$reserve = $transactionService->reserveFundsForWithdraw(
    TransactionDTO::create($walletId, 9999)
        ->setDescription('Order #12345')
        ->setReferenceSource('ecommerce')
        ->setReferenceId('order-12345')
);

// Capture payment when order ships
$transactionService->acceptFundsById($reserve->getTransactionId());

// Or cancel and release funds if order is cancelled
// $transactionService->rejectFundsById($reserve->getTransactionId());

Multi-Currency Wallets

// Create multiple wallets for same user
$usdWallet = $walletService->createWallet('USD', 'user-123', 100000, 2);
$eurWallet = $walletService->createWallet('EUR', 'user-123', 50000, 2);
$btcWallet = $walletService->createWallet('BTC', 'user-123', 5000000, 8);

// Transfer between wallets (with exchange rate logic in your app)
$walletService->transferFunds($usdWallet, $eurWallet, 10000);

Gaming/Betting Platform

// Reserve bet amount
$betReserve = $transactionService->reserveFundsForWithdraw(
    TransactionDTO::create($walletId, 5000)
        ->setDescription('Bet on Game #789')
);

// User wins - reject withdrawal and add winnings
$transactionService->rejectFundsById($betReserve->getTransactionId());
$transactionService->addFunds(
    TransactionDTO::create($walletId, 10000)
        ->setDescription('Bet winnings')
);

// User loses - accept withdrawal
// $transactionService->acceptFundsById($betReserve->getTransactionId());

Testing

# Start MySQL container
docker run --name mysql-container --rm \
  -e MYSQL_ROOT_PASSWORD=password \
  -p 3306:3306 -d mysql:8.0

# Run migrations
vendor/bin/migrate up mysql://root:password@localhost/test -path=db

# Run tests
vendor/bin/phpunit

API Reference

WalletService

  • createWallet(string $walletTypeId, string $userId, int $balance, int $scale = 2, int $minValue = 0, ?string $extra = null): int
  • getById(int $walletId): WalletEntity
  • getByUserId(string $userId, string $walletType = ""): array
  • getByWalletTypeId(string $walletTypeId): array
  • overrideBalance(int $walletId, int $newBalance, int $newScale = 2, int $newMinValue = 0, string $description = "Reset Balance"): ?int
  • partialBalance(int $walletId, int $balance, string $description = "Partial Balance"): TransactionEntity
  • closeWallet(int $walletId): ?int
  • transferFunds(int $walletSource, int $walletTarget, int $amount): array

TransactionService

  • addFunds(TransactionDTO $dto): TransactionEntity
  • withdrawFunds(TransactionDTO $dto): TransactionEntity
  • reserveFundsForWithdraw(TransactionDTO $dto): TransactionEntity
  • reserveFundsForDeposit(TransactionDTO $dto): TransactionEntity
  • acceptFundsById(int $transactionId): int
  • acceptFundsByUuid(string $uuid): int
  • rejectFundsById(int $transactionId): int
  • rejectFundsByUuid(string $uuid): int
  • acceptPartialFundsById(int $transactionId, TransactionDTO $transactionDTO, TransactionDTO $transactionRefundDTO): TransactionEntity
  • getById(int $transactionId): TransactionEntity
  • getByWallet(int $walletId, int $limit = null, int $offset = null): array
  • getByDate(int $walletId, string $startDate, string $endDate, int $limit = null, int $offset = null): array
  • getByReference(string $referenceSource, string $referenceId): array
  • getByUuid(string $uuid): ?TransactionEntity
  • existsTransactionByUuid(string $uuid): bool
  • getReservedTransactions(int $walletId): array

Dependencies

flowchart TD
    byjg/wallets --> ext-pdo
    byjg/wallets --> ext-openssl
    byjg/wallets --> byjg/micro-orm
Loading

Open source ByJG

统计信息

  • 总下载量: 2
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 2
  • 点击次数: 0
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 2
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2025-11-08