N+1: `db.notificationPrefs.findByCustomer(customerId)` runs once per invoice inside the outer loop, but `customerId` doesn't change between iterations of the inner loop. Hoist it above the inner loop, or replace the whole loop with a single `SELECT customer_id, pref FROM notification_prefs WHERE customer_id = ANY($1)` and join client-side. The 1000-invoice run is currently 1001 queries; after the fix it'd be 2.
src/billing/invoiceRun.js:26
If `stripe.charges.create` throws on invoice 5 of 100, the loop exits silently and the next cron tick re-charges the whole batch. Either wrap the loop in `Promise.allSettled` and resume from the last successful invoice, or persist a cursor (`lastInvoiceId`) so the next run starts where this one died. Skip if a single-process guard handles this elsewhere — fine either way.
src/billing/invoiceRun.js:14
Want this on every PR?
Install SiftPulse on GitHub
First review posts within 60 seconds. 14-day free trial.
Double-charge on retry. `idempotencyKey: invoice.id` is the same key for every attempt against the same invoice, so Stripe will de-dupe — but only if the rest of the request body matches byte-for-byte. The `retry` parameter (and any clock-skew between a Stripe request id rotation) breaks that match, and Stripe then accepts the second charge as a NEW request. Pass a per-attempt key, e.g. `idempotencyKey: invoice.id + ':' + retry`, and assert charge.amount === invoice.amountCents before recording the success row.
src/billing/invoiceRun.js:11