@mohasinac/appkit 2.7.42 → 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/dist/client.d.ts CHANGED
@@ -224,6 +224,8 @@ export { ClassifiedDetailView } from "./_internal/client/features/classified/Cla
224
224
  export type { ClassifiedDetailViewProps } from "./_internal/client/features/classified/ClassifiedDetailView";
225
225
  export { DigitalCodeDetailView } from "./_internal/client/features/digital-code/DigitalCodeDetailView";
226
226
  export type { DigitalCodeDetailViewProps } from "./_internal/client/features/digital-code/DigitalCodeDetailView";
227
+ export { CodeRevealPanel } from "./_internal/client/features/digital-code/CodeRevealPanel";
228
+ export type { CodeRevealPanelProps, RevealedCode } from "./_internal/client/features/digital-code/CodeRevealPanel";
227
229
  export { LiveItemDetailView } from "./_internal/client/features/live/LiveItemDetailView";
228
230
  export type { LiveItemDetailViewProps } from "./_internal/client/features/live/LiveItemDetailView";
229
231
  export { PhysicalLocationModal } from "./features/seller/components/PhysicalLocationModal";
package/dist/client.js CHANGED
@@ -242,5 +242,6 @@ export { NavPermissionsManager } from "./features/site-settings/components/NavPe
242
242
  // ── Classified + digital-code + live-item client views ────────────────────────
243
243
  export { ClassifiedDetailView } from "./_internal/client/features/classified/ClassifiedDetailView";
244
244
  export { DigitalCodeDetailView } from "./_internal/client/features/digital-code/DigitalCodeDetailView";
245
+ export { CodeRevealPanel } from "./_internal/client/features/digital-code/CodeRevealPanel";
245
246
  export { LiveItemDetailView } from "./_internal/client/features/live/LiveItemDetailView";
246
247
  export { PhysicalLocationModal } from "./features/seller/components/PhysicalLocationModal";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mohasinac/appkit",
3
- "version": "2.7.42",
3
+ "version": "2.7.44",
4
4
  "license": "MIT",
5
5
  "publishConfig": {
6
6
  "access": "public"