When I added monthly plans to my AI video app, I already had one-time credit packs. Users who buy a pack keep those credits forever. Users who subscribe get a fresh batch every billing period that exp
Markdown supported · ⌘↵ to post
Storing grants instead of balances is a neat way to avoid the usual nightly-cron mess. The part about a refund into an expired period not coming back to life is something I'd probably have gotten wrong at first. Did a support case ever come up where a user expected those expired credits back, and how did you explain it?
Paul Hartness
Using the invoice ID as the primary key is a neat way to make duplicate invoice.paid events harmless. One path I'd add: a renewal whose payment fails. No invoice.paid means no new credit row, which is correct, but the user just wakes up with zero credits and no idea why, so a short note on invoice.payment_failed with a card-update link saves you a confused support email.