Given your worry, prepaid credits. You can't lose money on usage you already got paid for.
For the race condition, do the decrement atomically in the database:
update credit_balances
set balance = balance - 1
where user_id = $1 and balance >= 1
returning balance;No row returned = not enough credits, don't generate. Two parallel requests can't both spend the last credit. Also write every change to a credit_ledger table so you can explain a balance to an angry user.
Credits come in through a checkout.session.completed webhook for a one-time payment.
where balance >= 1trick is so simple, thank you lily_chen · edited