Broken Transaction
The transfer endpoint has no ROLLBACK — a failed write corrupts the balance permanently.
Difficulty: Medium — 2 bugs in 1 file
What This Challenge Is
The POST /db/transfer endpoint moves credits from one user to another. It correctly opens a transaction with beginTransaction() and commits on success, but there is no try/catch and no rollBack(). If the second UPDATE fails (invalid user id, insufficient credits, database error), the first UPDATE is left permanently committed — credits were deducted but never arrived.
Load the broken file:
php artisan challenge:start db-broken-transactionThis adds:
app/Http/controllers/db/transfer.php—POST /db/transfer
The Two Bugs
Bug 1 — No try/catch around the transaction body
// BROKEN — if either UPDATE throws, the exception propagates uncaught
// and the transaction is left open with partial state
$pdo->beginTransaction();
$db->query(
'UPDATE users SET credits = credits - :amount WHERE id = :id',
['amount' => $amount, 'id' => $fromId]
);
$db->query(
'UPDATE users SET credits = credits + :amount WHERE id = :id',
['amount' => $amount, 'id' => $toId]
);
$pdo->commit();Fix: Wrap everything between beginTransaction() and commit() in a try block.
Bug 2 — No rollBack() in the error path
Without a catch block that calls $pdo->rollBack(), a failed second query leaves the first update committed. The sender's credits are gone, but the receiver never got them.
// CORRECT
try {
$pdo->beginTransaction();
$db->query(
'UPDATE users SET credits = credits - :amount WHERE id = :id',
['amount' => $amount, 'id' => $fromId]
);
$db->query(
'UPDATE users SET credits = credits + :amount WHERE id = :id',
['amount' => $amount, 'id' => $toId]
);
$pdo->commit();
header('Content-Type: application/json');
echo json_encode(['success' => true]);
} catch (\Exception $e) {
$pdo->rollBack();
http_response_code(500);
header('Content-Type: application/json');
echo json_encode(['error' => 'Transfer failed']);
}The catch block calls $pdo->rollBack(). This undoes the first UPDATE as if it never happened — both accounts stay consistent.
File to Edit
app/Http/controllers/db/transfer.php
Verify Your Solution
php artisan challenge:verifyThe verifier checks:
beginTransactionis present (keep it)commitis present (keep it)rollBackis present (add it)catchis present (add the try/catch)
Testing Manually
With php artisan serve running:
# Transfer 10 credits from user 1 to user 2
curl -X POST http://localhost:8000/db/transfer \
-d "from_id=1&to_id=2&amount=10"To test the error path, send an invalid to_id (one that doesn't exist) — the second UPDATE should fail, and you should see the error JSON response with no net change to user 1's balance.
Hints
$pdo->rollBack()undoes everything since$pdo->beginTransaction()— both UPDATEs vanish as if neither ran- The
catchblock must callrollBack()before sending the error response — order matters beginTransaction()andcommit()must stay in the code — they're not the bugs- Without
rollBack()incatch, an open failed transaction may leave your connection in an error state for subsequent queries