@qazuor/qzpay-drizzle 1.2.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1262,6 +1262,7 @@ var billingPromoCodes = pgCore.pgTable(
1262
1262
  maxUses: pgCore.integer("max_uses"),
1263
1263
  usedCount: pgCore.integer("used_count").default(0),
1264
1264
  maxPerCustomer: pgCore.integer("max_per_customer").default(1),
1265
+ maxUsesPerUser: pgCore.integer("max_uses_per_user").notNull().default(1),
1265
1266
  validPlans: pgCore.text("valid_plans").array(),
1266
1267
  newCustomersOnly: pgCore.boolean("new_customers_only").default(false),
1267
1268
  existingCustomersOnly: pgCore.boolean("existing_customers_only").default(false),
@@ -1291,7 +1292,8 @@ var billingPromoCodeUsage = pgCore.pgTable(
1291
1292
  },
1292
1293
  (table) => ({
1293
1294
  codeIdx: pgCore.index("idx_promo_usage_code").on(table.promoCodeId),
1294
- customerIdx: pgCore.index("idx_promo_usage_customer").on(table.customerId)
1295
+ customerIdx: pgCore.index("idx_promo_usage_customer").on(table.customerId),
1296
+ customerPromoUnique: pgCore.uniqueIndex("promo_code_usage_customer_promo_unique").on(table.customerId, table.promoCodeId)
1295
1297
  })
1296
1298
  );
1297
1299
  var billingPromoCodeInsertSchema = drizzleZod.createInsertSchema(billingPromoCodes);
@@ -4384,6 +4386,28 @@ var QZPayPromoCodesRepository = class {
4384
4386
  }).where(drizzleOrm.eq(billingPromoCodes.id, id)).returning();
4385
4387
  return firstOrThrow(result, "PromoCode", id);
4386
4388
  }
4389
+ /**
4390
+ * Atomically increment the usage count if (and only if) the promo
4391
+ * code has not reached its `maxUses` limit yet.
4392
+ *
4393
+ * Implemented as a single conditional `UPDATE ... WHERE used_count <
4394
+ * max_uses RETURNING *` statement so it is race-safe under
4395
+ * concurrency. Returns `null` when the increment would exceed the
4396
+ * limit; callers should treat that as "redemption limit reached".
4397
+ *
4398
+ * Promo codes with `maxUses = null` (no limit) always succeed.
4399
+ */
4400
+ async atomicIncrementUsage(id) {
4401
+ const result = await this.db.update(billingPromoCodes).set({
4402
+ usedCount: drizzleOrm.sql`COALESCE(${billingPromoCodes.usedCount}, 0) + 1`
4403
+ }).where(
4404
+ drizzleOrm.and(
4405
+ drizzleOrm.eq(billingPromoCodes.id, id),
4406
+ drizzleOrm.or(drizzleOrm.isNull(billingPromoCodes.maxUses), drizzleOrm.sql`COALESCE(${billingPromoCodes.usedCount}, 0) < ${billingPromoCodes.maxUses}`)
4407
+ )
4408
+ ).returning();
4409
+ return firstOrNull(result);
4410
+ }
4387
4411
  /**
4388
4412
  * Deactivate promo code
4389
4413
  */
@@ -5949,6 +5973,10 @@ var QZPayDrizzleStorageAdapter = class {
5949
5973
  async incrementRedemptions(id) {
5950
5974
  await repo.incrementUsage(id);
5951
5975
  },
5976
+ async atomicIncrementRedemptions(id) {
5977
+ const result = await repo.atomicIncrementUsage(id);
5978
+ return result ? mapDrizzlePromoCodeToCore(result) : null;
5979
+ },
5952
5980
  async list(options) {
5953
5981
  const limit = options?.limit ?? 20;
5954
5982
  const offset = options?.offset ?? 0;