/// // ── Credit deduction — fires when a conversion record is created ─────────────── onRecordBeforeCreateRequest((e) => { const userId = e.record.get('user') const fileHash = e.record.get('file_hash') $app.dao().runInTransaction((txDao) => { const user = txDao.findRecordById('users', userId) const balance = user.getInt('credits_balance') const freeUsed = user.getBool('free_used') // Free tier path — first conversion for this account if (!freeUsed) { user.set('free_used', true) txDao.saveRecord(user) const txRecord = new Record(txDao.findCollectionByNameOrId('transactions')) txRecord.set('user', userId) txRecord.set('type', 'free') txRecord.set('credits', 0) txRecord.set('balance_after', balance) txRecord.set('file_hash', fileHash) txDao.saveRecord(txRecord) return // allow conversion record to be created } // Paid path — check balance if (balance < 1) { throw new Error('INSUFFICIENT_CREDITS') } const newBalance = balance - 1 user.set('credits_balance', newBalance) txDao.saveRecord(user) const txRecord = new Record(txDao.findCollectionByNameOrId('transactions')) txRecord.set('user', userId) txRecord.set('type', 'conversion') txRecord.set('credits', -1) txRecord.set('balance_after', newBalance) txRecord.set('file_hash', fileHash) txDao.saveRecord(txRecord) }) }, 'conversions') // ── Credit refund — fires when a conversion is marked failed ───────────────── onRecordAfterUpdateRequest((e) => { if (e.record.get('status') !== 'failed') return const userId = e.record.get('user') $app.dao().runInTransaction((txDao) => { const user = txDao.findRecordById('users', userId) // Only refund paid conversions — free conversions cost nothing if (!user.getBool('free_used')) return const newBalance = user.getInt('credits_balance') + 1 user.set('credits_balance', newBalance) txDao.saveRecord(user) const txRecord = new Record(txDao.findCollectionByNameOrId('transactions')) txRecord.set('user', userId) txRecord.set('type', 'refund') txRecord.set('credits', +1) txRecord.set('balance_after', newBalance) txDao.saveRecord(txRecord) }) }, 'conversions') // ── Credit top-up — fires when a purchase record is created (from Stripe webhook) ── onRecordBeforeCreateRequest((e) => { const userId = e.record.get('user') const credits = e.record.getInt('credits') const stripeId = e.record.get('stripe_id') $app.dao().runInTransaction((txDao) => { const user = txDao.findRecordById('users', userId) const newBalance = user.getInt('credits_balance') + credits user.set('credits_balance', newBalance) txDao.saveRecord(user) const txRecord = new Record(txDao.findCollectionByNameOrId('transactions')) txRecord.set('user', userId) txRecord.set('type', 'purchase') txRecord.set('credits', credits) txRecord.set('balance_after', newBalance) txRecord.set('stripe_id', stripeId) txDao.saveRecord(txRecord) }) }, 'purchases')