An app running on a phone you own is an untrusted client. It can be modified, scripted, or made to send the same payload a thousand times. Every design decision below follows from that one assumption.
What arrives is a claim, not a fact
The report endpoint receives a user id, a product identifier, a product name, a platform, an original price, and a discount price. The server does not take any of it on faith. It validates that the fields exist and that the prices are finite numbers, then starts a decision the client cannot influence.
Four checks before payment
- The user must exist. A report from an unknown uid is rejected before anything is written.
- The identifier must be new. A unique index on the product identifier makes a second report the same product a duplicate, not a second reward.
- The prices must be usable. An original price gives the discount a reference point; without it, a low number is meaningless.
- The write must succeed. If the insert or the balance update fails, the whole transaction rolls back.
Why the transaction matters
The insert of the log, the reward, and the balance read all happen inside one database transaction. Either the product is recorded and the balance grows, or neither happens. There is no state where a node is paid for a row that was never stored.
The reward is not a thank-you for sending data. It is payment for a row that survived every check.Withdrawals follow the same shape, with a stronger lock. The user row is selected with a row-level lock, the balance is compared against the requested amount, and only then is it deducted. A request for more than the balance is refused while the balance stays untouched.
What the checks do not cover
Verification establishes uniqueness and internal consistency. It does not prove that the price on the page was real at that second; that would require fetching the listing again from the server, and stores do not make that cheap or reliable at scale.
So the network pays for a claim that is structurally valid and globally unique. Rejecting fakes is the first layer; the second layer is the market itself, which makes known-bad findings worthless because nobody acts on them.
Broadcast after commit
The Telegram post happens only after the transaction commits, and only then is the record marked as broadcast. If the channel is unreachable, the find still counts and the reward still lands; the flag stays false so the gap is visible rather than hidden.
That ordering is deliberate. Publishing first would put a product in front of an audience before the database agreed it existed. A public channel that occasionally lags is a much better failure than a paid record that never happened.