@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,228 @@
1
+ "use server";
2
+ /**
3
+ * Filing and adjudicating a protection claim — the first callers
4
+ * `protectionAvailability()` and `adjudicateClaim()` have ever had.
5
+ *
6
+ * ## What was wrong (D11)
7
+ *
8
+ * We charge 2% (min ₹30) shipment protection and 3% (min ₹49) sale protection
9
+ * at four money sites. The decision logic, the custody state machine, the
10
+ * window constants and the order fields all existed with **zero callers**, and
11
+ * the refund path did not know the add-on existed. A FAQ promised *"we pursue
12
+ * the courier and refund you"* and *"refunded no questions asked"*.
13
+ *
14
+ * ## The two gates, and why both are server-side
15
+ *
16
+ * 1. **Did the buyer actually buy this cover?** Read off the ORDER's own
17
+ * `shipmentProtectionFee` / `saleProtectionFee` — the amount charged — not
18
+ * off the live store or the live settings. A seller must not be able to
19
+ * withdraw cover after the sale, which is the same snapshot reasoning
20
+ * `finalSale` uses on the refund path.
21
+ * 2. **Is the window still open?** The `claim-shipping` / `claim-sale`
22
+ * obligations, derived from `deliveryDate` through
23
+ * `PROTECTION_CLAIM_WINDOW_HOURS` (48h / 168h). One definition, shared with
24
+ * the sweep that closes the window and earns the premium.
25
+ */
26
+ import { wrapAction } from "../../../shared/types/action-result.js";
27
+ import { requireRoleUser } from "../../../../providers/auth-firebase/helpers.js";
28
+ import { normalizeError } from "../../../../errors/normalize.js";
29
+ import { serverLogger } from "../../../../monitoring/index.js";
30
+ import { ValidationError, NotFoundError } from "../../../../errors/index.js";
31
+ import { orderRepository } from "../../../../features/orders/repository/orders.repository.js";
32
+ import { protectionClaimsRepository } from "../../../../features/protection-claims/repository/protection-claims.repository.js";
33
+ import { PROTECTION_CLAIM_DETAIL_MAX, PROTECTION_CLAIM_DETAIL_MIN, REASON_TO_KIND, createProtectionClaimId, } from "../../../../features/protection-claims/schemas/firestore.js";
34
+ import { adjudicateClaim, SALE_PROTECTION_MAX_VALUE, } from "../../../shared/features/shipping/protections.js";
35
+ import { deadlinesFor } from "../../../shared/obligations/deadlines.js";
36
+ import { paymentCasesRepository } from "../../../../features/payment-cases/repository/payment-cases.repository.js";
37
+ import { orderGoodsValue } from "../../../shared/checkout/order-math.js";
38
+ /**
39
+ * File a claim as the buyer.
40
+ *
41
+ * 🛑 The COVER KIND is derived from the reason, never taken from the caller.
42
+ * `REASON_TO_KIND` maps transit failures to shipment cover and item failures
43
+ * to sale cover — because letting the buyer pick would let them file a
44
+ * non-delivery against the 7-day sale window after the 48-hour transit window
45
+ * had closed, which is the window rule defeated by a dropdown.
46
+ */
47
+ export async function fileProtectionClaimAction(input) {
48
+ return wrapAction(async () => {
49
+ const user = await requireRoleUser(["user", "buyer", "seller", "admin"]);
50
+ const detail = input.detail?.trim() ?? "";
51
+ if (detail.length < PROTECTION_CLAIM_DETAIL_MIN) {
52
+ throw new ValidationError(`Please describe what happened in at least ${PROTECTION_CLAIM_DETAIL_MIN} characters — a claim with no account of the problem cannot be assessed.`);
53
+ }
54
+ if (detail.length > PROTECTION_CLAIM_DETAIL_MAX) {
55
+ throw new ValidationError(`Please keep the description under ${PROTECTION_CLAIM_DETAIL_MAX} characters.`);
56
+ }
57
+ const order = await orderRepository.findById(input.orderId);
58
+ if (!order)
59
+ throw new NotFoundError(`Order ${input.orderId} not found`);
60
+ if (order.userId !== user.uid) {
61
+ throw new ValidationError("You can only claim against your own order.");
62
+ }
63
+ const kind = REASON_TO_KIND[input.reason];
64
+ /*
65
+ * GATE 1 — did this order actually CARRY the cover?
66
+ *
67
+ * 🛑 Read off the order's own recorded fee, not the live store or the live
68
+ * settings. A seller flipping `supportsSaleProtection` off after the sale
69
+ * must not be able to withdraw cover the buyer paid for — the same
70
+ * reasoning `finalSale` uses on the refund path, and the same reason
71
+ * `OrderDocumentItem.finalSale` exists at all.
72
+ */
73
+ const feePaid = kind === "shipping" ? order.shipmentProtectionFee : order.saleProtectionFee;
74
+ if (!feePaid || feePaid <= 0) {
75
+ throw new ValidationError(kind === "shipping"
76
+ ? "This order does not carry shipment protection."
77
+ : "This order does not carry sale protection.");
78
+ }
79
+ /*
80
+ * GATE 2 — is the window still open?
81
+ *
82
+ * Derived from the obligation policy, so the buyer's form, the sweep that
83
+ * closes the window, and whatever page shows the deadline all read one
84
+ * definition.
85
+ *
86
+ * 🛑 No `deliveryDate` means the clock has not STARTED, which is not the
87
+ * same as closed — a parcel that never arrived is exactly what shipment
88
+ * cover is for, and refusing it for want of a delivery date would deny the
89
+ * claim the cover most exists for.
90
+ */
91
+ const obligationKind = kind === "shipping" ? "claim-shipping" : "claim-sale";
92
+ const window = deadlinesFor(order).find((d) => d.kind === obligationKind);
93
+ if (order.deliveryDate && window && window.dueAt.getTime() <= Date.now()) {
94
+ throw new ValidationError(`The ${kind === "shipping" ? "shipment" : "sale"} protection claim window for this order closed on ${window.dueAt.toLocaleDateString("en-IN")}.`);
95
+ }
96
+ /*
97
+ * The ceiling. `SALE_PROTECTION_MAX_VALUE` (₹25,000) is tested against what
98
+ * was PAID for the claimed goods, which is why `claimedAmount` is
99
+ * snapshotted at filing rather than recomputed later.
100
+ */
101
+ const claimedAmount = orderGoodsValue(order);
102
+ if (kind === "sale" && claimedAmount > SALE_PROTECTION_MAX_VALUE) {
103
+ throw new ValidationError(`Sale protection covers orders up to ₹${SALE_PROTECTION_MAX_VALUE.toLocaleString("en-IN")}. This order is above that, so the claim needs a human — please contact support.`);
104
+ }
105
+ const claim = await protectionClaimsRepository().file({
106
+ kind,
107
+ orderId: order.id,
108
+ storeId: order.storeId ?? "",
109
+ buyerId: user.uid,
110
+ reason: input.reason,
111
+ detail,
112
+ claimedAmount,
113
+ ...(input.itemIds?.length ? { itemIds: input.itemIds } : {}),
114
+ ...(input.evidenceUrls?.length ? { evidenceUrls: input.evidenceUrls } : {}),
115
+ ...(order.isTestData ? { isTestData: true } : {}),
116
+ });
117
+ /*
118
+ * Into the operator queue, because a claim is money at risk with a human
119
+ * decision attached — which is exactly what `paymentCases` is for. Keyed on
120
+ * the claim, so re-filing or a sweep re-observing it produces one case.
121
+ *
122
+ * Fire-and-forget: a queue write must never fail the buyer's claim.
123
+ */
124
+ void paymentCasesRepository()
125
+ .openOrObserve({
126
+ kind: "protection_claim",
127
+ subjectId: `claim:${claim.id}`,
128
+ severity: "warning",
129
+ title: `${kind === "shipping" ? "Shipment" : "Sale"} protection claim — ${order.id}`,
130
+ detail: [
131
+ `A buyer has claimed against protection cover they paid for.`,
132
+ "",
133
+ `Order: ${order.id}`,
134
+ `Store: ${order.storeId ?? "(none)"}`,
135
+ `Cover: ${kind} (fee paid: ${feePaid})`,
136
+ `Reason: ${input.reason}`,
137
+ `Claimed amount: ${claimedAmount}`,
138
+ "",
139
+ detail,
140
+ "",
141
+ `🛑 A PROTECTED claim is refunded unconditionally — the finding decides`,
142
+ `WHO PAYS, never whether the buyer is refunded. See adjudicateClaim.`,
143
+ ].join("\n"),
144
+ entityType: "order",
145
+ entityId: order.id,
146
+ amountAtRisk: claimedAmount,
147
+ orderIds: [order.id],
148
+ })
149
+ .catch((err) => {
150
+ void normalizeError(err);
151
+ });
152
+ serverLogger.warn("protection claim filed", {
153
+ claimId: claim.id,
154
+ orderId: order.id,
155
+ kind,
156
+ reason: input.reason,
157
+ });
158
+ return { claimId: claim.id };
159
+ });
160
+ }
161
+ /**
162
+ * Adjudicate as an admin or moderator.
163
+ *
164
+ * 🛑 THE FINDING DECIDES WHO PAYS, NEVER WHETHER A PROTECTED BUYER IS
165
+ * REFUNDED. `adjudicateClaim` encodes that and this action does not
166
+ * second-guess it: on protected cover a `change-of-mind` finding STILL refunds
167
+ * the buyer and moves the cost to the platform, raising a report — because the
168
+ * promise sold was unconditional, and the only honest enforcement against
169
+ * repeat abuse is withdrawing FUTURE eligibility, not denying this claim.
170
+ *
171
+ * 🛑 It records the decision and does NOT move money. The refund is
172
+ * `processRefundAction`'s job, with its own idempotency key — and keeping them
173
+ * apart means a claim that adjudicated but failed to refund leaves an `upheld`
174
+ * claim with no `refundId`, which is a findable state rather than a lost one.
175
+ */
176
+ export async function adjudicateProtectionClaimAction(claimId, finding) {
177
+ return wrapAction(async () => {
178
+ const user = await requireRoleUser(["admin", "moderator"]);
179
+ const claim = await protectionClaimsRepository().findById(claimId);
180
+ if (!claim)
181
+ throw new NotFoundError(`Protection claim ${claimId} not found`);
182
+ if (claim.adjudicatedAt) {
183
+ throw new ValidationError("This claim has already been adjudicated.");
184
+ }
185
+ /*
186
+ * `protectedSale` is whether SALE cover was bought — the only tier
187
+ * `adjudicateClaim` branches on. A shipment claim is assessed on the
188
+ * finding alone, which is correct: transit cover is about the parcel
189
+ * arriving, and "the buyer changed their mind" is not a transit outcome.
190
+ */
191
+ const outcome = adjudicateClaim(claim.kind === "sale", finding);
192
+ await protectionClaimsRepository().adjudicate(claimId, {
193
+ finding,
194
+ outcome,
195
+ adjudicatedBy: user.uid,
196
+ });
197
+ serverLogger.warn("protection claim adjudicated", {
198
+ claimId,
199
+ finding,
200
+ refundBuyer: outcome.refundBuyer,
201
+ costBorneBy: outcome.costBorneBy,
202
+ raisesReport: outcome.raisesReport,
203
+ });
204
+ return { outcome };
205
+ });
206
+ }
207
+ /** A buyer's own claims, for their order detail page. */
208
+ export async function getMyProtectionClaimsAction() {
209
+ return wrapAction(async () => {
210
+ const user = await requireRoleUser(["user", "buyer", "seller", "admin"]);
211
+ return protectionClaimsRepository().listForBuyer(user.uid);
212
+ });
213
+ }
214
+ /** Whether this order can be claimed against right now, for rendering the CTA. */
215
+ export async function canClaimProtection(order, kind) {
216
+ const feePaid = kind === "shipping" ? order.shipmentProtectionFee : order.saleProtectionFee;
217
+ if (!feePaid || feePaid <= 0)
218
+ return { can: false, reason: "not-covered" };
219
+ const existing = await protectionClaimsRepository().findById(createProtectionClaimId(order.id, kind));
220
+ if (existing)
221
+ return { can: false, reason: "already-claimed" };
222
+ const obligationKind = kind === "shipping" ? "claim-shipping" : "claim-sale";
223
+ const window = deadlinesFor(order).find((d) => d.kind === obligationKind);
224
+ if (order.deliveryDate && window && window.dueAt.getTime() <= Date.now()) {
225
+ return { can: false, reason: "window-closed" };
226
+ }
227
+ return { can: true };
228
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * protection-claims — server surface.
3
+ *
4
+ * Narrow by design (Root Cause #18): the three actions a route calls, plus the
5
+ * CTA predicate a page needs. The repository is imported directly by anything
6
+ * that queries.
7
+ */
8
+ export { fileProtectionClaimAction, adjudicateProtectionClaimAction, getMyProtectionClaimsAction, canClaimProtection, type FileProtectionClaimInput, } from "./actions";
@@ -0,0 +1,8 @@
1
+ /**
2
+ * protection-claims — server surface.
3
+ *
4
+ * Narrow by design (Root Cause #18): the three actions a route calls, plus the
5
+ * CTA predicate a page needs. The repository is imported directly by anything
6
+ * that queries.
7
+ */
8
+ export { fileProtectionClaimAction, adjudicateProtectionClaimAction, getMyProtectionClaimsAction, canClaimProtection, } from "./actions.js";
@@ -20,6 +20,26 @@ export type ProcessRefundInput = {
20
20
  confirmIrrevocable: true;
21
21
  /** uid of the user processing the refund (admin / seller). */
22
22
  refundedBy: string;
23
+ /**
24
+ * 🛑 REQUIRED (D2). The caller's stable key for this logical refund.
25
+ *
26
+ * `refundId` used to be a server-side `randomUUID()` per call, which can
27
+ * never be idempotent: a double-clicked button minted two ids and issued
28
+ * **two real gateway refunds**. The entry id is now
29
+ * `sha256(idempotencyKey)`, so a retry reproduces the identical entry and
30
+ * the claim document refuses the second attempt.
31
+ *
32
+ * Required rather than optional ON PURPOSE — an optional key with a
33
+ * `randomUUID()` fallback is the old bug wearing a new name, and it would
34
+ * be reached by exactly the callers that forgot.
35
+ *
36
+ * What to pass: the submitting UI generates ONE uuid per form OPEN (so a
37
+ * double-click reuses it and a genuine second refund gets a new one);
38
+ * server-initiated paths use a NATURAL key — the out-of-stock auto-refund
39
+ * uses `oos_{merchantOrderId}`, which also makes it idempotent across
40
+ * checkout retries, a money fix in its own right.
41
+ */
42
+ idempotencyKey: string;
23
43
  } & ({
24
44
  method: "phonepe";
25
45
  /** The order's PhonePe merchantOrderId (order.paymentId) — PhonePe refunds are order-keyed, never payment-keyed. */
@@ -15,8 +15,16 @@ import { wrapAction } from "@mohasinac/appkit/server";
15
15
  *
16
16
  * Setting `contestable` to false is a side-effect of `orderRepository.postRefundEvent`.
17
17
  */
18
- import { randomUUID } from "crypto";
18
+ /*
19
+ * `createHash`, not `randomUUID` — see `idempotencyKey` on the input type. A
20
+ * server-minted random id cannot be idempotent, which is what made a
21
+ * double-clicked button issue two real gateway refunds (D2).
22
+ */
23
+ import { createHash } from "crypto";
19
24
  import { getProviders } from "../../../../contracts/registry.js";
25
+ import { siteSettingsRepository } from "../../../../features/admin/repository/site-settings.repository.js";
26
+ import { refundCapacity, wouldBeFullRefund, MONEY_EPSILON, } from "../../../shared/checkout/refundable-base.js";
27
+ import { REFUND_CLAIMS_COLLECTION } from "../../../../features/orders/schemas/firestore.js";
20
28
  import { normalizeError } from "../../../../errors/normalize.js";
21
29
  import { rupeesToPaise } from "../../../../core/money.js";
22
30
  import { orderRepository } from "../../../../features/orders/repository/orders.repository.js";
@@ -86,8 +94,49 @@ export async function processRefundAction(input) {
86
94
  throw new NotFoundError(`Order ${input.orderId} not found`);
87
95
  if (input.amount <= 0)
88
96
  throw new ValidationError("Refund amount must be positive");
89
- if (input.amount > order.totalPrice) {
90
- throw new ValidationError("Refund amount exceeds order total");
97
+ /*
98
+ * One settings read, and only for the LEGACY case: orders written before
99
+ * `platformFeeGst` was persisted (2026-10-21) need their fee's GST
100
+ * derived, and deriving it from a rate we had to look up is still far
101
+ * better than D32's mistake of deriving the whole fee from a live
102
+ * percentage. Orders written after that date ignore this value entirely.
103
+ *
104
+ * Accepted cost: one read on an operator-initiated, rare action. The
105
+ * alternative — skipping it — would under-withhold on legacy orders,
106
+ * which sounds buyer-friendly but inflates `refundableBase`, so a
107
+ * "refund everything" would leave a residue and the order would never
108
+ * reach `status: "refunded"` (the D3 defect, reintroduced).
109
+ */
110
+ const siteSettings = await siteSettingsRepository.getSingleton().catch((err) => {
111
+ void normalizeError(err);
112
+ return null;
113
+ });
114
+ /*
115
+ * ── D1: the cap is the REMAINING balance, not the order total ────────
116
+ *
117
+ * This read `input.amount > order.totalPrice`, i.e. it compared each
118
+ * refund against the whole order — while the function's own docstring
119
+ * promised "≤ remaining refundable amount". So on a ₹600 order, ₹500 and
120
+ * then another ₹500 BOTH passed: ₹1,000 refunded, two real gateway
121
+ * calls, both successful.
122
+ *
123
+ * 🛑 The ceiling is `refundableBase`, not `totalPrice`: the platform fee
124
+ * is non-refundable by policy, which the product already tells the buyer
125
+ * (`REFUND_COPY.request.acknowledgments[2]`).
126
+ *
127
+ * This check is the EARLY, friendly one — it gives the operator a useful
128
+ * message before any provider call. It is NOT the authority: the real
129
+ * cap is re-evaluated inside `reserveRefund`'s transaction, because a
130
+ * check outside a transaction is TOCTOU and two concurrent requests
131
+ * could each see a clear cap and both proceed. That is exactly how the
132
+ * double refund above was reachable even with a correct comparison.
133
+ */
134
+ const capacity = refundCapacity(order, siteSettings?.commissions?.gstPercent);
135
+ if (input.amount > capacity.remaining + MONEY_EPSILON) {
136
+ throw new ValidationError(`Refund of ₹${input.amount.toFixed(2)} exceeds the ₹${capacity.remaining.toFixed(2)} still refundable on this order` +
137
+ (capacity.withheld > 0
138
+ ? ` (₹${capacity.withheld.toFixed(2)} platform fee is non-refundable).`
139
+ : "."));
91
140
  }
92
141
  // Guard: non-refundable orders (prize draws, bundles). A property of the
93
142
  // listing TYPE, decided by the platform — checked first because it
@@ -118,17 +167,72 @@ export async function processRefundAction(input) {
118
167
  // SB-UNI-N — digital-code refund gate: block if code is already claimed (revealed).
119
168
  const digitalCodeItems = (order.items ?? []).filter((i) => (i.listingType ?? "standard") === "digital-code");
120
169
  await assertDigitalCodesNotClaimed(input.orderId, digitalCodeItems);
121
- const refundId = randomUUID();
170
+ /*
171
+ * ── D2: IDEMPOTENCY ──────────────────────────────────────────────────
172
+ *
173
+ * `const refundId = randomUUID()` was generated SERVER-SIDE, per call.
174
+ * A server-minted id can never be idempotent: a double-clicked button
175
+ * produced two different ids and therefore **two real gateway refunds**.
176
+ *
177
+ * The id is now derived from the caller's `idempotencyKey`, so a retry
178
+ * of the same logical request produces the identical entry. The client
179
+ * generates one key per form OPEN, so a double-click reuses it while a
180
+ * genuine second refund gets a new one.
181
+ *
182
+ * Truncated to 32 chars to stay comfortably inside a Firestore document
183
+ * id while keeping ~128 bits of the digest — collision risk is
184
+ * negligible and a collision degrades to "treated as a retry", which is
185
+ * the safe direction.
186
+ */
187
+ const refundId = createHash("sha256").update(input.idempotencyKey).digest("hex").slice(0, 32);
122
188
  const now = new Date();
123
- let phonepeRefundId;
124
- if (input.method === "phonepe") {
125
- const payment = getProviders().payment;
126
- if (!payment)
127
- throw new ValidationError("Payment provider not configured");
128
- const result = await payment.refund(input.phonepeOrderId, rupeesToPaise(input.amount));
129
- phonepeRefundId = result.id;
189
+ /*
190
+ * The at-most-once gate, BEFORE any provider call. Same `.create()`
191
+ * fails-if-exists primitive the gateway order claim uses — the fourth
192
+ * use of it in this codebase.
193
+ *
194
+ * 🛑 Claimed first and released on nothing. A claim that outlives a
195
+ * crash is deliberate: it is what stops a retry from issuing a second
196
+ * provider call for a refund that may already have succeeded. An
197
+ * unsettled claim older than an hour is what the reconciler looks for
198
+ * (Phase 4 R3), and resolving it means READING the gateway, never
199
+ * guessing.
200
+ */
201
+ const claimRef = getAdminDb().collection(REFUND_CLAIMS_COLLECTION).doc(refundId);
202
+ try {
203
+ await claimRef.create({
204
+ orderId: input.orderId,
205
+ amount: input.amount,
206
+ state: "claimed",
207
+ claimedAt: now,
208
+ claimedBy: input.refundedBy,
209
+ });
210
+ }
211
+ catch (claimErr) {
212
+ void normalizeError(claimErr);
213
+ /*
214
+ * Already claimed. Return the WINNER's refundId as a SUCCESS, not an
215
+ * error — the refund either happened or is in flight, and an error
216
+ * here makes the operator click again, which is the behaviour this
217
+ * whole mechanism exists to stop.
218
+ */
219
+ serverLogger.info("refund: duplicate idempotency key — returning the existing refund", {
220
+ orderId: input.orderId,
221
+ refundId,
222
+ });
223
+ return { success: true, refundId };
130
224
  }
131
- const isFull = input.amount >= order.totalPrice;
225
+ /*
226
+ * ── D3: `isFull` measured against the REFUNDABLE BASE ────────────────
227
+ *
228
+ * This was `input.amount >= order.totalPrice`, which is unreachable once
229
+ * the platform fee is withheld: the largest possible refund is strictly
230
+ * LESS than `totalPrice`, so an order could **never** reach
231
+ * `status: "refunded"` and stayed fully refundable forever. It also
232
+ * ignored prior partial refunds entirely — ₹300 then ₹200 of ₹500 left
233
+ * the order open.
234
+ */
235
+ const isFull = wouldBeFullRefund(order, input.amount, siteSettings?.commissions?.gstPercent);
132
236
  const event = {
133
237
  refundId,
134
238
  type: input.type,
@@ -144,7 +248,6 @@ export async function processRefundAction(input) {
144
248
  : RETURN_REASON_LABEL[input.reasonCode],
145
249
  refundedAt: now,
146
250
  refundedBy: input.refundedBy,
147
- ...(phonepeRefundId ? { phonepeRefundId } : {}),
148
251
  ...(input.method === "manual" && input.manualTransactionId
149
252
  ? { manualTransactionId: input.manualTransactionId }
150
253
  : {}),
@@ -155,11 +258,117 @@ export async function processRefundAction(input) {
155
258
  }
156
259
  : {}),
157
260
  };
158
- await orderRepository.postRefundEvent(input.orderId, event, isFull);
261
+ /*
262
+ * ── D6: TWO-PHASE WRITE AROUND THE PROVIDER CALL ─────────────────────
263
+ *
264
+ * The old sequence was: call the gateway, then `postRefundEvent`, which
265
+ * does `findById` then `[...existing, event]`. Two problems.
266
+ *
267
+ * (a) The append is a read-then-write, so two concurrent refunds both
268
+ * read the same array and the second **loses the first's event
269
+ * entirely**.
270
+ * (b) A crash between the gateway call and the write leaves money gone
271
+ * from the gateway and NO RECORD of it on our side — unrecoverable
272
+ * without manually reading the provider.
273
+ *
274
+ * Reserve (transactional, cap enforced inside) → provider call (outside
275
+ * any transaction, because a transaction RETRIES its body and a retried
276
+ * network call is a second refund) → settle. This is the only ordering
277
+ * in which "money left and we do not know" is impossible: a crash now
278
+ * leaves a `reserved` entry plus an unsettled claim, which is a
279
+ * detectable, resolvable state.
280
+ */
281
+ await orderRepository.reserveRefund(input.orderId, event, {
282
+ refundableBase: capacity.base,
283
+ epsilon: MONEY_EPSILON,
284
+ });
285
+ let gatewayRefundId;
286
+ let gatewayName;
287
+ if (input.method === "phonepe") {
288
+ const payment = getProviders().payment;
289
+ if (!payment)
290
+ throw new ValidationError("Payment provider not configured");
291
+ try {
292
+ const result = await payment.refund(input.phonepeOrderId, rupeesToPaise(input.amount));
293
+ gatewayRefundId = result.id;
294
+ gatewayName = payment.name;
295
+ }
296
+ catch (providerErr) {
297
+ void normalizeError(providerErr);
298
+ /*
299
+ * Release the reservation by settling it `failed`, so the amount
300
+ * returns to the refundable balance — `committedRefundTotal` skips
301
+ * failed entries, which is why no explicit release exists to forget.
302
+ * Then rethrow: the operator must know the gateway refused.
303
+ */
304
+ await orderRepository
305
+ .settleRefund(input.orderId, refundId, {
306
+ status: "failed",
307
+ failureReason: providerErr instanceof Error ? providerErr.message : String(providerErr),
308
+ })
309
+ .catch((settleErr) => {
310
+ void normalizeError(settleErr);
311
+ serverLogger.error("refund: could not mark a failed reservation — it will hold the balance until reconciled", {
312
+ orderId: input.orderId,
313
+ refundId,
314
+ });
315
+ });
316
+ await claimRef
317
+ .update({ state: "failed", settledAt: new Date() })
318
+ .catch(() => { });
319
+ throw providerErr;
320
+ }
321
+ }
322
+ /*
323
+ * ── D7: the gateway id, recorded gateway-BLIND and provider-specific ──
324
+ *
325
+ * Razorpay's refund id used to be written to `phonepeRefundId`
326
+ * unconditionally (`phonepeRefundId = result.id`, no branch), while the
327
+ * declared `razorpayRefundId` was written by nothing — so a Razorpay
328
+ * refund existed in their dashboard with no way to tie it to ours.
329
+ *
330
+ * `gatewayRefundId` is what a reconciler reads and needs no knowledge of
331
+ * which provider ran. The provider-specific field is populated too, by
332
+ * the provider that actually produced the id, for an operator searching
333
+ * one dashboard.
334
+ */
335
+ await orderRepository.settleRefund(input.orderId, refundId, {
336
+ status: "processed",
337
+ ...(gatewayRefundId ? { gatewayRefundId } : {}),
338
+ ...(gatewayName ? { gateway: gatewayName } : {}),
339
+ providerFields: gatewayRefundId && gatewayName === "razorpay"
340
+ ? { razorpayRefundId: gatewayRefundId }
341
+ : gatewayRefundId
342
+ ? { phonepeRefundId: gatewayRefundId }
343
+ : {},
344
+ }, isFull);
345
+ await claimRef
346
+ .update({ state: "settled", settledAt: new Date() })
347
+ .catch((err) => {
348
+ void normalizeError(err);
349
+ // Harmless: the refund is recorded. An unsettled claim only means
350
+ // the reconciler will look at it once and find it already done.
351
+ serverLogger.warn("refund: claim not marked settled", { refundId });
352
+ });
159
353
  // SB-UNI-N — revoke any available (unclaimed) digital codes linked to this order.
160
354
  revokeAvailableDigitalCodes(input.orderId, digitalCodeItems);
161
- // Fire-and-forget: deduct from the store's pending payout if one exists.
162
- // Failure here must not roll back the already-posted refund event.
355
+ /*
356
+ * Clawback — deduct from the store's pending payout if one exists.
357
+ *
358
+ * 🛑 Still fire-and-forget on the FAILURE path, and that part is
359
+ * correct: the refund event is already posted and the buyer's money is
360
+ * already moving, so an accounting side effect must never roll it back.
361
+ *
362
+ * What changed on 2026-10-21 is that the RESULT is no longer discarded.
363
+ * This read `.catch(() => {})` with no `.then`, on the strength of the
364
+ * action's own docstring claiming an unrecovered clawback was "reflected
365
+ * in the next payout cycle" — which is false, verified: there is no
366
+ * seller ledger and each payout is computed fresh from a new batch of
367
+ * delivered orders, so the seller simply keeps money for goods that came
368
+ * back (D10). The action now records that case durably and returns a
369
+ * destination; this logs which one, so a refund and its clawback can be
370
+ * followed in one place.
371
+ */
163
372
  if (order.storeId) {
164
373
  applyRefundDeductionAction({
165
374
  storeId: order.storeId,
@@ -169,7 +378,40 @@ export async function processRefundAction(input) {
169
378
  // Same derived string the refund event records, so the payout
170
379
  // deduction and the refund history read identically.
171
380
  reason: event.reason,
172
- }).catch(() => { });
381
+ })
382
+ .then((res) => {
383
+ if (!res.ok) {
384
+ serverLogger.error("clawback action failed", {
385
+ orderId: input.orderId,
386
+ refundId,
387
+ });
388
+ return;
389
+ }
390
+ if (res.data.applied) {
391
+ serverLogger.info("clawback netted off a pending payout", {
392
+ orderId: input.orderId,
393
+ payoutId: res.data.payoutId,
394
+ });
395
+ }
396
+ else {
397
+ /*
398
+ * The seller has been paid for returned goods. Loud, because
399
+ * there is no automatic recovery — `recordedAs` names the
400
+ * durable row at /admin/maintenance/payment-rollbacks.
401
+ */
402
+ serverLogger.error("clawback UNRECOVERED — seller retains refunded amount", {
403
+ orderId: input.orderId,
404
+ storeId: res.data.storeId,
405
+ amount: res.data.amount,
406
+ reason: res.data.reason,
407
+ recordedAs: res.data.recordedAs,
408
+ });
409
+ }
410
+ })
411
+ .catch((clawbackErr) => {
412
+ void normalizeError(clawbackErr);
413
+ serverLogger.error("clawback threw", { orderId: input.orderId, refundId });
414
+ });
173
415
  }
174
416
  return { success: true, refundId };
175
417
  });
@@ -0,0 +1,29 @@
1
+ /**
2
+ * How much of an order can still be refunded — the one server-side answer.
3
+ *
4
+ * `refundable-base.ts` is deliberately PURE: it takes `legacyGstPercent` as an
5
+ * argument rather than reading settings, so it stays testable and so a caller
6
+ * cannot accidentally apply today's GST rate to a historical order. That purity
7
+ * is right, and it leaves every server caller needing the same three lines to
8
+ * supply the argument.
9
+ *
10
+ * Three callers now need them — `cancelOrderWithRefund`, `obligationSweep`'s
11
+ * auto-refund branches, and `processRefundAction`'s own cap check — and three
12
+ * copies of a money calculation is Root Cause #75's exact shape. So the
13
+ * settings read lives here, once.
14
+ *
15
+ * 🛑 The read is for ONE case and it is not optional. Orders written before
16
+ * `platformFeeGst` was persisted (2026-10-21) carry no stored fee GST, so it
17
+ * has to be derived from the rate — and if two callers derive it differently,
18
+ * or one skips it, they disagree about the cap. The direction of that
19
+ * disagreement matters: skipping the GST INFLATES the refundable base, so a
20
+ * "refund everything" leaves a sub-rupee residue and the order can never reach
21
+ * `status: "refunded"` — which is D3, reintroduced by omission.
22
+ *
23
+ * A failed settings read degrades to `undefined`, which means legacy orders
24
+ * under-withhold rather than the whole refund failing. That is the safer
25
+ * direction for the buyer and it is visible: the residue shows up as an order
26
+ * stuck short of `refunded`, which R5 in `paymentReconcile` reports.
27
+ */
28
+ import { type RefundCapacity, type RefundableOrderLike } from "../../../shared/checkout/refundable-base";
29
+ export declare function resolveRefundCapacity(order: RefundableOrderLike): Promise<RefundCapacity>;
@@ -0,0 +1,37 @@
1
+ /**
2
+ * How much of an order can still be refunded — the one server-side answer.
3
+ *
4
+ * `refundable-base.ts` is deliberately PURE: it takes `legacyGstPercent` as an
5
+ * argument rather than reading settings, so it stays testable and so a caller
6
+ * cannot accidentally apply today's GST rate to a historical order. That purity
7
+ * is right, and it leaves every server caller needing the same three lines to
8
+ * supply the argument.
9
+ *
10
+ * Three callers now need them — `cancelOrderWithRefund`, `obligationSweep`'s
11
+ * auto-refund branches, and `processRefundAction`'s own cap check — and three
12
+ * copies of a money calculation is Root Cause #75's exact shape. So the
13
+ * settings read lives here, once.
14
+ *
15
+ * 🛑 The read is for ONE case and it is not optional. Orders written before
16
+ * `platformFeeGst` was persisted (2026-10-21) carry no stored fee GST, so it
17
+ * has to be derived from the rate — and if two callers derive it differently,
18
+ * or one skips it, they disagree about the cap. The direction of that
19
+ * disagreement matters: skipping the GST INFLATES the refundable base, so a
20
+ * "refund everything" leaves a sub-rupee residue and the order can never reach
21
+ * `status: "refunded"` — which is D3, reintroduced by omission.
22
+ *
23
+ * A failed settings read degrades to `undefined`, which means legacy orders
24
+ * under-withhold rather than the whole refund failing. That is the safer
25
+ * direction for the buyer and it is visible: the residue shows up as an order
26
+ * stuck short of `refunded`, which R5 in `paymentReconcile` reports.
27
+ */
28
+ import { siteSettingsRepository } from "../../../../features/admin/repository/site-settings.repository.js";
29
+ import { refundCapacity, } from "../../../shared/checkout/refundable-base.js";
30
+ import { normalizeError } from "../../../../errors/normalize.js";
31
+ export async function resolveRefundCapacity(order) {
32
+ const settings = await siteSettingsRepository.getSingleton().catch((err) => {
33
+ void normalizeError(err);
34
+ return null;
35
+ });
36
+ return refundCapacity(order, settings?.commissions?.gstPercent);
37
+ }