A user starts a live camera session, runs it for half a minute, and closes the tab. When they come back, the balance can still show the credits set aside at the start. The browser is gone, but the server still has a session to close and an amount to reconcile.
I encountered this while building LiveFaceSwap, a browser studio that meters live video by the second. A camera preview can stop before the billing record catches up. I wanted the server to settle from the usage recorded for that session and return the rest of the reservation.
Set a ceiling before opening the stream
At the start, the server reserves up to the amount the user can spend on a single session. It writes the ledger debit and the new session together, with a balance check on the debit. The reservation also sets the budget for the live connection. LiveFaceSwap currently meters at 0.6 credits per second, with a 900-second session cap.
Here is an example. With 60 credits reserved, the budget covers 100 seconds. If the service reports 32 seconds used, the final charge is 20 credits. That's 19.2 rounded up to a whole credit. The remaining 40 go back to the balance. The numbers illustrate the calculation and are independent of any real account.
Give the browser a fast path
The normal Stop button closes the live connection and sends an authenticated request to end the session. The server closes the metered connection, reads its usage, and returns the charge and updated balance when settlement succeeds.
Closing the tab is a different path through the same endpoint. The studio listens for pagehide and attempts a small POST using fetch with keepalive. The browser can keep that request alive after the page unloads. A mobile OS can also kill the browser without delivering the event, so the server has to handle a session it never gets asked to end.
Give the server a way to catch missed requests
A scheduled Cloudflare Worker looks for sessions past their deadline and calls the same settlement logic. The current schedule runs every five minutes. That interval describes how often the sweep starts. An abandoned session must first become eligible for cleanup, and settlement can take longer if usage lookup fails.
The server calculates the charge from the metering service's reported usage. It rounds to a whole credit and caps the charge at the reservation. When two requests try to settle the same session, only the one that changes it from active to settled issues the refund. The ledger also uses a unique refund key for the session. Those checks address repeated requests. The state update and refund write are separate operations, so recovery from a failure between them deserves its own test.
Test the calculation and the delivery paths separately
The local unit tests passed for rounding, the reservation cap, and invalid inputs. To check the full flow in a test account, I'd compare the session record and ledger after three cases:
- Click Stop and confirm that the returned balance matches the charge.
- Close the tab with the end request blocked, then check that the sweep settles the session after its deadline.
- Send the end request twice and confirm there's only one refund in the ledger.
The next time I build a metered browser feature, I'll start with those three cases. The client needs a fast way to say it is done. The server needs enough information to finish the accounting on its own.
AI assistance was used to draft and edit this article from the implementation and public documentation.
