feat(billing): provision Stripe customer + subscription on tenant create

Wire the Stripe lifecycle into TenantsService (best-effort, gated on stripe.enabled): on create open a Stripe customer and, for a priced plan with seats >= 1, lazily create a Stripe Product+Price (persisted to Price.stripePriceIds[currency]) and a send_invoice subscription; mirror seat changes to the subscription quantity; pause/resume on suspend/resume; cancel on delete. A Stripe failure never blocks the tenant — the local Subscription stays the source of truth for derived MRR.
This commit is contained in:
Ronni Baslund
2026-05-30 08:29:34 +02:00
parent 22925599e7
commit 69197e11ae
3 changed files with 205 additions and 3 deletions
@@ -30,6 +30,19 @@ export class PricesService {
return price.amounts[currency]
}
// Persist the Stripe Price id we created for (this row, currency) so the
// next tenant on the same plan/cycle/currency reuses it instead of creating
// a duplicate Stripe Price. Keyed write — never clobbers other currencies.
async setStripePriceId(
priceId: PriceDocument['_id'],
currency: PriceCurrency,
stripePriceId: string,
): Promise<void> {
await this.priceModel
.updateOne({ _id: priceId }, { $set: { [`stripePriceIds.${currency}`]: stripePriceId } })
.exec()
}
async create(dto: CreatePriceDto): Promise<PriceDocument> {
try {
return await this.priceModel.create({ ...dto, active: dto.active ?? true })