@mohasinac/appkit 4.59.0 → 4.60.1

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.
Files changed (532) hide show
  1. package/dist/.build-stamp.json +3 -3
  2. package/dist/_internal/client/features/layout/DashboardLayoutClient.js +62 -5
  3. package/dist/_internal/client/features/maintenance/views/MaintenanceDashboardView.js +2 -2
  4. package/dist/_internal/server/features/brands/actions.js +78 -4
  5. package/dist/_internal/server/features/checkout/actions.d.ts +36 -13
  6. package/dist/_internal/server/features/checkout/actions.js +668 -183
  7. package/dist/_internal/server/features/checkout/supplier-identity.js +18 -1
  8. package/dist/_internal/server/features/gateway-events/index.d.ts +10 -0
  9. package/dist/_internal/server/features/gateway-events/index.js +10 -0
  10. package/dist/_internal/server/features/gateway-events/ingest.d.ts +127 -0
  11. package/dist/_internal/server/features/gateway-events/ingest.js +188 -0
  12. package/dist/_internal/server/features/gateway-events/redact.d.ts +66 -0
  13. package/dist/_internal/server/features/gateway-events/redact.js +151 -0
  14. package/dist/_internal/server/features/grievance/actions.d.ts +63 -0
  15. package/dist/_internal/server/features/grievance/actions.js +173 -0
  16. package/dist/_internal/server/features/grievance/index.d.ts +8 -0
  17. package/dist/_internal/server/features/grievance/index.js +8 -0
  18. package/dist/_internal/server/features/grouped/data.js +28 -3
  19. package/dist/_internal/server/features/lottery/actions.js +99 -4
  20. package/dist/_internal/server/features/order-completion/actions.d.ts +92 -0
  21. package/dist/_internal/server/features/order-completion/actions.js +336 -0
  22. package/dist/_internal/server/features/order-completion/index.d.ts +7 -0
  23. package/dist/_internal/server/features/order-completion/index.js +7 -0
  24. package/dist/_internal/server/features/orders/actions.d.ts +13 -0
  25. package/dist/_internal/server/features/orders/actions.js +56 -2
  26. package/dist/_internal/server/features/orders/adapters.d.ts +20 -2
  27. package/dist/_internal/server/features/orders/adapters.js +44 -0
  28. package/dist/_internal/server/features/orders/cancellation-refund.d.ts +116 -0
  29. package/dist/_internal/server/features/orders/cancellation-refund.js +466 -0
  30. package/dist/_internal/server/features/orders/index.d.ts +2 -1
  31. package/dist/_internal/server/features/orders/index.js +1 -1
  32. package/dist/_internal/server/features/orders/service.d.ts +60 -0
  33. package/dist/_internal/server/features/orders/service.js +74 -0
  34. package/dist/_internal/server/features/payment-cases/disputes.d.ts +76 -0
  35. package/dist/_internal/server/features/payment-cases/disputes.js +241 -0
  36. package/dist/_internal/server/features/payment-cases/index.d.ts +9 -0
  37. package/dist/_internal/server/features/payment-cases/index.js +9 -0
  38. package/dist/_internal/server/features/payouts/actions.d.ts +28 -3
  39. package/dist/_internal/server/features/payouts/actions.js +84 -10
  40. package/dist/_internal/server/features/points/adjust.d.ts +61 -0
  41. package/dist/_internal/server/features/points/adjust.js +121 -0
  42. package/dist/_internal/server/features/points/award.d.ts +65 -0
  43. package/dist/_internal/server/features/points/award.js +145 -0
  44. package/dist/_internal/server/features/price-index/market.d.ts +33 -0
  45. package/dist/_internal/server/features/price-index/market.js +54 -1
  46. package/dist/_internal/server/features/products/data.js +40 -22
  47. package/dist/_internal/server/features/protection-claims/actions.d.ts +72 -0
  48. package/dist/_internal/server/features/protection-claims/actions.js +228 -0
  49. package/dist/_internal/server/features/protection-claims/index.d.ts +8 -0
  50. package/dist/_internal/server/features/protection-claims/index.js +8 -0
  51. package/dist/_internal/server/features/refunds/actions.d.ts +20 -0
  52. package/dist/_internal/server/features/refunds/actions.js +259 -17
  53. package/dist/_internal/server/features/refunds/capacity.d.ts +29 -0
  54. package/dist/_internal/server/features/refunds/capacity.js +37 -0
  55. package/dist/_internal/server/features/reviews/actions.js +3 -2
  56. package/dist/_internal/server/features/search/index-build/sources.js +20 -1
  57. package/dist/_internal/server/features/shipping/reconcile.d.ts +10 -1
  58. package/dist/_internal/server/features/shipping/reconcile.js +34 -3
  59. package/dist/_internal/server/features/shipping/reimbursement.d.ts +117 -0
  60. package/dist/_internal/server/features/shipping/reimbursement.js +223 -0
  61. package/dist/_internal/server/features/stores/tabs.d.ts +19 -4
  62. package/dist/_internal/server/features/stores/tabs.js +24 -4
  63. package/dist/_internal/server/functions/https.d.ts +31 -1
  64. package/dist/_internal/server/functions/https.js +52 -1
  65. package/dist/_internal/server/functions/scheduled.d.ts +72 -1
  66. package/dist/_internal/server/functions/scheduled.js +124 -1
  67. package/dist/_internal/server/jobs/core/autoPayoutEligibility.js +62 -16
  68. package/dist/_internal/server/jobs/core/boxRollupReconcile.d.ts +46 -0
  69. package/dist/_internal/server/jobs/core/boxRollupReconcile.js +142 -0
  70. package/dist/_internal/server/jobs/core/dailyDataCleanup.js +149 -1
  71. package/dist/_internal/server/jobs/core/dailyStatusDigest.d.ts +14 -0
  72. package/dist/_internal/server/jobs/core/dailyStatusDigest.js +79 -1
  73. package/dist/_internal/server/jobs/core/index.d.ts +2 -0
  74. package/dist/_internal/server/jobs/core/index.js +2 -0
  75. package/dist/_internal/server/jobs/core/marketPriceRollup.d.ts +64 -0
  76. package/dist/_internal/server/jobs/core/marketPriceRollup.js +153 -0
  77. package/dist/_internal/server/jobs/core/obligationSweep.d.ts +36 -0
  78. package/dist/_internal/server/jobs/core/obligationSweep.js +503 -0
  79. package/dist/_internal/server/jobs/core/paymentReconcile.d.ts +79 -0
  80. package/dist/_internal/server/jobs/core/paymentReconcile.js +614 -0
  81. package/dist/_internal/server/jobs/core/paymentWindowTimeout.js +34 -0
  82. package/dist/_internal/server/jobs/core/weeklyPayoutEligibility.js +35 -4
  83. package/dist/_internal/server/jobs/handlers/boxRollupReconcile.d.ts +2 -0
  84. package/dist/_internal/server/jobs/handlers/boxRollupReconcile.js +15 -0
  85. package/dist/_internal/server/jobs/handlers/countersReconcileNow.d.ts +65 -0
  86. package/dist/_internal/server/jobs/handlers/countersReconcileNow.js +89 -0
  87. package/dist/_internal/server/jobs/handlers/index.d.ts +5 -0
  88. package/dist/_internal/server/jobs/handlers/index.js +5 -0
  89. package/dist/_internal/server/jobs/handlers/marketPriceRollup.d.ts +2 -0
  90. package/dist/_internal/server/jobs/handlers/marketPriceRollup.js +2 -0
  91. package/dist/_internal/server/jobs/handlers/obligationSweep.d.ts +2 -0
  92. package/dist/_internal/server/jobs/handlers/obligationSweep.js +15 -0
  93. package/dist/_internal/server/jobs/handlers/onShipmentAllocationSync.js +30 -1
  94. package/dist/_internal/server/jobs/handlers/paymentReconcile.d.ts +2 -0
  95. package/dist/_internal/server/jobs/handlers/paymentReconcile.js +6 -0
  96. package/dist/_internal/shared/actions/action-registry.d.ts +1 -1
  97. package/dist/_internal/shared/actions/action-registry.js +71 -0
  98. package/dist/_internal/shared/checkout/coupon-discount.d.ts +73 -0
  99. package/dist/_internal/shared/checkout/coupon-discount.js +103 -0
  100. package/dist/_internal/shared/checkout/lanes.d.ts +42 -0
  101. package/dist/_internal/shared/checkout/lanes.js +46 -0
  102. package/dist/_internal/shared/checkout/order-math.d.ts +149 -1
  103. package/dist/_internal/shared/checkout/order-math.js +251 -3
  104. package/dist/_internal/shared/checkout/refundable-base.d.ts +116 -0
  105. package/dist/_internal/shared/checkout/refundable-base.js +122 -0
  106. package/dist/_internal/shared/features/auctions/config.d.ts +14 -1
  107. package/dist/_internal/shared/features/auctions/config.js +30 -1
  108. package/dist/_internal/shared/features/brands/schema.d.ts +42 -0
  109. package/dist/_internal/shared/features/brands/schema.js +18 -0
  110. package/dist/_internal/shared/features/orders/refund-copy.d.ts +22 -0
  111. package/dist/_internal/shared/features/orders/refund-copy.js +34 -0
  112. package/dist/_internal/shared/features/print/label-profiles.d.ts +198 -0
  113. package/dist/_internal/shared/features/print/label-profiles.js +350 -0
  114. package/dist/_internal/shared/features/products/to-product-item.js +33 -5
  115. package/dist/_internal/shared/fees/calculator.d.ts +31 -2
  116. package/dist/_internal/shared/fees/calculator.js +103 -5
  117. package/dist/_internal/shared/fees/india-states.d.ts +195 -0
  118. package/dist/_internal/shared/fees/india-states.js +190 -0
  119. package/dist/_internal/shared/fees/payout-hold.d.ts +80 -0
  120. package/dist/_internal/shared/fees/payout-hold.js +74 -0
  121. package/dist/_internal/shared/obligations/deadlines.d.ts +94 -0
  122. package/dist/_internal/shared/obligations/deadlines.js +155 -0
  123. package/dist/_internal/shared/shipping/quote.d.ts +96 -0
  124. package/dist/_internal/shared/shipping/quote.js +67 -0
  125. package/dist/_internal/shared/time/business-days.d.ts +91 -0
  126. package/dist/_internal/shared/time/business-days.js +185 -0
  127. package/dist/cli/index.js +0 -2
  128. package/dist/client.d.ts +10 -4
  129. package/dist/client.js +29 -3
  130. package/dist/configs/tailwind.d.ts +0 -12
  131. package/dist/configs/tailwind.js +28 -15
  132. package/dist/constants/api-endpoints.d.ts +51 -9
  133. package/dist/constants/api-endpoints.js +23 -7
  134. package/dist/constants/index.d.ts +1 -1
  135. package/dist/constants/index.js +1 -1
  136. package/dist/contracts/index.d.ts +1 -1
  137. package/dist/contracts/payment.d.ts +131 -0
  138. package/dist/features/about/components/AboutView.js +1 -1
  139. package/dist/features/about/components/DeveloperView.js +5 -2
  140. package/dist/features/about/components/FAQPageView.js +2 -1
  141. package/dist/features/about/components/FeesView.js +2 -1
  142. package/dist/features/about/components/HelpPageView.js +2 -1
  143. package/dist/features/about/components/HowAuctionsWorkView.js +2 -1
  144. package/dist/features/about/components/HowCheckoutWorksView.js +2 -1
  145. package/dist/features/about/components/HowEmiWorksView.js +2 -1
  146. package/dist/features/about/components/HowOffersWorkView.js +2 -1
  147. package/dist/features/about/components/HowOrdersWorkView.js +2 -1
  148. package/dist/features/about/components/HowPayoutsWorkView.js +2 -1
  149. package/dist/features/about/components/HowPreOrdersWorkView.js +2 -1
  150. package/dist/features/about/components/HowReviewsWorkView.js +2 -1
  151. package/dist/features/about/components/PolicyPageView.js +2 -1
  152. package/dist/features/about/components/ProseHero.d.ts +73 -0
  153. package/dist/features/about/components/ProseHero.js +18 -0
  154. package/dist/features/about/components/SecurityPrivacyView.js +2 -1
  155. package/dist/features/about/components/ShippingPolicyView.js +2 -1
  156. package/dist/features/about/components/TrackOrderView.js +2 -1
  157. package/dist/features/about/components/UnauthorizedView.js +1 -1
  158. package/dist/features/about/index.d.ts +2 -0
  159. package/dist/features/about/index.js +5 -0
  160. package/dist/features/account/actions/address-actions.d.ts +26 -2
  161. package/dist/features/account/actions/address-actions.js +25 -1
  162. package/dist/features/account/components/BuyerShoppingGuideView.js +1 -1
  163. package/dist/features/account/components/NotificationPreferencesPanel.js +1 -1
  164. package/dist/features/account/components/UserOrdersView.js +17 -0
  165. package/dist/features/account/components/UserSidebar.js +1 -1
  166. package/dist/features/account/hooks/useNotifications.js +29 -0
  167. package/dist/features/admin/actions/admin-coupon-actions.js +36 -0
  168. package/dist/features/admin/components/AdminBeysView.js +2 -2
  169. package/dist/features/admin/components/AdminBlogEditorView.js +2 -2
  170. package/dist/features/admin/components/AdminBrandEditorView.js +32 -2
  171. package/dist/features/admin/components/AdminBundleEditorView.js +1 -1
  172. package/dist/features/admin/components/AdminCarouselEditorView.js +1 -1
  173. package/dist/features/admin/components/AdminCategoriesView.js +1 -1
  174. package/dist/features/admin/components/AdminCategoryEditorView.js +73 -17
  175. package/dist/features/admin/components/AdminContactEditorView.js +1 -1
  176. package/dist/features/admin/components/AdminCouponEditorView.js +26 -1
  177. package/dist/features/admin/components/AdminFaqEditorView.js +1 -1
  178. package/dist/features/admin/components/AdminFaqsView.js +2 -2
  179. package/dist/features/admin/components/AdminGatewayEventsView.d.ts +54 -0
  180. package/dist/features/admin/components/AdminGatewayEventsView.js +209 -0
  181. package/dist/features/admin/components/AdminGuideHubView.js +1 -1
  182. package/dist/features/admin/components/AdminMoneyCasesView.d.ts +60 -0
  183. package/dist/features/admin/components/AdminMoneyCasesView.js +287 -0
  184. package/dist/features/admin/components/AdminPartsView.js +2 -2
  185. package/dist/features/admin/components/AdminProductsView.js +75 -4
  186. package/dist/features/admin/components/AdminSidebar.js +2 -2
  187. package/dist/features/admin/components/AdminSiteSettingsView.js +65 -7
  188. package/dist/features/admin/components/AdminSublistingCategoriesView.js +2 -0
  189. package/dist/features/admin/components/AdminSupportTicketDetailView.js +1 -1
  190. package/dist/features/admin/components/AdminSupportTicketsView.js +2 -2
  191. package/dist/features/admin/components/AdminTesterChecklistView.js +2 -2
  192. package/dist/features/admin/components/AdminUsersView.js +72 -1
  193. package/dist/features/admin/components/AdminViewCards.d.ts +3 -1
  194. package/dist/features/admin/components/AdminViewCards.js +4 -4
  195. package/dist/features/admin/components/DataListingView.d.ts +28 -0
  196. package/dist/features/admin/components/DataListingView.js +175 -14
  197. package/dist/features/admin/components/DataTable.d.ts +12 -1
  198. package/dist/features/admin/components/DataTable.js +3 -3
  199. package/dist/features/admin/components/OwnerQuickActions.d.ts +1 -1
  200. package/dist/features/admin/components/OwnerQuickActions.js +60 -2
  201. package/dist/features/admin/components/RecordOwnerQuickActions.d.ts +1 -1
  202. package/dist/features/admin/components/RecordOwnerQuickActions.js +61 -14
  203. package/dist/features/admin/components/analytics/AdminStatCard.d.ts +35 -1
  204. package/dist/features/admin/components/analytics/AdminStatCard.js +77 -2
  205. package/dist/features/admin/components/index.d.ts +4 -0
  206. package/dist/features/admin/components/index.js +6 -0
  207. package/dist/features/admin/constants/filter-tabs.d.ts +156 -0
  208. package/dist/features/admin/constants/filter-tabs.js +106 -0
  209. package/dist/features/admin/repository/site-settings.repository.d.ts +22 -0
  210. package/dist/features/admin/repository/site-settings.repository.js +82 -1
  211. package/dist/features/admin/schemas/admin-editor-forms.d.ts +21 -0
  212. package/dist/features/admin/schemas/admin-editor-forms.js +44 -0
  213. package/dist/features/admin/schemas/firestore.js +5 -1
  214. package/dist/features/admin/schemas/site-settings-form.js +30 -1
  215. package/dist/features/auctions/actions/bid-actions.js +13 -1
  216. package/dist/features/auctions/components/AuctionBottomActions.js +2 -2
  217. package/dist/features/auctions/components/AuctionDetailPageView.js +3 -1
  218. package/dist/features/auctions/components/MarketplaceAuctionCard.d.ts +11 -0
  219. package/dist/features/auctions/components/MarketplaceAuctionCard.js +5 -3
  220. package/dist/features/auctions/components/PlaceBidFormClient.d.ts +8 -1
  221. package/dist/features/auctions/components/PlaceBidFormClient.js +2 -2
  222. package/dist/features/audit-log/schemas/firestore.d.ts +28 -0
  223. package/dist/features/audit-log/schemas/firestore.js +14 -0
  224. package/dist/features/auth/permissions/constants.d.ts +17 -1
  225. package/dist/features/auth/permissions/constants.js +18 -0
  226. package/dist/features/blog/components/BlogPostView.js +1 -1
  227. package/dist/features/catalogue/components/AdminCatalogueApprovalsView.js +1 -1
  228. package/dist/features/categories/api/[id]/description-template/route.js +107 -1
  229. package/dist/features/categories/components/BrandDetailPageView.js +2 -2
  230. package/dist/features/categories/components/BreadcrumbTrail.js +7 -1
  231. package/dist/features/categories/components/BundleDetailView.js +1 -1
  232. package/dist/features/categories/components/CategoryDetailPageView.js +2 -2
  233. package/dist/features/categories/components/CategoryForm.js +12 -1
  234. package/dist/features/categories/components/CategoryTemplatePicker.js +38 -5
  235. package/dist/features/categories/repository/market-price-index.repository.d.ts +123 -0
  236. package/dist/features/categories/repository/market-price-index.repository.js +105 -0
  237. package/dist/features/categories/repository/price-observation.repository.d.ts +166 -0
  238. package/dist/features/categories/repository/price-observation.repository.js +132 -0
  239. package/dist/features/events/components/EventSaleBanner.js +1 -1
  240. package/dist/features/events/components/EventStatusBadge.js +1 -1
  241. package/dist/features/faq/components/FAQAccordion.js +2 -1
  242. package/dist/features/faq/components/FAQCategorySidebar.js +2 -2
  243. package/dist/features/faq/components/FAQHelpfulButtons.js +4 -4
  244. package/dist/features/faq/components/RelatedFAQs.js +1 -1
  245. package/dist/features/filters/FilterFacetSection.js +1 -1
  246. package/dist/features/filters/SwitchFilter.js +1 -1
  247. package/dist/features/gateway-events/repository/gateway-events.repository.d.ts +117 -0
  248. package/dist/features/gateway-events/repository/gateway-events.repository.js +274 -0
  249. package/dist/features/gateway-events/schemas/firestore.d.ts +217 -0
  250. package/dist/features/gateway-events/schemas/firestore.js +125 -0
  251. package/dist/features/gateway-events/schemas/index.d.ts +81 -0
  252. package/dist/features/gateway-events/schemas/index.js +79 -0
  253. package/dist/features/grouped/components/GroupedListingDetailView.js +1 -1
  254. package/dist/features/homepage/components/AnnouncementBar.js +37 -1
  255. package/dist/features/homepage/components/AnnouncementBar.style.css +30 -0
  256. package/dist/features/homepage/components/BrandsSection.js +1 -1
  257. package/dist/features/homepage/components/CTABannerSection.js +1 -1
  258. package/dist/features/homepage/components/FeaturedBundlesSection.js +14 -24
  259. package/dist/features/homepage/components/HeroCarousel.js +2 -2
  260. package/dist/features/homepage/components/SocialPostCard.js +1 -1
  261. package/dist/features/inventory/components/AdminMoveLedgerView.d.ts +7 -0
  262. package/dist/features/inventory/components/AdminMoveLedgerView.js +157 -0
  263. package/dist/features/inventory/components/AdminStorageBoxesView.d.ts +11 -0
  264. package/dist/features/inventory/components/AdminStorageBoxesView.js +168 -0
  265. package/dist/features/inventory/components/ReceiveStationView.d.ts +39 -0
  266. package/dist/features/inventory/components/ReceiveStationView.js +413 -0
  267. package/dist/features/inventory/components/operator-message.d.ts +29 -0
  268. package/dist/features/inventory/components/operator-message.js +41 -0
  269. package/dist/features/inventory/index.d.ts +13 -0
  270. package/dist/features/inventory/index.js +28 -0
  271. package/dist/features/inventory/repository/inventory-movements.repository.d.ts +97 -0
  272. package/dist/features/inventory/repository/inventory-movements.repository.js +186 -0
  273. package/dist/features/inventory/repository/inventory-placements.repository.d.ts +179 -0
  274. package/dist/features/inventory/repository/inventory-placements.repository.js +345 -0
  275. package/dist/features/inventory/repository/storage-boxes.repository.d.ts +83 -0
  276. package/dist/features/inventory/repository/storage-boxes.repository.js +127 -0
  277. package/dist/features/inventory/schemas/firestore.d.ts +197 -0
  278. package/dist/features/inventory/schemas/firestore.js +159 -0
  279. package/dist/features/layout/AppLayoutShell.d.ts +12 -1
  280. package/dist/features/layout/AppLayoutShell.js +25 -12
  281. package/dist/features/layout/AutoBreadcrumbs.js +162 -3
  282. package/dist/features/layout/AutoBreadcrumbs.style.css +45 -0
  283. package/dist/features/layout/BottomNavbar.d.ts +1 -1
  284. package/dist/features/layout/BottomNavbar.js +2 -2
  285. package/dist/features/media/MediaAvatar.js +1 -1
  286. package/dist/features/media/MediaImage.d.ts +24 -4
  287. package/dist/features/media/MediaImage.js +44 -4
  288. package/dist/features/media/MediaLightbox.js +1 -1
  289. package/dist/features/media/modals/ImageCropModal.js +1 -1
  290. package/dist/features/media/modals/ImageEditor.js +1 -1
  291. package/dist/features/media/upload/MediaUploadField.js +91 -3
  292. package/dist/features/obligations/constants/policies.d.ts +92 -0
  293. package/dist/features/obligations/constants/policies.js +378 -0
  294. package/dist/features/obligations/repository/obligations.repository.d.ts +68 -0
  295. package/dist/features/obligations/repository/obligations.repository.js +158 -0
  296. package/dist/features/obligations/schemas/firestore.d.ts +160 -0
  297. package/dist/features/obligations/schemas/firestore.js +87 -0
  298. package/dist/features/orders/actions/order-actions.d.ts +43 -4
  299. package/dist/features/orders/actions/order-actions.js +60 -3
  300. package/dist/features/orders/actions/refund-actions.js +70 -10
  301. package/dist/features/orders/components/OrderCompletionPanel.d.ts +27 -0
  302. package/dist/features/orders/components/OrderCompletionPanel.js +192 -0
  303. package/dist/features/orders/components/OrdersList.js +2 -2
  304. package/dist/features/orders/components/RefundHistoryTable.js +3 -1
  305. package/dist/features/orders/components/index.d.ts +2 -0
  306. package/dist/features/orders/components/index.js +6 -0
  307. package/dist/features/orders/constants/transitions.d.ts +59 -0
  308. package/dist/features/orders/constants/transitions.js +104 -0
  309. package/dist/features/orders/repository/orders.repository.d.ts +109 -6
  310. package/dist/features/orders/repository/orders.repository.js +464 -21
  311. package/dist/features/orders/schemas/firestore.d.ts +270 -0
  312. package/dist/features/orders/schemas/firestore.js +24 -0
  313. package/dist/features/orders/schemas/index.d.ts +61 -0
  314. package/dist/features/orders/schemas/index.js +22 -0
  315. package/dist/features/orders/types/index.d.ts +79 -0
  316. package/dist/features/parts/components/PartsIndexPageView.js +8 -1
  317. package/dist/features/payment-cases/repository/payment-cases.repository.d.ts +170 -0
  318. package/dist/features/payment-cases/repository/payment-cases.repository.js +342 -0
  319. package/dist/features/payment-cases/schemas/firestore.d.ts +229 -0
  320. package/dist/features/payment-cases/schemas/firestore.js +105 -0
  321. package/dist/features/payment-cases/schemas/resolve-form.d.ts +97 -0
  322. package/dist/features/payment-cases/schemas/resolve-form.js +114 -0
  323. package/dist/features/payments/repository/payout.repository.d.ts +0 -1
  324. package/dist/features/payments/repository/payout.repository.js +0 -1
  325. package/dist/features/points/repository/points.repository.d.ts +122 -0
  326. package/dist/features/points/repository/points.repository.js +304 -1
  327. package/dist/features/points/schemas/adjust-form.d.ts +58 -0
  328. package/dist/features/points/schemas/adjust-form.js +63 -0
  329. package/dist/features/points/schemas/firestore.d.ts +108 -1
  330. package/dist/features/points/schemas/firestore.js +84 -0
  331. package/dist/features/pre-orders/components/MarketplacePreorderCard.js +3 -3
  332. package/dist/features/pre-orders/components/PreOrderBadge.js +1 -1
  333. package/dist/features/pre-orders/components/PreOrderDetailPageView.js +1 -1
  334. package/dist/features/price-index/components/PriceIndexToolView.d.ts +23 -4
  335. package/dist/features/price-index/components/PriceIndexToolView.js +71 -6
  336. package/dist/features/price-index/market-stats.d.ts +82 -0
  337. package/dist/features/price-index/market-stats.js +230 -0
  338. package/dist/features/print/components/BoxLabelPrintView.d.ts +25 -0
  339. package/dist/features/print/components/BoxLabelPrintView.js +66 -0
  340. package/dist/features/print/components/LabelProfilePicker.d.ts +13 -0
  341. package/dist/features/print/components/LabelProfilePicker.js +38 -0
  342. package/dist/features/print/components/labels.d.ts +117 -0
  343. package/dist/features/print/components/labels.js +258 -0
  344. package/dist/features/print/components/labels.style.css +46 -0
  345. package/dist/features/print/index.d.ts +6 -0
  346. package/dist/features/print/index.js +16 -0
  347. package/dist/features/products/columns/productTableColumns.js +5 -4
  348. package/dist/features/products/components/MarketplaceBundleCard.js +2 -2
  349. package/dist/features/products/components/MarketplacePrizeDrawCard.js +6 -6
  350. package/dist/features/products/components/PrizeDrawCollage.js +1 -1
  351. package/dist/features/products/components/PrizeDrawItemsEditor.js +1 -1
  352. package/dist/features/products/components/PrizeRevealModal.js +2 -2
  353. package/dist/features/products/components/ProductDetailPageView.js +18 -4
  354. package/dist/features/products/components/ProductForm.js +34 -23
  355. package/dist/features/products/components/ProductGrid.js +86 -62
  356. package/dist/features/products/constants/card-badges.d.ts +97 -0
  357. package/dist/features/products/constants/card-badges.js +100 -0
  358. package/dist/features/products/repository/products.repository.js +34 -1
  359. package/dist/features/products/schemas/firestore.d.ts +46 -3
  360. package/dist/features/products/schemas/firestore.js +21 -0
  361. package/dist/features/promotions/components/PromotionsView.js +1 -1
  362. package/dist/features/promotions/repository/claimed-coupons.repository.d.ts +32 -0
  363. package/dist/features/promotions/repository/claimed-coupons.repository.js +58 -0
  364. package/dist/features/promotions/schemas/firestore.js +30 -3
  365. package/dist/features/promotions/schemas/index.d.ts +6 -6
  366. package/dist/features/protection-claims/repository/protection-claims.repository.d.ts +54 -0
  367. package/dist/features/protection-claims/repository/protection-claims.repository.js +174 -0
  368. package/dist/features/protection-claims/schemas/firestore.d.ts +168 -0
  369. package/dist/features/protection-claims/schemas/firestore.js +130 -0
  370. package/dist/features/reviews/actions/review-actions.js +3 -2
  371. package/dist/features/reviews/components/ReviewModal.js +1 -1
  372. package/dist/features/reviews/components/ReviewsList.js +1 -1
  373. package/dist/features/reviews/schemas/firestore.d.ts +20 -1
  374. package/dist/features/scams/constants/scam-types.d.ts +2 -0
  375. package/dist/features/scams/constants/scam-types.js +18 -0
  376. package/dist/features/scams/schemas/report-form.d.ts +3 -3
  377. package/dist/features/scams/schemas/report-form.js +26 -1
  378. package/dist/features/search/components/Search.js +9 -9
  379. package/dist/features/search/components/SearchResultsClient.js +18 -3
  380. package/dist/features/search/components/SearchResultsPageView.d.ts +1 -6
  381. package/dist/features/search/components/SearchResultsPageView.js +2 -2
  382. package/dist/features/seller/actions/offer-actions.js +16 -1
  383. package/dist/features/seller/actions/seller-actions.js +14 -3
  384. package/dist/features/seller/components/CategoryInlineSelect.js +75 -11
  385. package/dist/features/seller/components/PrintCenterView.d.ts +5 -0
  386. package/dist/features/seller/components/PrintCenterView.js +41 -30
  387. package/dist/features/seller/components/SellerOrdersView.js +1 -1
  388. package/dist/features/seller/components/SellerPayoutRequestView.js +16 -5
  389. package/dist/features/seller/components/SellerPayoutSettingsView.js +1 -1
  390. package/dist/features/seller/components/SellerProductShell.d.ts +16 -0
  391. package/dist/features/seller/components/SellerProductShell.js +123 -13
  392. package/dist/features/seller/components/SellerReviewsView.js +4 -2
  393. package/dist/features/seller/components/SellerSidebar.js +1 -1
  394. package/dist/features/seller/schemas/coupon-form.d.ts +26 -0
  395. package/dist/features/seller/schemas/coupon-form.js +27 -0
  396. package/dist/features/seller/schemas/product-form.d.ts +3 -0
  397. package/dist/features/seller/schemas/product-form.js +5 -0
  398. package/dist/features/shell/FormShell.js +2 -2
  399. package/dist/features/shell/QuickFormDrawer.js +14 -0
  400. package/dist/features/shell/SectionForm.d.ts +19 -0
  401. package/dist/features/shell/SectionForm.js +133 -9
  402. package/dist/features/shell/SectionForm.style.css +54 -0
  403. package/dist/features/shipments/components/AdminShipmentsView.js +16 -1
  404. package/dist/features/shipments/repository/shipment-items.repository.d.ts +9 -0
  405. package/dist/features/shipments/repository/shipment-items.repository.js +14 -0
  406. package/dist/features/site-settings/components/ActionPermissionsManager.js +1 -1
  407. package/dist/features/sticker-trace/components/StickerCommitPanel.js +1 -1
  408. package/dist/features/store-extensions/schemas/report-review-form.d.ts +8 -8
  409. package/dist/features/stores/components/StoreGuideHubView.js +1 -1
  410. package/dist/features/stores/components/StoreHeader.js +1 -1
  411. package/dist/features/support/schemas/firestore.d.ts +73 -2
  412. package/dist/features/support/schemas/firestore.js +59 -0
  413. package/dist/index.d.ts +16 -24
  414. package/dist/index.js +38 -47
  415. package/dist/next/ErrorBoundary.js +1 -1
  416. package/dist/next/routing/route-map.d.ts +24 -0
  417. package/dist/next/routing/route-map.js +40 -0
  418. package/dist/providers/payment-manual/index.d.ts +15 -1
  419. package/dist/providers/payment-manual/index.js +37 -0
  420. package/dist/providers/payment-phonepe/index.d.ts +14 -1
  421. package/dist/providers/payment-phonepe/index.js +36 -0
  422. package/dist/providers/payment-razorpay/index.d.ts +44 -1
  423. package/dist/providers/payment-razorpay/index.js +99 -0
  424. package/dist/providers/shipping-shiprocket/index.d.ts +18 -0
  425. package/dist/providers/shipping-shiprocket/index.js +18 -0
  426. package/dist/schemas/registry.d.ts +44 -11
  427. package/dist/server-entry.d.ts +2 -0
  428. package/dist/server-entry.js +27 -0
  429. package/dist/server.d.ts +43 -0
  430. package/dist/server.js +98 -0
  431. package/dist/styles.css +418 -149
  432. package/dist/tailwind-utilities.css +1 -1
  433. package/dist/tokens/themes/default-dark.js +15 -12
  434. package/dist/tokens/themes/default-light.js +14 -11
  435. package/dist/tokens/tokens.css +106 -23
  436. package/dist/ui/DataTable.style.css +4 -4
  437. package/dist/ui/columns/cell-renderers.d.ts +20 -0
  438. package/dist/ui/columns/cell-renderers.js +26 -1
  439. package/dist/ui/columns/index.d.ts +1 -1
  440. package/dist/ui/columns/index.js +4 -1
  441. package/dist/ui/components/Badge.style.css +1 -1
  442. package/dist/ui/components/Breadcrumb.js +19 -1
  443. package/dist/ui/components/BulkActionBar.style.css +4 -4
  444. package/dist/ui/components/Button.style.css +1 -1
  445. package/dist/ui/components/Checkbox.style.css +1 -1
  446. package/dist/ui/components/FallbackShell.js +1 -1
  447. package/dist/ui/components/Form.d.ts +14 -1
  448. package/dist/ui/components/Form.js +2 -2
  449. package/dist/ui/components/FormField.js +18 -4
  450. package/dist/ui/components/FormField.style.css +43 -3
  451. package/dist/ui/components/GatedPrice.style.css +1 -1
  452. package/dist/ui/components/IconButton.style.css +1 -1
  453. package/dist/ui/components/Input.js +22 -1
  454. package/dist/ui/components/Input.style.css +38 -22
  455. package/dist/ui/components/ListingFilterDrawer.js +1 -1
  456. package/dist/ui/components/ListingToolbar.js +1 -1
  457. package/dist/ui/components/PaginatedSelect.js +0 -0
  458. package/dist/ui/components/Radio.style.css +1 -1
  459. package/dist/ui/components/Repeater.d.ts +53 -0
  460. package/dist/ui/components/Repeater.js +7 -0
  461. package/dist/ui/components/RichTextEditor.d.ts +20 -1
  462. package/dist/ui/components/RichTextEditor.js +23 -2
  463. package/dist/ui/components/Select.style.css +7 -21
  464. package/dist/ui/components/Semantic.js +27 -3
  465. package/dist/ui/components/TagInput.style.css +4 -9
  466. package/dist/ui/components/Textarea.style.css +4 -18
  467. package/dist/ui/components/Toggle.style.css +1 -1
  468. package/dist/ui/components/Typography.d.ts +3 -3
  469. package/dist/ui/components/Typography.js +15 -15
  470. package/dist/ui/components/ViewToggle.style.css +4 -4
  471. package/dist/ui/forms/AddAnotherToggle.d.ts +16 -0
  472. package/dist/ui/forms/AddAnotherToggle.js +56 -0
  473. package/dist/ui/forms/ColorPickerField.js +15 -2
  474. package/dist/ui/forms/FieldCheckbox.js +15 -2
  475. package/dist/ui/forms/FieldInput.d.ts +22 -1
  476. package/dist/ui/forms/FieldInput.js +19 -5
  477. package/dist/ui/forms/FieldLabel.d.ts +35 -0
  478. package/dist/ui/forms/FieldLabel.js +70 -0
  479. package/dist/ui/forms/FieldSelect.d.ts +4 -1
  480. package/dist/ui/forms/FieldSelect.js +27 -5
  481. package/dist/ui/forms/FieldTextarea.d.ts +4 -1
  482. package/dist/ui/forms/FieldTextarea.js +22 -5
  483. package/dist/ui/forms/FormShell.d.ts +17 -1
  484. package/dist/ui/forms/FormShell.js +36 -2
  485. package/dist/ui/forms/FormShell.style.css +41 -33
  486. package/dist/ui/forms/index.d.ts +5 -1
  487. package/dist/ui/forms/index.js +2 -0
  488. package/dist/ui/icons/icon-registry.d.ts +1 -0
  489. package/dist/ui/icons/icon-registry.js +17 -1
  490. package/dist/utils/id-generators.d.ts +80 -0
  491. package/dist/utils/id-generators.js +93 -0
  492. package/dist/utils/search-txt-builders.d.ts +12 -0
  493. package/dist/utils/search-txt-builders.js +17 -0
  494. package/package.json +1 -1
  495. package/scripts/seed-cli.mjs +106 -5
  496. package/dist/features/loyalty/api/admin/loyalty/grant/route.d.ts +0 -12
  497. package/dist/features/loyalty/api/admin/loyalty/grant/route.js +0 -38
  498. package/dist/features/loyalty/api/balance/route.d.ts +0 -12
  499. package/dist/features/loyalty/api/balance/route.js +0 -41
  500. package/dist/features/loyalty/api/earn/route.d.ts +0 -12
  501. package/dist/features/loyalty/api/earn/route.js +0 -38
  502. package/dist/features/loyalty/api/history/route.d.ts +0 -12
  503. package/dist/features/loyalty/api/history/route.js +0 -58
  504. package/dist/features/loyalty/api/redeem/route.d.ts +0 -12
  505. package/dist/features/loyalty/api/redeem/route.js +0 -38
  506. package/dist/features/loyalty/columns/index.d.ts +0 -17
  507. package/dist/features/loyalty/columns/index.js +0 -40
  508. package/dist/features/loyalty/components/CoinsDisplay.d.ts +0 -12
  509. package/dist/features/loyalty/components/CoinsDisplay.js +0 -10
  510. package/dist/features/loyalty/components/index.d.ts +0 -1
  511. package/dist/features/loyalty/components/index.js +0 -1
  512. package/dist/features/loyalty/hooks/useLoyaltyBalance.d.ts +0 -4
  513. package/dist/features/loyalty/hooks/useLoyaltyBalance.js +0 -25
  514. package/dist/features/loyalty/index.d.ts +0 -6
  515. package/dist/features/loyalty/index.js +0 -6
  516. package/dist/features/loyalty/manifest.d.ts +0 -2
  517. package/dist/features/loyalty/manifest.js +0 -16
  518. package/dist/features/loyalty/messages/en.json +0 -19
  519. package/dist/features/loyalty/repository/loyalty.repository.d.ts +0 -8
  520. package/dist/features/loyalty/repository/loyalty.repository.js +0 -22
  521. package/dist/features/loyalty/schemas/index.d.ts +0 -85
  522. package/dist/features/loyalty/schemas/index.js +0 -40
  523. package/dist/features/loyalty/server.d.ts +0 -6
  524. package/dist/features/loyalty/server.js +0 -6
  525. package/dist/features/loyalty/types/index.d.ts +0 -31
  526. package/dist/features/loyalty/types/index.js +0 -1
  527. package/dist/features/loyalty/types/loyalty-config.d.ts +0 -7
  528. package/dist/features/loyalty/types/loyalty-config.js +0 -1
  529. package/dist/features/loyalty/types/loyalty-math.d.ts +0 -9
  530. package/dist/features/loyalty/types/loyalty-math.js +0 -27
  531. package/dist/features/products/components/ProductOwnerQuickActions.d.ts +0 -11
  532. package/dist/features/products/components/ProductOwnerQuickActions.js +0 -120
@@ -0,0 +1,614 @@
1
+ /**
2
+ * Payment reconciliation — R1 through R4.
3
+ *
4
+ * ## What this is for
5
+ *
6
+ * Every other check in this system starts from OUR records and asks whether
7
+ * they are internally consistent. R2 starts from the **gateway's** records and
8
+ * asks whether we have an order at all — and that is the only direction that
9
+ * could have detected the 2026-10-21 incident, where our side had nothing to be
10
+ * inconsistent with. ₹2,511.80 was captured, no order existed, and the first
11
+ * signal was the buyer asking where their order was. **Had R2 existed it would
12
+ * have been a case on a dashboard within a day.**
13
+ *
14
+ * ## Cost, honestly
15
+ *
16
+ * One new Cloud Scheduler job (`paymentReconcile`, daily 05:30 UTC). Scheduler
17
+ * bills **per registered job** — 3 free, 28 already registered — so this is
18
+ * ~$0.10/month. It is NOT folded into `countersReconcile`, which already runs
19
+ * at its 540 s ceiling, nor into `autoPayoutEligibility`: `priceIndexRollup`'s
20
+ * own comment sets the precedent that a job should be able to fail without
21
+ * taking an unrelated one down with it.
22
+ *
23
+ * R1 is different and is folded into the existing `paymentWindowTimeout`
24
+ * (every 5 minutes) for free, because a stuck event needs a tight loop and the
25
+ * query is one indexed read.
26
+ *
27
+ * Reads per daily run, pre-launch: well under 50 against a 50,000/day free
28
+ * quota. **The count is logged on every run** — the same discipline
29
+ * `searchIndexBuild` uses — so a regression to always-full is visible before it
30
+ * is a quota wall. Root Cause #92 and the 2026-10-09 HTTP 402 are both what
31
+ * happens when nobody logs the number.
32
+ */
33
+ import { getProviders } from "../../../../contracts/registry.js";
34
+ import { normalizeError } from "../../../../errors/normalize.js";
35
+ import { gatewayEventsRepository } from "../../../../features/gateway-events/repository/gateway-events.repository.js";
36
+ import { GATEWAY_EVENT_MAX_ATTEMPTS, } from "../../../../features/gateway-events/schemas/firestore.js";
37
+ import { paymentCasesRepository } from "../../../../features/payment-cases/repository/payment-cases.repository.js";
38
+ import { orderRepository } from "../../../../features/orders/repository/orders.repository.js";
39
+ import { REFUND_CLAIMS_COLLECTION } from "../../../../features/orders/schemas/firestore.js";
40
+ import { reconcileDisputes } from "../../features/payment-cases/disputes.js";
41
+ import { refundableBase, committedRefundTotal, MONEY_EPSILON, } from "../../../shared/checkout/refundable-base.js";
42
+ /** How far back R2 looks. Two days of overlap, so a run that fails is covered by the next. */
43
+ const PAYMENT_WINDOW_HOURS = 48;
44
+ /** A refund claim unsettled for longer than this is a crash between call and record. */
45
+ const STALE_CLAIM_HOURS = 1;
46
+ /** Escalate a case this close to its external deadline. */
47
+ const DUE_SOON_HOURS = 72;
48
+ /* ───────────────────────── R1: events we received and lost ───────────────── */
49
+ /**
50
+ * Re-dispatch gateway events whose lease lapsed before they settled.
51
+ *
52
+ * 🛑 This catches "we received it and lost it" with a **5-minute** window
53
+ * instead of never. Before the inbox existed there was no artefact at all; the
54
+ * inbox alone makes the loss *visible*, and this is what makes it *recoverable*.
55
+ *
56
+ * Exported separately so `paymentWindowTimeout` can fold it in — a stuck
57
+ * payment event should not wait for a daily job.
58
+ *
59
+ * 🛑 It does NOT re-run the placement itself. Re-dispatching from a job would
60
+ * mean duplicating the webhook route's whole switch (and its RTDB signalling)
61
+ * in a second place, which is Root Cause #75's shape on the dispatch axis.
62
+ * Instead it RESETS the lease so the gateway's own redelivery — or an operator
63
+ * replaying from the admin page in Phase 8 — is accepted rather than
64
+ * short-circuited as a duplicate, and escalates to a case once the attempt
65
+ * budget is spent. The gateway retries for hours; this just stops the inbox
66
+ * from refusing the retry.
67
+ */
68
+ export async function reconcileStuckGatewayEvents(ctx) {
69
+ let redispatched = 0;
70
+ let escalated = 0;
71
+ let stuck = [];
72
+ try {
73
+ stuck = await gatewayEventsRepository().listStuck(ctx.now, 20);
74
+ }
75
+ catch (err) {
76
+ void normalizeError(err);
77
+ ctx.logger.error("R1: stuck-event query failed", { err });
78
+ return { redispatched: 0, escalated: 0, reads: 0 };
79
+ }
80
+ for (const event of stuck) {
81
+ try {
82
+ if (event.attempts >= GATEWAY_EVENT_MAX_ATTEMPTS) {
83
+ await paymentCasesRepository().openOrObserve({
84
+ kind: "unprocessed_event",
85
+ subjectId: event.id,
86
+ severity: "error",
87
+ title: `Webhook never processed after ${event.attempts} attempts — ${event.rawEvent}`,
88
+ detail: [
89
+ `Gateway: ${event.gateway}`,
90
+ `Event: ${event.rawEvent} (${event.kind})`,
91
+ `Attempts: ${event.attempts}`,
92
+ event.gatewayOrderId ? `Gateway order: ${event.gatewayOrderId}` : null,
93
+ event.gatewayPaymentId ? `Gateway payment: ${event.gatewayPaymentId}` : null,
94
+ event.lastError ? `Last error: ${event.lastError}` : null,
95
+ "",
96
+ "The event body is preserved on the gatewayEvents row. If this is a",
97
+ "`paid` event, a buyer may have been charged with no order.",
98
+ ]
99
+ .filter((l) => l !== null)
100
+ .join("\n"),
101
+ entityType: "event",
102
+ entityId: event.id,
103
+ ...(typeof event.amount === "number" ? { amountAtRisk: event.amount } : {}),
104
+ gatewayEventIds: [event.id],
105
+ ...(event.gatewayPaymentId ? { gatewayPaymentId: event.gatewayPaymentId } : {}),
106
+ now: ctx.now,
107
+ });
108
+ /*
109
+ * Settled `failed` + `terminal` so it leaves the stuck query. Without
110
+ * this the same row is re-examined every 5 minutes forever — which
111
+ * `openOrObserve` would dedupe into one case, but at the cost of a
112
+ * pointless read and write every tick for the life of the collection.
113
+ */
114
+ await gatewayEventsRepository().settle(event.id, {
115
+ status: "failed",
116
+ terminal: true,
117
+ lastError: `attempt budget exhausted (${event.attempts})`,
118
+ now: ctx.now,
119
+ });
120
+ escalated += 1;
121
+ continue;
122
+ }
123
+ await gatewayEventsRepository().renewLease(event.id, event.attempts, ctx.now);
124
+ redispatched += 1;
125
+ }
126
+ catch (err) {
127
+ void normalizeError(err);
128
+ ctx.logger.error("R1: could not handle a stuck event — continuing", { id: event.id });
129
+ }
130
+ }
131
+ if (stuck.length > 0) {
132
+ ctx.logger.warn("R1: stuck gateway events found", {
133
+ found: stuck.length,
134
+ redispatched,
135
+ escalated,
136
+ });
137
+ }
138
+ return { redispatched, escalated, reads: stuck.length };
139
+ }
140
+ /* ──────────────── R2: the gateway has it — do we? ───────────────────────── */
141
+ async function reconcileGatewayPayments(ctx) {
142
+ const payment = getProviders().payment;
143
+ let examined = 0;
144
+ let missing = 0;
145
+ let amountMismatch = 0;
146
+ let refundMismatch = 0;
147
+ let reads = 0;
148
+ if (!payment) {
149
+ ctx.logger.warn("R2: no payment provider registered — cannot reconcile");
150
+ return { examined, missing, amountMismatch, refundMismatch, reads };
151
+ }
152
+ const from = new Date(ctx.now.getTime() - PAYMENT_WINDOW_HOURS * 3600000);
153
+ let records;
154
+ try {
155
+ records = await payment.listPayments({ from, to: ctx.now, limit: 100 });
156
+ }
157
+ catch (err) {
158
+ void normalizeError(err);
159
+ ctx.logger.error("R2: gateway payment listing failed", { err });
160
+ return { examined, missing, amountMismatch, refundMismatch, reads };
161
+ }
162
+ /*
163
+ * 🛑 An empty list means "NO INFORMATION" on the manual and PhonePe
164
+ * providers, which cannot enumerate payments at all — and concluding "nothing
165
+ * is missing" from a provider that cannot answer is the false-clean this
166
+ * whole job exists to avoid. So nothing is inferred from emptiness; the run
167
+ * simply records that it learned nothing.
168
+ */
169
+ if (records.length === 0) {
170
+ ctx.logger.info("R2: provider returned no payments — no conclusion drawn", {
171
+ gateway: payment.name,
172
+ });
173
+ return { examined, missing, amountMismatch, refundMismatch, reads };
174
+ }
175
+ for (const record of records) {
176
+ /*
177
+ * Only CAPTURED payments. An `authorized`-but-uncaptured or `failed`
178
+ * payment having no order is correct, not a defect — and opening a case for
179
+ * every declined card would bury the real findings. Anything unrecognised
180
+ * is treated as not-captured, which fails toward silence.
181
+ */
182
+ if (record.status !== "captured" && record.status !== "refunded")
183
+ continue;
184
+ examined += 1;
185
+ try {
186
+ /*
187
+ * Keyed on the gateway ORDER id, because that is what this codebase
188
+ * stores as `order.paymentId` — see the Razorpay provider's own class
189
+ * comment about keying every lookup on one id. Falls back to the payment
190
+ * id for gateways where they coincide.
191
+ */
192
+ const lookupId = record.orderId ?? record.id;
193
+ const orders = await orderRepository.findByPaymentId(lookupId);
194
+ reads += 1;
195
+ if (orders.length === 0) {
196
+ missing += 1;
197
+ await paymentCasesRepository().openOrObserve({
198
+ kind: "missing_order",
199
+ subjectId: record.id,
200
+ severity: "error",
201
+ title: `Captured payment with no order — ${record.id}`,
202
+ detail: [
203
+ `🛑 The gateway says this payment was captured and we have no order for it.`,
204
+ `This is the 2026-10-21 shape: the buyer has been charged.`,
205
+ "",
206
+ `Gateway: ${record.gateway}`,
207
+ `Payment: ${record.id}`,
208
+ record.orderId ? `Gateway order: ${record.orderId}` : null,
209
+ `Amount: ${record.amount} ${record.currency}`,
210
+ record.capturedAt ? `Captured: ${record.capturedAt.toISOString()}` : null,
211
+ "",
212
+ `Check gatewayEvents for ${record.orderId ?? record.id} to see what the`,
213
+ `webhook did, and gatewayOrderClaims for a claim that was never released.`,
214
+ ]
215
+ .filter((l) => l !== null)
216
+ .join("\n"),
217
+ entityType: "payment",
218
+ entityId: record.id,
219
+ amountAtRisk: record.amount,
220
+ gatewayPaymentId: record.id,
221
+ now: ctx.now,
222
+ });
223
+ continue;
224
+ }
225
+ for (const order of orders) {
226
+ /*
227
+ * Amount check. Compared against the order's own recorded total rather
228
+ * than recomputed from its lines — recomputing would re-derive the
229
+ * pricing pipeline here and disagree with it the day a rate changes,
230
+ * which is a sixth copy of the money rule (Root Cause #75).
231
+ */
232
+ const recorded = order.totalPrice ?? 0;
233
+ if (Math.abs(recorded - record.amount) > MONEY_EPSILON) {
234
+ amountMismatch += 1;
235
+ await paymentCasesRepository().openOrObserve({
236
+ kind: "amount_mismatch",
237
+ subjectId: `${record.id}:${order.id}`,
238
+ severity: "error",
239
+ title: `Captured ${record.amount} but recorded ${recorded} — ${order.id}`,
240
+ detail: [
241
+ `The gateway captured a different amount from the one the order records.`,
242
+ `Either the buyer was over-charged or the order under-states what was paid.`,
243
+ "",
244
+ `Gateway: ${record.gateway}`,
245
+ `Payment: ${record.id}`,
246
+ `Order: ${order.id}`,
247
+ `Captured: ${record.amount} ${record.currency}`,
248
+ `Order total: ${recorded}`,
249
+ `Difference: ${(record.amount - recorded).toFixed(2)}`,
250
+ ].join("\n"),
251
+ entityType: "order",
252
+ entityId: order.id,
253
+ amountAtRisk: Math.abs(record.amount - recorded),
254
+ orderIds: [order.id],
255
+ gatewayPaymentId: record.id,
256
+ now: ctx.now,
257
+ });
258
+ }
259
+ /*
260
+ * Refund check — the gateway's `amount_refunded` against our own
261
+ * non-failed refund events. `committedRefundTotal` is the same helper
262
+ * the refund cap uses, so "what we think we refunded" has ONE
263
+ * definition rather than a second one written here.
264
+ */
265
+ const ours = committedRefundTotal(order);
266
+ if (Math.abs(ours - record.amountRefunded) > MONEY_EPSILON) {
267
+ refundMismatch += 1;
268
+ await paymentCasesRepository().openOrObserve({
269
+ kind: "refund_mismatch",
270
+ subjectId: `${record.id}:refunds`,
271
+ severity: record.amountRefunded > ours ? "error" : "warning",
272
+ title: `Refund total disagrees with the gateway — ${order.id}`,
273
+ detail: [
274
+ record.amountRefunded > ours
275
+ ? `🛑 The gateway has refunded MORE than our records show. Money has left that we did not record.`
276
+ : `Our records show more refunded than the gateway did — a refund may have been reserved and never completed.`,
277
+ "",
278
+ `Gateway: ${record.gateway}`,
279
+ `Payment: ${record.id}`,
280
+ `Order: ${order.id}`,
281
+ `Gateway refunded: ${record.amountRefunded}`,
282
+ `Our committed refunds: ${ours}`,
283
+ `Refundable base: ${refundableBase(order)}`,
284
+ record.refundStatus ? `Gateway refund status: ${record.refundStatus}` : null,
285
+ ]
286
+ .filter((l) => l !== null)
287
+ .join("\n"),
288
+ entityType: "order",
289
+ entityId: order.id,
290
+ amountAtRisk: Math.abs(record.amountRefunded - ours),
291
+ orderIds: [order.id],
292
+ gatewayPaymentId: record.id,
293
+ now: ctx.now,
294
+ });
295
+ }
296
+ }
297
+ }
298
+ catch (err) {
299
+ void normalizeError(err);
300
+ ctx.logger.error("R2: one payment failed to reconcile — continuing", {
301
+ paymentId: record.id,
302
+ });
303
+ }
304
+ }
305
+ return { examined, missing, amountMismatch, refundMismatch, reads };
306
+ }
307
+ /* ──────────────── R3: refund claimed, never settled ─────────────────────── */
308
+ /**
309
+ * A `refundClaims` row still `claimed` an hour later is a crash between
310
+ * "provider called" and "entry settled".
311
+ *
312
+ * 🛑 It is NOT resolved by guessing. The money may have left; the only
313
+ * authoritative answer is the gateway's, which a human reads. So this opens a
314
+ * case and stops — a sweep that decided for itself whether a refund had
315
+ * happened would be the double-refund generator the idempotency claim exists
316
+ * to prevent.
317
+ */
318
+ async function reconcileStaleRefundClaims(ctx) {
319
+ const cutoff = new Date(ctx.now.getTime() - STALE_CLAIM_HOURS * 3600000);
320
+ let stale = 0;
321
+ try {
322
+ const snap = await ctx.db
323
+ .collection(REFUND_CLAIMS_COLLECTION)
324
+ .where("state", "==", "claimed")
325
+ .where("claimedAt", "<", cutoff)
326
+ .limit(50)
327
+ .get();
328
+ for (const doc of snap.docs) {
329
+ const data = doc.data();
330
+ stale += 1;
331
+ await paymentCasesRepository().openOrObserve({
332
+ kind: "refund_mismatch",
333
+ subjectId: `claim:${doc.id}`,
334
+ severity: "error",
335
+ title: `Refund claimed but never settled — ${doc.id}`,
336
+ detail: [
337
+ `A refund was claimed over ${STALE_CLAIM_HOURS}h ago and never recorded an outcome.`,
338
+ `That is a crash between calling the gateway and writing the result, so`,
339
+ `🛑 THE MONEY MAY HAVE LEFT. Read the gateway's refund list for this`,
340
+ `payment — do not assume either way, and do not retry the refund.`,
341
+ "",
342
+ `Claim: ${doc.id}`,
343
+ data.orderId ? `Order: ${data.orderId}` : null,
344
+ typeof data.amount === "number" ? `Amount: ${data.amount}` : null,
345
+ data.claimedBy ? `Claimed by: ${data.claimedBy}` : null,
346
+ ]
347
+ .filter((l) => l !== null)
348
+ .join("\n"),
349
+ entityType: "refund",
350
+ entityId: doc.id,
351
+ ...(typeof data.amount === "number" ? { amountAtRisk: data.amount } : {}),
352
+ ...(data.orderId ? { orderIds: [data.orderId] } : {}),
353
+ now: ctx.now,
354
+ });
355
+ }
356
+ return { stale, reads: snap.size };
357
+ }
358
+ catch (err) {
359
+ void normalizeError(err);
360
+ ctx.logger.error("R3: stale-claim query failed", { err });
361
+ return { stale, reads: 0 };
362
+ }
363
+ }
364
+ /* ──────────────── R5: a cancellation that still owes money ──────────────── */
365
+ /**
366
+ * A cancelled order whose recorded refund was never actually paid.
367
+ *
368
+ * ## 🛑 Why R3 cannot see this, which is the whole reason R5 exists
369
+ *
370
+ * R3 above watches `refundClaims` for a row stuck in `claimed` — i.e. a crash
371
+ * **after** `processRefundAction` took its idempotency claim. But
372
+ * `cancelOrderWithRefund` writes the cancellation and the refund *intent*
373
+ * first, and only then calls `processRefundAction`. So a failure in that window
374
+ * — the process dies, the settings read throws, the provider is unreachable
375
+ * before a claim is written — leaves **no `refundClaims` row at all**, and R3
376
+ * is structurally blind to it.
377
+ *
378
+ * What it does leave is an order that says, in its own fields, that it owes
379
+ * money: `status: "cancelled"` with a `refundAmount` and
380
+ * `refundStatus: "pending"`. That is the artefact, and it exists precisely
381
+ * because the intent is written before the money moves.
382
+ *
383
+ * ## Two equalities, no inequality, and the reason
384
+ *
385
+ * `where(status).where(refundStatus)` plus a `limit`. Deliberately **no
386
+ * `orderBy`**: `cancellationDate` and `refundAmount` are both optional fields,
387
+ * and an `orderBy` on an optional field silently excludes every document that
388
+ * lacks it (Root Cause #100) — which here would mean the sweep quietly skipping
389
+ * exactly the malformed rows it is looking for. With no `orderBy` Firestore
390
+ * orders by document id, which is stable and needs no second index.
391
+ *
392
+ * ## The settled check reads `refunds[]`, not the flag
393
+ *
394
+ * The flag says what was INTENDED; `refunds[]` says what HAPPENED, and it is
395
+ * maintained transactionally by reserve/settle. `settleRefund` now clears the
396
+ * flag to `"completed"` on a full processed refund, so a healthy refund leaves
397
+ * this query's result set on its own — but a crash between the provider call
398
+ * and that write would leave the flag standing with the money gone. Comparing
399
+ * against the committed total is what tells those two apart, and getting it
400
+ * backwards would report every successful refund as a failure.
401
+ */
402
+ async function reconcileUnpaidCancellations(ctx) {
403
+ let owed = 0;
404
+ let stale = 0;
405
+ try {
406
+ const snap = await ctx.db
407
+ .collection("orders")
408
+ .where("status", "==", "cancelled")
409
+ .where("refundStatus", "==", "pending")
410
+ .limit(100)
411
+ .get();
412
+ /*
413
+ * Saturation is reported, never silent. A full page means there may be more
414
+ * behind it, and "we processed 100 and said nothing" reads identically to
415
+ * "there were exactly 100" — the `truncated` discipline.
416
+ */
417
+ if (snap.size === 100) {
418
+ ctx.logger.warn("R5: hit the 100-order page limit — more unpaid cancellations may exist behind it");
419
+ }
420
+ for (const doc of snap.docs) {
421
+ const data = doc.data();
422
+ const intended = data.refundAmount ?? 0;
423
+ if (intended <= MONEY_EPSILON) {
424
+ /*
425
+ * `refundStatus: "pending"` with no amount. `cancelOrder` only writes
426
+ * the flag together with an amount, so this is a legacy row or a
427
+ * hand-edit — not money at risk, and not worth a case.
428
+ */
429
+ continue;
430
+ }
431
+ const committed = (data.refunds ?? [])
432
+ .filter((r) => r.status !== "failed")
433
+ .reduce((sum, r) => sum + (r.amount ?? 0), 0);
434
+ if (committed >= intended - MONEY_EPSILON) {
435
+ /*
436
+ * The money DID move; only the flag is stale — a crash between the
437
+ * provider call and `settleRefund`'s write, or a refund settled through
438
+ * a path that predates the flag being cleared. Repaired in place rather
439
+ * than escalated: there is nothing for a human to decide, and leaving
440
+ * it would make this order reappear in every future run.
441
+ */
442
+ stale += 1;
443
+ await doc.ref
444
+ .update({ refundStatus: "completed", updatedAt: ctx.now })
445
+ .catch((err) => {
446
+ void normalizeError(err);
447
+ ctx.logger.warn("R5: could not clear a stale refundStatus", { orderId: doc.id });
448
+ });
449
+ continue;
450
+ }
451
+ owed += 1;
452
+ const shortfall = intended - committed;
453
+ await paymentCasesRepository().openOrObserve({
454
+ /*
455
+ * The SAME kind and the SAME `subjectId` that `cancelOrderWithRefund`
456
+ * uses when it opens this case synchronously — so when the failure was
457
+ * caught at the time, this run increments `observedCount` instead of
458
+ * opening a second case. That turns the count into "this has been owed
459
+ * for N days", which is the number an operator should be triaging by.
460
+ */
461
+ kind: "manual_refund_due",
462
+ subjectId: doc.id,
463
+ severity: "error",
464
+ title: `Cancelled order still owes ₹${shortfall.toFixed(2)} — ${doc.id}`,
465
+ detail: [
466
+ "This order is cancelled and records a refund it has not paid.",
467
+ "",
468
+ "🛑 R3 cannot see this case: it watches `refundClaims`, and a failure",
469
+ "BEFORE a claim was taken leaves no claim row. The evidence is on the",
470
+ "order itself — a recorded `refundAmount` that `refunds[]` does not",
471
+ "cover.",
472
+ "",
473
+ `Order: ${doc.id}`,
474
+ `Recorded as owed: ₹${intended.toFixed(2)}`,
475
+ `Actually refunded: ₹${committed.toFixed(2)}`,
476
+ `Shortfall: ₹${shortfall.toFixed(2)}`,
477
+ data.paymentMethod ? `Payment method: ${data.paymentMethod}` : null,
478
+ data.paymentId ? `Gateway reference: ${data.paymentId}` : null,
479
+ data.userId ? `Buyer: ${data.userId}` : null,
480
+ data.cancellationReason ? `Cancelled because: ${data.cancellationReason}` : null,
481
+ "",
482
+ "Refunding from the admin order page is idempotent on this order, so a",
483
+ "retry cannot double-refund — if an earlier attempt did reach the",
484
+ "gateway, the retry returns that same refund.",
485
+ ]
486
+ .filter((l) => l !== null)
487
+ .join("\n"),
488
+ entityType: "order",
489
+ entityId: doc.id,
490
+ amountAtRisk: shortfall,
491
+ orderIds: [doc.id],
492
+ ...(data.paymentId ? { gatewayPaymentId: data.paymentId } : {}),
493
+ now: ctx.now,
494
+ });
495
+ }
496
+ return { owed, stale, reads: snap.size };
497
+ }
498
+ catch (err) {
499
+ void normalizeError(err);
500
+ ctx.logger.error("R5: unpaid-cancellation query failed", { err });
501
+ return { owed, stale, reads: 0 };
502
+ }
503
+ }
504
+ /* ──────────────── R4: disputes, and deadlines about to pass ─────────────── */
505
+ async function reconcileDisputesAndDeadlines(ctx) {
506
+ let recorded = 0;
507
+ let escalated = 0;
508
+ let reads = 0;
509
+ const payment = getProviders().payment;
510
+ if (payment) {
511
+ try {
512
+ const disputes = await payment.listDisputes();
513
+ if (disputes.length > 0)
514
+ recorded = await reconcileDisputes(disputes);
515
+ }
516
+ catch (err) {
517
+ void normalizeError(err);
518
+ ctx.logger.error("R4: dispute listing failed", { err });
519
+ }
520
+ }
521
+ /*
522
+ * Deadline escalation. `listDueBefore` can only see cases that HAVE a
523
+ * `dueAt`, because a Firestore inequality excludes documents lacking the
524
+ * field (Root Cause #100) — correct here, since a case with no deadline
525
+ * cannot be nearly-overdue.
526
+ *
527
+ * Escalating to `error` is all this does. It does NOT send a notification per
528
+ * run: `openOrObserve`'s severity-only escalation plus the daily digest's
529
+ * "nearest external deadline" line is the alert, and a per-run email on a
530
+ * case that stays open for its whole 7-day response window would be seven
531
+ * emails against an 80/day budget for one fact.
532
+ */
533
+ try {
534
+ const horizon = new Date(ctx.now.getTime() + DUE_SOON_HOURS * 3600000);
535
+ const due = await paymentCasesRepository().listDueBefore(horizon, 50);
536
+ reads += due.length;
537
+ for (const kase of due) {
538
+ if (kase.severity === "error")
539
+ continue;
540
+ await paymentCasesRepository().openOrObserve({
541
+ kind: kase.kind,
542
+ subjectId: kase.dedupeKey.slice(kase.kind.length + 1),
543
+ severity: "error",
544
+ title: kase.title,
545
+ detail: kase.detail,
546
+ entityType: kase.entityType,
547
+ entityId: kase.entityId,
548
+ ...(kase.dueAt ? { dueAt: kase.dueAt } : {}),
549
+ ...(typeof kase.amountAtRisk === "number"
550
+ ? { amountAtRisk: kase.amountAtRisk }
551
+ : {}),
552
+ now: ctx.now,
553
+ });
554
+ escalated += 1;
555
+ }
556
+ }
557
+ catch (err) {
558
+ void normalizeError(err);
559
+ ctx.logger.error("R4: deadline escalation failed", { err });
560
+ }
561
+ return { recorded, escalated, reads };
562
+ }
563
+ /* ─────────────────────────────── the job ────────────────────────────────── */
564
+ export async function runPaymentReconcile(ctx) {
565
+ ctx.logger.info("Starting payment reconciliation (R2 + R3 + R4 + R5)");
566
+ /*
567
+ * 🛑 R1 is deliberately NOT called here. It belongs to
568
+ * `paymentWindowTimeout`, which runs every 5 minutes — a stuck payment event
569
+ * should not wait up to 24 hours. Running it in both places would double its
570
+ * writes for no extra coverage.
571
+ */
572
+ const [payments, claims, disputes, cancellations] = await Promise.all([
573
+ reconcileGatewayPayments(ctx),
574
+ reconcileStaleRefundClaims(ctx),
575
+ reconcileDisputesAndDeadlines(ctx),
576
+ reconcileUnpaidCancellations(ctx),
577
+ ]);
578
+ const result = {
579
+ stuckEventsRedispatched: 0,
580
+ stuckEventsEscalated: 0,
581
+ paymentsExamined: payments.examined,
582
+ missingOrders: payments.missing,
583
+ amountMismatches: payments.amountMismatch,
584
+ refundMismatches: payments.refundMismatch,
585
+ staleClaims: claims.stale,
586
+ disputesRecorded: disputes.recorded,
587
+ casesEscalated: disputes.escalated,
588
+ unpaidCancellations: cancellations.owed,
589
+ staleRefundFlagsCleared: cancellations.stale,
590
+ reads: payments.reads + claims.reads + disputes.reads + cancellations.reads,
591
+ };
592
+ /*
593
+ * Logged on EVERY run, including a clean one. The read count is the early
594
+ * warning for a regression to always-full scanning — which is exactly how
595
+ * the 2026-10-09 suspension and Root Cause #92's 12M invocations both became
596
+ * visible only on a billing page.
597
+ */
598
+ ctx.logger.info("Payment reconciliation complete", { ...result });
599
+ if (result.missingOrders > 0) {
600
+ ctx.logger.error("🛑 Captured payments with no order", { count: result.missingOrders });
601
+ }
602
+ /*
603
+ * Loud, for the same reason as the line above: a buyer whose cancelled order
604
+ * never gave their money back has no way to tell, and nothing else in the
605
+ * system complains. Separated from `staleRefundFlagsCleared`, which is
606
+ * housekeeping and must not be reported in the same breath as money owed.
607
+ */
608
+ if (result.unpaidCancellations > 0) {
609
+ ctx.logger.error("🛑 Cancelled orders still owing a refund", {
610
+ count: result.unpaidCancellations,
611
+ });
612
+ }
613
+ return result;
614
+ }
@@ -5,6 +5,7 @@ import { restoreStockForOrder } from "../../features/checkout/stock-restore.js";
5
5
  import { OrderStatusValues } from "../../../../features/orders/schemas/firestore.js";
6
6
  import { PAYMENT_WINDOW_EXPIRED_REASON } from "../../../../features/orders/constants/payment-window.js";
7
7
  import { ORDER_MESSAGES } from "../handlers/messages.js";
8
+ import { reconcileStuckGatewayEvents } from "./paymentReconcile.js";
8
9
  /**
9
10
  * 15-minute payment window sweep — cancels + restocks any `upi_manual`/
10
11
  * `cash`/`emi` order still `pending`/`pending` with no proof uploaded past
@@ -19,6 +20,39 @@ import { ORDER_MESSAGES } from "../handlers/messages.js";
19
20
  * (`paymentReviewAutoApprove`), not this one's.
20
21
  */
21
22
  export async function runPaymentWindowTimeout(ctx) {
23
+ /*
24
+ * ── R1 rides along: gateway events we received and lost ───────────────────
25
+ *
26
+ * Folded into this sweep rather than given its own cron, for two reasons that
27
+ * both matter. Cloud Scheduler bills **per registered job**, so a fifth
28
+ * 5-minute function is a real recurring cost for one indexed read. And this
29
+ * job already runs at the cadence R1 needs: a webhook whose handler died
30
+ * mid-dispatch leaves a lapsed lease, and noticing that within ~5 minutes
31
+ * rather than up to 24 hours is the difference between the gateway's own
32
+ * retry still being in flight and a buyer having already emailed support.
33
+ *
34
+ * 🛑 FIRST, and outside the early return below. The original shape of this
35
+ * function returns as soon as there are no expired payment windows — which is
36
+ * most ticks — so appending R1 after that return would have made it run only
37
+ * on the minority of ticks that happened to have unrelated work. That is the
38
+ * sweep-coverage trap Root Cause #27 is about: three sweeps whose filters
39
+ * were each disjoint, with no proof any order was covered by one.
40
+ *
41
+ * It never throws: a reconciliation failure must not stop the payment-window
42
+ * cancellations this job exists for.
43
+ */
44
+ try {
45
+ const r1 = await reconcileStuckGatewayEvents(ctx);
46
+ if (r1.redispatched > 0 || r1.escalated > 0) {
47
+ ctx.logger.warn("R1: recovered stuck gateway events", { ...r1 });
48
+ }
49
+ }
50
+ catch (err) {
51
+ void normalizeError(err);
52
+ ctx.logger.error("R1 failed inside paymentWindowTimeout — continuing with the sweep", {
53
+ err,
54
+ });
55
+ }
22
56
  ctx.logger.info("Scanning orders past their 15-minute payment deadline");
23
57
  const expired = await orderRepository.getExpiredPaymentDeadlines();
24
58
  if (expired.length === 0) {