Skip to main content

Overview

The Wallet & Transactions feature provides a comprehensive digital wallet system for managing investor funds. Each user automatically receives a wallet (Cartera) upon registration, allowing them to deposit funds from bank accounts, withdraw to bank accounts, and track their complete transaction history.

Key Capabilities

Automatic Wallets

Every user gets a wallet automatically created during registration with zero initial balance

Deposit & Withdraw

Transfer funds between bank accounts and wallet with real-time balance updates

Transaction History

Complete audit trail of all deposits and withdrawals with timestamps

Balance Tracking

Real-time balance calculation across wallet and linked bank accounts

Data Models

Cartera (Wallet) Entity

FieldTypeDescription
idCarteraintUnique wallet identifier (auto-generated)
saldodoubleCurrent wallet balance
idUsulongUser ID (foreign key to Usuario)
usuarioUsuarioAssociated user entity
Each user has exactly one wallet, created automatically during registration with saldo initialized to 0.

Transacciones Entity

FieldTypeDescription
idTransaccionlongUnique transaction identifier (auto-generated)
montodoubleTransaction amount
fechaDateTransaction date and time
idCuentaBancariaintBank account ID involved in transaction
cuentaBancariaCuentaBancariaBank account entity
idTipoTransaccionlongTransaction type: 1 = Deposito, 2 = Retiro
tipoTransaccionTipoTransaccionTransaction type entity

TipoTransaccion Entity

Defines transaction types:
IDTypeDescription
1DepositoTransfer from bank account to wallet
2RetiroTransfer from wallet to bank account

Wallet System Architecture

Wallet Lifecycle

1

Wallet Creation

When a user registers via POST /api/registrar, the system automatically creates a wallet:
Cartera cartera = new Cartera();
cartera.setSaldo(0);
cartera.setIdUsu(objUsuario.getId());
Cartera objCartera = carteraService.insertaActualizaCartera(cartera);
See implementation in UsuarioController.java:131-138
2

Balance Management

Wallet balance is updated atomically during deposits, withdrawals, and investment transactions.
3

Transaction Recording

Every balance change creates a transaction record for complete audit trails.

Transaction Workflows

Deposit Workflow (Bank Account → Wallet)

The deposit process transfers funds from a user’s bank account to their wallet:
1

Validate Bank Account

System verifies:
  • Bank account exists
  • Bank account belongs to current user
  • Bank account has sufficient balance
2

Calculate New Balances

// Deduct from bank account
double nuevoSaldoCB = saldoActualCB - transaccion.getMonto();

// Add to wallet
double nuevoSaldoCartera = saldoActualCartera + transaccion.getMonto();
3

Update Both Balances

Update bank account and wallet balances atomically.
4

Record Transaction

Create transaction record:
  • idTipoTransaccion = 1 (Deposito)
  • fecha = current date/time
  • monto = deposited amount
  • Link to bank account
// From TransaccionController.java:92-136
@PostMapping("/deposito")
public ResponseEntity<?> depositarCuenta(@RequestBody Transacciones transaccion, HttpSession session) {
    long idUsuAct = (long) session.getAttribute("idUsuActual");
    List<CuentaBancaria> lista = cuentaBancariaService.listaCuentaBancariaxIdUsuAct(idUsuAct);
    int IdCuentaB = transaccion.getIdCuentaBancaria();
    
    // Verify bank account belongs to user
    boolean existe = lista.stream().anyMatch(cuenta -> cuenta.getIdCuentaBancaria() == IdCuentaB);
    if (!existe) {
        return new ResponseEntity<>("La cuenta bancaria no existe", HttpStatus.BAD_REQUEST);
    }
    
    // Get current balances
    Cartera cartera = carteraService.buscarCartera(idUsuAct);
    double saldoActualCartera = cartera.getSaldo();
    
    Optional<CuentaBancaria> optional = cuentaBancariaService.buscarxId(IdCuentaB);
    CuentaBancaria cuenta = optional.get();
    double saldoActualCB = cuenta.getSaldo();
    
    // Verify sufficient funds in bank account
    if (saldoActualCB < transaccion.getMonto()) {
        return new ResponseEntity<>("No cuenta con saldo suficiente", HttpStatus.BAD_REQUEST);
    }
    
    // Update balances
    double nuevoSaldoCB = saldoActualCB - transaccion.getMonto();
    cuenta.setSaldo(nuevoSaldoCB);
    cuentaBancariaService.insertaActualizaCuentaBancaria(cuenta);
    
    double nuevoSaldoCartera = saldoActualCartera + transaccion.getMonto();
    cartera.setSaldo(nuevoSaldoCartera);
    carteraService.insertaActualizaCartera(cartera);
    
    // Record transaction
    transaccion.setIdTipoTransaccion(1); // Deposito
    transaccion.setFecha(new Date());
    transaccionService.insertaTransaccion(transaccion);
    
    return ResponseEntity.ok("Deposito realizado con exito");
}
See full implementation at TransaccionController.java:92-136

Withdrawal Workflow (Wallet → Bank Account)

The withdrawal process transfers funds from wallet to a bank account:
1

Validate Wallet Balance

System verifies:
  • Bank account exists
  • Bank account belongs to current user
  • Wallet has sufficient balance
2

Calculate New Balances

// Add to bank account
double nuevoSaldoCB = saldoActualCB + transaccion.getMonto();

// Deduct from wallet
double nuevoSaldoCartera = saldoActualCartera - transaccion.getMonto();
3

Update Both Balances

Update wallet and bank account balances atomically.
4

Record Transaction

Create transaction record:
  • idTipoTransaccion = 2 (Retiro)
  • fecha = current date/time
  • monto = withdrawn amount
  • Link to bank account
// From TransaccionController.java:138-182
@PostMapping("/retiro")
public ResponseEntity<?> retirarCuenta(@RequestBody Transacciones transaccion, HttpSession session) {
    long idUsuAct = (long) session.getAttribute("idUsuActual");
    List<CuentaBancaria> lista = cuentaBancariaService.listaCuentaBancariaxIdUsuAct(idUsuAct);
    int IdCuentaB = transaccion.getIdCuentaBancaria();
    
    // Verify bank account belongs to user
    boolean existe = lista.stream().anyMatch(cuenta -> cuenta.getIdCuentaBancaria() == IdCuentaB);
    if (!existe) {
        return new ResponseEntity<>("La cuenta bancaria no existe", HttpStatus.BAD_REQUEST);
    }
    
    // Get current balances
    Cartera cartera = carteraService.buscarCartera(idUsuAct);
    double saldoActualCartera = cartera.getSaldo();
    
    Optional<CuentaBancaria> optional = cuentaBancariaService.buscarxId(IdCuentaB);
    CuentaBancaria cuenta = optional.get();
    double saldoActualCB = cuenta.getSaldo();
    
    // Verify sufficient funds in wallet
    if (saldoActualCartera < transaccion.getMonto()) {
        return new ResponseEntity<>("No cuenta con saldo suficiente en su cartera", HttpStatus.BAD_REQUEST);
    }
    
    // Update balances
    double nuevoSaldoCB = saldoActualCB + transaccion.getMonto();
    cuenta.setSaldo(nuevoSaldoCB);
    cuentaBancariaService.insertaActualizaCuentaBancaria(cuenta);
    
    double nuevoSaldoCartera = saldoActualCartera - transaccion.getMonto();
    cartera.setSaldo(nuevoSaldoCartera);
    carteraService.insertaActualizaCartera(cartera);
    
    // Record transaction
    transaccion.setIdTipoTransaccion(2); // Retiro
    transaccion.setFecha(new Date());
    transaccionService.insertaTransaccion(transaccion);
    
    return ResponseEntity.ok("Retiro realizado con exito");
}
See full implementation at TransaccionController.java:138-182

Main API Endpoints

Wallet Endpoints

GET /api/detalleCartera
Retrieves the current user’s wallet details and balance.Authentication: Requires valid JWT token and active session.Response:
{
  "idCartera": 15,
  "saldo": 25000.50,
  "idUsu": 15,
  "usuario": {
    "id": 15,
    "nombre": "Juan",
    "username": "jperez"
  }
}
See implementation at CarteraController.java:28-44

Transaction Endpoints

POST /api/deposito
Transfer funds from bank account to wallet.Request Body:
{
  "monto": 5000.0,
  "idCuentaBancaria": 3
}
Validations:
  • Bank account must belong to current user
  • Bank account must have sufficient balance
Success Response:
{
  "mensaje": "Deposito realizado con exito"
}
Error Responses:
  • 400 BAD_REQUEST: “No cuenta con saldo suficiente en su cuenta Bancaria!”
  • 400 BAD_REQUEST: “La cuenta bancaria con Id: X no existe”
See implementation at TransaccionController.java:92-136

Transaction History Endpoints

GET /api/user/listaTransacciones
Retrieves all transactions for the current user.Response:
[
  {
    "idTransaccion": 101,
    "monto": 5000.0,
    "fecha": "2026-03-05",
    "tipoTransaccion": {
      "idTipoTransaccion": 1,
      "tipo": "Deposito"
    },
    "cuentaBancaria": {
      "idCuentaBancaria": 3,
      "numeroCuenta": "1234567890"
    }
  },
  {
    "idTransaccion": 102,
    "monto": 2000.0,
    "fecha": "2026-03-06",
    "tipoTransaccion": {
      "idTipoTransaccion": 2,
      "tipo": "Retiro"
    },
    "cuentaBancaria": {
      "idCuentaBancaria": 3,
      "numeroCuenta": "1234567890"
    }
  }
]
See implementation at TransaccionController.java:63-69

Utility Endpoints

GET /api/listaTipoTransacciones
Returns available transaction types.Response:
[
  {
    "idTipoTransaccion": 1,
    "tipo": "Deposito"
  },
  {
    "idTipoTransaccion": 2,
    "tipo": "Retiro"
  }
]
See implementation at TransaccionController.java:49-54

Use Cases

Use Case 1: Investor Funding Their Wallet

Scenario: An investor wants to add funds to their wallet to invest in opportunities.
  1. Investor checks wallet balance: GET /api/detalleCartera
    • Response shows: saldo: 1000.0
  2. Investor decides to deposit $5,000 from bank account #3
  3. Investor calls POST /api/deposito:
    {
      "monto": 5000.0,
      "idCuentaBancaria": 3
    }
    
  4. System:
    • Verifies bank account #3 belongs to investor
    • Checks bank account has at least $5,000
    • Deducts $5,000 from bank account
    • Adds 5,000towallet(newbalance:5,000 to wallet (new balance: 6,000)
    • Records transaction with idTipoTransaccion: 1 (Deposito)
  5. Investor can now use the $6,000 wallet balance to invest

Use Case 2: Cashing Out Profits

Scenario: An investor received returns from investments and wants to withdraw to their bank.
  1. Investor checks wallet: GET /api/detalleCartera
    • Response shows: saldo: 12000.0 (original 6,000+6,000 + 6,000 returns)
  2. Investor decides to withdraw $8,000 to bank account #3
  3. Investor calls POST /api/retiro:
    {
      "monto": 8000.0,
      "idCuentaBancaria": 3
    }
    
  4. System:
    • Verifies bank account #3 belongs to investor
    • Checks wallet has at least $8,000
    • Adds $8,000 to bank account
    • Deducts 8,000fromwallet(newbalance:8,000 from wallet (new balance: 4,000)
    • Records transaction with idTipoTransaccion: 2 (Retiro)
  5. Investor retains $4,000 in wallet for future investments

Use Case 3: Reviewing Transaction History

Scenario: An investor wants to review their deposit and withdrawal history.
  1. Investor calls GET /api/user/listTransacciones/page/0
  2. System returns first 8 transactions showing:
    • Transaction IDs
    • Amounts
    • Dates
    • Types (Deposito or Retiro)
    • Associated bank accounts
  3. Investor can:
    • Calculate total deposits
    • Calculate total withdrawals
    • Reconcile with bank statements
    • Track account activity patterns

Use Case 4: Admin Monitoring Platform Liquidity

Scenario: Admin wants to monitor total funds in the platform.
  1. Admin calls GET /api/admin/listarCarteras
  2. System returns all user wallets with balances
  3. Admin calculates:
    • Total platform liquidity: sum of all saldo values
    • Distribution of funds across users
    • Identify high-value investors
  4. Admin calls GET /api/admin/listaTransacciones
  5. Admin analyzes:
    • Total deposits vs withdrawals
    • Platform cash flow trends
    • Most active investors

Balance Validation

The system implements strict balance validation to prevent overdrafts:

Deposit Validation

Before deposit:
if (saldoActualCB < transaccion.getMonto()) {
    return "No cuenta con saldo suficiente";
}
Ensures bank account has sufficient funds.

Withdrawal Validation

Before withdrawal:
if (saldoActualCartera < transaccion.getMonto()) {
    return "No cuenta con saldo suficiente en su cartera";
}
Ensures wallet has sufficient funds.

Transaction Integrity

Atomic Operations

Each transaction involves multiple database updates that must all succeed or all fail:
  1. Update source balance (bank account or wallet)
  2. Update destination balance (wallet or bank account)
  3. Create transaction record
The service layer should wrap these operations in a transaction:
@Transactional
public void procesarDeposito(Transacciones transaccion, long idUsuario) {
    // All updates within this method execute atomically
    // If any step fails, all changes are rolled back
}
Ensure proper transaction management at the service layer to maintain data consistency. The controller implementations should delegate to transactional service methods.

Best Practices

Validate Ownership

Always verify bank accounts belong to the current user before processing transactions.

Check Balances

Validate sufficient funds before attempting any balance update to prevent overdrafts.

Audit Trail

Record every transaction with complete details (amount, date, type, account) for compliance.

Error Handling

Provide clear error messages distinguishing between insufficient funds, invalid accounts, etc.

Error Handling

Common error scenarios:
ScenarioHTTP StatusResponse
Insufficient wallet balance400 BAD_REQUEST{"mensaje": "No cuenta con saldo suficiente en su cartera!"}
Insufficient bank balance400 BAD_REQUEST{"mensaje": "No cuenta con saldo suficiente en su cuenta Bancaria!"}
Bank account not owned400 BAD_REQUEST{"mensaje": "La cuenta bancaria con Id: X no existe"}
Wallet not found404 NOT_FOUND{"mensaje": "El no se encontro la cartera..."}

Build docs developers (and LLMs) love