Overview
Transactions in Mueve represent the core exchange operations where users buy or sell USD, converting to or from Bolivares (Bs) at the current market rate. Each transaction includes comprehensive validation, commission calculation, and status tracking to ensure secure and accurate currency exchanges.
Transaction Types
Mueve supports two primary transaction types:
Buy Transaction (Comprar)
Sell Transaction (Vender)
{
"transaction_type" : "Comprar" ,
"amount_usd" : 50.00 ,
"rate_bs" : 42.15 ,
"payment_reference" : "REF123456789" ,
"type_pay" : "bank_transfer" ,
"recipient_account" : "0102-1234-5678"
}
Buy vs Sell Logic
The calculation differs significantly between buying and selling:
Buy (Comprar) - Commission Added
When buying USD, the commission is added to the amount: const total_usd = amount_usd + commission_usd ;
const total_bs = total_usd * rate_bs ;
Example : Buy $50 USD
Base amount: $50.00
Commission: $1.40
Total to pay : $51.40 USD (2,166.51 Bs at rate 42.15)
Sell (Vender) - Commission Deducted
When selling USD, the commission is deducted from the amount: const net_usd = amount_usd - commission_usd ;
const total_bs = net_usd * rate_bs ;
Example : Sell $50 USD
Base amount: $50.00
Commission: $1.40
Net received : $48.60 USD (2,048.49 Bs at rate 42.15)
Buy Transaction Logic (transactionController.js:51-58)
Sell Transaction Logic (transactionController.js:38-48)
// Default: treat as Comprar
const total_usd = round2 ( A + commission_usd );
return {
commission_usd ,
total_usd ,
total_bs: round2 ( total_usd * r ),
raw_amount_usd: A ,
};
For sell transactions, if the commission equals or exceeds the amount, the transaction is rejected with error: “La comisión excede o iguala el monto proporcionado”
Transaction Status
All transactions are created with an initial status that tracks their lifecycle:
Status: Pendiente (Pending)
Every new transaction starts with the “Pendiente” status:
Transaction Creation (transactionController.js:105-121)
Success Response
const [ result ] = await pool . query (
`INSERT INTO transactions
(user_id, transaction_type, amount_usd, commission_usd, total_usd, rate_bs, total_bs, payment_reference, status, type_pay, recipient_account)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'Pendiente', ?, ?)` ,
[
userId ,
transaction_type ,
amount_usd ,
commission_usd ,
total_usd ,
rate_bs ,
total_bs ,
payment_reference ,
type_pay ,
recipient_account ,
],
);
The “Pendiente” status indicates that the transaction has been recorded but is awaiting verification or completion by the system administrator.
Payment Reference Validation
To prevent duplicate transactions and fraud, Mueve validates that each payment reference is unique:
Check Reference Uniqueness
Before creating a transaction, the system queries the database to verify the payment reference hasn’t been used before
Reject Duplicates
If the reference exists, the transaction is rejected with a 409 Conflict status
Proceed with Creation
Only unique references are allowed to proceed to transaction creation
Reference Validation (transactionController.js:18-26)
Duplicate Check (transactionController.js:92-96)
Duplicate Reference Error
const isPaymentReferenceUsed = async ( reference ) => {
if ( ! reference ) return false ;
const [ rows ] = await pool . query (
"SELECT id FROM transactions WHERE payment_reference = ? LIMIT 1" ,
[ reference ],
);
return rows . length > 0 ;
};
Always generate unique payment references for each transaction. Using the same reference twice will result in a 409 Conflict error.
Rate Validation
Mueve implements strict rate validation to ensure users complete transactions with current market rates:
Validation Process
Fetch Current Rate
The system retrieves the current USD to Bolivares rate from Redis
Compare with User Rate
The rate submitted by the user is compared against the current rate
Apply Tolerance
A tolerance of ±0.35 is applied to account for minor fluctuations
Reject or Proceed
If the difference exceeds tolerance, the transaction is rejected
Rate Validation (transactionController.js:80-91)
Example Validation
Example Rejection
const rateInRedis = await getBTC ();
const rateSentByUser = parseFloat ( rate_bs );
const tolerance = 0.35 ;
const diff = Math . abs ( rateInRedis - rateSentByUser );
if ( diff > tolerance ) {
return res . status ( 400 ). json ({
message:
"La tasa ha cambiado. Por favor actualiza la página para continuar." ,
});
}
To avoid rate validation errors, always fetch the latest rate immediately before creating a transaction. Rates cached for more than a few seconds may become stale.
Required Fields
All transaction creation requests must include the following fields:
Type of transaction: "Comprar" (buy) or "Vender" (sell)
Amount in USD (base amount before commission)
Current USD to Bolivares exchange rate (must be within tolerance)
Unique payment reference identifier
Payment method (e.g., "bank_transfer", "mobile_payment")
Destination account identifier
Validation Check (transactionController.js:74-78)
Missing Fields Error
if ( ! transaction_type || ! amount_usd || ! rate_bs || ! payment_reference ) {
return res
. status ( 400 )
. json ({ message: "Todos los campos son obligatorios" });
}
Transaction Totals Calculation
The system uses a computeTotals helper function that handles all financial calculations:
Compute Totals (transactionController.js:29-59)
Rounding Helper (transactionController.js:5-7)
const computeTotals = ({ transaction_type , amount_usd , rate_bs }) => {
const commission_usd = round2 ( getCommissionUsd ( amount_usd ));
const A = parseFloat ( amount_usd );
const r = parseFloat ( rate_bs );
if ( isNaN ( A ) || isNaN ( r )) {
throw new Error ( "Invalid numeric values for amount or rate" );
}
if ( transaction_type === "Vender" || transaction_type === "VENTA" ) {
const net_usd = round2 ( A - commission_usd );
if ( net_usd <= 0 ) {
return { error: "La comisión excede o iguala el monto proporcionado" };
}
return {
commission_usd ,
total_usd: net_usd ,
total_bs: round2 ( net_usd * r ),
raw_amount_usd: A ,
};
}
// Default: treat as Comprar
const total_usd = round2 ( A + commission_usd );
return {
commission_usd ,
total_usd ,
total_bs: round2 ( total_usd * r ),
raw_amount_usd: A ,
};
};
All monetary values are rounded to 2 decimal places using the round2 helper function to ensure consistency across calculations.
Retrieving Transactions
Users can retrieve their transaction history through authenticated endpoints:
Get All User Transactions
Get User Transactions (transactionController.js:162-174)
Response Example
export const getUserTransactions = async ( req , res ) => {
try {
const userId = req . user . id ;
const [ transactions ] = await pool . query (
"SELECT * FROM transactions WHERE user_id = ? ORDER BY created_at DESC" ,
[ userId ],
);
res . json ( transactions );
} catch ( error ) {
console . error ( "Error al obtener transacciones:" , error );
res . status ( 500 ). json ({ message: "Error del servidor" });
}
};
Get Specific Transaction
Get Transaction by ID (transactionController.js:177-196)
Not Found Error
export const getTransactionById = async ( req , res ) => {
try {
const userId = req . user . id ;
const transactionId = req . params . id ;
const [ rows ] = await pool . query (
"SELECT * FROM transactions WHERE id = ? AND user_id = ?" ,
[ transactionId , userId ],
);
if ( rows . length === 0 ) {
return res . status ( 404 ). json ({ message: "Transacción no encontrada" });
}
res . json ( rows [ 0 ]);
} catch ( error ) {
console . error ( "Error al obtener transacción:" , error );
res . status ( 500 ). json ({ message: "Error del servidor" });
}
};
Transactions are scoped to the authenticated user - users can only view their own transactions.
Error Handling
The transaction system implements comprehensive error handling:
Error Code Scenario Message 400 Missing required fields ”Todos los campos son obligatorios” 400 Rate changed beyond tolerance ”La tasa ha cambiado. Por favor actualiza la página para continuar.” 400 Commission exceeds sell amount ”La comisión excede o iguala el monto proporcionado” 409 Duplicate payment reference ”La referencia de pago ya fue utilizada” 404 Transaction not found ”Transacción no encontrada” 500 Server error ”Error del servidor”
Best Practices
Always validate rates - Fetch the latest rate immediately before transaction creation
Generate unique references - Use UUIDs or timestamp-based identifiers for payment references
Handle rate change errors - Implement automatic retry with updated rates when validation fails
Show commission clearly - Display both base amount and total (with commission) to users
Verify minimum amounts - Ensure amounts are sufficient to cover commissions, especially for sell transactions