ADS-009
Credit maths for a traffic exchange
Send N, receive M, and keep the difference as the house margin. The one transaction that moves credits between two members without leaking any.
An exchange runs on a ratio. A member surfing ten sites might earn eight credits toward their own traffic, with the other two absorbed by the house. That margin is what the operator sells, and it is also what stops the credit supply inflating forever.
The transfer has to be a single transaction: debit the advertiser whose site was shown, credit the surfer, record the house cut. Split those across separate statements without a transaction and a failure halfway leaves credits created or destroyed with nothing to show why.
Keep the ratio in settings and store it on the row. Changing the ratio should not rewrite history.
function award_surf_credit(PDO $pdo, int $sessionId, int $siteId, int $surferId): bool
{
$ratio = (float) setting('surf_ratio', '0.8'); // surfer keeps 80%, house 20%
$pdo->beginTransaction();
try {
// Lock the advertiser's balance and confirm they can still pay.
$st = $pdo->prepare('SELECT owner_id, credits FROM surf_sites WHERE id = ? FOR UPDATE');
$st->execute([$siteId]);
$site = $st->fetch(PDO::FETCH_ASSOC);
if (!$site || (int) $site['credits'] < 1) {
$pdo->rollBack();
return false; // out of credits: stop showing it
}
$pdo->prepare('UPDATE surf_sites SET credits = credits - 1, views = views + 1 WHERE id = ?')
->execute([$siteId]);
// Fractional credits are held as thousandths so nothing is lost to rounding.
$earned = (int) round($ratio * 1000);
$pdo->prepare('UPDATE members SET credit_milli = credit_milli + ? WHERE id = ?')
->execute([$earned, $surferId]);
$pdo->prepare(
'INSERT INTO surf_views (session_id, site_id, surfer_id, earned_milli, ratio_used, created_at)
VALUES (?, ?, ?, ?, ?, NOW())'
)->execute([$sessionId, $siteId, $surferId, $earned, $ratio]);
$pdo->commit();
return true;
} catch (Throwable $e) {
$pdo->rollBack();
throw $e;
}
}
Using it
Hold credits in thousandths internally and display whole credits. A ratio of 0.8 on integer credits either rounds to nothing or rounds in someone's favour a thousand times a day.
Record the ratio used on each view. When a member asks why an old session earned differently, that column is the answer.
What bites people
Check the advertiser's remaining credits inside the transaction with the row locked. Checking before you start the transaction lets a popular site go negative under concurrent surfers.
The house cut is not revenue until someone buys credits. It is inventory you now own — spend it or sell it, but do not count it twice.
This one touches real money. Point it at a throwaway wallet and watch a full cycle before you trust it with a live balance.