@mohasinac/appkit 4.41.16 → 4.42.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (165) hide show
  1. package/dist/_internal/client/features/maintenance/views/MaintenanceDashboardView.js +1 -1
  2. package/dist/_internal/server/features/checkout/actions.d.ts +16 -14
  3. package/dist/_internal/server/features/checkout/actions.js +145 -97
  4. package/dist/_internal/server/features/checkout/bundle-expansion.d.ts +1 -1
  5. package/dist/_internal/server/features/checkout/index.d.ts +1 -1
  6. package/dist/_internal/server/features/checkout/index.js +1 -1
  7. package/dist/_internal/server/features/checkout/locked-lines.js +1 -1
  8. package/dist/_internal/server/features/checkout/source-context.js +2 -2
  9. package/dist/_internal/server/features/maintenance/analyze.js +1 -1
  10. package/dist/_internal/server/features/refunds/actions.d.ts +4 -4
  11. package/dist/_internal/server/features/refunds/actions.js +9 -8
  12. package/dist/_internal/server/functions/scheduled.d.ts +1 -2
  13. package/dist/_internal/server/functions/scheduled.js +1 -9
  14. package/dist/_internal/server/jobs/core/index.d.ts +0 -1
  15. package/dist/_internal/server/jobs/core/index.js +0 -1
  16. package/dist/_internal/server/jobs/handlers/index.d.ts +0 -1
  17. package/dist/_internal/server/jobs/handlers/index.js +0 -1
  18. package/dist/_internal/shared/actions/action-registry.js +2 -2
  19. package/dist/_internal/shared/checkout/order-math.d.ts +1 -1
  20. package/dist/_internal/shared/checkout/order-math.js +1 -1
  21. package/dist/_internal/shared/checkout/rules/prize-draw.rule.js +2 -2
  22. package/dist/_internal/shared/features/blog/schema.d.ts +4 -4
  23. package/dist/_internal/shared/features/cart/schema.d.ts +6 -6
  24. package/dist/_internal/shared/features/orders/refund-copy.d.ts +1 -1
  25. package/dist/_internal/shared/features/orders/refund-copy.js +1 -1
  26. package/dist/_internal/shared/features/orders/schema.d.ts +5 -5
  27. package/dist/_internal/shared/features/orders/schema.js +1 -1
  28. package/dist/_internal/shared/features/products/schema.d.ts +2 -2
  29. package/dist/_internal/shared/features/promotions/schema.d.ts +4 -4
  30. package/dist/_internal/shared/features/reviews/schema.d.ts +1 -1
  31. package/dist/client.d.ts +1 -1
  32. package/dist/client.js +1 -1
  33. package/dist/configs/next.js +2 -2
  34. package/dist/constants/field-names.d.ts +1 -1
  35. package/dist/constants/field-names.js +1 -1
  36. package/dist/contracts/index.d.ts +0 -2
  37. package/dist/contracts/index.js +0 -1
  38. package/dist/contracts/payment.d.ts +13 -2
  39. package/dist/contracts/payment.js +2 -2
  40. package/dist/core/integration-keys.d.ts +7 -3
  41. package/dist/core/integration-keys.js +13 -7
  42. package/dist/core/money.d.ts +11 -0
  43. package/dist/core/money.js +15 -0
  44. package/dist/errors/index.d.ts +1 -1
  45. package/dist/errors/index.js +1 -1
  46. package/dist/errors/messages.d.ts +1 -0
  47. package/dist/errors/messages.js +1 -0
  48. package/dist/errors/{razorpay-unreachable.d.ts → payment-provider-unreachable.d.ts} +1 -1
  49. package/dist/errors/{razorpay-unreachable.js → payment-provider-unreachable.js} +1 -1
  50. package/dist/features/addresses/schemas/address-form.d.ts +20 -20
  51. package/dist/features/addresses/schemas/index.d.ts +2 -2
  52. package/dist/features/admin/components/AdminSiteSettingsView.js +32 -16
  53. package/dist/features/admin/constants/site-settings-tabs.d.ts +1 -1
  54. package/dist/features/admin/schemas/admin-ops-forms.d.ts +10 -10
  55. package/dist/features/admin/schemas/admin-user-form.d.ts +2 -2
  56. package/dist/features/admin/schemas/firestore.d.ts +17 -8
  57. package/dist/features/admin/schemas/firestore.js +1 -1
  58. package/dist/features/admin/schemas/nav-item-form.d.ts +2 -2
  59. package/dist/features/auctions/schemas/index.d.ts +2 -2
  60. package/dist/features/auth/schemas/index.d.ts +2 -2
  61. package/dist/features/before-after/schemas/index.d.ts +2 -2
  62. package/dist/features/blog/actions/blog-actions.d.ts +4 -4
  63. package/dist/features/blog/schemas/blog-form.d.ts +2 -2
  64. package/dist/features/blog/schemas/index.d.ts +4 -4
  65. package/dist/features/cart/schemas/index.d.ts +3 -3
  66. package/dist/features/catalogue/schemas/validation.d.ts +4 -4
  67. package/dist/features/categories/schemas/index.d.ts +2 -2
  68. package/dist/features/checkout/index.d.ts +0 -4
  69. package/dist/features/checkout/index.js +0 -3
  70. package/dist/features/checkout/types/index.d.ts +0 -25
  71. package/dist/features/checkout/types/index.js +1 -2
  72. package/dist/features/collections/schemas/index.d.ts +2 -2
  73. package/dist/features/consultation/schemas/index.d.ts +4 -4
  74. package/dist/features/corporate/schemas/index.d.ts +2 -2
  75. package/dist/features/events/schemas/index.d.ts +4 -4
  76. package/dist/features/grouped/schemas/index.d.ts +2 -2
  77. package/dist/features/homepage/schemas/index.d.ts +2 -2
  78. package/dist/features/lottery/schemas/zod.d.ts +4 -4
  79. package/dist/features/loyalty/schemas/index.d.ts +6 -6
  80. package/dist/features/orders/components/OrderPaymentSummary.d.ts +1 -1
  81. package/dist/features/orders/components/OrderPaymentSummary.js +5 -5
  82. package/dist/features/orders/components/RefundHistoryTable.js +1 -1
  83. package/dist/features/orders/repository/orders.repository.d.ts +10 -1
  84. package/dist/features/orders/repository/orders.repository.js +17 -1
  85. package/dist/features/orders/schemas/firestore.d.ts +11 -12
  86. package/dist/features/orders/schemas/firestore.js +0 -1
  87. package/dist/features/orders/schemas/index.d.ts +48 -48
  88. package/dist/features/orders/schemas/index.js +1 -1
  89. package/dist/features/orders/types/index.d.ts +1 -3
  90. package/dist/features/orders/types/index.js +1 -2
  91. package/dist/features/payments/manifest.js +1 -1
  92. package/dist/features/payments/repository/payout.repository.d.ts +1 -4
  93. package/dist/features/payments/repository/payout.repository.js +0 -29
  94. package/dist/features/payments/schemas/firestore.d.ts +8 -15
  95. package/dist/features/payments/schemas/firestore.js +7 -5
  96. package/dist/features/payments/schemas/index.d.ts +52 -125
  97. package/dist/features/payments/schemas/index.js +0 -58
  98. package/dist/features/payments/types/index.d.ts +2 -4
  99. package/dist/features/payments/types/index.js +1 -2
  100. package/dist/features/products/schemas/index.d.ts +14 -14
  101. package/dist/features/products/schemas/product-features.validators.d.ts +10 -10
  102. package/dist/features/promotions/schemas/index.d.ts +20 -20
  103. package/dist/features/reviews/schemas/index.d.ts +8 -8
  104. package/dist/features/scams/schemas/index.d.ts +10 -10
  105. package/dist/features/search/action-index/settings-entries.js +4 -4
  106. package/dist/features/seller/schemas/index.d.ts +12 -12
  107. package/dist/features/seller/schemas/order-forms.d.ts +4 -4
  108. package/dist/features/shipments/schemas/validation.d.ts +28 -28
  109. package/dist/features/store-extensions/schemas/payout-method-form.d.ts +50 -50
  110. package/dist/features/store-extensions/schemas/report-create-form.d.ts +2 -2
  111. package/dist/features/store-extensions/schemas/shipping-config-form.d.ts +10 -10
  112. package/dist/features/support/schemas/index.d.ts +14 -14
  113. package/dist/features/tester/actions/checklist-item-actions.d.ts +4 -4
  114. package/dist/features/tester/schemas/checklist-item-form.d.ts +2 -2
  115. package/dist/features/tester/seed-data/_money-flows.js +145 -0
  116. package/dist/features/tester/seed-data/authored/account-auth__signup-login.js +21 -2
  117. package/dist/features/tester/seed-data/authored/admin__site-system.js +38 -0
  118. package/dist/features/tester/seed-data/authored/buying__buying-checkout.js +10 -10
  119. package/dist/features/tester/seed-data/authored/buying__offers.js +11 -10
  120. package/dist/features/tester/seed-data/authored/cta-layout__checkout-bottom-bar.js +1 -1
  121. package/dist/features/tester/seed-data/tester-checklist-seed-data.js +24 -12
  122. package/dist/features/wishlist/schemas/index.d.ts +2 -2
  123. package/dist/index.d.ts +6 -28
  124. package/dist/index.js +8 -51
  125. package/dist/jobs.d.ts +1 -1
  126. package/dist/jobs.js +1 -1
  127. package/dist/providers/payment-phonepe/index.d.ts +117 -0
  128. package/dist/providers/payment-phonepe/index.js +221 -0
  129. package/dist/providers.d.ts +3 -2
  130. package/dist/providers.js +3 -2
  131. package/dist/schemas/registry.d.ts +163 -2623
  132. package/dist/schemas/registry.js +1 -6
  133. package/dist/seed/faq-seed-data.js +5 -5
  134. package/dist/seed/payouts-seed-data.js +3 -3
  135. package/dist/seed/site-settings-seed-data.js +7 -4
  136. package/dist/server-entry.d.ts +1 -2
  137. package/dist/server-entry.js +1 -3
  138. package/dist/server.d.ts +3 -9
  139. package/dist/server.js +5 -23
  140. package/dist/validation/schemas.d.ts +2 -2
  141. package/package.json +5 -4
  142. package/dist/_internal/server/features/payments/actions.d.ts +0 -30
  143. package/dist/_internal/server/features/payments/actions.js +0 -45
  144. package/dist/_internal/server/features/payments/data.d.ts +0 -13
  145. package/dist/_internal/server/features/payments/data.js +0 -20
  146. package/dist/_internal/server/features/payments/index.d.ts +0 -3
  147. package/dist/_internal/server/features/payments/index.js +0 -3
  148. package/dist/_internal/server/jobs/core/payoutBatch.d.ts +0 -2
  149. package/dist/_internal/server/jobs/core/payoutBatch.js +0 -120
  150. package/dist/_internal/server/jobs/handlers/payoutBatch.d.ts +0 -2
  151. package/dist/_internal/server/jobs/handlers/payoutBatch.js +0 -2
  152. package/dist/_internal/shared/features/payments/config.d.ts +0 -3
  153. package/dist/_internal/shared/features/payments/config.js +0 -3
  154. package/dist/contracts/client-payment-gateway.d.ts +0 -64
  155. package/dist/contracts/client-payment-gateway.js +0 -25
  156. package/dist/features/checkout/hooks/useCheckout.d.ts +0 -9
  157. package/dist/features/checkout/hooks/useCheckout.js +0 -24
  158. package/dist/features/checkout/hooks/useCheckoutApi.d.ts +0 -72
  159. package/dist/features/checkout/hooks/useCheckoutApi.js +0 -47
  160. package/dist/features/checkout/hooks/usePaymentCheckout.d.ts +0 -77
  161. package/dist/features/checkout/hooks/usePaymentCheckout.js +0 -192
  162. package/dist/providers/payment-razorpay/index.d.ts +0 -115
  163. package/dist/providers/payment-razorpay/index.js +0 -208
  164. package/dist/schemas/webhooks/razorpay.d.ts +0 -2634
  165. package/dist/schemas/webhooks/razorpay.js +0 -104
@@ -9,5 +9,5 @@ const card = {
9
9
  export function MaintenanceDashboardView({ counts, basePath, }) {
10
10
  return (_jsxs(Div, { style: { padding: "1.5rem", maxWidth: "1200px", margin: "0 auto" }, children: [_jsx(Heading, { level: 1, style: { fontSize: "1.5rem", fontWeight: 600 }, children: "Maintenance" }), _jsx(Text, { as: "p", style: { color: "var(--appkit-color-text-muted)", marginBottom: "1.25rem" }, children: "Errors and failures across the platform \u2014 server, client, and Cloud Functions." }), _jsxs(Div, { style: { display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(170px, 1fr))", gap: "0.75rem", marginBottom: "1.5rem" }, children: [_jsxs(Div, { style: card, children: [_jsx(Div, { style: { fontSize: "0.78rem", color: "var(--appkit-color-text-muted)" }, children: "Last 24 hours" }), _jsx(Div, { style: { fontSize: "1.75rem", fontWeight: 700 }, children: counts.last24h })] }), _jsxs(Div, { style: card, children: [_jsx(Div, { style: { fontSize: "0.78rem", color: "var(--appkit-color-text-muted)" }, children: "Last 7 days" }), _jsx(Div, { style: { fontSize: "1.75rem", fontWeight: 700 }, children: counts.last7d })] }), _jsxs(Div, { style: card, children: [_jsx(Div, { style: { fontSize: "0.78rem", color: "var(--appkit-color-text-muted)" }, children: "Last 30 days" }), _jsx(Div, { style: { fontSize: "1.75rem", fontWeight: 700 }, children: counts.last30d })] }), _jsxs(Div, { style: card, children: [_jsx(Div, { style: { fontSize: "0.78rem", color: "var(--appkit-color-text-muted)" }, children: "Server (Vercel)" }), _jsx(Div, { style: { fontSize: "1.5rem", fontWeight: 700 }, children: counts.bySource.vercel })] }), _jsxs(Div, { style: card, children: [_jsx(Div, { style: { fontSize: "0.78rem", color: "var(--appkit-color-text-muted)" }, children: "Client" }), _jsx(Div, { style: { fontSize: "1.5rem", fontWeight: 700 }, children: counts.bySource.client })] }), _jsxs(Div, { style: card, children: [_jsx(Div, { style: { fontSize: "0.78rem", color: "var(--appkit-color-text-muted)" }, children: "Cloud Functions" }), _jsx(Div, { style: { fontSize: "1.5rem", fontWeight: 700 }, children: counts.bySource.function })] })] }), _jsxs(Section, { style: { marginBottom: "1.5rem" }, children: [_jsx(Heading, { level: 2, style: { fontSize: "1rem", fontWeight: 600 }, children: "Top codes (30d)" }), _jsx(Ul, { style: { listStyle: "none", padding: 0 }, children: counts.topCodes.length === 0 ? _jsx(Li, { style: { color: "var(--appkit-color-text-muted)" }, children: "No errors recorded." }) :
11
11
  counts.topCodes.map((c) => (_jsxs(Li, { style: { display: "flex", justifyContent: "space-between", padding: "0.25rem 0", borderBottom: "1px solid var(--appkit-color-border-subtle)" }, children: [_jsx(Span, { style: { fontFamily: "monospace" }, children: c.code }), _jsx(Span, { children: c.count })] }, c.code))) })] }), _jsxs(Section, { style: { marginBottom: "1.5rem" }, children: [_jsx(Heading, { level: 2, style: { fontSize: "1rem", fontWeight: 600 }, children: "Top routes (30d)" }), _jsx(Ul, { style: { listStyle: "none", padding: 0 }, children: counts.topRoutes.length === 0 ? _jsx(Li, { style: { color: "var(--appkit-color-text-muted)" }, children: "No errors recorded." }) :
12
- counts.topRoutes.map((r) => (_jsxs(Li, { style: { display: "flex", justifyContent: "space-between", padding: "0.25rem 0", borderBottom: "1px solid var(--appkit-color-border-subtle)" }, children: [_jsxs(Span, { style: { fontFamily: "monospace" }, children: ["[", r.source, "] ", r.route] }), _jsx(Span, { children: r.count })] }, `${r.source}:${r.route}`))) })] }), _jsxs(Nav, { "aria-label": "Maintenance sections", style: { display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(200px, 1fr))", gap: "0.5rem" }, children: [_jsxs(Anchor, { href: `${basePath}/server-errors`, tone: "default", underline: "none", style: { ...card }, children: [_jsx(Span, { weight: "bold", children: "Server errors" }), _jsx(Div, { style: { color: "var(--appkit-color-text-muted)", fontSize: "0.85rem" }, children: "Vercel function failures" })] }), _jsxs(Anchor, { href: `${basePath}/client-errors`, tone: "default", underline: "none", style: { ...card }, children: [_jsx(Span, { weight: "bold", children: "Client errors" }), _jsx(Div, { style: { color: "var(--appkit-color-text-muted)", fontSize: "0.85rem" }, children: "window.onerror + unhandledrejection + boundaries" })] }), _jsxs(Anchor, { href: `${basePath}/function-errors`, tone: "default", underline: "none", style: { ...card }, children: [_jsx(Span, { weight: "bold", children: "Function errors" }), _jsx(Div, { style: { color: "var(--appkit-color-text-muted)", fontSize: "0.85rem" }, children: "Cloud Functions handler exceptions" })] }), _jsxs(Anchor, { href: `${basePath}/cloud-logs`, tone: "default", underline: "none", style: { ...card }, children: [_jsx(Span, { weight: "bold", children: "Cloud Logging" }), _jsx(Div, { style: { color: "var(--appkit-color-text-muted)", fontSize: "0.85rem" }, children: "Live stream from Cloud Logging API" })] }), _jsxs(Anchor, { href: `${basePath}/payment-rollbacks`, tone: "default", underline: "none", style: { ...card }, children: [_jsx(Span, { weight: "bold", children: "Payment rollbacks" }), _jsx(Div, { style: { color: "var(--appkit-color-text-muted)", fontSize: "0.85rem" }, children: "Razorpay refund + upstream-unavailable rows" })] }), _jsxs(Anchor, { href: `${basePath}/analysis`, tone: "default", underline: "none", style: { ...card }, children: [_jsx(Span, { weight: "bold", children: "Analysis" }), _jsx(Div, { style: { color: "var(--appkit-color-text-muted)", fontSize: "0.85rem" }, children: "Run the maintenance analyzer + recommendations" })] })] })] }));
12
+ counts.topRoutes.map((r) => (_jsxs(Li, { style: { display: "flex", justifyContent: "space-between", padding: "0.25rem 0", borderBottom: "1px solid var(--appkit-color-border-subtle)" }, children: [_jsxs(Span, { style: { fontFamily: "monospace" }, children: ["[", r.source, "] ", r.route] }), _jsx(Span, { children: r.count })] }, `${r.source}:${r.route}`))) })] }), _jsxs(Nav, { "aria-label": "Maintenance sections", style: { display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(200px, 1fr))", gap: "0.5rem" }, children: [_jsxs(Anchor, { href: `${basePath}/server-errors`, tone: "default", underline: "none", style: { ...card }, children: [_jsx(Span, { weight: "bold", children: "Server errors" }), _jsx(Div, { style: { color: "var(--appkit-color-text-muted)", fontSize: "0.85rem" }, children: "Vercel function failures" })] }), _jsxs(Anchor, { href: `${basePath}/client-errors`, tone: "default", underline: "none", style: { ...card }, children: [_jsx(Span, { weight: "bold", children: "Client errors" }), _jsx(Div, { style: { color: "var(--appkit-color-text-muted)", fontSize: "0.85rem" }, children: "window.onerror + unhandledrejection + boundaries" })] }), _jsxs(Anchor, { href: `${basePath}/function-errors`, tone: "default", underline: "none", style: { ...card }, children: [_jsx(Span, { weight: "bold", children: "Function errors" }), _jsx(Div, { style: { color: "var(--appkit-color-text-muted)", fontSize: "0.85rem" }, children: "Cloud Functions handler exceptions" })] }), _jsxs(Anchor, { href: `${basePath}/cloud-logs`, tone: "default", underline: "none", style: { ...card }, children: [_jsx(Span, { weight: "bold", children: "Cloud Logging" }), _jsx(Div, { style: { color: "var(--appkit-color-text-muted)", fontSize: "0.85rem" }, children: "Live stream from Cloud Logging API" })] }), _jsxs(Anchor, { href: `${basePath}/payment-rollbacks`, tone: "default", underline: "none", style: { ...card }, children: [_jsx(Span, { weight: "bold", children: "Payment rollbacks" }), _jsx(Div, { style: { color: "var(--appkit-color-text-muted)", fontSize: "0.85rem" }, children: "PhonePe refund + upstream-unavailable rows" })] }), _jsxs(Anchor, { href: `${basePath}/analysis`, tone: "default", underline: "none", style: { ...card }, children: [_jsx(Span, { weight: "bold", children: "Analysis" }), _jsx(Div, { style: { color: "var(--appkit-color-text-muted)", fontSize: "0.85rem" }, children: "Run the maintenance analyzer + recommendations" })] })] })] }));
13
13
  }
@@ -104,7 +104,7 @@ export interface CheckoutPricingPreview {
104
104
  /**
105
105
  * Read-only "what will I actually be charged" preview for the checkout
106
106
  * Order Summary. Reuses the exact same building blocks
107
- * createCheckoutOrderAction / verifyAndPlaceRazorpayOrderAction use when an
107
+ * createCheckoutOrderAction / verifyAndPlacePhonePeOrderAction use when an
108
108
  * order is actually placed — resolveShippingCost, the compute*Fee helpers,
109
109
  * computeGroupCouponDiscount, and the GST calc — so the number shown to the
110
110
  * buyer can never diverge from what gets charged/recorded. Never decrements
@@ -120,23 +120,25 @@ export declare function attachPaymentAction(input: {
120
120
  paymentId: string;
121
121
  paidAmount: number;
122
122
  }): Promise<void>;
123
- export interface VerifyAndPlaceRazorpayOrderInput {
124
- userId: string;
125
- userName: string;
126
- userEmail: string;
127
- razorpay_order_id: string;
128
- razorpay_payment_id: string;
129
- razorpay_signature: string;
130
- /** Required for physical carts; omitted for digital-code-only carts. */
123
+ export interface VerifyAndPlacePhonePeOrderInput {
124
+ /** PhonePe's merchantOrderId — our own id, generated in PhonePeProvider.createOrder(). */
125
+ merchantOrderId: string;
126
+ /**
127
+ * Buyer identity. Omit on the webhook path — no session exists there, so
128
+ * it's resolved from `uid` packed into PhonePe's `metaInfo` at
129
+ * create-order time and re-fetched from `userRepository`.
130
+ */
131
+ userId?: string;
132
+ userName?: string;
133
+ userEmail?: string;
134
+ /** Present on the buyer-present path; falls back to metaInfo (webhook path) when omitted. Required for physical carts. */
131
135
  addressId?: string;
132
136
  notes?: string;
133
137
  /**
134
138
  * Buyer's choice for what to do when a cart item is unavailable at
135
- * checkout time. Defaults to "cancel_order" — matches this path's
136
- * historical (only) behavior for any caller/old-client that omits the
137
- * field, since "skip_items" partial fulfillment has no prior precedent
138
- * on the Razorpay path.
139
+ * checkout time. Defaults to "cancel_order" when neither the caller nor
140
+ * metaInfo supplies one — matches this path's historical (only) behavior.
139
141
  */
140
142
  outOfStockPolicy?: OutOfStockPolicy;
141
143
  }
142
- export declare function verifyAndPlaceRazorpayOrderAction(input: VerifyAndPlaceRazorpayOrderInput): Promise<CheckoutOrderResult>;
144
+ export declare function verifyAndPlacePhonePeOrderAction(input: VerifyAndPlacePhonePeOrderInput): Promise<CheckoutOrderResult>;
@@ -29,7 +29,8 @@ import { getCartItemMemberIds, getExpandedDecrements, bucketCartItemsByStock, is
29
29
  import { expandCartLineToOrderRows } from "./expand-order-items";
30
30
  import { OrderStatusValues, PaymentStatusValues, PaymentMethodValues, OutOfStockPolicyValues, } from "../../../../features/orders/schemas/index";
31
31
  import { getDefaultCurrency } from "../../../../core/index";
32
- import { verifyPaymentSignatureWithKeys, fetchRazorpayOrder, paiseToRupees, } from "../../../../providers/payment-razorpay/index";
32
+ import { getProviders } from "../../../../contracts";
33
+ import { paiseToRupees } from "../../../../core/money";
33
34
  import { CHECKOUT_DEFAULT_COMMISSIONS, CHECKOUT_DEFAULT_EMI_SETTINGS } from "../../../shared/features/checkout/config";
34
35
  import { checkEmiEligibility, computeEmiSchedule } from "../../../shared/features/emi/schedule";
35
36
  import { formatShippingAddress } from "./data";
@@ -204,7 +205,7 @@ async function resolveStoreState(storeId) {
204
205
  * Prorates every buyer-applied coupon against one seller-group's total. Pure
205
206
  * — no side effects (usage-accumulator bookkeeping is layered on by callers
206
207
  * that actually place an order). Shared by createOrderForGroup,
207
- * createRazorpayGroupOrder, and the read-only previewCheckoutPricing so the
208
+ * createPhonePeGroupOrder, and the read-only previewCheckoutPricing so the
208
209
  * three can never drift on how a coupon splits across sellers (was three
209
210
  * near-identical copies before this extraction — Duplication Decision
210
211
  * Framework's Rule of Three).
@@ -455,8 +456,8 @@ async function createOrderForGroup(group, orderType, ctx) {
455
456
  }
456
457
  }
457
458
  // The platform commission is now charged on EVERY payment method. It used to
458
- // be added only on the Razorpay path, so COD / UPI-manual / cash / EMI buyers
459
- // silently never paid it.
459
+ // be added only on the online-payment path, so COD / UPI-manual / cash / EMI
460
+ // buyers silently never paid it.
460
461
  const orderTotal = Math.max(0, groupTotal - couponDiscount) + shippingFee + codHandlingFee + whatsappNotifyFee + giftWrapFee + shipmentProtectionFee + platformFee + platformFeeGst + (emiSchedule?.surchargeAmount ?? 0) + (gstBreakdown?.gstAmount ?? 0);
461
462
  const imageUrls = [
462
463
  ...new Set(group
@@ -471,7 +472,7 @@ async function createOrderForGroup(group, orderType, ctx) {
471
472
  };
472
473
  // ── 15-minute payment window + seller UPI resolution (Tier PP) ──────────
473
474
  // Deadline + displayed UPI apply only to methods with a buyer-uploaded
474
- // proof step — never cod (paid on delivery), razorpay (paid before order
475
+ // proof step — never cod (paid on delivery), online (paid before order
475
476
  // creation), or admin_bypass (already marked paid above).
476
477
  const hasPaymentWindow = !adminBypass &&
477
478
  (paymentMethod === PaymentMethodValues.UPI_MANUAL ||
@@ -888,7 +889,7 @@ const EMPTY_PRICING_PREVIEW = {
888
889
  /**
889
890
  * Read-only "what will I actually be charged" preview for the checkout
890
891
  * Order Summary. Reuses the exact same building blocks
891
- * createCheckoutOrderAction / verifyAndPlaceRazorpayOrderAction use when an
892
+ * createCheckoutOrderAction / verifyAndPlacePhonePeOrderAction use when an
892
893
  * order is actually placed — resolveShippingCost, the compute*Fee helpers,
893
894
  * computeGroupCouponDiscount, and the GST calc — so the number shown to the
894
895
  * buyer can never diverge from what gets charged/recorded. Never decrements
@@ -1039,23 +1040,27 @@ export async function attachPaymentAction(input) {
1039
1040
  });
1040
1041
  }
1041
1042
  /**
1042
- * Place order(s) from the user's cart after a Razorpay payment is verified.
1043
- * Mirrors the existing /api/payment/verify route handler.
1043
+ * Place order(s) from the user's cart after a PhonePe payment is confirmed.
1044
+ * Called from both /api/payment/verify (buyer present) and the webhook
1045
+ * (buyer may be gone) — see the module-level comment above.
1044
1046
  *
1045
- * Consumers must authenticate the user before calling. The action performs:
1046
- * 1. HMAC signature verification (rejects forged callbacks)
1047
- * 2. Cart re-validation against current product prices/stock
1048
- * 3. Amount cross-check against the Razorpay order record
1049
- * 4. Atomic stock decrement + cart clear via unitOfWork batch
1050
- * 5. Multi-coupon pro-rating per order group
1051
- * 6. order_placed notifications (buyer + seller)
1052
- * 7. Confirmation email + RTDB success signal (both fire-and-forget)
1047
+ * Performs:
1048
+ * 1. Order Status confirmation via `getProviders().payment.getOrder()` —
1049
+ * the sole source of truth; PhonePe never hands the browser a
1050
+ * verifiable proof of payment.
1051
+ * 2. An idempotency claim so only one of the two callers proceeds.
1052
+ * 3. Cart re-validation against current product prices/stock.
1053
+ * 4. Amount cross-check against the confirmed PhonePe order.
1054
+ * 5. Atomic stock decrement + cart clear via unitOfWork batch.
1055
+ * 6. Multi-coupon pro-rating per order group.
1056
+ * 7. order_placed notifications (buyer + seller).
1057
+ * 8. Confirmation email + RTDB success signal (both fire-and-forget).
1053
1058
  */
1054
1059
  /**
1055
- * Auto-refund the value of items dropped from a Razorpay checkout under the
1056
- * "skip_items" out-of-stock policy. Razorpay captures payment for the FULL
1060
+ * Auto-refund the value of items dropped from a PhonePe checkout under the
1061
+ * "skip_items" out-of-stock policy. PhonePe captures payment for the FULL
1057
1062
  * cart before the stock check runs, so anything not placed as an order must
1058
- * be given back. Extracted from `verifyAndPlaceRazorpayOrderAction` — same
1063
+ * be given back. Extracted from `verifyAndPlacePhonePeOrderAction` — same
1059
1064
  * behavior, kept as a named step so the parent function stays readable.
1060
1065
  *
1061
1066
  * Awaited by the caller (this moves money, never fire-and-forget). On
@@ -1063,8 +1068,8 @@ export async function attachPaymentAction(input) {
1063
1068
  * created and paid); the failure is surfaced via `order.refundPending` + an
1064
1069
  * admin notification fan-out, never silently dropped (Rule #8).
1065
1070
  */
1066
- async function refundDroppedItemsForRazorpayCheckout(input) {
1067
- const { unavailablePaid, orderIds, productByIdPaid, razorpayPaymentId } = input;
1071
+ async function refundDroppedItemsForPhonePeCheckout(input) {
1072
+ const { unavailablePaid, orderIds, productByIdPaid, phonepeOrderId } = input;
1068
1073
  if (unavailablePaid.length === 0 || orderIds.length === 0)
1069
1074
  return;
1070
1075
  const droppedValue = unavailablePaid.reduce((sum, u) => sum + (productByIdPaid.get(u.productId)?.price ?? 0) * u.requestedQty, 0);
@@ -1076,9 +1081,9 @@ async function refundDroppedItemsForRazorpayCheckout(input) {
1076
1081
  // Cap defensively at the primary order's total — processRefundAction
1077
1082
  // rejects any amount exceeding order.totalPrice, and a multi-order
1078
1083
  // batch's dropped-items value isn't cleanly attributable to a single
1079
- // order (Razorpay refunds are keyed by paymentId, not orderId; the
1080
- // FIRST order created in this batch is used as the refund's book-
1081
- // keeping anchor).
1084
+ // order (PhonePe refunds are keyed by the original order id, not a
1085
+ // payment id; the FIRST order created in this batch is used as the
1086
+ // refund's book-keeping anchor).
1082
1087
  const refundAmount = primaryOrder
1083
1088
  ? Math.min(droppedValue, primaryOrder.totalPrice)
1084
1089
  : droppedValue;
@@ -1095,8 +1100,8 @@ async function refundDroppedItemsForRazorpayCheckout(input) {
1095
1100
  */
1096
1101
  reasonCode: "not_received",
1097
1102
  reasonNote: `Automatic refund — ${unavailablePaid.length} item(s) unavailable at checkout: ${unavailablePaid.map((u) => u.productTitle).join(", ")}`,
1098
- method: "razorpay",
1099
- razorpayPaymentId,
1103
+ method: "phonepe",
1104
+ phonepeOrderId,
1100
1105
  confirmIrrevocable: true,
1101
1106
  refundedBy: "system:checkout-auto-refund",
1102
1107
  });
@@ -1106,7 +1111,7 @@ async function refundDroppedItemsForRazorpayCheckout(input) {
1106
1111
  }
1107
1112
  catch (refundErr) {
1108
1113
  void normalizeError(refundErr);
1109
- serverLogger.warn("verifyAndPlaceRazorpayOrderAction: automatic partial refund for dropped items failed — flagging for manual follow-up", {
1114
+ serverLogger.warn("verifyAndPlacePhonePeOrderAction: automatic partial refund for dropped items failed — flagging for manual follow-up", {
1110
1115
  orderId: primaryOrderId,
1111
1116
  droppedValue,
1112
1117
  err: refundErr instanceof Error ? refundErr.message : String(refundErr),
@@ -1115,7 +1120,7 @@ async function refundDroppedItemsForRazorpayCheckout(input) {
1115
1120
  .update(primaryOrderId, { refundPending: true })
1116
1121
  .catch((updErr) => {
1117
1122
  void normalizeError(updErr);
1118
- serverLogger.error("verifyAndPlaceRazorpayOrderAction: failed to flag refundPending after auto-refund failure", { orderId: primaryOrderId, err: updErr instanceof Error ? updErr.message : String(updErr) });
1123
+ serverLogger.error("verifyAndPlacePhonePeOrderAction: failed to flag refundPending after auto-refund failure", { orderId: primaryOrderId, err: updErr instanceof Error ? updErr.message : String(updErr) });
1119
1124
  });
1120
1125
  /*
1121
1126
  * One admin-inbox row, not one notification per admin.
@@ -1143,20 +1148,20 @@ async function refundDroppedItemsForRazorpayCheckout(input) {
1143
1148
  })
1144
1149
  .catch((notifyErr) => {
1145
1150
  void normalizeError(notifyErr);
1146
- serverLogger.error("verifyAndPlaceRazorpayOrderAction: failed to write admin notification for failed auto-refund", { orderId: primaryOrderId });
1151
+ serverLogger.error("verifyAndPlacePhonePeOrderAction: failed to write admin notification for failed auto-refund", { orderId: primaryOrderId });
1147
1152
  });
1148
1153
  }
1149
1154
  }
1150
1155
  /**
1151
- * Places one order for one seller-group within `verifyAndPlaceRazorpayOrderAction`
1156
+ * Places one order for one seller-group within `verifyAndPlacePhonePeOrderAction`
1152
1157
  * — mirrors `createOrderForGroup`'s role on the COD/cash/EMI path (same
1153
1158
  * per-group fee/coupon/order-doc/notification shape), extracted into its own
1154
1159
  * function purely to keep the parent function under the LARGE_COMPONENT
1155
1160
  * audit threshold (`scripts/audit-code-quality.mjs`). No behavior change
1156
1161
  * from the inline version this replaced.
1157
1162
  */
1158
- async function createRazorpayGroupOrder(group, orderType, ctx) {
1159
- const { appliedCoupons, cartSubtotal, storeAddons, platformFeeByStore, siteSettings, uid, userName, userEmail, razorpay_order_id, razorpay_payment_id, razorpay_signature, shippingAddress, notes, outOfStockPolicy, unavailablePaid, orderIds, emailsToSend, couponUsageAccumulator, productById: razorpayProductById, } = ctx;
1163
+ async function createPhonePeGroupOrder(group, orderType, ctx) {
1164
+ const { appliedCoupons, cartSubtotal, storeAddons, platformFeeByStore, siteSettings, uid, userName, userEmail, merchantOrderId, phonepeOrderId, transactionId, shippingAddress, notes, outOfStockPolicy, unavailablePaid, orderIds, emailsToSend, couponUsageAccumulator, productById: phonepeProductById, } = ctx;
1160
1165
  const firstItem = group[0].item;
1161
1166
  const groupTotal = group.reduce((sum, { item, product }) => sum + lineTotalFor(item, product), 0);
1162
1167
  // Reuses the same store/seller lookup as the COD/UPI path above instead
@@ -1167,7 +1172,7 @@ async function createRazorpayGroupOrder(group, orderType, ctx) {
1167
1172
  const { couponDiscount, appliedDiscounts } = computeGroupCouponDiscount(appliedCoupons, groupLines, groupTotal, cartSubtotal, firstItem.storeId);
1168
1173
  // Mirrors createOrderForGroup — without this the online-payment path wrote
1169
1174
  // the discount onto the order but never incremented usage.currentUsage or
1170
- // the per-user counter, so limits were unenforceable for Razorpay orders.
1175
+ // the per-user counter, so limits were unenforceable for PhonePe orders.
1171
1176
  const groupCouponCodes = new Set();
1172
1177
  for (const discount of appliedDiscounts) {
1173
1178
  if (discount.couponId) {
@@ -1194,25 +1199,25 @@ async function createRazorpayGroupOrder(group, orderType, ctx) {
1194
1199
  const giftWrapMessage = addons.giftWrapMessage;
1195
1200
  const shipmentProtectionFee = computeShipmentProtectionFee(groupTotal, addons.shipmentProtectionAddon ?? false, commissionRates);
1196
1201
  const orderTotal = Math.max(0, groupTotal - couponDiscount) + shippingFee + whatsappNotifyFee + giftWrapFee + shipmentProtectionFee + platformFee + platformFeeGst;
1197
- // P-8 GST — deliberately NOT wired into this Razorpay-verify path. The
1202
+ // P-8 GST — deliberately NOT wired into this PhonePe-verify path. The
1198
1203
  // amount-mismatch check above (expectedPaymentAmountRs) compares against
1199
- // what the buyer already paid via the Razorpay order created earlier in
1204
+ // what the buyer already paid via the PhonePe order created earlier in
1200
1205
  // the flow; adding product GST here without also adding it to that
1201
1206
  // upstream pre-payment amount calculation would either fail the mismatch
1202
1207
  // check or silently under/over-charge. Wiring GST through the full
1203
- // Razorpay create→verify round-trip is separate follow-up work, tracked
1204
- // alongside P-13 (Razorpay is disabled by default today, so this order
1208
+ // PhonePe create→confirm round-trip is separate follow-up work, tracked
1209
+ // alongside P-13 (PhonePe is disabled by default today, so this order
1205
1210
  // type doesn't currently carry a GST breakdown).
1206
1211
  // S-SBUNI-RULES 2026-05-13 — order-item decoration via rule registry.
1207
1212
  // Same expansion as the COD/UPI path. This was a THIRD hand-rolled copy of
1208
1213
  // the price rule (`isBundle ? item.price : product.price`) that — like the
1209
- // two in the consumer's Razorpay route — silently omitted `lockedPrice`, so
1210
- // an accepted offer paid through Razorpay was RECORDED at the seller's list
1214
+ // two in the consumer's checkout route — silently omitted `lockedPrice`, so
1215
+ // an accepted offer paid through PhonePe was RECORDED at the seller's list
1211
1216
  // price. `lineTotalFor` inside the expansion is the single definition.
1212
1217
  const orderItems = group.flatMap(({ item, product }) => {
1213
1218
  const lt = (product?.listingType ?? "standard");
1214
1219
  const itemRule = getListingRule(lt);
1215
- return expandCartLineToOrderRows(item, product, razorpayProductById).map((baseLine) => itemRule.decorateOrderItem(baseLine, product));
1220
+ return expandCartLineToOrderRows(item, product, phonepeProductById).map((baseLine) => itemRule.decorateOrderItem(baseLine, product));
1216
1221
  });
1217
1222
  const totalQuantity = group.reduce((sum, { item }) => sum + item.quantity, 0);
1218
1223
  const imageUrls = [
@@ -1221,10 +1226,10 @@ async function createRazorpayGroupOrder(group, orderType, ctx) {
1221
1226
  .filter((url) => typeof url === "string" && url.length > 0)),
1222
1227
  ];
1223
1228
  // S-SBUNI-RULES 2026-05-13 — order-doc decoration via rule registry.
1224
- const lt0Rzp = (group[0].product?.listingType ?? "standard");
1225
- const groupRuleRzp = getListingRule(lt0Rzp);
1229
+ const lt0Pp = (group[0].product?.listingType ?? "standard");
1230
+ const groupRulePp = getListingRule(lt0Pp);
1226
1231
  const extraOrderFields = {
1227
- ...groupRuleRzp.decorateOrderDoc(group[0].item, group[0].product),
1232
+ ...groupRulePp.decorateOrderDoc(group[0].item, group[0].product),
1228
1233
  };
1229
1234
  // Both creation paths call the SAME builder — two hand-written copies is the
1230
1235
  // create-vs-create axis of Root Cause #39, and these two functions have
@@ -1256,18 +1261,20 @@ async function createRazorpayGroupOrder(group, orderType, ctx) {
1256
1261
  status: OrderStatusValues.CONFIRMED,
1257
1262
  paymentStatus: PaymentStatusValues.PAID,
1258
1263
  paymentMethod: PaymentMethodValues.ONLINE,
1259
- paymentId: razorpay_payment_id,
1264
+ // Our own generated id, not PhonePe's — this is what refunds/lookups key
1265
+ // on (PhonePe's refund API is `originalMerchantOrderId`-keyed anyway).
1266
+ paymentId: merchantOrderId,
1260
1267
  paymentRecord: {
1261
- method: "razorpay",
1262
- transactionId: razorpay_payment_id,
1268
+ method: "phonepe",
1269
+ transactionId: transactionId ?? merchantOrderId,
1263
1270
  amount: orderTotal,
1264
1271
  paidAt: new Date(),
1265
- verifiedBy: "razorpay-webhook",
1266
- verificationMethod: "webhook",
1272
+ verifiedBy: "phonepe-order-status",
1273
+ verificationMethod: "order-status",
1267
1274
  gatewayRef: {
1268
- orderId: razorpay_order_id,
1269
- paymentId: razorpay_payment_id,
1270
- signature: razorpay_signature,
1275
+ merchantOrderId,
1276
+ phonepeOrderId,
1277
+ transactionId,
1271
1278
  },
1272
1279
  },
1273
1280
  shippingAddress,
@@ -1334,53 +1341,93 @@ async function createRazorpayGroupOrder(group, orderType, ctx) {
1334
1341
  });
1335
1342
  return orderTotal;
1336
1343
  }
1337
- export async function verifyAndPlaceRazorpayOrderAction(input) {
1338
- const { userId: uid, userName, userEmail, razorpay_order_id, razorpay_payment_id, razorpay_signature, addressId, notes, outOfStockPolicy = OutOfStockPolicyValues.CANCEL_ORDER, } = input;
1339
- // Add-on fees are no longer resolved here. They are per store, and this
1340
- // function has no single store — each group reads its own selection off
1341
- // `cart.storeAddons` inside createRazorpayGroupOrder.
1342
- const siteSettings = await siteSettingsRepository.getSingleton();
1343
- const isValid = await verifyPaymentSignatureWithKeys({
1344
- razorpay_order_id,
1345
- razorpay_payment_id,
1346
- razorpay_signature,
1347
- });
1348
- if (!isValid) {
1349
- serverLogger.warn(`Payment signature verification failed for user ${uid}`);
1344
+ export async function verifyAndPlacePhonePeOrderAction(input) {
1345
+ const { merchantOrderId } = input;
1346
+ // Idempotency: the buyer's /verify call and the async webhook can both
1347
+ // race to confirm+place for the same PhonePe payment (see the module
1348
+ // comment above). `.create()` throws on conflict, so whichever caller
1349
+ // creates this claim doc first is the sole placer; the loser returns
1350
+ // whatever the winner already placed, or a retryable "in progress" error
1351
+ // if the winner genuinely hasn't finished yet.
1352
+ const claimRef = getAdminDb().collection("phonepeOrderClaims").doc(merchantOrderId);
1353
+ try {
1354
+ await claimRef.create({ claimedAt: new Date() });
1355
+ }
1356
+ catch (claimErr) {
1357
+ void normalizeError(claimErr);
1358
+ const existingOrders = await unitOfWork.orders.findByPaymentId(merchantOrderId);
1359
+ if (existingOrders.length > 0) {
1360
+ return {
1361
+ orderIds: existingOrders.map((o) => o.id),
1362
+ total: existingOrders.reduce((sum, o) => sum + o.totalPrice, 0),
1363
+ itemCount: existingOrders.length,
1364
+ };
1365
+ }
1366
+ throw new ValidationError(ERROR_MESSAGES.CHECKOUT.ORDER_PLACEMENT_IN_PROGRESS);
1367
+ }
1368
+ // Order Status confirmation — PhonePe never hands the browser a
1369
+ // verifiable proof of payment, so this call IS the "signature check".
1370
+ const phonepeOrder = await getProviders().payment.getOrder(merchantOrderId);
1371
+ if (phonepeOrder.status !== "paid") {
1372
+ serverLogger.warn(`PhonePe order not completed: merchantOrderId=${merchantOrderId} state=${phonepeOrder.status}`);
1350
1373
  failedCheckoutRepository
1351
- .logPayment(uid, "signature_mismatch", "HMAC signature invalid", {
1352
- gatewayOrderId: razorpay_order_id,
1353
- gatewayPaymentId: razorpay_payment_id,
1354
- addressId,
1374
+ .logPayment(input.userId ?? "unknown", "signature_mismatch", "PhonePe order not COMPLETED", {
1375
+ gatewayOrderId: phonepeOrder.gatewayOrderId,
1376
+ gatewayPaymentId: merchantOrderId,
1377
+ addressId: input.addressId,
1355
1378
  })
1356
1379
  .catch((logErr) => { void normalizeError(logErr); serverLogger.warn(AUDIT_LOG_FAIL_MSG, { error: logErr instanceof Error ? logErr.message : String(logErr) }); });
1357
1380
  throw new ValidationError(ERROR_MESSAGES.CHECKOUT.PAYMENT_FAILED);
1358
1381
  }
1382
+ // The buyer-present /verify call always supplies these; the webhook path
1383
+ // (no session) reconstructs them from PhonePe's metaInfo, packed at
1384
+ // create-order time — see `PhonePeCheckoutIntent`.
1385
+ const intent = phonepeOrder.metadata;
1386
+ const uid = input.userId ?? intent?.uid;
1387
+ if (!uid) {
1388
+ throw new ValidationError(ERROR_MESSAGES.CHECKOUT.PAYMENT_FAILED);
1389
+ }
1390
+ let userName = input.userName;
1391
+ let userEmail = input.userEmail;
1392
+ if (userName === undefined || userEmail === undefined) {
1393
+ const userDoc = await userRepository.findById(uid);
1394
+ userName = userName ?? userDoc?.displayName ?? "Customer";
1395
+ userEmail = userEmail ?? userDoc?.email ?? "";
1396
+ }
1397
+ const addressId = input.addressId ?? intent?.addressId;
1398
+ const notes = input.notes ?? intent?.notes;
1399
+ const outOfStockPolicy = input.outOfStockPolicy ??
1400
+ intent?.outOfStockPolicy ??
1401
+ OutOfStockPolicyValues.CANCEL_ORDER;
1402
+ // Add-on fees are no longer resolved here. They are per store, and this
1403
+ // function has no single store — each group reads its own selection off
1404
+ // `cart.storeAddons` inside createPhonePeGroupOrder.
1405
+ const siteSettings = await siteSettingsRepository.getSingleton();
1359
1406
  const cart = await unitOfWork.carts.getOrCreate(uid);
1360
1407
  if (!cart.items || cart.items.length === 0) {
1361
1408
  throw new ValidationError(ERROR_MESSAGES.CHECKOUT.CART_EMPTY);
1362
1409
  }
1363
1410
  // Same lane priority + locked-line revalidation the manual/COD path runs.
1364
- // The Razorpay path settles the whole cart (no selectedItemIds), so the
1411
+ // The PhonePe path settles the whole cart (no selectedItemIds), so the
1365
1412
  // "selected" set is the cart itself.
1366
1413
  assertCheckoutLane(cart.items, cart.items);
1367
1414
  await assertLockedLinesStillValid(cart.items, uid);
1368
- const isDigitalCartRazorpay = cartIsDigitalOnly(cart.items);
1415
+ const isDigitalCartPp = cartIsDigitalOnly(cart.items);
1369
1416
  let shippingAddress;
1370
- let resolvedAddressRzp = null;
1371
- if (!isDigitalCartRazorpay) {
1417
+ let resolvedAddressPp = null;
1418
+ if (!isDigitalCartPp) {
1372
1419
  if (!addressId) {
1373
1420
  throw new NotFoundError(ERROR_MESSAGES.CHECKOUT.ADDRESS_REQUIRED);
1374
1421
  }
1375
1422
  const addressDoc = await unitOfWork.addresses.findById(addressId);
1376
- resolvedAddressRzp =
1423
+ resolvedAddressPp =
1377
1424
  addressDoc && addressDoc.ownerType === "user" && addressDoc.ownerId === uid
1378
1425
  ? addressDoc
1379
1426
  : null;
1380
- if (!resolvedAddressRzp) {
1427
+ if (!resolvedAddressPp) {
1381
1428
  throw new NotFoundError(ERROR_MESSAGES.CHECKOUT.ADDRESS_REQUIRED);
1382
1429
  }
1383
- shippingAddress = formatShippingAddress(resolvedAddressRzp);
1430
+ shippingAddress = formatShippingAddress(resolvedAddressPp);
1384
1431
  }
1385
1432
  // SB-UNI-5 2026-05-13 — bundle-aware product fetch + validation. Each
1386
1433
  // cart line gets paired with a "representative" product (first member id
@@ -1412,17 +1459,17 @@ export async function verifyAndPlaceRazorpayOrderAction(input) {
1412
1459
  // S-SBUNI-RULES 2026-05-13 — sync preflight via rule registry.
1413
1460
  // Multi-member lines skip the prize-pool cap, same reasoning as the COD path.
1414
1461
  const preflightPairs = productChecks.filter((p) => p.product !== null && p.product !== undefined && !isMultiMemberLine(p.item));
1415
- // This whole action is the Razorpay path — paymentMethod is always "online".
1462
+ // This whole action is the online-payment path — paymentMethod is always "online".
1416
1463
  runSyncPreflight(preflightPairs, "online");
1417
1464
  // SB-UNI-O 2026-05-15 — Live-item jurisdiction guard.
1418
- if (!isDigitalCartRazorpay && resolvedAddressRzp) {
1419
- assertLiveJurisdiction(cart.items, productByIdPaid, resolvedAddressRzp.state);
1465
+ if (!isDigitalCartPp && resolvedAddressPp) {
1466
+ assertLiveJurisdiction(cart.items, productByIdPaid, resolvedAddressPp.state);
1420
1467
  }
1421
1468
  // SB-UNI-5 — validate every required member product across the cart with
1422
1469
  // cumulative decrement awareness (two bundles sharing a member must NOT
1423
1470
  // both succeed unless the product has enough stock for the sum).
1424
1471
  //
1425
- // Out-of-stock policy: unlike the COD/UPI path, Razorpay payment has
1472
+ // Out-of-stock policy: unlike the COD/UPI path, PhonePe payment has
1426
1473
  // ALREADY been captured for the full cart by the time this runs — so
1427
1474
  // "skip_items" here means placing orders for the available items only and
1428
1475
  // auto-refunding the dropped items' value (below), not skipping payment.
@@ -1436,8 +1483,8 @@ export async function verifyAndPlaceRazorpayOrderAction(input) {
1436
1483
  .logPayment(uid, reason, exists
1437
1484
  ? `Product ${u.productId} has ${u.availableQty} left, requested ${u.requestedQty}`
1438
1485
  : `Product ${u.productId} not published`, {
1439
- gatewayOrderId: razorpay_order_id,
1440
- gatewayPaymentId: razorpay_payment_id,
1486
+ gatewayOrderId: phonepeOrder.gatewayOrderId,
1487
+ gatewayPaymentId: merchantOrderId,
1441
1488
  addressId,
1442
1489
  })
1443
1490
  .catch((err) => { void normalizeError(err); serverLogger.warn(AUDIT_LOG_FAIL_MSG, { error: err instanceof Error ? err.message : String(err) }); });
@@ -1445,7 +1492,7 @@ export async function verifyAndPlaceRazorpayOrderAction(input) {
1445
1492
  }
1446
1493
  // outOfStockPolicy: "cancel_order" (also the default for old clients that
1447
1494
  // omit the field) — reject the whole checkout batch, exactly like every
1448
- // Razorpay checkout behaved before this policy existed.
1495
+ // online-payment checkout behaved before this policy existed.
1449
1496
  if (outOfStockPolicy === OutOfStockPolicyValues.CANCEL_ORDER && unavailablePaid.length > 0) {
1450
1497
  throw new ValidationError(ERROR_MESSAGES.CHECKOUT.INSUFFICIENT_STOCK);
1451
1498
  }
@@ -1469,7 +1516,7 @@ export async function verifyAndPlaceRazorpayOrderAction(input) {
1469
1516
  const commissionRates = siteSettings?.commissions ?? CHECKOUT_DEFAULT_COMMISSIONS;
1470
1517
  const { platformFee: expectedPlatformFee, gstOnFee: expectedGstOnFee } = computeCheckoutFees(cartSubtotalRs, commissionRates);
1471
1518
  // Same per-seller-group shipping sum /api/payment/create-order charged
1472
- // upfront (and the same resolveShippingCost createRazorpayGroupOrder
1519
+ // upfront (and the same resolveShippingCost createPhonePeGroupOrder
1473
1520
  // below will use to build each order's recorded totalPrice) — keeps this
1474
1521
  // floor check from passing an amount that's short by the shipping fee.
1475
1522
  const expectedShippingGroups = splitCartIntoOrderGroups(bucketedPaid.available);
@@ -1488,14 +1535,15 @@ export async function verifyAndPlaceRazorpayOrderAction(input) {
1488
1535
  computeShipmentProtectionFee(groupSubtotal, addons.shipmentProtectionAddon ?? false, commissionRates));
1489
1536
  }, 0);
1490
1537
  const expectedPaymentAmountRs = cartSubtotalRs + expectedPlatformFee + expectedGstOnFee + expectedAddonFees + expectedShippingFee;
1491
- const rzpOrderRecord = await fetchRazorpayOrder(razorpay_order_id);
1492
- const paidAmountRs = paiseToRupees(rzpOrderRecord.amount);
1538
+ // Reuses the order already fetched above to confirm COMPLETED — PhonePe's
1539
+ // own amount is in its wire-format smallest unit. // audit-money-units-ok: describes the PhonePe boundary conversion this line performs, not our storage convention
1540
+ const paidAmountRs = paiseToRupees(phonepeOrder.amount);
1493
1541
  if (paidAmountRs < expectedPaymentAmountRs - 1) {
1494
1542
  serverLogger.warn(`Payment amount mismatch for user ${uid}: paid ₹${paidAmountRs}, expected ≥ ₹${expectedPaymentAmountRs}`);
1495
1543
  failedCheckoutRepository
1496
1544
  .logPayment(uid, "amount_mismatch", `Paid ₹${paidAmountRs}, expected ≥ ₹${expectedPaymentAmountRs}`, {
1497
- gatewayOrderId: razorpay_order_id,
1498
- gatewayPaymentId: razorpay_payment_id,
1545
+ gatewayOrderId: phonepeOrder.gatewayOrderId,
1546
+ gatewayPaymentId: merchantOrderId,
1499
1547
  amountRs: paidAmountRs,
1500
1548
  addressId,
1501
1549
  })
@@ -1506,7 +1554,7 @@ export async function verifyAndPlaceRazorpayOrderAction(input) {
1506
1554
  const { coupons: appliedCoupons, dropped: droppedCoupons } = await revalidateAppliedCoupons(cart.appliedCoupons ?? [], bucketedPaid.available, uid);
1507
1555
  // "skip_items" (or an all-available cart under any policy) — build order
1508
1556
  // groups from the available bucket only. Dropped items are never ordered;
1509
- // their value is refunded below since Razorpay already captured payment
1557
+ // their value is refunded below since PhonePe already captured payment
1510
1558
  // for the full cart before this stock check ran.
1511
1559
  const orderGroups = splitCartIntoOrderGroups(bucketedPaid.available);
1512
1560
  const orderIds = [];
@@ -1521,7 +1569,7 @@ export async function verifyAndPlaceRazorpayOrderAction(input) {
1521
1569
  g.reduce((sum, { item, product }) => sum + lineTotalFor(item, product), 0),
1522
1570
  ]), computeCheckoutFees(cartSubtotal, siteSettings?.commissions ?? CHECKOUT_DEFAULT_COMMISSIONS));
1523
1571
  for (const { items: group, orderType } of orderGroups) {
1524
- total += await createRazorpayGroupOrder(group, orderType, {
1572
+ total += await createPhonePeGroupOrder(group, orderType, {
1525
1573
  appliedCoupons,
1526
1574
  cartSubtotal,
1527
1575
  storeAddons: cart.storeAddons,
@@ -1530,9 +1578,9 @@ export async function verifyAndPlaceRazorpayOrderAction(input) {
1530
1578
  uid,
1531
1579
  userName,
1532
1580
  userEmail,
1533
- razorpay_order_id,
1534
- razorpay_payment_id,
1535
- razorpay_signature,
1581
+ merchantOrderId,
1582
+ phonepeOrderId: phonepeOrder.gatewayOrderId,
1583
+ transactionId: phonepeOrder.transactionId,
1536
1584
  shippingAddress,
1537
1585
  notes,
1538
1586
  outOfStockPolicy,
@@ -1567,24 +1615,24 @@ export async function verifyAndPlaceRazorpayOrderAction(input) {
1567
1615
  });
1568
1616
  });
1569
1617
  // outOfStockPolicy: "skip_items" — auto-refund the dropped items' value.
1570
- // Razorpay already captured payment for the FULL cart before this stock
1618
+ // PhonePe already captured payment for the FULL cart before this stock
1571
1619
  // check ran, so anything not placed as an order must be given back.
1572
1620
  // AWAITED (not fire-and-forget) — this moves money. On failure the orders
1573
1621
  // themselves are NOT rolled back (they're already validly created and
1574
1622
  // paid); instead the failure is surfaced via order.refundPending + an
1575
1623
  // admin notification, never silently dropped (Rule #8).
1576
- await refundDroppedItemsForRazorpayCheckout({
1624
+ await refundDroppedItemsForPhonePeCheckout({
1577
1625
  unavailablePaid,
1578
1626
  orderIds,
1579
1627
  productByIdPaid,
1580
- razorpayPaymentId: razorpay_payment_id,
1628
+ phonepeOrderId: merchantOrderId,
1581
1629
  });
1582
1630
  if (emailsToSend.length > 0) {
1583
1631
  Promise.all(emailsToSend.map((e) => sendOrderConfirmationEmail(e))).catch((err) => serverLogger.error("Order confirmation email error:", err));
1584
1632
  }
1585
- serverLogger.info(`verifyAndPlaceRazorpayOrderAction: ${orderIds.length} order(s) placed for uid=${uid} — payment ${razorpay_payment_id}`);
1633
+ serverLogger.info(`verifyAndPlacePhonePeOrderAction: ${orderIds.length} order(s) placed for uid=${uid} — payment ${merchantOrderId}`);
1586
1634
  getAdminRealtimeDb()
1587
- .ref(`${RTDB_PATHS.PAYMENT_EVENTS}/${razorpay_order_id}`)
1635
+ .ref(`${RTDB_PATHS.PAYMENT_EVENTS}/${merchantOrderId}`)
1588
1636
  .update({ status: "success", orderIds, updatedAt: Date.now() })
1589
1637
  .catch((err) => serverLogger.warn("Payment event RTDB signal failed (non-critical)", { err }));
1590
1638
  return {
@@ -56,7 +56,7 @@ export declare function validateCartItemStock(item: CartItemDocument, productByI
56
56
  /**
57
57
  * Result of bucketing a cart into stock-satisfiable vs. stock-short lines.
58
58
  * Shared shape consumed by both checkout entry points (COD/UPI transaction
59
- * path and Razorpay post-payment path) so "what happens when an item is
59
+ * path and PhonePe post-payment path) so "what happens when an item is
60
60
  * unavailable" (skip it vs. cancel the whole checkout) is decided ONCE, by
61
61
  * the caller, from the same bucketing logic.
62
62
  */
@@ -1,3 +1,3 @@
1
- export { createCheckoutOrderAction, attachPaymentAction, verifyAndPlaceRazorpayOrderAction, previewCheckoutPricing, resolveShippingCost, type CreateCheckoutOrderInput, type VerifyAndPlaceRazorpayOrderInput, type CheckoutPricingPreviewInput, type CheckoutPricingPreview, } from "./actions";
1
+ export { createCheckoutOrderAction, attachPaymentAction, verifyAndPlacePhonePeOrderAction, previewCheckoutPricing, resolveShippingCost, type CreateCheckoutOrderInput, type VerifyAndPlacePhonePeOrderInput, type CheckoutPricingPreviewInput, type CheckoutPricingPreview, } from "./actions";
2
2
  export { formatShippingAddress, type CheckoutOrderResult, } from "./data";
3
3
  export { CHECKOUT_DEFAULT_COMMISSIONS, CHECKOUT_PAYMENT_METHODS, type CheckoutPaymentMethod, } from "../../../shared/features/checkout/config";
@@ -1,3 +1,3 @@
1
- export { createCheckoutOrderAction, attachPaymentAction, verifyAndPlaceRazorpayOrderAction, previewCheckoutPricing, resolveShippingCost, } from "./actions";
1
+ export { createCheckoutOrderAction, attachPaymentAction, verifyAndPlacePhonePeOrderAction, previewCheckoutPricing, resolveShippingCost, } from "./actions";
2
2
  export { formatShippingAddress, } from "./data";
3
3
  export { CHECKOUT_DEFAULT_COMMISSIONS, CHECKOUT_PAYMENT_METHODS, } from "../../../shared/features/checkout/config";
@@ -8,7 +8,7 @@
8
8
  * and a won bid had no link to the order it became.
9
9
  * WHAT: `assertLockedLinesStillValid` (pre-order-creation guard) and
10
10
  * `finalizeLockedLines` (post-order-creation close-out). Both are called
11
- * from every order-creation path so the manual/COD and Razorpay flows
11
+ * from every order-creation path so the manual/COD and PhonePe flows
12
12
  * cannot diverge.
13
13
  *
14
14
  * EXPORTS:
@@ -6,11 +6,11 @@
6
6
  * `orderType: "auction"`. Everything else was recoverable only by reading
7
7
  * the auction or offer document, which is mutable and eventually archived.
8
8
  * WHAT: `buildOrderSourceContext()` — the ONE builder for `OrderSourceContext`,
9
- * called by both order-creation paths (manual/COD and Razorpay).
9
+ * called by both order-creation paths (manual/COD and PhonePe).
10
10
  *
11
11
  * ## Why one shared function and not two inline blocks
12
12
  *
13
- * `createOrderForGroup` and `createRazorpayGroupOrder` are near-identical
13
+ * `createOrderForGroup` and `createPhonePeGroupOrder` are near-identical
14
14
  * ~200-line twins that already drifted once on add-on fees (Root Cause #65).
15
15
  * Two hand-written copies of this would be the create-vs-create axis of Root
16
16
  * Cause #39: one lane records provenance, the other silently doesn't, and the