Comprehensive guide to input validation, business validation, and fraud/risk controls in Three-D Wallet.
This document defines the validation and fraud detection rules enforced by the Wallet API during wallet creation, funding, transfers, withdrawals, refunds, and dispute flows. Rules are grouped into hard failures (block the request) and soft flags (allow but log/alert).
Request is rejected with error. Examples: invalid tokens, duplicate transaction, insufficient balance.
HardRequest may proceed but is flagged and logged for review/alerting. Examples: unusual velocity, geo mismatch.
Softactive; inactive/defaulter blocked.transaction_ref can be reused across opposite types (e.g., booking OUT and refund IN).transaction_ref cannot be reused for the same type on the same wallet.{
"payable_id": 1001,
"type": "OUT",
"amount": 500,
"currency": "PKR",
"transaction_ref": "ORDER-123",
"entry_type": "booking"
}
// Second request with same (payable_id, type, transaction_ref)
// → 409 Conflict: "Duplicate transaction_ref for this type"
Time window: 10 minutes Events: 8 OUT txns of 200 PKR each Gaps: 5-10 seconds between txns Action: Allow, flag with risk_score=0.7 and send alert
Wallet balance: 1,000 PKR Request: OUT 1,050 PKR (including fee) Action: 422 Unprocessable Entity: "Insufficient balance"
Stored balance: 10,000 PKR Sum(success txns): 9,500 PKR Drift: 500 PKR (> threshold 100) Action: Auto-inactive + alert (if configured)
// Unique index (MySQL) preventing duplicates ALTER TABLE wallet_transactions ADD UNIQUE KEY uniq_wallet_type_ref (payable_id, type, transaction_ref); // Helpful indexes CREATE INDEX idx_wallet_created_at ON wallet_transactions (payable_id, created_at); CREATE INDEX idx_type_created_at ON wallet_transactions (type, created_at);
function processTransaction(req) {
assertValidTokens(req.headers); // Hard
assertIpWhitelisted(req.ip); // Hard
validateSchema(req.body); // Hard
enforceDuplicateRule(req.body); // Hard
withWalletLock(req.payable_id, () => {
enforceLimits(req.body); // Hard
assertSufficientBalance(req.body); // Hard
recordTransaction();
});
riskScore = evaluateFraudSignals(req); // Soft → Hard by threshold
if (riskScore > 0.9) autoDisableWallet();
return success();
}