@mohasinac/appkit 4.1.1 → 4.2.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/_internal/client/features/maintenance/index.d.ts +2 -0
- package/dist/_internal/client/features/maintenance/index.js +1 -0
- package/dist/_internal/client/features/maintenance/views/CloudLogsListView.d.ts +9 -0
- package/dist/_internal/client/features/maintenance/views/CloudLogsListView.js +55 -0
- package/dist/_internal/server/features/auctions/index.d.ts +1 -1
- package/dist/_internal/server/features/auctions/index.js +1 -1
- package/dist/_internal/server/features/auctions/service.js +2 -2
- package/dist/_internal/server/features/checkout/actions.d.ts +15 -0
- package/dist/_internal/server/features/checkout/actions.js +245 -182
- package/dist/_internal/server/features/maintenance/cloud-logs-data.d.ts +50 -0
- package/dist/_internal/server/features/maintenance/cloud-logs-data.js +152 -0
- package/dist/_internal/server/functions/scheduled.d.ts +2 -1
- package/dist/_internal/server/functions/scheduled.js +9 -1
- package/dist/_internal/server/jobs/core/jobRunners.js +10 -0
- package/dist/_internal/server/jobs/core/newsletterExport.d.ts +15 -0
- package/dist/_internal/server/jobs/core/newsletterExport.js +43 -0
- package/dist/_internal/server/jobs/core/onOrderCreate.d.ts +4 -12
- package/dist/_internal/server/jobs/core/onOrderCreate.js +3 -3
- package/dist/_internal/server/jobs/core/onOrderStatusChange.d.ts +2 -0
- package/dist/_internal/server/jobs/core/onOrderStatusChange.js +1 -0
- package/dist/_internal/server/jobs/core/onProductWrite.js +14 -2
- package/dist/_internal/server/jobs/core/paymentWindowTimeout.js +1 -0
- package/dist/_internal/server/jobs/core/pendingOrderTimeout.js +1 -0
- package/dist/_internal/server/jobs/core/revenueRollup.d.ts +13 -0
- package/dist/_internal/server/jobs/core/revenueRollup.js +22 -0
- package/dist/_internal/server/jobs/core/whatsappNotify.d.ts +35 -0
- package/dist/_internal/server/jobs/core/whatsappNotify.js +82 -0
- package/dist/_internal/server/jobs/handlers/index.d.ts +1 -0
- package/dist/_internal/server/jobs/handlers/index.js +1 -0
- package/dist/_internal/server/jobs/handlers/revenueRollup.d.ts +2 -0
- package/dist/_internal/server/jobs/handlers/revenueRollup.js +2 -0
- package/dist/_internal/shared/features/auctions/config.d.ts +1 -1
- package/dist/_internal/shared/features/auctions/config.js +1 -1
- package/dist/_internal/shared/features/auctions/schema.js +1 -1
- package/dist/_internal/shared/features/cart/schema.js +2 -2
- package/dist/_internal/shared/features/checkout/config.d.ts +7 -0
- package/dist/_internal/shared/features/checkout/config.js +7 -0
- package/dist/_internal/shared/features/events/schema.js +1 -1
- package/dist/_internal/shared/features/orders/schema.js +1 -1
- package/dist/_internal/shared/features/products/schema.js +3 -3
- package/dist/_internal/shared/features/promotions/schema.js +3 -3
- package/dist/_internal/shared/fees/calculator.d.ts +32 -7
- package/dist/_internal/shared/fees/calculator.js +25 -0
- package/dist/client.d.ts +4 -4
- package/dist/client.js +4 -2
- package/dist/constants/api-endpoints.d.ts +9 -6
- package/dist/constants/api-endpoints.js +3 -2
- package/dist/contracts/client-payment-gateway.d.ts +1 -2
- package/dist/features/about/components/AboutView.d.ts +6 -16
- package/dist/features/about/components/AboutView.js +3 -3
- package/dist/features/about/components/FeesView.js +18 -0
- package/dist/features/about/index.d.ts +2 -1
- package/dist/features/about/schemas/firestore.d.ts +47 -0
- package/dist/features/about/schemas/firestore.js +6 -0
- package/dist/features/about/schemas/index.d.ts +210 -0
- package/dist/features/about/schemas/index.js +45 -0
- package/dist/features/account/hooks/useProfile.js +3 -0
- package/dist/features/admin/actions/notification-actions.d.ts +4 -1
- package/dist/features/admin/actions/notification-actions.js +56 -10
- package/dist/features/admin/components/AdminAdsView.js +1 -1
- package/dist/features/admin/components/AdminCarouselGroupEditorView.d.ts +6 -0
- package/dist/features/admin/components/AdminCarouselGroupEditorView.js +50 -0
- package/dist/features/admin/components/AdminCartsView.js +16 -3
- package/dist/features/admin/components/AdminCouponEditorView.js +2 -2
- package/dist/features/admin/components/AdminCouponsView.js +14 -4
- package/dist/features/admin/components/AdminEmployeeEditorView.js +1 -42
- package/dist/features/admin/components/AdminMediaView.js +1 -1
- package/dist/features/admin/components/AdminNewsletterView.js +42 -11
- package/dist/features/admin/components/AdminNotificationsView.js +31 -19
- package/dist/features/admin/components/AdminOrdersView.js +7 -0
- package/dist/features/admin/components/AdminPayoutsView.js +18 -3
- package/dist/features/admin/components/AdminProductsView.js +17 -2
- package/dist/features/admin/components/AdminReviewsView.js +16 -2
- package/dist/features/admin/components/AdminScammersView.js +7 -0
- package/dist/features/admin/components/AdminSectionsView.js +4 -0
- package/dist/features/admin/components/AdminSiteConfigGuideView.js +1 -1
- package/dist/features/admin/components/AdminSiteSettingsView.js +127 -8
- package/dist/features/admin/components/AdminStoreEditorView.d.ts +2 -1
- package/dist/features/admin/components/AdminStoreEditorView.js +4 -4
- package/dist/features/admin/components/AdminStoresView.js +1 -1
- package/dist/features/admin/components/AdminSupportTicketDetailView.js +23 -5
- package/dist/features/admin/components/AdminSupportTicketsView.js +20 -2
- package/dist/features/admin/components/AdminTesterChecklistItemEditorView.js +4 -1
- package/dist/features/admin/components/AdminTesterChecklistView.js +8 -0
- package/dist/features/admin/components/index.d.ts +2 -0
- package/dist/features/admin/components/index.js +1 -0
- package/dist/features/admin/constants/filter-tabs.d.ts +55 -41
- package/dist/features/admin/constants/filter-tabs.js +55 -27
- package/dist/features/admin/repository/analytics-rollup.repository.d.ts +25 -0
- package/dist/features/admin/repository/analytics-rollup.repository.js +31 -0
- package/dist/features/admin/repository/site-settings.repository.js +8 -1
- package/dist/features/admin/schemas/firestore.d.ts +46 -0
- package/dist/features/admin/schemas/firestore.js +62 -24
- package/dist/features/auctions/actions/bid-actions.js +1 -1
- package/dist/features/auctions/components/AuctionDetailPageView.js +2 -2
- package/dist/features/auctions/components/AuctionsListView.js +8 -1
- package/dist/features/auth/components/LoginForm.js +2 -2
- package/dist/features/auth/components/RegisterForm.js +2 -2
- package/dist/features/auth/permissions/constants.d.ts +16 -0
- package/dist/features/auth/permissions/constants.js +53 -0
- package/dist/features/auth/schemas/firestore.js +1 -0
- package/dist/features/blog/components/BlogFilters.js +1 -1
- package/dist/features/cart/schemas/firestore.d.ts +1 -1
- package/dist/features/categories/schemas/firestore.d.ts +1 -1
- package/dist/features/consultation/components/ConsultationForm.js +1 -1
- package/dist/features/events/components/EventFilters.js +2 -0
- package/dist/features/homepage/components/WelcomeSection.js +2 -2
- package/dist/features/layout/BackToTop.d.ts +9 -5
- package/dist/features/layout/BackToTop.js +17 -21
- package/dist/features/layout/TitleBarLayout.d.ts +2 -2
- package/dist/features/layout/TitleBarLayout.js +4 -4
- package/dist/features/media/AvatarUpload.js +7 -3
- package/dist/features/media/MediaVideo.js +1 -1
- package/dist/features/media/finalize.d.ts +9 -13
- package/dist/features/media/finalize.js +23 -74
- package/dist/features/media/server.d.ts +1 -1
- package/dist/features/media/server.js +1 -1
- package/dist/features/orders/actions/refund-actions.js +1 -0
- package/dist/features/orders/hooks/useOrders.js +16 -4
- package/dist/features/orders/schemas/firestore.d.ts +19 -5
- package/dist/features/payments/schemas/firestore.d.ts +2 -2
- package/dist/features/pre-orders/components/PreOrdersListView.js +7 -0
- package/dist/features/products/components/ArtStickersListView.js +5 -0
- package/dist/features/products/components/ProductDetailPageView.d.ts +7 -1
- package/dist/features/products/components/ProductForm.js +14 -1
- package/dist/features/products/components/ProductsIndexPageView.js +7 -0
- package/dist/features/products/constants/action-defs.d.ts +29 -3
- package/dist/features/products/constants/action-defs.js +77 -1
- package/dist/features/products/schemas/firestore.d.ts +1 -1
- package/dist/features/products/schemas/product-templates.d.ts +1 -1
- package/dist/features/products/types/index.d.ts +4 -0
- package/dist/features/scams/schemas/index.js +2 -2
- package/dist/features/search/components/Search.js +3 -3
- package/dist/features/seller/components/SellerAuctionsView.js +27 -2
- package/dist/features/seller/components/SellerOrdersView.js +2 -2
- package/dist/features/seller/components/SellerPayoutsView.js +24 -10
- package/dist/features/seller/components/SellerPreOrdersView.js +12 -4
- package/dist/features/seller/components/SellerPrizeDrawsView.js +23 -5
- package/dist/features/seller/components/SellerProductShell.d.ts +2 -0
- package/dist/features/seller/components/SellerProductShell.js +1 -1
- package/dist/features/seller/components/SellerProductsView.js +8 -6
- package/dist/features/seller/components/SellerReviewsView.js +27 -6
- package/dist/features/seller/components/index.d.ts +0 -2
- package/dist/features/seller/components/index.js +0 -1
- package/dist/features/stores/components/StoreListingsGuideView.js +2 -1
- package/dist/features/stores/components/StoreProductsView.js +1 -1
- package/dist/features/stores/schemas/firestore.d.ts +11 -0
- package/dist/features/stores/schemas/firestore.js +5 -0
- package/dist/features/stores/schemas/index.d.ts +2 -2
- package/dist/features/tester/actions/checklist-item-actions.d.ts +6 -0
- package/dist/features/tester/actions/checklist-item-actions.js +2 -0
- package/dist/features/tester/components/AdminTesterFeedbackIssuesView.js +13 -2
- package/dist/features/tester/components/AdminTesterFeedbackListView.js +2 -1
- package/dist/features/tester/components/AdminTesterFeedbackReportView.js +9 -5
- package/dist/features/tester/components/TesterHubView.js +34 -21
- package/dist/features/tester/repository/tester-checklist-item.repository.js +1 -0
- package/dist/features/tester/repository/tester-checklist-response.repository.d.ts +18 -4
- package/dist/features/tester/repository/tester-checklist-response.repository.js +43 -8
- package/dist/features/tester/schemas/firestore.d.ts +8 -3
- package/dist/features/tester/schemas/firestore.js +6 -0
- package/dist/features/tester/seed-data/tester-checklist-seed-data.js +21 -9
- package/dist/features/tester/utils/phases.d.ts +31 -0
- package/dist/features/tester/utils/phases.js +46 -0
- package/dist/features/whatsapp-bot/components/SellerWhatsAppSettingsView.js +1 -1
- package/dist/features/whatsapp-bot/helpers/whatsapp.d.ts +10 -1
- package/dist/features/whatsapp-bot/helpers/whatsapp.js +47 -0
- package/dist/features/whatsapp-bot/types/index.d.ts +20 -1
- package/dist/features/wishlist/types/index.d.ts +3 -0
- package/dist/index.d.ts +15 -6
- package/dist/index.js +19 -9
- package/dist/next/components/NotFoundView.js +2 -1
- package/dist/next/routing/route-map.d.ts +6 -12
- package/dist/next/routing/route-map.js +3 -4
- package/dist/providers/db-firebase/admin.d.ts +1 -0
- package/dist/providers/db-firebase/admin.js +1 -1
- package/dist/repositories/index.d.ts +2 -0
- package/dist/repositories/index.js +1 -0
- package/dist/schemas/registry.d.ts +2 -2
- package/dist/seed/addresses-seed-data.js +18 -18
- package/dist/seed/bids-seed-data.js +4 -4
- package/dist/seed/blog-posts-seed-data.js +82 -82
- package/dist/seed/carousel-slides-seed-data.js +14 -10
- package/dist/seed/carousels-seed-data.js +3 -3
- package/dist/seed/cart-seed-data.js +92 -92
- package/dist/seed/claimed-coupons-seed-data.js +10 -10
- package/dist/seed/conversations-seed-data.js +75 -75
- package/dist/seed/coupon-usage-seed-data.js +12 -12
- package/dist/seed/coupons-seed-data.js +41 -41
- package/dist/seed/events-seed-data.js +105 -105
- package/dist/seed/grouped-listings-seed-data.d.ts +2 -137
- package/dist/seed/grouped-listings-seed-data.js +79 -143
- package/dist/seed/history-seed-data.js +38 -38
- package/dist/seed/homepage-sections-seed-data.js +9 -9
- package/dist/seed/notifications-seed-data.js +56 -56
- package/dist/seed/offers-seed-data.js +91 -91
- package/dist/seed/orders-seed-data.js +121 -106
- package/dist/seed/payouts-seed-data.js +42 -42
- package/dist/seed/products-auctions-seed-data.js +10 -2
- package/dist/seed/products-standard-seed-data.js +40 -8
- package/dist/seed/reviews-seed-data.js +42 -23
- package/dist/seed/scammers-seed-data.js +22 -22
- package/dist/seed/sessions-seed-data.js +7 -7
- package/dist/seed/shipments-seed-data.js +10 -10
- package/dist/seed/site-settings-seed-data.js +89 -0
- package/dist/seed/store-addresses-seed-data.js +2 -2
- package/dist/seed/store-extensions-seed-data.js +93 -93
- package/dist/seed/stores-seed-data.js +2 -0
- package/dist/seed/support-tickets-seed-data.js +47 -47
- package/dist/seed/users-seed-data.js +55 -89
- package/dist/seed/wishlists-seed-data.js +22 -22
- package/dist/server.d.ts +4 -3
- package/dist/server.js +7 -7
- package/dist/styles.css +1 -1
- package/dist/tailwind-utilities.css +1 -1
- package/dist/ui/components/PageLoader.js +8 -3
- package/dist/ui/components/Select.d.ts +8 -1
- package/dist/ui/components/Select.js +2 -2
- package/dist/ui/components/SiteMark.d.ts +23 -0
- package/dist/ui/components/SiteMark.js +76 -0
- package/dist/ui/index.d.ts +2 -0
- package/dist/ui/index.js +1 -0
- package/dist/validation/schemas.d.ts +0 -2
- package/dist/validation/schemas.js +0 -2
- package/package.json +1 -1
- package/scripts/seed-cli.mjs +17 -15
|
@@ -12,14 +12,14 @@ import { roundRupees } from "../../../../utils/number.formatter";
|
|
|
12
12
|
import { ApiError, ValidationError, NotFoundError, ERROR_MESSAGES } from "../../../../errors";
|
|
13
13
|
import { ORDER_FIELDS } from "../../../../constants/field-names";
|
|
14
14
|
import { serverLogger } from "../../../../monitoring";
|
|
15
|
-
import { unitOfWork, siteSettingsRepository, userRepository, storeRepository, couponsRepository,
|
|
15
|
+
import { unitOfWork, siteSettingsRepository, userRepository, storeRepository, couponsRepository, claimedCouponsRepository, addressesRepository, } from "../../../../repositories";
|
|
16
16
|
import { calculateGst } from "../../../shared/fees/calculator";
|
|
17
17
|
import { computePreOrderDepositAmount } from "../../../shared/checkout/order-math";
|
|
18
18
|
import { failedCheckoutRepository } from "../../../../features/checkout/repository/failed-checkout.repository";
|
|
19
19
|
import { sendOrderConfirmationEmail } from "../../../../features/contact/server";
|
|
20
20
|
import { splitCartIntoOrderGroups } from "../../../../features/orders/index";
|
|
21
21
|
import { resolveDate } from "../../../../utils";
|
|
22
|
-
import { computeCodHandlingFee } from "../../../shared/fees/calculator";
|
|
22
|
+
import { computeCodHandlingFee, computeWhatsAppNotifyFee, computeGiftWrapFee, computeShipmentProtectionFee, } from "../../../shared/fees/calculator";
|
|
23
23
|
import { getAdminDb, getAdminRealtimeDb, RTDB_PATHS, } from "../../../../providers/db-firebase";
|
|
24
24
|
import { PRODUCT_COLLECTION, PRODUCT_CODES_SUBCOLLECTION } from "../../../../features/products/schemas/firestore";
|
|
25
25
|
import { CART_COLLECTION } from "../../../../features/cart/schemas/index";
|
|
@@ -112,9 +112,8 @@ function assertLiveJurisdiction(cartItemsArg, productByIdArg, buyerState) {
|
|
|
112
112
|
* either party. We emit the buyer + seller rows here at the create boundary.
|
|
113
113
|
*/
|
|
114
114
|
function emitOrderPlacedNotifications(args) {
|
|
115
|
-
const { orderId, buyerUid, buyerName, storeOwnerId, productLabel, paid } = args;
|
|
116
|
-
const buyerNotif =
|
|
117
|
-
.create({
|
|
115
|
+
const { orderId, buyerUid, buyerName, storeOwnerId, productLabel, paid, whatsappNotifyAddon } = args;
|
|
116
|
+
const buyerNotif = sendNotification({
|
|
118
117
|
userId: buyerUid,
|
|
119
118
|
type: "order_placed",
|
|
120
119
|
priority: "normal",
|
|
@@ -123,14 +122,13 @@ function emitOrderPlacedNotifications(args) {
|
|
|
123
122
|
relatedId: orderId,
|
|
124
123
|
relatedType: "order",
|
|
125
124
|
actionUrl: `/user/orders/view/${orderId}`,
|
|
126
|
-
|
|
127
|
-
|
|
125
|
+
orderWhatsappAddonPaid: whatsappNotifyAddon,
|
|
126
|
+
}).catch((err) => serverLogger.warn("Failed to send buyer order_placed notification", {
|
|
128
127
|
err: err instanceof Error ? err.message : String(err),
|
|
129
128
|
orderId,
|
|
130
129
|
}));
|
|
131
130
|
const sellerNotif = storeOwnerId
|
|
132
|
-
?
|
|
133
|
-
.create({
|
|
131
|
+
? sendNotification({
|
|
134
132
|
userId: storeOwnerId,
|
|
135
133
|
type: "order_placed",
|
|
136
134
|
priority: "high",
|
|
@@ -139,8 +137,7 @@ function emitOrderPlacedNotifications(args) {
|
|
|
139
137
|
relatedId: orderId,
|
|
140
138
|
relatedType: "order",
|
|
141
139
|
actionUrl: `/store/orders/${orderId}/view`,
|
|
142
|
-
})
|
|
143
|
-
.catch((err) => serverLogger.warn("Failed to create seller order_placed notification", {
|
|
140
|
+
}).catch((err) => serverLogger.warn("Failed to send seller order_placed notification", {
|
|
144
141
|
err: err instanceof Error ? err.message : String(err),
|
|
145
142
|
orderId,
|
|
146
143
|
}))
|
|
@@ -251,7 +248,7 @@ function unitPriceFor(item, product) {
|
|
|
251
248
|
* so the caller can accumulate the checkout-wide `total`.
|
|
252
249
|
*/
|
|
253
250
|
async function createOrderForGroup(group, orderType, ctx) {
|
|
254
|
-
const { paymentMethod, emiTenureMonths, emiSettings, commissions, appliedCoupons, cartSubtotal, couponUsageAccumulator, uid, userName, userEmail, shippingAddress, notes, adminBypass, adminBypassBy, adminBatchId, orderIds, emailsToSend, outOfStockPolicy, droppedItems, buyerState, gstSettings, siteContactUpiVpa, } = ctx;
|
|
251
|
+
const { paymentMethod, emiTenureMonths, emiSettings, commissions, appliedCoupons, cartSubtotal, couponUsageAccumulator, uid, userName, userEmail, shippingAddress, notes, adminBypass, adminBypassBy, adminBatchId, orderIds, emailsToSend, outOfStockPolicy, droppedItems, buyerState, gstSettings, siteContactUpiVpa, whatsappNotifyAddon, giftWrapAddon, giftWrapMessage, shipmentProtectionAddon, } = ctx;
|
|
255
252
|
const firstItem = group[0].item;
|
|
256
253
|
const firstProduct = group[0].product;
|
|
257
254
|
const groupTotal = group.reduce((sum, { item, product }) => sum + unitPriceFor(item, product) * item.quantity, 0);
|
|
@@ -327,6 +324,9 @@ async function createOrderForGroup(group, orderType, ctx) {
|
|
|
327
324
|
// Handling fee charged to the buyer for choosing COD, on top of the order
|
|
328
325
|
// total — not deducted from the deposit/remaining split above.
|
|
329
326
|
const codHandlingFee = paymentMethod === "cod" ? computeCodHandlingFee(groupTotal, commissions) : 0;
|
|
327
|
+
const whatsappNotifyFee = computeWhatsAppNotifyFee(whatsappNotifyAddon, commissions);
|
|
328
|
+
const giftWrapFee = computeGiftWrapFee(giftWrapAddon, commissions);
|
|
329
|
+
const shipmentProtectionFee = computeShipmentProtectionFee(groupTotal, shipmentProtectionAddon, commissions);
|
|
330
330
|
let couponDiscount = 0;
|
|
331
331
|
const appliedDiscounts = [];
|
|
332
332
|
const groupCouponCodes = new Set();
|
|
@@ -371,7 +371,7 @@ async function createOrderForGroup(group, orderType, ctx) {
|
|
|
371
371
|
}
|
|
372
372
|
}
|
|
373
373
|
couponDiscount = Math.min(couponDiscount, groupTotal);
|
|
374
|
-
const orderTotal = Math.max(0, groupTotal - couponDiscount) + shippingFee + codHandlingFee + (emiSchedule?.surchargeAmount ?? 0) + (gstBreakdown?.gstAmount ?? 0);
|
|
374
|
+
const orderTotal = Math.max(0, groupTotal - couponDiscount) + shippingFee + codHandlingFee + whatsappNotifyFee + giftWrapFee + shipmentProtectionFee + (emiSchedule?.surchargeAmount ?? 0) + (gstBreakdown?.gstAmount ?? 0);
|
|
375
375
|
const imageUrls = [
|
|
376
376
|
...new Set(group
|
|
377
377
|
.map(({ product }) => product.mainImage)
|
|
@@ -428,6 +428,13 @@ async function createOrderForGroup(group, orderType, ctx) {
|
|
|
428
428
|
depositAmount: adminBypass ? undefined : depositAmount,
|
|
429
429
|
codRemainingAmount: adminBypass ? undefined : codRemainingAmount,
|
|
430
430
|
codHandlingFee: !adminBypass && codHandlingFee > 0 ? codHandlingFee : undefined,
|
|
431
|
+
whatsappNotifyAddon: !adminBypass && whatsappNotifyFee > 0 ? true : undefined,
|
|
432
|
+
whatsappNotifyFee: !adminBypass && whatsappNotifyFee > 0 ? whatsappNotifyFee : undefined,
|
|
433
|
+
giftWrapAddon: !adminBypass && giftWrapFee > 0 ? true : undefined,
|
|
434
|
+
giftWrapFee: !adminBypass && giftWrapFee > 0 ? giftWrapFee : undefined,
|
|
435
|
+
giftWrapMessage: !adminBypass && giftWrapFee > 0 ? giftWrapMessage?.slice(0, 500) : undefined,
|
|
436
|
+
shipmentProtectionAddon: !adminBypass && shipmentProtectionFee > 0 ? true : undefined,
|
|
437
|
+
shipmentProtectionFee: !adminBypass && shipmentProtectionFee > 0 ? shipmentProtectionFee : undefined,
|
|
431
438
|
taxableAmount: gstBreakdown?.taxableAmount,
|
|
432
439
|
gstAmount: gstBreakdown?.gstAmount,
|
|
433
440
|
cgst: gstBreakdown?.cgst || undefined,
|
|
@@ -486,6 +493,7 @@ async function createOrderForGroup(group, orderType, ctx) {
|
|
|
486
493
|
storeOwnerId,
|
|
487
494
|
productLabel: orderItems.length > 1 ? `${orderItems.length} items` : firstItem.productTitle,
|
|
488
495
|
paid: adminBypass,
|
|
496
|
+
whatsappNotifyAddon: !adminBypass && whatsappNotifyFee > 0,
|
|
489
497
|
});
|
|
490
498
|
return groupTotal;
|
|
491
499
|
}
|
|
@@ -496,7 +504,7 @@ async function createOrderForGroup(group, orderType, ctx) {
|
|
|
496
504
|
* auth + rate-limiting before calling this and supply the resolved user fields.
|
|
497
505
|
*/
|
|
498
506
|
export async function createCheckoutOrderAction(input) {
|
|
499
|
-
const { userId: uid, userName, userEmail, addressId, paymentMethod, emiTenureMonths, notes, excludedProductIds = [], adminBypass = false, adminBypassBy, outOfStockPolicy = OutOfStockPolicyValues.SKIP_ITEMS, } = input;
|
|
507
|
+
const { userId: uid, userName, userEmail, addressId, paymentMethod, emiTenureMonths, notes, excludedProductIds = [], adminBypass = false, adminBypassBy, outOfStockPolicy = OutOfStockPolicyValues.SKIP_ITEMS, whatsappNotifyAddon = false, giftWrapAddon = false, giftWrapMessage, shipmentProtectionAddon = false, } = input;
|
|
500
508
|
const siteSettings = await siteSettingsRepository.getSingleton();
|
|
501
509
|
const commissions = siteSettings?.commissions ?? CHECKOUT_DEFAULT_COMMISSIONS;
|
|
502
510
|
const emiSettings = siteSettings?.emi ?? CHECKOUT_DEFAULT_EMI_SETTINGS;
|
|
@@ -747,6 +755,10 @@ export async function createCheckoutOrderAction(input) {
|
|
|
747
755
|
buyerState: resolvedAddress?.state,
|
|
748
756
|
gstSettings: siteSettings?.gst,
|
|
749
757
|
siteContactUpiVpa: siteSettings?.contact?.upiVpa,
|
|
758
|
+
whatsappNotifyAddon,
|
|
759
|
+
giftWrapAddon,
|
|
760
|
+
giftWrapMessage,
|
|
761
|
+
shipmentProtectionAddon,
|
|
750
762
|
};
|
|
751
763
|
for (const { items: group, orderType } of orderGroups) {
|
|
752
764
|
total += await createOrderForGroup(group, orderType, groupCtx);
|
|
@@ -872,11 +884,212 @@ async function refundDroppedItemsForRazorpayCheckout(input) {
|
|
|
872
884
|
}
|
|
873
885
|
}
|
|
874
886
|
}
|
|
887
|
+
/**
|
|
888
|
+
* Places one order for one seller-group within `verifyAndPlaceRazorpayOrderAction`
|
|
889
|
+
* — mirrors `createOrderForGroup`'s role on the COD/cash/EMI path (same
|
|
890
|
+
* per-group fee/coupon/order-doc/notification shape), extracted into its own
|
|
891
|
+
* function purely to keep the parent function under the LARGE_COMPONENT
|
|
892
|
+
* audit threshold (`scripts/audit-code-quality.mjs`). No behavior change
|
|
893
|
+
* from the inline version this replaced.
|
|
894
|
+
*/
|
|
895
|
+
async function createRazorpayGroupOrder(group, orderType, ctx) {
|
|
896
|
+
const { appliedCoupons, cartSubtotal, platformFeePercent, gstPercent, whatsappNotifyFee, giftWrapFee, giftWrapMessage, shipmentProtectionAddon, siteSettings, uid, userName, userEmail, razorpay_order_id, razorpay_payment_id, razorpay_signature, shippingAddress, notes, outOfStockPolicy, unavailablePaid, orderIds, emailsToSend, } = ctx;
|
|
897
|
+
// SB-UNI-5 2026-05-13 — bundle cart-lines use item.price (locked bundle
|
|
898
|
+
// price); regular lines use product.price. The helper keeps the math
|
|
899
|
+
// local to this block.
|
|
900
|
+
const unitPriceFor = (item, product) => item.bundleCategorySlug && item.bundleProductIds?.length
|
|
901
|
+
? item.price
|
|
902
|
+
: product.price;
|
|
903
|
+
const firstItem = group[0].item;
|
|
904
|
+
const groupTotal = group.reduce((sum, { item, product }) => sum + unitPriceFor(item, product) * item.quantity, 0);
|
|
905
|
+
// Reuses the same store/seller lookup as the COD/UPI path above instead
|
|
906
|
+
// of re-implementing it inline — was two sequential findById calls per
|
|
907
|
+
// seller group here, duplicated from resolveShippingCost.
|
|
908
|
+
const { shippingFee, storeOwnerId } = await resolveShippingCost(firstItem.storeId);
|
|
909
|
+
let couponDiscount = 0;
|
|
910
|
+
const appliedDiscounts = [];
|
|
911
|
+
for (const coupon of appliedCoupons) {
|
|
912
|
+
let couponGroupDiscount = 0;
|
|
913
|
+
const isSellerScoped = coupon.scope === "seller" && coupon.storeId;
|
|
914
|
+
if (isSellerScoped) {
|
|
915
|
+
if (coupon.storeId !== firstItem.storeId)
|
|
916
|
+
continue;
|
|
917
|
+
if (coupon.applicableItemIds?.length) {
|
|
918
|
+
const eligibleTotal = group
|
|
919
|
+
.filter(({ item }) => coupon.applicableItemIds.includes(item.itemId))
|
|
920
|
+
.reduce((s, { item, product }) => s + unitPriceFor(item, product) * item.quantity, 0);
|
|
921
|
+
couponGroupDiscount =
|
|
922
|
+
eligibleTotal > 0
|
|
923
|
+
? Math.min(roundRupees((eligibleTotal / groupTotal) * coupon.discountAmount), eligibleTotal)
|
|
924
|
+
: 0;
|
|
925
|
+
}
|
|
926
|
+
else {
|
|
927
|
+
couponGroupDiscount = Math.min(coupon.discountAmount, groupTotal);
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
else if (cartSubtotal > 0) {
|
|
931
|
+
couponGroupDiscount = Math.min(roundRupees((groupTotal / cartSubtotal) * coupon.discountAmount), groupTotal);
|
|
932
|
+
}
|
|
933
|
+
if (couponGroupDiscount > 0) {
|
|
934
|
+
couponDiscount += couponGroupDiscount;
|
|
935
|
+
appliedDiscounts.push({
|
|
936
|
+
code: coupon.code,
|
|
937
|
+
couponId: coupon.couponId,
|
|
938
|
+
type: "coupon",
|
|
939
|
+
discountAmount: couponGroupDiscount,
|
|
940
|
+
scope: coupon.scope,
|
|
941
|
+
storeId: coupon.storeId,
|
|
942
|
+
});
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
couponDiscount = Math.min(couponDiscount, groupTotal);
|
|
946
|
+
const rawPlatformFee = roundRupees(groupTotal * (platformFeePercent / 100));
|
|
947
|
+
const gstOnFee = roundRupees(rawPlatformFee * (gstPercent / 100));
|
|
948
|
+
const platformFee = rawPlatformFee + gstOnFee;
|
|
949
|
+
const shipmentProtectionFee = computeShipmentProtectionFee(groupTotal, shipmentProtectionAddon, siteSettings?.commissions ?? CHECKOUT_DEFAULT_COMMISSIONS);
|
|
950
|
+
const orderTotal = Math.max(0, groupTotal - couponDiscount) + shippingFee + whatsappNotifyFee + giftWrapFee + shipmentProtectionFee;
|
|
951
|
+
// P-8 GST — deliberately NOT wired into this Razorpay-verify path. The
|
|
952
|
+
// amount-mismatch check above (expectedPaymentAmountRs) compares against
|
|
953
|
+
// what the buyer already paid via the Razorpay order created earlier in
|
|
954
|
+
// the flow; adding product GST here without also adding it to that
|
|
955
|
+
// upstream pre-payment amount calculation would either fail the mismatch
|
|
956
|
+
// check or silently under/over-charge. Wiring GST through the full
|
|
957
|
+
// Razorpay create→verify round-trip is separate follow-up work, tracked
|
|
958
|
+
// alongside P-13 (Razorpay is disabled by default today, so this order
|
|
959
|
+
// type doesn't currently carry a GST breakdown).
|
|
960
|
+
// S-SBUNI-RULES 2026-05-13 — order-item decoration via rule registry.
|
|
961
|
+
const orderItems = group.map(({ item, product }) => {
|
|
962
|
+
const lt = (product?.listingType ?? "standard");
|
|
963
|
+
const itemRule = getListingRule(lt);
|
|
964
|
+
// SB-UNI-5 2026-05-13 — bundle cart-lines surface the locked
|
|
965
|
+
// bundlePrice (item.price), not the representative member's
|
|
966
|
+
// product.price. Stock decrement still runs per member elsewhere.
|
|
967
|
+
const isBundle = Boolean(item.bundleCategorySlug && item.bundleProductIds?.length);
|
|
968
|
+
const unitPrice = isBundle ? item.price : product.price;
|
|
969
|
+
const bundleFields = isBundle
|
|
970
|
+
? {
|
|
971
|
+
bundleCategorySlug: item.bundleCategorySlug,
|
|
972
|
+
bundleProductIds: item.bundleProductIds,
|
|
973
|
+
}
|
|
974
|
+
: {};
|
|
975
|
+
const baseLine = {
|
|
976
|
+
productId: item.productId,
|
|
977
|
+
productTitle: item.productTitle,
|
|
978
|
+
quantity: item.quantity,
|
|
979
|
+
unitPrice,
|
|
980
|
+
totalPrice: unitPrice * item.quantity,
|
|
981
|
+
...bundleFields,
|
|
982
|
+
};
|
|
983
|
+
return itemRule.decorateOrderItem(baseLine, product);
|
|
984
|
+
});
|
|
985
|
+
const totalQuantity = group.reduce((sum, { item }) => sum + item.quantity, 0);
|
|
986
|
+
const imageUrls = [
|
|
987
|
+
...new Set(group
|
|
988
|
+
.map(({ product }) => product?.mainImage)
|
|
989
|
+
.filter((url) => typeof url === "string" && url.length > 0)),
|
|
990
|
+
];
|
|
991
|
+
// S-SBUNI-RULES 2026-05-13 — order-doc decoration via rule registry.
|
|
992
|
+
const lt0Rzp = (group[0].product?.listingType ?? "standard");
|
|
993
|
+
const groupRuleRzp = getListingRule(lt0Rzp);
|
|
994
|
+
const extraOrderFields = {
|
|
995
|
+
...groupRuleRzp.decorateOrderDoc(group[0].item, group[0].product),
|
|
996
|
+
...(orderType === "prize-draw" && group[0].product
|
|
997
|
+
? { prizeRevealDeadline: computePrizeRevealDeadline(group[0].product) }
|
|
998
|
+
: {}),
|
|
999
|
+
};
|
|
1000
|
+
const order = await unitOfWork.orders.create({
|
|
1001
|
+
productId: firstItem.productId,
|
|
1002
|
+
productTitle: firstItem.productTitle,
|
|
1003
|
+
userId: uid,
|
|
1004
|
+
userName,
|
|
1005
|
+
userEmail,
|
|
1006
|
+
quantity: totalQuantity,
|
|
1007
|
+
unitPrice: group[0].product.price,
|
|
1008
|
+
totalPrice: orderTotal,
|
|
1009
|
+
currency: firstItem.currency ?? getDefaultCurrency(),
|
|
1010
|
+
storeId: firstItem.storeId || undefined,
|
|
1011
|
+
storeName: firstItem.storeName || undefined,
|
|
1012
|
+
items: orderItems,
|
|
1013
|
+
orderType,
|
|
1014
|
+
offerId: firstItem.offerId ?? undefined,
|
|
1015
|
+
status: OrderStatusValues.CONFIRMED,
|
|
1016
|
+
paymentStatus: PaymentStatusValues.PAID,
|
|
1017
|
+
paymentMethod: PaymentMethodValues.ONLINE,
|
|
1018
|
+
paymentId: razorpay_payment_id,
|
|
1019
|
+
paymentRecord: {
|
|
1020
|
+
method: "razorpay",
|
|
1021
|
+
transactionId: razorpay_payment_id,
|
|
1022
|
+
amount: orderTotal,
|
|
1023
|
+
paidAt: new Date(),
|
|
1024
|
+
verifiedBy: "razorpay-webhook",
|
|
1025
|
+
verificationMethod: "webhook",
|
|
1026
|
+
gatewayRef: {
|
|
1027
|
+
orderId: razorpay_order_id,
|
|
1028
|
+
paymentId: razorpay_payment_id,
|
|
1029
|
+
signature: razorpay_signature,
|
|
1030
|
+
},
|
|
1031
|
+
},
|
|
1032
|
+
shippingAddress,
|
|
1033
|
+
notes,
|
|
1034
|
+
platformFee,
|
|
1035
|
+
shippingFee: shippingFee > 0 ? shippingFee : undefined,
|
|
1036
|
+
whatsappNotifyAddon: whatsappNotifyFee > 0 ? true : undefined,
|
|
1037
|
+
whatsappNotifyFee: whatsappNotifyFee > 0 ? whatsappNotifyFee : undefined,
|
|
1038
|
+
giftWrapAddon: giftWrapFee > 0 ? true : undefined,
|
|
1039
|
+
giftWrapFee: giftWrapFee > 0 ? giftWrapFee : undefined,
|
|
1040
|
+
giftWrapMessage: giftWrapFee > 0 ? giftWrapMessage?.slice(0, 500) : undefined,
|
|
1041
|
+
shipmentProtectionAddon: shipmentProtectionFee > 0 ? true : undefined,
|
|
1042
|
+
shipmentProtectionFee: shipmentProtectionFee > 0 ? shipmentProtectionFee : undefined,
|
|
1043
|
+
couponCode: appliedDiscounts[0]?.code,
|
|
1044
|
+
couponDiscount: couponDiscount > 0 ? couponDiscount : undefined,
|
|
1045
|
+
appliedDiscounts: appliedDiscounts.length > 0 ? appliedDiscounts : undefined,
|
|
1046
|
+
imageUrls: imageUrls.length > 0 ? imageUrls : undefined,
|
|
1047
|
+
outOfStockPolicy,
|
|
1048
|
+
droppedItems: unavailablePaid.length > 0 ? unavailablePaid : undefined,
|
|
1049
|
+
...extraOrderFields,
|
|
1050
|
+
});
|
|
1051
|
+
orderIds.push(order.id);
|
|
1052
|
+
// SB-UNI-N — claim a digital code for digital-code orders (fire-and-forget,
|
|
1053
|
+
// order is already persisted so a claim failure is logged, not thrown).
|
|
1054
|
+
if ((group[0]?.product?.listingType ?? "standard") === "digital-code") {
|
|
1055
|
+
claimDigitalCodeForOrder(getAdminDb(), firstItem.productId, order.id, uid, {
|
|
1056
|
+
userEmail: userEmail || undefined,
|
|
1057
|
+
userName,
|
|
1058
|
+
productTitle: firstItem.productTitle,
|
|
1059
|
+
}).catch((e) => serverLogger.error("claimDigitalCode", e));
|
|
1060
|
+
}
|
|
1061
|
+
if (userEmail) {
|
|
1062
|
+
emailsToSend.push({
|
|
1063
|
+
to: userEmail,
|
|
1064
|
+
userName,
|
|
1065
|
+
orderId: order.id,
|
|
1066
|
+
productTitle: orderItems.length > 1 ? `${orderItems.length} items` : firstItem.productTitle,
|
|
1067
|
+
quantity: totalQuantity,
|
|
1068
|
+
totalPrice: orderTotal,
|
|
1069
|
+
currency: firstItem.currency ?? getDefaultCurrency(),
|
|
1070
|
+
shippingAddress,
|
|
1071
|
+
paymentMethod: PaymentMethodValues.ONLINE,
|
|
1072
|
+
items: orderItems,
|
|
1073
|
+
});
|
|
1074
|
+
}
|
|
1075
|
+
emitOrderPlacedNotifications({
|
|
1076
|
+
orderId: order.id,
|
|
1077
|
+
buyerUid: uid,
|
|
1078
|
+
buyerName: userName,
|
|
1079
|
+
storeOwnerId,
|
|
1080
|
+
productLabel: orderItems.length > 1 ? `${orderItems.length} items` : firstItem.productTitle,
|
|
1081
|
+
paid: true,
|
|
1082
|
+
whatsappNotifyAddon: whatsappNotifyFee > 0,
|
|
1083
|
+
});
|
|
1084
|
+
return orderTotal;
|
|
1085
|
+
}
|
|
875
1086
|
export async function verifyAndPlaceRazorpayOrderAction(input) {
|
|
876
|
-
const { userId: uid, userName, userEmail, razorpay_order_id, razorpay_payment_id, razorpay_signature, addressId, notes, outOfStockPolicy = OutOfStockPolicyValues.CANCEL_ORDER, } = input;
|
|
1087
|
+
const { userId: uid, userName, userEmail, razorpay_order_id, razorpay_payment_id, razorpay_signature, addressId, notes, outOfStockPolicy = OutOfStockPolicyValues.CANCEL_ORDER, whatsappNotifyAddon = false, giftWrapAddon = false, giftWrapMessage, shipmentProtectionAddon = false, } = input;
|
|
877
1088
|
const siteSettings = await siteSettingsRepository.getSingleton();
|
|
878
1089
|
const platformFeePercent = siteSettings?.commissions?.platformFeePercent ?? 5;
|
|
879
1090
|
const gstPercent = siteSettings?.commissions?.gstPercent ?? 18;
|
|
1091
|
+
const whatsappNotifyFee = computeWhatsAppNotifyFee(whatsappNotifyAddon, siteSettings?.commissions ?? CHECKOUT_DEFAULT_COMMISSIONS);
|
|
1092
|
+
const giftWrapFee = computeGiftWrapFee(giftWrapAddon, siteSettings?.commissions ?? CHECKOUT_DEFAULT_COMMISSIONS);
|
|
880
1093
|
const isValid = await verifyPaymentSignatureWithKeys({
|
|
881
1094
|
razorpay_order_id,
|
|
882
1095
|
razorpay_payment_id,
|
|
@@ -1051,178 +1264,28 @@ export async function verifyAndPlaceRazorpayOrderAction(input) {
|
|
|
1051
1264
|
const cartSubtotal = orderGroups.reduce((s, { items: g }) => s +
|
|
1052
1265
|
g.reduce((gs, { item, product }) => gs + unitPriceFor(item, product) * item.quantity, 0), 0);
|
|
1053
1266
|
for (const { items: group, orderType } of orderGroups) {
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
if (isSellerScoped) {
|
|
1066
|
-
if (coupon.storeId !== firstItem.storeId)
|
|
1067
|
-
continue;
|
|
1068
|
-
if (coupon.applicableItemIds?.length) {
|
|
1069
|
-
const eligibleTotal = group
|
|
1070
|
-
.filter(({ item }) => coupon.applicableItemIds.includes(item.itemId))
|
|
1071
|
-
.reduce((s, { item, product }) => s + unitPriceFor(item, product) * item.quantity, 0);
|
|
1072
|
-
couponGroupDiscount =
|
|
1073
|
-
eligibleTotal > 0
|
|
1074
|
-
? Math.min(roundRupees((eligibleTotal / groupTotal) * coupon.discountAmount), eligibleTotal)
|
|
1075
|
-
: 0;
|
|
1076
|
-
}
|
|
1077
|
-
else {
|
|
1078
|
-
couponGroupDiscount = Math.min(coupon.discountAmount, groupTotal);
|
|
1079
|
-
}
|
|
1080
|
-
}
|
|
1081
|
-
else if (cartSubtotal > 0) {
|
|
1082
|
-
couponGroupDiscount = Math.min(roundRupees((groupTotal / cartSubtotal) * coupon.discountAmount), groupTotal);
|
|
1083
|
-
}
|
|
1084
|
-
if (couponGroupDiscount > 0) {
|
|
1085
|
-
couponDiscount += couponGroupDiscount;
|
|
1086
|
-
appliedDiscounts.push({
|
|
1087
|
-
code: coupon.code,
|
|
1088
|
-
couponId: coupon.couponId,
|
|
1089
|
-
type: "coupon",
|
|
1090
|
-
discountAmount: couponGroupDiscount,
|
|
1091
|
-
scope: coupon.scope,
|
|
1092
|
-
storeId: coupon.storeId,
|
|
1093
|
-
});
|
|
1094
|
-
}
|
|
1095
|
-
}
|
|
1096
|
-
couponDiscount = Math.min(couponDiscount, groupTotal);
|
|
1097
|
-
const rawPlatformFee = roundRupees(groupTotal * (platformFeePercent / 100));
|
|
1098
|
-
const gstOnFee = roundRupees(rawPlatformFee * (gstPercent / 100));
|
|
1099
|
-
const platformFee = rawPlatformFee + gstOnFee;
|
|
1100
|
-
const orderTotal = Math.max(0, groupTotal - couponDiscount) + shippingFee;
|
|
1101
|
-
total += orderTotal;
|
|
1102
|
-
// P-8 GST — deliberately NOT wired into this Razorpay-verify path. The
|
|
1103
|
-
// amount-mismatch check above (expectedPaymentAmountRs) compares against
|
|
1104
|
-
// what the buyer already paid via the Razorpay order created earlier in
|
|
1105
|
-
// the flow; adding product GST here without also adding it to that
|
|
1106
|
-
// upstream pre-payment amount calculation would either fail the mismatch
|
|
1107
|
-
// check or silently under/over-charge. Wiring GST through the full
|
|
1108
|
-
// Razorpay create→verify round-trip is separate follow-up work, tracked
|
|
1109
|
-
// alongside P-13 (Razorpay is disabled by default today, so this order
|
|
1110
|
-
// type doesn't currently carry a GST breakdown).
|
|
1111
|
-
// S-SBUNI-RULES 2026-05-13 — order-item decoration via rule registry.
|
|
1112
|
-
const orderItems = group.map(({ item, product }) => {
|
|
1113
|
-
const lt = (product?.listingType ?? "standard");
|
|
1114
|
-
const itemRule = getListingRule(lt);
|
|
1115
|
-
// SB-UNI-5 2026-05-13 — bundle cart-lines surface the locked
|
|
1116
|
-
// bundlePrice (item.price), not the representative member's
|
|
1117
|
-
// product.price. Stock decrement still runs per member elsewhere.
|
|
1118
|
-
const isBundle = Boolean(item.bundleCategorySlug && item.bundleProductIds?.length);
|
|
1119
|
-
const unitPrice = isBundle ? item.price : product.price;
|
|
1120
|
-
const bundleFields = isBundle
|
|
1121
|
-
? {
|
|
1122
|
-
bundleCategorySlug: item.bundleCategorySlug,
|
|
1123
|
-
bundleProductIds: item.bundleProductIds,
|
|
1124
|
-
}
|
|
1125
|
-
: {};
|
|
1126
|
-
const baseLine = {
|
|
1127
|
-
productId: item.productId,
|
|
1128
|
-
productTitle: item.productTitle,
|
|
1129
|
-
quantity: item.quantity,
|
|
1130
|
-
unitPrice,
|
|
1131
|
-
totalPrice: unitPrice * item.quantity,
|
|
1132
|
-
...bundleFields,
|
|
1133
|
-
};
|
|
1134
|
-
return itemRule.decorateOrderItem(baseLine, product);
|
|
1135
|
-
});
|
|
1136
|
-
const totalQuantity = group.reduce((sum, { item }) => sum + item.quantity, 0);
|
|
1137
|
-
const imageUrls = [
|
|
1138
|
-
...new Set(group
|
|
1139
|
-
.map(({ product }) => product?.mainImage)
|
|
1140
|
-
.filter((url) => typeof url === "string" && url.length > 0)),
|
|
1141
|
-
];
|
|
1142
|
-
// S-SBUNI-RULES 2026-05-13 — order-doc decoration via rule registry.
|
|
1143
|
-
const lt0Rzp = (group[0].product?.listingType ?? "standard");
|
|
1144
|
-
const groupRuleRzp = getListingRule(lt0Rzp);
|
|
1145
|
-
const extraOrderFields = {
|
|
1146
|
-
...groupRuleRzp.decorateOrderDoc(group[0].item, group[0].product),
|
|
1147
|
-
...(orderType === "prize-draw" && group[0].product
|
|
1148
|
-
? { prizeRevealDeadline: computePrizeRevealDeadline(group[0].product) }
|
|
1149
|
-
: {}),
|
|
1150
|
-
};
|
|
1151
|
-
const order = await unitOfWork.orders.create({
|
|
1152
|
-
productId: firstItem.productId,
|
|
1153
|
-
productTitle: firstItem.productTitle,
|
|
1154
|
-
userId: uid,
|
|
1267
|
+
total += await createRazorpayGroupOrder(group, orderType, {
|
|
1268
|
+
appliedCoupons,
|
|
1269
|
+
cartSubtotal,
|
|
1270
|
+
platformFeePercent,
|
|
1271
|
+
gstPercent,
|
|
1272
|
+
whatsappNotifyFee,
|
|
1273
|
+
giftWrapFee,
|
|
1274
|
+
giftWrapMessage,
|
|
1275
|
+
shipmentProtectionAddon,
|
|
1276
|
+
siteSettings,
|
|
1277
|
+
uid,
|
|
1155
1278
|
userName,
|
|
1156
1279
|
userEmail,
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
currency: firstItem.currency ?? getDefaultCurrency(),
|
|
1161
|
-
storeId: firstItem.storeId || undefined,
|
|
1162
|
-
storeName: firstItem.storeName || undefined,
|
|
1163
|
-
items: orderItems,
|
|
1164
|
-
orderType,
|
|
1165
|
-
offerId: firstItem.offerId ?? undefined,
|
|
1166
|
-
status: OrderStatusValues.CONFIRMED,
|
|
1167
|
-
paymentStatus: PaymentStatusValues.PAID,
|
|
1168
|
-
paymentMethod: PaymentMethodValues.ONLINE,
|
|
1169
|
-
paymentId: razorpay_payment_id,
|
|
1170
|
-
paymentRecord: {
|
|
1171
|
-
method: "razorpay",
|
|
1172
|
-
transactionId: razorpay_payment_id,
|
|
1173
|
-
amount: orderTotal,
|
|
1174
|
-
paidAt: new Date(),
|
|
1175
|
-
verifiedBy: "razorpay-webhook",
|
|
1176
|
-
verificationMethod: "webhook",
|
|
1177
|
-
gatewayRef: {
|
|
1178
|
-
orderId: razorpay_order_id,
|
|
1179
|
-
paymentId: razorpay_payment_id,
|
|
1180
|
-
signature: razorpay_signature,
|
|
1181
|
-
},
|
|
1182
|
-
},
|
|
1280
|
+
razorpay_order_id,
|
|
1281
|
+
razorpay_payment_id,
|
|
1282
|
+
razorpay_signature,
|
|
1183
1283
|
shippingAddress,
|
|
1184
1284
|
notes,
|
|
1185
|
-
platformFee,
|
|
1186
|
-
shippingFee: shippingFee > 0 ? shippingFee : undefined,
|
|
1187
|
-
couponCode: appliedDiscounts[0]?.code,
|
|
1188
|
-
couponDiscount: couponDiscount > 0 ? couponDiscount : undefined,
|
|
1189
|
-
appliedDiscounts: appliedDiscounts.length > 0 ? appliedDiscounts : undefined,
|
|
1190
|
-
imageUrls: imageUrls.length > 0 ? imageUrls : undefined,
|
|
1191
1285
|
outOfStockPolicy,
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
orderIds.push(order.id);
|
|
1196
|
-
// SB-UNI-N — claim a digital code for digital-code orders (fire-and-forget,
|
|
1197
|
-
// order is already persisted so a claim failure is logged, not thrown).
|
|
1198
|
-
if ((group[0]?.product?.listingType ?? "standard") === "digital-code") {
|
|
1199
|
-
claimDigitalCodeForOrder(getAdminDb(), firstItem.productId, order.id, uid, {
|
|
1200
|
-
userEmail: userEmail || undefined,
|
|
1201
|
-
userName,
|
|
1202
|
-
productTitle: firstItem.productTitle,
|
|
1203
|
-
}).catch((e) => serverLogger.error("claimDigitalCode", e));
|
|
1204
|
-
}
|
|
1205
|
-
if (userEmail) {
|
|
1206
|
-
emailsToSend.push({
|
|
1207
|
-
to: userEmail,
|
|
1208
|
-
userName,
|
|
1209
|
-
orderId: order.id,
|
|
1210
|
-
productTitle: orderItems.length > 1 ? `${orderItems.length} items` : firstItem.productTitle,
|
|
1211
|
-
quantity: totalQuantity,
|
|
1212
|
-
totalPrice: orderTotal,
|
|
1213
|
-
currency: firstItem.currency ?? getDefaultCurrency(),
|
|
1214
|
-
shippingAddress,
|
|
1215
|
-
paymentMethod: PaymentMethodValues.ONLINE,
|
|
1216
|
-
items: orderItems,
|
|
1217
|
-
});
|
|
1218
|
-
}
|
|
1219
|
-
emitOrderPlacedNotifications({
|
|
1220
|
-
orderId: order.id,
|
|
1221
|
-
buyerUid: uid,
|
|
1222
|
-
buyerName: userName,
|
|
1223
|
-
storeOwnerId,
|
|
1224
|
-
productLabel: orderItems.length > 1 ? `${orderItems.length} items` : firstItem.productTitle,
|
|
1225
|
-
paid: true,
|
|
1286
|
+
unavailablePaid,
|
|
1287
|
+
orderIds,
|
|
1288
|
+
emailsToSend,
|
|
1226
1289
|
});
|
|
1227
1290
|
}
|
|
1228
1291
|
// SB-UNI-5 2026-05-13 — bundle-aware batch decrement. We iterate the
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Google Cloud Logging integration for the `/admin/maintenance/cloud-logs`
|
|
3
|
+
* page. Reuses the same 3-tier credential resolution as
|
|
4
|
+
* `providers/db-firebase/admin.ts` (local `firebase-admin-key.json` → Vercel
|
|
5
|
+
* `FIREBASE_ADMIN_*` env vars → Application Default Credentials), so no new
|
|
6
|
+
* env vars are required.
|
|
7
|
+
*
|
|
8
|
+
* Bounded to ONE `Logging#getEntries()` call per invocation — never
|
|
9
|
+
* `getEntriesStream()` in an unbounded loop — to respect the Vercel Hobby
|
|
10
|
+
* 10s sync-function ceiling (CLAUDE.md Rule #6). Pagination is client-driven
|
|
11
|
+
* via `nextPageToken`, not exhausted server-side.
|
|
12
|
+
*
|
|
13
|
+
* `@google-cloud/logging` is a consumer-side (root `package.json`) dependency
|
|
14
|
+
* — it is never imported at module scope here. The `Logging` client is
|
|
15
|
+
* lazily `require()`d inside a function body via `(module as any).require`,
|
|
16
|
+
* exactly like `admin.ts` lazily loads `firebase-admin/*`, so Turbopack never
|
|
17
|
+
* sees a static import of a Node-only client reachable from a client bundle
|
|
18
|
+
* (CLAUDE.md Root Cause #6 / #24). A minimal local interface describes the
|
|
19
|
+
* only surface we call — this file never imports `@google-cloud/logging`'s
|
|
20
|
+
* own types, so it type-checks even though the package is deliberately not
|
|
21
|
+
* listed as an appkit dependency (it lives in the consumer's root
|
|
22
|
+
* `package.json` only, since this integration is Next.js-route-only).
|
|
23
|
+
*/
|
|
24
|
+
export interface CloudLogEntry {
|
|
25
|
+
id: string;
|
|
26
|
+
timestamp: string;
|
|
27
|
+
severity: string;
|
|
28
|
+
resourceType: string;
|
|
29
|
+
resourceLabels: Record<string, string>;
|
|
30
|
+
logName: string;
|
|
31
|
+
message: string;
|
|
32
|
+
}
|
|
33
|
+
export interface ListCloudLogEntriesOpts {
|
|
34
|
+
/** Full Cloud Logging filter string. Defaults to a 24h `resource.type` window (see below). */
|
|
35
|
+
filter?: string;
|
|
36
|
+
/** Capped at 100. Defaults to 50. */
|
|
37
|
+
pageSize?: number;
|
|
38
|
+
/** Opaque continuation token from a previous call's `nextPageToken`. */
|
|
39
|
+
pageToken?: string;
|
|
40
|
+
}
|
|
41
|
+
export interface ListCloudLogEntriesResult {
|
|
42
|
+
entries: CloudLogEntry[];
|
|
43
|
+
nextPageToken: string | null;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Bounded, single-page Cloud Logging read. One `getEntries()` call per
|
|
47
|
+
* invocation — the caller drives further pages via `opts.pageToken`
|
|
48
|
+
* (client-side "Load more"), never a server-side exhaust loop.
|
|
49
|
+
*/
|
|
50
|
+
export declare const listCloudLogEntries: (opts?: ListCloudLogEntriesOpts) => Promise<ListCloudLogEntriesResult>;
|