Machine Coding Problem

Digital Wallet (Advanced)

macoAllfintechserialized-balance-updates
Commonly Asked By:PayPalBlockStripe

Functional Specifications

  • Serialized Dynamic Transfers: Processes transfers with strong transactional consistency across multi-wallet transfers.
  • OCC Conflict Protection: Restricts double-spending by failing/retrying transactions on conflicting version check validations.
  • Auditable Double-Entry Ledger: Every single transaction creates matching Debit and Credit lines synchronously.
  • Real-time Fraud Locks: Dynamically quarantines funds without altering aggregate account registers until review completion.

Production reference implementations demonstrating retry loops, CAS, double entry ledger logs, and reconciliation audits:

// ─── JAVA BLUEPRINT ──────────────────────────────────────────────────────────
import java.util.*;
import java.util.concurrent.*;

enum WalletStatus {
    ACTIVE, SUSPENDED, UNDER_REVIEW
}

class Wallet {
    private final String walletId;
    private final String ownerName;
    private final String currency;
    private double balance;
    private double heldAmount; // Fraud / auth holds
    private int version; // For OCC
    private WalletStatus status;

    public Wallet(String walletId, String ownerName, double initialBalance, String currency) {
        this.walletId = walletId;
        this.ownerName = ownerName;
        this.balance = initialBalance;
        this.heldAmount = 0.0;
        this.version = 1;
        this.currency = currency;
        this.status = WalletStatus.ACTIVE;
    }

    public String getWalletId() { return walletId; }
    public String getOwnerName() { return ownerName; }
    public synchronized double getBalance() { return balance; }
    public synchronized double getHeldAmount() { return heldAmount; }
    public synchronized double getAvailableBalance() { return balance - heldAmount; }
    public synchronized int getVersion() { return version; }
    public String getCurrency() { return currency; }
    public synchronized WalletStatus getStatus() { return status; }
    public synchronized void setStatus(WalletStatus status) { this.status = status; }

    public synchronized boolean compareAndSwapBalance(double oldBalance, double newBalance, int expectedVersion) {
        if (this.balance == oldBalance && this.version == expectedVersion) {
            this.balance = newBalance;
            this.version++;
            return true;
        }
        return false;
    }

    public synchronized boolean compareAndSwapHold(double oldHold, double newHold, int expectedVersion) {
        if (this.heldAmount == oldHold && this.version == expectedVersion) {
            this.heldAmount = newHold;
            this.version++;
            return true;
        }
        return false;
    }
}

class LedgerEntry {
    private final String entryId;
    private final String debitWalletId;
    private final String creditWalletId;
    private final double amount;
    private final String referenceId;
    private final long timestamp;

    public LedgerEntry(String debitWalletId, String creditWalletId, double amount, String referenceId) {
        this.entryId = UUID.randomUUID().toString();
        this.debitWalletId = debitWalletId;
        this.creditWalletId = creditWalletId;
        this.amount = amount;
        this.referenceId = referenceId;
        this.timestamp = System.currentTimeMillis();
    }

    public String getEntryId() { return entryId; }
    public String getDebitWalletId() { return debitWalletId; }
    public String getCreditWalletId() { return creditWalletId; }
    public double getAmount() { return amount; }
    public String getReferenceId() { return referenceId; }
    public long getTimestamp() { return timestamp; }
}

class WalletService {
    private final Map<String, Wallet> wallets = new ConcurrentHashMap<>();
    private final List<LedgerEntry> ledger = new CopyOnWriteArrayList<>();
    private static final int MAX_OCC_RETRIES = 5;

    public void addWallet(Wallet wallet) {
        wallets.put(wallet.getWalletId(), wallet);
    }

    public Wallet getWallet(String id) {
        return wallets.get(id);
    }

    public boolean transfer(String fromId, String toId, double amount, String referenceId) {
        Wallet from = wallets.get(fromId);
        Wallet to = wallets.get(toId);

        if (from == null || to == null) return false;
        if (from.getStatus() != WalletStatus.ACTIVE || to.getStatus() != WalletStatus.ACTIVE) return false;

        // Resource ordering to prevent deadlock
        Wallet firstLock = fromId.compareTo(toId) < 0 ? from : to;
        Wallet secondLock = fromId.compareTo(toId) < 0 ? to : from;

        int retry = 0;
        while (retry < MAX_OCC_RETRIES) {
            double fromBalance = from.getBalance();
            double fromHold = from.getHeldAmount();
            int fromVersion = from.getVersion();

            double toBalance = to.getBalance();
            int toVersion = to.getVersion();

            if (fromBalance - fromHold < amount) {
                throw new IllegalArgumentException("Insufficient active funds for debit");
            }

            double newFromBalance = fromBalance - amount;
            double newToBalance = toBalance + amount;

            synchronized (firstLock) {
                synchronized (secondLock) {
                    if (from.getVersion() == fromVersion && to.getVersion() == toVersion) {
                        if (from.compareAndSwapBalance(fromBalance, newFromBalance, fromVersion) &&
                            to.compareAndSwapBalance(toBalance, newToBalance, toVersion)) {
                            ledger.add(new LedgerEntry(fromId, toId, amount, referenceId));
                            return true;
                        }
                    }
                }
            }

            retry++;
            try {
                Thread.sleep(10 + ThreadLocalRandom.current().nextInt(15));
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                return false;
            }
        }
        return false;
    }

    public List<LedgerEntry> getLedger() { return ledger; }
}

class FraudHoldService {
    public boolean placeHold(Wallet wallet, double amount) {
        while (true) {
            double currentHold = wallet.getHeldAmount();
            double available = wallet.getAvailableBalance();
            int currentVersion = wallet.getVersion();

            if (available < amount) return false;

            if (wallet.compareAndSwapHold(currentHold, currentHold + amount, currentVersion)) {
                return true;
            }
        }
    }

    public boolean releaseHold(Wallet wallet, double amount) {
        while (true) {
            double currentHold = wallet.getHeldAmount();
            int currentVersion = wallet.getVersion();

            if (currentHold < amount) return false;

            if (wallet.compareAndSwapHold(currentHold, currentHold - amount, currentVersion)) {
                return true;
            }
        }
    }
}

class BalanceReconciler {
    public boolean reconcile(Wallet wallet, List<LedgerEntry> ledger, double initialBalance) {
        double credits = ledger.stream()
            .filter(e -> e.getCreditWalletId().equals(wallet.getWalletId()))
            .mapToDouble(LedgerEntry::getAmount).sum();

        double debits = ledger.stream()
            .filter(e -> e.getDebitWalletId().equals(wallet.getWalletId()))
            .mapToDouble(LedgerEntry::getAmount).sum();

        double expectedBalance = initialBalance + credits - debits;
        return Math.abs(wallet.getBalance() - expectedBalance) < 0.001;
    }
}

public class Main {
    public static void main(String[] args) {
        System.out.println("=== DIGITAL WALLET ADVANCED DEMO (JAVA) ===");

        WalletService walletService = new WalletService();
        FraudHoldService fraudHoldService = new FraudHoldService();
        BalanceReconciler reconciler = new BalanceReconciler();

        Wallet alice = new Wallet("W_ALICE", "Alice", 500.0, "USD");
        Wallet bob = new Wallet("W_BOB", "Bob", 250.0, "USD");
        Wallet system = new Wallet("W_SYSTEM", "System Escrow", 1000.0, "USD");

        walletService.addWallet(alice);
        walletService.addWallet(bob);
        walletService.addWallet(system);

        System.out.println("Initial balances:");
        System.out.println("Alice: $" + alice.getBalance() + " (v" + alice.getVersion() + ")");
        System.out.println("Bob: $" + bob.getBalance() + " (v" + bob.getVersion() + ")");

        System.out.println("\n--- Executing OCC Transfer: System to Alice $150 ---");
        boolean t1 = walletService.transfer("W_SYSTEM", "W_ALICE", 150.0, "TX_101");
        System.out.println("Transfer Success: " + t1);
        System.out.println("Alice Balance: $" + alice.getBalance() + " (v" + alice.getVersion() + ")");

        System.out.println("\n--- Placing Fraud Hold of $50 on Bob ---");
        boolean h1 = fraudHoldService.placeHold(bob, 50.0);
        System.out.println("Hold Placed: " + h1);
        System.out.println("Bob Total Balance: $" + bob.getBalance());
        System.out.println("Bob Available Funds: $" + bob.getAvailableBalance());

        System.out.println("\n--- Performing Audit Reconciliation ---");
        boolean aliceReconciled = reconciler.reconcile(alice, walletService.getLedger(), 500.0);
        System.out.println("Alice Ledger Reconciled: " + aliceReconciled);
    }
}