@mohasinac/appkit 2.7.43 → 2.7.44

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.
@@ -14,7 +14,7 @@ import { sendOrderConfirmationEmail } from "../../../../features/contact/server"
14
14
  import { splitCartIntoOrderGroups } from "../../../../features/orders/index";
15
15
  import { resolveDate } from "../../../../utils";
16
16
  import { getAdminDb, getAdminRealtimeDb, RTDB_PATHS, } from "../../../../providers/db-firebase";
17
- import { PRODUCT_COLLECTION } from "../../../../features/products/schemas/firestore";
17
+ import { PRODUCT_COLLECTION, PRODUCT_CODES_SUBCOLLECTION } from "../../../../features/products/schemas/firestore";
18
18
  import { CART_COLLECTION } from "../../../../features/cart/schemas/index";
19
19
  // S-SBUNI-RULES 2026-05-13 — bundle cart-line stock fan-out helpers.
20
20
  import { getCartItemMemberIds, getExpandedDecrements, validateCartItemStock, } from "./bundle-expansion";
@@ -27,6 +27,36 @@ import { formatShippingAddress } from "./data";
27
27
  import { enforceMaxPerUserForCart, computePrizeRevealDeadline, } from "./prize-bundle-gates";
28
28
  import { getListingRule, runSyncPreflight, } from "../../../shared/checkout/rules";
29
29
  import { cartIsDigitalOnly } from "../../../shared/listing-types/cart-shipping";
30
+ /**
31
+ * SB-UNI-N — Atomically claim the next available digital code for a confirmed
32
+ * order. Pre-fetches a candidate code outside the transaction then locks and
33
+ * verifies in a micro-transaction. Fire-and-forget safe (logs on exhaustion
34
+ * but never fails the already-created order).
35
+ */
36
+ async function claimDigitalCodeForOrder(db, productId, orderId, userId) {
37
+ const codesRef = db
38
+ .collection(PRODUCT_COLLECTION)
39
+ .doc(productId)
40
+ .collection(PRODUCT_CODES_SUBCOLLECTION);
41
+ const snap = await codesRef.where("status", "==", "available").limit(1).get();
42
+ if (snap.empty) {
43
+ serverLogger.warn("claimDigitalCode: code pool exhausted", { productId, orderId });
44
+ return;
45
+ }
46
+ const codeRef = snap.docs[0].ref;
47
+ await db.runTransaction(async (tx) => {
48
+ const latest = await tx.get(codeRef);
49
+ if (!latest.exists || latest.data()?.status !== "available")
50
+ return;
51
+ tx.update(codeRef, {
52
+ status: "claimed",
53
+ orderId,
54
+ claimedByUserId: userId,
55
+ claimedAt: new Date(),
56
+ updatedAt: new Date(),
57
+ });
58
+ });
59
+ }
30
60
  /**
31
61
  * SB-UNI-O — Live-item jurisdiction guard.
32
62
  * Throws ValidationError if any live cart item's allowed states don't include
@@ -470,6 +500,11 @@ export async function createCheckoutOrderAction(input) {
470
500
  ...extraOrderFields,
471
501
  });
472
502
  orderIds.push(order.id);
503
+ // SB-UNI-N — claim a digital code for digital-code orders (fire-and-forget,
504
+ // order is already persisted so a claim failure is logged, not thrown).
505
+ if ((group[0]?.product?.listingType ?? "standard") === "digital-code") {
506
+ claimDigitalCodeForOrder(getAdminDb(), firstItem.productId, order.id, uid).catch((e) => serverLogger.error("claimDigitalCode", e));
507
+ }
473
508
  for (const code of groupCouponCodes) {
474
509
  const entry = couponUsageAccumulator.get(code);
475
510
  if (entry)
@@ -840,6 +875,11 @@ export async function verifyAndPlaceRazorpayOrderAction(input) {
840
875
  ...extraOrderFields,
841
876
  });
842
877
  orderIds.push(order.id);
878
+ // SB-UNI-N — claim a digital code for digital-code orders (fire-and-forget,
879
+ // order is already persisted so a claim failure is logged, not thrown).
880
+ if ((group[0]?.product?.listingType ?? "standard") === "digital-code") {
881
+ claimDigitalCodeForOrder(getAdminDb(), firstItem.productId, order.id, uid).catch((e) => serverLogger.error("claimDigitalCode", e));
882
+ }
843
883
  if (userEmail) {
844
884
  emailsToSend.push({
845
885
  to: userEmail,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mohasinac/appkit",
3
- "version": "2.7.43",
3
+ "version": "2.7.44",
4
4
  "license": "MIT",
5
5
  "publishConfig": {
6
6
  "access": "public"