@mohasinac/appkit 4.63.4 → 4.64.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 (232) hide show
  1. package/dist/.build-stamp.json +3 -3
  2. package/dist/_internal/server/features/categories/adapters.d.ts +1 -1
  3. package/dist/_internal/server/features/categories/adapters.js +13 -0
  4. package/dist/_internal/server/features/checkout/actions.d.ts +2 -0
  5. package/dist/_internal/server/features/checkout/actions.js +22 -5
  6. package/dist/_internal/server/features/events/like-actions.d.ts +25 -0
  7. package/dist/_internal/server/features/events/like-actions.js +67 -0
  8. package/dist/_internal/server/features/events/likes.d.ts +65 -0
  9. package/dist/_internal/server/features/events/likes.js +146 -0
  10. package/dist/_internal/server/features/orders/adapters.js +3 -1
  11. package/dist/_internal/server/features/seo/index.d.ts +3 -0
  12. package/dist/_internal/server/features/seo/index.js +2 -0
  13. package/dist/_internal/server/features/seo/product-detail-types.d.ts +1 -0
  14. package/dist/_internal/server/features/seo/product-detail-types.js +26 -0
  15. package/dist/_internal/server/features/seo/robots.js +20 -1
  16. package/dist/_internal/server/features/seo/sitemap.js +125 -27
  17. package/dist/_internal/server/features/seo/static-params.d.ts +107 -0
  18. package/dist/_internal/server/features/seo/static-params.js +228 -0
  19. package/dist/_internal/server/features/seo/taxonomy-links.d.ts +44 -0
  20. package/dist/_internal/server/features/seo/taxonomy-links.js +85 -0
  21. package/dist/_internal/server/features/site-settings/adapters.js +20 -2
  22. package/dist/_internal/server/features/stores/adapters.d.ts +2 -2
  23. package/dist/_internal/server/features/stores/adapters.js +88 -1
  24. package/dist/_internal/server/functions/scheduled.d.ts +16 -1
  25. package/dist/_internal/server/functions/scheduled.js +23 -1
  26. package/dist/_internal/server/jobs/core/analyticsAlertEvaluate.js +5 -2
  27. package/dist/_internal/server/jobs/core/jobRunners.js +5 -0
  28. package/dist/_internal/server/jobs/core/onOrderCreate.js +104 -10
  29. package/dist/_internal/server/jobs/core/onOrderStatusChange.d.ts +2 -0
  30. package/dist/_internal/server/jobs/core/onOrderStatusChange.js +1 -0
  31. package/dist/_internal/server/jobs/core/onScamReportCreate.js +1 -0
  32. package/dist/_internal/server/jobs/core/onScamReportRejected.js +1 -0
  33. package/dist/_internal/server/jobs/core/onScamReportVerified.js +1 -0
  34. package/dist/_internal/server/jobs/core/paymentWindowTimeout.js +1 -0
  35. package/dist/_internal/server/jobs/core/pendingOrderTimeout.js +1 -0
  36. package/dist/_internal/server/jobs/core/sellerStandingRollup.d.ts +63 -0
  37. package/dist/_internal/server/jobs/core/sellerStandingRollup.js +412 -0
  38. package/dist/_internal/server/jobs/core/whatsappNotify.d.ts +35 -0
  39. package/dist/_internal/server/jobs/core/whatsappNotify.js +140 -0
  40. package/dist/_internal/server/jobs/handlers/index.d.ts +1 -0
  41. package/dist/_internal/server/jobs/handlers/index.js +1 -0
  42. package/dist/_internal/server/jobs/handlers/sellerStandingRollup.d.ts +12 -0
  43. package/dist/_internal/server/jobs/handlers/sellerStandingRollup.js +14 -0
  44. package/dist/_internal/server/notifications/channel-health.d.ts +8 -6
  45. package/dist/_internal/server/notifications/channel-health.js +2 -2
  46. package/dist/_internal/server/notifications/send-guard.d.ts +1 -1
  47. package/dist/_internal/server/notifications/send-guard.js +4 -2
  48. package/dist/_internal/shared/actions/action-registry.js +89 -0
  49. package/dist/_internal/shared/checkout/order-math.d.ts +1 -1
  50. package/dist/_internal/shared/checkout/order-math.js +1 -1
  51. package/dist/_internal/shared/features/checkout/config.d.ts +2 -0
  52. package/dist/_internal/shared/features/checkout/config.js +2 -0
  53. package/dist/_internal/shared/features/messaging/config.d.ts +5 -7
  54. package/dist/_internal/shared/features/messaging/config.js +4 -1
  55. package/dist/_internal/shared/features/seller-standing/config.d.ts +130 -0
  56. package/dist/_internal/shared/features/seller-standing/config.js +153 -0
  57. package/dist/_internal/shared/features/site-settings/fees.d.ts +2 -0
  58. package/dist/_internal/shared/features/site-settings/fees.js +2 -0
  59. package/dist/_internal/shared/features/site-settings/types.d.ts +27 -2
  60. package/dist/_internal/shared/fees/calculator.d.ts +8 -0
  61. package/dist/_internal/shared/fees/calculator.js +7 -0
  62. package/dist/cli/index.js +2 -0
  63. package/dist/client.d.ts +2 -2
  64. package/dist/client.js +1 -1
  65. package/dist/constants/api-endpoints.d.ts +39 -0
  66. package/dist/constants/api-endpoints.js +16 -0
  67. package/dist/constants/notification-types.d.ts +1 -1
  68. package/dist/constants/notification-types.js +1 -1
  69. package/dist/core/integration-keys.d.ts +5 -0
  70. package/dist/core/integration-keys.js +10 -0
  71. package/dist/features/about/components/FeesView.js +6 -0
  72. package/dist/features/about/components/HelpPageView.js +9 -1
  73. package/dist/features/account/components/BecomeSellerView.d.ts +15 -3
  74. package/dist/features/account/components/BecomeSellerView.js +2 -2
  75. package/dist/features/account/components/BuyerShoppingGuideView.js +1 -1
  76. package/dist/features/account/components/NotificationPreferencesPanel.js +4 -3
  77. package/dist/features/account/types/index.d.ts +2 -0
  78. package/dist/features/admin/actions/notification-actions.d.ts +17 -2
  79. package/dist/features/admin/actions/notification-actions.js +154 -9
  80. package/dist/features/admin/components/AdminOrderEditorView.d.ts +1 -1
  81. package/dist/features/admin/components/AdminOrdersView.js +2 -0
  82. package/dist/features/admin/components/AdminSiteSettingsView.js +66 -6
  83. package/dist/features/admin/components/AdminStoreEditorView.d.ts +12 -1
  84. package/dist/features/admin/components/AdminStoreEditorView.js +31 -2
  85. package/dist/features/admin/components/AdminStoresGuideView.js +1 -1
  86. package/dist/features/admin/components/AdminStoresView.js +59 -2
  87. package/dist/features/admin/components/AdminTrustGuideView.js +10 -0
  88. package/dist/features/admin/constants/site-settings-tabs.d.ts +12 -7
  89. package/dist/features/admin/constants/site-settings-tabs.js +12 -6
  90. package/dist/features/admin/repository/site-settings.repository.js +1 -1
  91. package/dist/features/admin/schemas/admin-editor-forms.d.ts +10 -0
  92. package/dist/features/admin/schemas/admin-editor-forms.js +34 -0
  93. package/dist/features/admin/schemas/admin-user-form.d.ts +61 -0
  94. package/dist/features/admin/schemas/admin-user-form.js +44 -3
  95. package/dist/features/admin/schemas/firestore.d.ts +73 -0
  96. package/dist/features/admin/schemas/firestore.js +8 -3
  97. package/dist/features/auth/checkout-value-otp.d.ts +1 -1
  98. package/dist/features/auth/checkout-value-otp.js +1 -1
  99. package/dist/features/auth/permissions/constants.d.ts +2 -2
  100. package/dist/features/auth/permissions/constants.js +2 -1
  101. package/dist/features/blog/components/BlogIndexPageView.js +2 -2
  102. package/dist/features/blog/components/BlogListView.js +2 -1
  103. package/dist/features/blog/components/BlogPostView.js +2 -1
  104. package/dist/features/blog/constants/category-badge.d.ts +33 -0
  105. package/dist/features/blog/constants/category-badge.js +40 -0
  106. package/dist/features/cart/components/CartPriceBreakdown.d.ts +2 -0
  107. package/dist/features/cart/components/CartPriceBreakdown.js +1 -1
  108. package/dist/features/cart/components/StoreAddonsPicker.d.ts +5 -2
  109. package/dist/features/cart/components/StoreAddonsPicker.js +5 -4
  110. package/dist/features/cart/repository/cart.repository.js +2 -1
  111. package/dist/features/cart/schemas/firestore.d.ts +2 -1
  112. package/dist/features/categories/components/TaxonomyLinkIndex.d.ts +39 -0
  113. package/dist/features/categories/components/TaxonomyLinkIndex.js +11 -0
  114. package/dist/features/categories/schemas/firestore.d.ts +25 -0
  115. package/dist/features/categories/utils/load-category-options.d.ts +33 -0
  116. package/dist/features/categories/utils/load-category-options.js +50 -0
  117. package/dist/features/checkout/actions/checkout-value-otp-actions.d.ts +5 -10
  118. package/dist/features/checkout/actions/checkout-value-otp-actions.js +88 -2
  119. package/dist/features/events/components/EventCard.js +4 -12
  120. package/dist/features/events/components/EventInsights.d.ts +27 -0
  121. package/dist/features/events/components/EventInsights.js +10 -0
  122. package/dist/features/events/components/EventLikeButton.d.ts +21 -0
  123. package/dist/features/events/components/EventLikeButton.js +59 -0
  124. package/dist/features/events/components/EventLikersRail.d.ts +23 -0
  125. package/dist/features/events/components/EventLikersRail.js +54 -0
  126. package/dist/features/events/components/EventTypeBadge.d.ts +26 -0
  127. package/dist/features/events/components/EventTypeBadge.js +7 -0
  128. package/dist/features/events/components/EventsListPageView.js +2 -2
  129. package/dist/features/events/components/SpinWheel.d.ts +41 -0
  130. package/dist/features/events/components/SpinWheel.js +128 -0
  131. package/dist/features/events/components/SpinWheelView.js +36 -15
  132. package/dist/features/events/constants/event-type-meta.d.ts +54 -0
  133. package/dist/features/events/constants/event-type-meta.js +109 -0
  134. package/dist/features/events/repository/event-likes.repository.d.ts +57 -0
  135. package/dist/features/events/repository/event-likes.repository.js +151 -0
  136. package/dist/features/events/schemas/firestore.d.ts +111 -0
  137. package/dist/features/events/schemas/likes.d.ts +81 -0
  138. package/dist/features/events/schemas/likes.js +59 -0
  139. package/dist/features/events/types/index.d.ts +7 -0
  140. package/dist/features/events/utils/carousel-slide.d.ts +42 -0
  141. package/dist/features/events/utils/carousel-slide.js +174 -0
  142. package/dist/features/events/utils/event-charts.d.ts +65 -0
  143. package/dist/features/events/utils/event-charts.js +172 -0
  144. package/dist/features/faq/constants/help-guides.js +9 -0
  145. package/dist/features/faq/schemas/firestore.js +1 -0
  146. package/dist/features/faq/types/index.d.ts +19 -0
  147. package/dist/features/homepage/components/BrandsSection.js +25 -1
  148. package/dist/features/homepage/components/HeroCarousel.js +47 -10
  149. package/dist/features/homepage/lib/section-renderer.js +1 -1
  150. package/dist/features/homepage/types/index.d.ts +7 -0
  151. package/dist/features/orders/actions/refund-actions.js +1 -0
  152. package/dist/features/orders/components/OrderAddonBadges.d.ts +3 -1
  153. package/dist/features/orders/components/OrderAddonBadges.js +4 -3
  154. package/dist/features/orders/repository/orders.repository.d.ts +8 -0
  155. package/dist/features/orders/repository/orders.repository.js +2 -0
  156. package/dist/features/orders/schemas/firestore.d.ts +4 -0
  157. package/dist/features/orders/types/index.d.ts +5 -2
  158. package/dist/features/search/action-index/settings-entries.js +9 -0
  159. package/dist/features/store-extensions/repository/store-extensions.repositories.d.ts +6 -1
  160. package/dist/features/store-extensions/repository/store-extensions.repositories.js +10 -1
  161. package/dist/features/store-extensions/schemas/firestore.d.ts +17 -1
  162. package/dist/features/store-extensions/schemas/firestore.js +10 -0
  163. package/dist/features/stores/components/InteractiveStoreCard.js +2 -1
  164. package/dist/features/stores/components/SellerStandingPanel.d.ts +31 -0
  165. package/dist/features/stores/components/SellerStandingPanel.js +25 -0
  166. package/dist/features/stores/components/SellerTierBadge.d.ts +57 -0
  167. package/dist/features/stores/components/SellerTierBadge.js +19 -0
  168. package/dist/features/stores/components/StoreCapabilitiesGuideView.js +33 -2
  169. package/dist/features/stores/components/StoreCreateForm.d.ts +46 -0
  170. package/dist/features/stores/components/StoreCreateForm.js +107 -0
  171. package/dist/features/stores/components/StoreGuideHubView.js +18 -5
  172. package/dist/features/stores/components/StoreHeader.js +2 -1
  173. package/dist/features/stores/components/StoreSettingsGuideView.js +6 -2
  174. package/dist/features/stores/repository/store.repository.d.ts +23 -1
  175. package/dist/features/stores/repository/store.repository.js +52 -1
  176. package/dist/features/stores/schemas/firestore.d.ts +76 -7
  177. package/dist/features/stores/schemas/firestore.js +21 -6
  178. package/dist/features/stores/schemas/index.d.ts +88 -2
  179. package/dist/features/stores/schemas/index.js +14 -0
  180. package/dist/features/stores/schemas/store-create.d.ts +47 -0
  181. package/dist/features/stores/schemas/store-create.js +86 -0
  182. package/dist/features/stores/types/index.d.ts +42 -0
  183. package/dist/features/whatsapp-bot/api/send-status/route.d.ts +12 -0
  184. package/dist/features/whatsapp-bot/api/send-status/route.js +38 -0
  185. package/dist/features/whatsapp-bot/api/webhook/route.d.ts +12 -0
  186. package/dist/features/whatsapp-bot/api/webhook/route.js +43 -0
  187. package/dist/features/whatsapp-bot/components/SellerWhatsAppSettingsView.d.ts +57 -0
  188. package/dist/features/whatsapp-bot/components/SellerWhatsAppSettingsView.js +76 -0
  189. package/dist/features/whatsapp-bot/components/WhatsAppChatButton.d.ts +9 -0
  190. package/dist/features/whatsapp-bot/components/WhatsAppChatButton.js +7 -0
  191. package/dist/features/whatsapp-bot/components/index.d.ts +3 -0
  192. package/dist/features/whatsapp-bot/components/index.js +2 -0
  193. package/dist/features/whatsapp-bot/helpers/whatsapp.d.ts +52 -0
  194. package/dist/features/whatsapp-bot/helpers/whatsapp.js +288 -0
  195. package/dist/features/whatsapp-bot/index.d.ts +3 -0
  196. package/dist/features/whatsapp-bot/index.js +3 -0
  197. package/dist/features/whatsapp-bot/manifest.d.ts +2 -0
  198. package/dist/features/whatsapp-bot/manifest.js +13 -0
  199. package/dist/features/whatsapp-bot/messages/en.json +7 -0
  200. package/dist/features/whatsapp-bot/server.d.ts +7 -0
  201. package/dist/features/whatsapp-bot/server.js +7 -0
  202. package/dist/features/whatsapp-bot/types/index.d.ts +126 -0
  203. package/dist/features/whatsapp-bot/types/index.js +1 -0
  204. package/dist/index.d.ts +23 -6
  205. package/dist/index.js +38 -12
  206. package/dist/next/routing/route-map.d.ts +6 -1
  207. package/dist/next/routing/route-map.js +14 -0
  208. package/dist/providers/db-firebase/base.repository.d.ts +1 -1
  209. package/dist/providers/db-firebase/base.repository.js +1 -1
  210. package/dist/repositories/index.d.ts +1 -1
  211. package/dist/repositories/index.js +1 -1
  212. package/dist/schemas/registry.d.ts +115 -0
  213. package/dist/schemas/registry.js +2 -1
  214. package/dist/security/pii-encrypt.js +1 -1
  215. package/dist/server.d.ts +15 -2
  216. package/dist/server.js +32 -2
  217. package/dist/styles.css +36 -1
  218. package/dist/tailwind-utilities.css +1 -1
  219. package/dist/ui/components/HorizontalScroller.js +15 -2
  220. package/dist/ui/components/NavigationLoader.d.ts +14 -1
  221. package/dist/ui/components/NavigationLoader.js +79 -30
  222. package/dist/ui/components/PageHeroHeading.d.ts +40 -0
  223. package/dist/ui/components/PageHeroHeading.js +15 -0
  224. package/dist/ui/components/Typography.d.ts +3 -3
  225. package/dist/ui/components/Typography.js +59 -31
  226. package/dist/ui/components/Typography.style.css +35 -0
  227. package/dist/ui/index.d.ts +2 -0
  228. package/dist/ui/index.js +1 -0
  229. package/package.json +1 -1
  230. package/scripts/seed-cli.mjs +4 -2
  231. package/dist/features/orders/utils/order-announcement-message.d.ts +0 -18
  232. package/dist/features/orders/utils/order-announcement-message.js +0 -16
@@ -0,0 +1,43 @@
1
+ /**
2
+ * feat-whatsapp-bot — POST /api/whatsapp/webhook
3
+ *
4
+ * WhatsApp webhook receiver for incoming messages.
5
+ *
6
+ * Consumer stub:
7
+ * ```ts
8
+ * export { whatsappWebhookPOST as POST } from "@mohasinac/feat-whatsapp-bot";
9
+ * ```
10
+ */
11
+ import { NextResponse } from "next/server.js";
12
+ import { getProviders } from "../../../../contracts/index.js";
13
+ import { normalizeError } from "../../../../errors/normalize.js";
14
+ export async function POST(request) {
15
+ try {
16
+ const body = await request.json();
17
+ const { db, eventBus } = getProviders();
18
+ if (!db)
19
+ return NextResponse.json({ success: false, error: "DB not configured" }, { status: 503 });
20
+ // Store webhook payload for processing
21
+ const repo = db.getRepository("whatsapp_messages");
22
+ const created = await repo.create({
23
+ ...body,
24
+ receivedAt: new Date(),
25
+ processed: false,
26
+ });
27
+ // Emit event for async processing if eventBus available
28
+ if (eventBus) {
29
+ await eventBus.emit("whatsapp.message.received", created);
30
+ }
31
+ return NextResponse.json({
32
+ success: true,
33
+ data: created,
34
+ }, { status: 201 });
35
+ }
36
+ catch (error) {
37
+ void normalizeError(error);
38
+ return NextResponse.json({
39
+ success: false,
40
+ error: error instanceof Error ? error.message : "Internal server error",
41
+ }, { status: 500 });
42
+ }
43
+ }
@@ -0,0 +1,57 @@
1
+ /**
2
+ * WHY: a seller's WhatsApp settings are now ONE decision — do they pay the
3
+ * monthly fee to have order updates sent to their buyers on the
4
+ * platform's WhatsApp number.
5
+ *
6
+ * 🛑 This replaced a 574-line credential form that asked every seller for a
7
+ * WhatsApp Business Account ID, a Catalogue ID and a **System User Access
8
+ * Token**, then stored that token encrypted on their store document.
9
+ *
10
+ * Three things were wrong with it, and they are why none of it is coming back:
11
+ *
12
+ * 1. Almost no small seller will create a Meta app and mint a system user
13
+ * token. A setting nobody can complete is a feature nobody has.
14
+ * 2. It made LetItRip the custodian of a live Meta credential per seller.
15
+ * `StoreRepository.mapDoc` decrypted that token on EVERY read, so any
16
+ * code path that forgot to project was handling plaintext — a gap
17
+ * CLAUDE.md flags as deserving its own session.
18
+ * 3. The catalogue half it existed for is gone: `/whatsapp/{slug}/feed.csv`
19
+ * is a public feed the seller points their own Commerce Manager at, so
20
+ * no credential crosses the boundary at all.
21
+ *
22
+ * WHAT: subscription status, the price, and the toggle.
23
+ *
24
+ * ## Presentational on purpose
25
+ *
26
+ * Takes the price and the current state as PROPS rather than reading site
27
+ * settings itself. Site settings are server-read and passed down — the
28
+ * monthly fee lives beside the platform's Meta credentials in a document that
29
+ * must never reach a browser, and a client hook here would be a second reader
30
+ * of it (Root Cause #70).
31
+ *
32
+ * EXPORTS: SellerWhatsAppSettingsView, SellerWhatsAppSettingsViewProps
33
+ *
34
+ * @tag domain:whatsapp
35
+ * @tag layer:component
36
+ * @tag access:client
37
+ */
38
+ import React from "react";
39
+ export interface SellerWhatsAppSettingsViewProps {
40
+ /** Is this store currently subscribed? Server-resolved from the store document. */
41
+ subscribed: boolean;
42
+ /** Admin-set monthly price, in rupees. Server-resolved from site settings. */
43
+ monthlyFee: number;
44
+ /** Currency for the price label. */
45
+ currency?: string;
46
+ /**
47
+ * Is the channel switched on platform-wide? When false the toggle is
48
+ * disabled and says so — a seller who subscribes into a disabled channel
49
+ * pays for silence.
50
+ */
51
+ channelEnabled: boolean;
52
+ /** Persists the change. Resolves to the new state. */
53
+ onToggleSubscription: (next: boolean) => Promise<void>;
54
+ /** Gate from the store's capabilities; false renders a read-only explanation. */
55
+ hasCapability?: boolean;
56
+ }
57
+ export declare function SellerWhatsAppSettingsView({ subscribed, monthlyFee, currency, channelEnabled, onToggleSubscription, hasCapability, }: SellerWhatsAppSettingsViewProps): React.JSX.Element;
@@ -0,0 +1,76 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ /**
4
+ * WHY: a seller's WhatsApp settings are now ONE decision — do they pay the
5
+ * monthly fee to have order updates sent to their buyers on the
6
+ * platform's WhatsApp number.
7
+ *
8
+ * 🛑 This replaced a 574-line credential form that asked every seller for a
9
+ * WhatsApp Business Account ID, a Catalogue ID and a **System User Access
10
+ * Token**, then stored that token encrypted on their store document.
11
+ *
12
+ * Three things were wrong with it, and they are why none of it is coming back:
13
+ *
14
+ * 1. Almost no small seller will create a Meta app and mint a system user
15
+ * token. A setting nobody can complete is a feature nobody has.
16
+ * 2. It made LetItRip the custodian of a live Meta credential per seller.
17
+ * `StoreRepository.mapDoc` decrypted that token on EVERY read, so any
18
+ * code path that forgot to project was handling plaintext — a gap
19
+ * CLAUDE.md flags as deserving its own session.
20
+ * 3. The catalogue half it existed for is gone: `/whatsapp/{slug}/feed.csv`
21
+ * is a public feed the seller points their own Commerce Manager at, so
22
+ * no credential crosses the boundary at all.
23
+ *
24
+ * WHAT: subscription status, the price, and the toggle.
25
+ *
26
+ * ## Presentational on purpose
27
+ *
28
+ * Takes the price and the current state as PROPS rather than reading site
29
+ * settings itself. Site settings are server-read and passed down — the
30
+ * monthly fee lives beside the platform's Meta credentials in a document that
31
+ * must never reach a browser, and a client hook here would be a second reader
32
+ * of it (Root Cause #70).
33
+ *
34
+ * EXPORTS: SellerWhatsAppSettingsView, SellerWhatsAppSettingsViewProps
35
+ *
36
+ * @tag domain:whatsapp
37
+ * @tag layer:component
38
+ * @tag access:client
39
+ */
40
+ import { useState } from "react";
41
+ import { Alert, Badge, Div, Heading, Row, Section, Span, Stack, Text, Toggle, useToast, } from "../../../ui/index.js";
42
+ import { normalizeError } from "../../../errors/normalize.js";
43
+ import { formatCurrency } from "../../../utils/number.formatter.js";
44
+ export function SellerWhatsAppSettingsView({ subscribed, monthlyFee, currency = "INR", channelEnabled, onToggleSubscription, hasCapability = true, }) {
45
+ const [isOn, setIsOn] = useState(subscribed);
46
+ const [saving, setSaving] = useState(false);
47
+ const { showToast } = useToast();
48
+ const handleToggle = async (next) => {
49
+ if (saving)
50
+ return;
51
+ const previous = isOn;
52
+ setIsOn(next);
53
+ setSaving(true);
54
+ try {
55
+ await onToggleSubscription(next);
56
+ showToast(next
57
+ ? "WhatsApp order updates are on. Your buyers will get them from the next order."
58
+ : "WhatsApp order updates are off. Buyers still get email and in-app updates.", "success");
59
+ }
60
+ catch (err) {
61
+ /*
62
+ * Not swallowed, and the optimistic flip is REVERTED. A toggle that
63
+ * stays on after a failed write tells the seller they are subscribed
64
+ * when they are not — and the first they would hear of it is a buyer
65
+ * asking why no updates arrived.
66
+ */
67
+ void normalizeError(err);
68
+ setIsOn(previous);
69
+ showToast("Could not change your subscription. Please try again.", "error");
70
+ }
71
+ finally {
72
+ setSaving(false);
73
+ }
74
+ };
75
+ return (_jsx(Section, { children: _jsxs(Div, { className: "mx-auto max-w-2xl", children: [_jsx(Heading, { level: 1, className: "mb-1", color: "primary", size: "2xl", weight: "bold", children: "WhatsApp order updates" }), _jsx(Text, { variant: "secondary", className: "mb-6", size: "sm", children: "Send your buyers order updates on WhatsApp \u2014 confirmed, shipped, delivered \u2014 from LetItRip's verified business number. You don't need a WhatsApp Business account of your own, and nothing is installed on your phone." }), !channelEnabled && (_jsx(Alert, { variant: "warning", className: "mb-4", children: "WhatsApp updates are currently switched off platform-wide, so nothing will be sent even if you subscribe. We'll start sending as soon as it's back on." })), _jsxs(Stack, { gap: "md", surface: "card", padding: "md", children: [_jsxs(Row, { justify: "between", align: "center", gap: "md", children: [_jsxs(Div, { className: "flex-1 min-w-0", children: [_jsxs(Row, { gap: "sm", align: "center", children: [_jsx(Text, { size: "sm", weight: "semibold", children: "Subscription" }), _jsx(Badge, { variant: isOn ? "success" : "inactive", children: isOn ? "Active" : "Not subscribed" })] }), _jsxs(Text, { color: "muted", size: "sm", children: [formatCurrency(monthlyFee, currency), " per month. Covers every order update to every buyer \u2014 not charged per message."] })] }), _jsx(Toggle, { checked: isOn, onChange: handleToggle, disabled: saving || !hasCapability, size: "md" })] }), !hasCapability && (_jsx(Div, { textSize: "sm", className: "border border-warning/20", color: "warning", surface: "warning-surface", padding: "inlineSm", rounded: "lg", children: "Your store doesn't have this add-on enabled. Contact support to turn it on." }))] }), _jsx(Heading, { level: 2, className: "mt-8 mb-3", size: "lg", weight: "semibold", children: "What your buyers get" }), _jsxs(Stack, { gap: "sm", surface: "card", padding: "md", children: [_jsxs(Text, { color: "muted", size: "sm", children: ["A WhatsApp message when an order is ", _jsx(Span, { weight: "semibold", children: "confirmed" }), ",", " ", _jsx(Span, { weight: "semibold", children: "shipped" }), ", ", _jsx(Span, { weight: "semibold", children: "delivered" }), ",", " ", _jsx(Span, { weight: "semibold", children: "cancelled" }), " or ", _jsx(Span, { weight: "semibold", children: "refunded" }), ". Nothing promotional \u2014 buyers who opted out of WhatsApp never receive these."] }), _jsx(Text, { color: "muted", size: "sm", children: "Messages come from LetItRip's number, not yours, so your personal number stays private and replies land with our support team rather than on your phone." }), _jsx(Text, { color: "muted", size: "sm", children: "Turning this off changes nothing else: buyers keep getting the same updates by email and in their account." })] })] }) }));
76
+ }
@@ -0,0 +1,9 @@
1
+ import React from "react";
2
+ interface WhatsAppChatButtonProps {
3
+ waNumber: string;
4
+ message?: string;
5
+ label?: string;
6
+ className?: string;
7
+ }
8
+ export declare function WhatsAppChatButton({ waNumber, message, label, className, }: WhatsAppChatButtonProps): React.JSX.Element;
9
+ export {};
@@ -0,0 +1,7 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import Link from "next/link.js";
3
+ const CLS_WA_BTN = "inline-flex items-center gap-[var(--appkit-space-2)] rounded-lg bg-success-solid px-[var(--appkit-space-4)] py-[var(--appkit-space-2)] text-[length:var(--appkit-text-sm)] font-semibold text-success-on-solid transition-colors hover:brightness-110";
4
+ export function WhatsAppChatButton({ waNumber, message = "Hi! I have a question.", label = "Chat on WhatsApp", className, }) {
5
+ const href = `https://wa.me/${waNumber}?text=${encodeURIComponent(message)}`;
6
+ return (_jsxs(Link, { href: href, target: "_blank", rel: "noopener noreferrer", className: `${CLS_WA_BTN} ${className ?? ""}`, children: [_jsx("svg", { className: "h-4 w-4", viewBox: "0 0 24 24", fill: "currentColor", "aria-hidden": "true", children: _jsx("path", { d: "M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 00-3.48-8.413z" }) }), label] }));
7
+ }
@@ -0,0 +1,3 @@
1
+ export { WhatsAppChatButton } from "./WhatsAppChatButton";
2
+ export { SellerWhatsAppSettingsView } from "./SellerWhatsAppSettingsView";
3
+ export type { SellerWhatsAppSettingsViewProps } from "./SellerWhatsAppSettingsView";
@@ -0,0 +1,2 @@
1
+ export { WhatsAppChatButton } from "./WhatsAppChatButton.js";
2
+ export { SellerWhatsAppSettingsView } from "./SellerWhatsAppSettingsView.js";
@@ -0,0 +1,52 @@
1
+ import type { CheckoutMessageInput, StatusNotificationInput, WebhookVerifyInput, IncomingWebhookPayload, SendWhatsAppInput, StatusMessageInput, WaBusinessSendInput, WaBusinessTemplateSendInput, CatalogSyncInput, CatalogSyncResult, PurchaseAnnouncementInput } from "../types";
2
+ export declare function buildCheckoutMessageURL(input: CheckoutMessageInput): string;
3
+ export declare function buildStatusNotificationURL(input: StatusNotificationInput): string;
4
+ export declare function verifyWebhookSignature(input: WebhookVerifyInput): boolean;
5
+ export declare function isAdminNumber(incomingNumber: string, adminBotNumber: string): boolean;
6
+ /**
7
+ * Parse an incoming WhatsApp webhook payload (Twilio form-encoded or Wati.io JSON).
8
+ * Returns null if the payload cannot be parsed or is missing required fields.
9
+ */
10
+ export declare function parseIncomingWebhookPayload(rawBody: string, contentType: string): IncomingWebhookPayload | null;
11
+ /**
12
+ * Build a human-readable status notification message for an order.
13
+ * Pass a custom `statusMessages` map to override the defaults.
14
+ */
15
+ export declare function buildStatusMessage(input: StatusMessageInput, statusMessages?: Record<string, string>): string;
16
+ /**
17
+ * Send a WhatsApp message via Meta WhatsApp Business Cloud API.
18
+ * Distinct from the Twilio-based sendWhatsAppMessage — used for server-initiated
19
+ * platform announcements. Returns true on success, false on any failure.
20
+ */
21
+ export declare function sendWhatsAppBusinessMessage(input: WaBusinessSendInput): Promise<boolean>;
22
+ /**
23
+ * Send a pre-approved Meta message TEMPLATE via WhatsApp Business Cloud API.
24
+ * Required for business-initiated sends outside the 24h customer-service
25
+ * window (proactive order/status notifications) — Meta rejects free-form
26
+ * `sendWhatsAppBusinessMessage` `type: "text"` sends in that case. Returns
27
+ * true on success, false on any failure (including a missing/unapproved
28
+ * template — Meta returns a 4xx with an error body, not a network failure).
29
+ */
30
+ export declare function sendWhatsAppTemplateMessage(input: WaBusinessTemplateSendInput): Promise<boolean>;
31
+ /**
32
+ * Sync a store's products to a Meta Commerce Catalog via items_batch API.
33
+ * Batches at 50 items per call (Meta limit). Returns aggregate results.
34
+ */
35
+ export declare function syncProductsToCatalog(input: CatalogSyncInput): Promise<CatalogSyncResult>;
36
+ /**
37
+ * Build a plain-text purchase announcement message for WhatsApp.
38
+ * Sent to admin numbers and store owner when a new order is placed.
39
+ */
40
+ export declare function buildPurchaseAnnouncementMessage(input: PurchaseAnnouncementInput): string;
41
+ /**
42
+ * Build a wa.me share link with a pre-filled message.
43
+ * Opens WhatsApp contact picker — user selects group manually.
44
+ * This is the only API-compliant way to target a WhatsApp group.
45
+ */
46
+ export declare function buildGroupShareLink(message: string): string;
47
+ export { buildPaymentProofReviewMessage } from "../../orders/utils/payment-proof-message";
48
+ /**
49
+ * Send an outbound WhatsApp message via Twilio REST API.
50
+ * Returns true on success, false on failure.
51
+ */
52
+ export declare function sendWhatsAppMessage(input: SendWhatsAppInput): Promise<boolean>;
@@ -0,0 +1,288 @@
1
+ import { normalizeError } from "../../../errors/normalize.js";
2
+ import { createHmac, timingSafeEqual } from "crypto";
3
+ import { getDefaultCurrencySymbol, getDefaultLocale, } from "../../../core/baseline-resolver.js";
4
+ const META_GRAPH_BASE = "https://graph.facebook.com/v20.0";
5
+ export function buildCheckoutMessageURL(input) {
6
+ const { waNumber, cart, total, address, isPreorder = false } = input;
7
+ const prefix = isPreorder ? "\uD83D\uDD16 *PRE-ORDER*\n\n" : "";
8
+ const lines = cart.map((i) => `• ${i.name} ×${i.qty} — ${getDefaultCurrencySymbol()}${(i.salePrice * i.qty).toLocaleString(getDefaultLocale())}`);
9
+ const body = [
10
+ `${prefix}Hi Hobson! I'd like to place an order:`,
11
+ "",
12
+ ...lines,
13
+ "",
14
+ `*Total: ${getDefaultCurrencySymbol()}${total.toLocaleString(getDefaultLocale())}*`,
15
+ "",
16
+ `Deliver to: ${address.name}, ${address.line1}${address.line2 ? ", " + address.line2 : ""}, ${address.city} - ${address.pincode}`,
17
+ `Phone: ${address.phone}`,
18
+ "",
19
+ "Please share payment details.",
20
+ ].join("\n");
21
+ return `https://wa.me/${waNumber}?text=${encodeURIComponent(body)}`;
22
+ }
23
+ export function buildStatusNotificationURL(input) {
24
+ const { userPhone, template, vars } = input;
25
+ const body = template.replace(/\{(\w+)\}/g, (_, key) => vars[key] ?? `{${key}}`);
26
+ return `https://wa.me/${userPhone}?text=${encodeURIComponent(body)}`;
27
+ }
28
+ export function verifyWebhookSignature(input) {
29
+ const { payload, signature, secret } = input;
30
+ const expected = createHmac("sha256", secret).update(payload).digest("hex");
31
+ const expectedBuf = Buffer.from(expected, "hex");
32
+ const sigBuf = Buffer.from(signature, "hex");
33
+ if (expectedBuf.length !== sigBuf.length)
34
+ return false;
35
+ return timingSafeEqual(expectedBuf, sigBuf);
36
+ }
37
+ export function isAdminNumber(incomingNumber, adminBotNumber) {
38
+ const clean = (n) => n.replace(/\D/g, "");
39
+ return clean(incomingNumber).endsWith(clean(adminBotNumber));
40
+ }
41
+ /**
42
+ * Parse an incoming WhatsApp webhook payload (Twilio form-encoded or Wati.io JSON).
43
+ * Returns null if the payload cannot be parsed or is missing required fields.
44
+ */
45
+ export function parseIncomingWebhookPayload(rawBody, contentType) {
46
+ try {
47
+ if (contentType.includes("application/x-www-form-urlencoded")) {
48
+ const params = new URLSearchParams(rawBody);
49
+ const from = params.get("From") ?? "";
50
+ const body = params.get("Body") ?? "";
51
+ if (!from || !body)
52
+ return null;
53
+ return {
54
+ from: from.replace(/^whatsapp:/i, "").replace(/\D/g, ""),
55
+ body: body.trim(),
56
+ };
57
+ }
58
+ // JSON format (Wati.io or generic)
59
+ const json = JSON.parse(rawBody);
60
+ const from = typeof json.senderWaId === "string"
61
+ ? json.senderWaId
62
+ : typeof json.from === "string"
63
+ ? json.from
64
+ : "";
65
+ const body = typeof json.text?.body ===
66
+ "string"
67
+ ? json.text.body
68
+ : typeof json.body === "string"
69
+ ? json.body
70
+ : "";
71
+ if (!from || !body)
72
+ return null;
73
+ return { from: from.replace(/\D/g, ""), body: body.trim() };
74
+ }
75
+ catch (_err) {
76
+ void normalizeError(_err);
77
+ return null;
78
+ }
79
+ }
80
+ const DEFAULT_STATUS_MESSAGES = {
81
+ pending_payment: "🛒 Your order #{id} has been received and is awaiting payment confirmation.",
82
+ payment_confirmed: "✅ Payment confirmed for order #{id}! We're getting it ready.",
83
+ processing: "📦 Your order #{id} is being packed and prepared for dispatch.",
84
+ shipped: "🚚 Your order #{id} is on its way!{tracking}",
85
+ out_for_delivery: "🏃 Your order #{id} is out for delivery today!",
86
+ delivered: "🎉 Order #{id} delivered! Thank you for your order!",
87
+ cancelled: "❌ Your order #{id} has been cancelled. Contact us if you have any questions.",
88
+ refund_initiated: "💸 Refund initiated for order #{id}. It should reflect within 5–7 business days.",
89
+ };
90
+ /**
91
+ * Build a human-readable status notification message for an order.
92
+ * Pass a custom `statusMessages` map to override the defaults.
93
+ */
94
+ export function buildStatusMessage(input, statusMessages = DEFAULT_STATUS_MESSAGES) {
95
+ const { orderId, status, trackingNumber, courierName } = input;
96
+ const template = statusMessages[status] ??
97
+ `Your order #${orderId} status updated to: ${status}.`;
98
+ const trackingLine = trackingNumber
99
+ ? `\nTracking: ${courierName ? courierName + " — " : ""}${trackingNumber}`
100
+ : "";
101
+ return template.replace("{id}", orderId).replace("{tracking}", trackingLine);
102
+ }
103
+ /**
104
+ * Send a WhatsApp message via Meta WhatsApp Business Cloud API.
105
+ * Distinct from the Twilio-based sendWhatsAppMessage — used for server-initiated
106
+ * platform announcements. Returns true on success, false on any failure.
107
+ */
108
+ export async function sendWhatsAppBusinessMessage(input) {
109
+ const { toPhone, message, phoneNumberId, accessToken } = input;
110
+ const cleanPhone = toPhone.replace(/\D/g, "");
111
+ if (!cleanPhone || !phoneNumberId || !accessToken)
112
+ return false;
113
+ try {
114
+ const res = await fetch(`${META_GRAPH_BASE}/${phoneNumberId}/messages`, {
115
+ method: "POST",
116
+ headers: {
117
+ "Content-Type": "application/json",
118
+ Authorization: `Bearer ${accessToken}`,
119
+ },
120
+ body: JSON.stringify({
121
+ messaging_product: "whatsapp",
122
+ to: cleanPhone,
123
+ type: "text",
124
+ text: { body: message },
125
+ }),
126
+ });
127
+ return res.ok;
128
+ }
129
+ catch (_err) {
130
+ void normalizeError(_err);
131
+ return false;
132
+ }
133
+ }
134
+ /**
135
+ * Send a pre-approved Meta message TEMPLATE via WhatsApp Business Cloud API.
136
+ * Required for business-initiated sends outside the 24h customer-service
137
+ * window (proactive order/status notifications) — Meta rejects free-form
138
+ * `sendWhatsAppBusinessMessage` `type: "text"` sends in that case. Returns
139
+ * true on success, false on any failure (including a missing/unapproved
140
+ * template — Meta returns a 4xx with an error body, not a network failure).
141
+ */
142
+ export async function sendWhatsAppTemplateMessage(input) {
143
+ const { toPhone, phoneNumberId, accessToken, templateName, languageCode, bodyParams } = input;
144
+ const cleanPhone = toPhone.replace(/\D/g, "");
145
+ if (!cleanPhone || !phoneNumberId || !accessToken || !templateName)
146
+ return false;
147
+ try {
148
+ const res = await fetch(`${META_GRAPH_BASE}/${phoneNumberId}/messages`, {
149
+ method: "POST",
150
+ headers: {
151
+ "Content-Type": "application/json",
152
+ Authorization: `Bearer ${accessToken}`,
153
+ },
154
+ body: JSON.stringify({
155
+ messaging_product: "whatsapp",
156
+ to: cleanPhone,
157
+ type: "template",
158
+ template: {
159
+ name: templateName,
160
+ language: { code: languageCode },
161
+ ...(bodyParams?.length
162
+ ? {
163
+ components: [
164
+ {
165
+ type: "body",
166
+ parameters: bodyParams.map((text) => ({ type: "text", text })),
167
+ },
168
+ ],
169
+ }
170
+ : {}),
171
+ },
172
+ }),
173
+ });
174
+ return res.ok;
175
+ }
176
+ catch (_err) {
177
+ void normalizeError(_err);
178
+ return false;
179
+ }
180
+ }
181
+ /**
182
+ * Sync a store's products to a Meta Commerce Catalog via items_batch API.
183
+ * Batches at 50 items per call (Meta limit). Returns aggregate results.
184
+ */
185
+ export async function syncProductsToCatalog(input) {
186
+ const { catalogId, accessToken, products } = input;
187
+ const result = {
188
+ handles: [],
189
+ successCount: 0,
190
+ failureCount: 0,
191
+ errors: [],
192
+ };
193
+ const BATCH_SIZE = 50;
194
+ for (let i = 0; i < products.length; i += BATCH_SIZE) {
195
+ const batch = products.slice(i, i + BATCH_SIZE);
196
+ const requests = batch.map((p) => ({
197
+ method: "UPDATE",
198
+ retailer_id: p.id,
199
+ data: {
200
+ name: p.title,
201
+ description: p.description,
202
+ // Meta expects "<amount> <ISO_CURRENCY>" e.g. "450.00 INR"
203
+ price: `${p.price.toFixed(2)} ${p.currency}`,
204
+ image_url: p.imageUrl,
205
+ availability: p.availability,
206
+ condition: p.condition,
207
+ ...(p.link ? { url: p.link } : {}),
208
+ },
209
+ }));
210
+ try {
211
+ const res = await fetch(`${META_GRAPH_BASE}/${catalogId}/items_batch`, {
212
+ method: "POST",
213
+ headers: {
214
+ "Content-Type": "application/json",
215
+ Authorization: `Bearer ${accessToken}`,
216
+ },
217
+ body: JSON.stringify({ requests }),
218
+ });
219
+ const json = (await res.json());
220
+ if (res.ok && json.handles) {
221
+ result.handles.push(...json.handles);
222
+ result.successCount += batch.length;
223
+ }
224
+ else {
225
+ result.failureCount += batch.length;
226
+ result.errors.push(json.error?.message ?? `Batch ${i / BATCH_SIZE + 1} failed`);
227
+ }
228
+ }
229
+ catch (err) {
230
+ void normalizeError(err);
231
+ result.failureCount += batch.length;
232
+ result.errors.push(String(err));
233
+ }
234
+ }
235
+ return result;
236
+ }
237
+ /**
238
+ * Build a plain-text purchase announcement message for WhatsApp.
239
+ * Sent to admin numbers and store owner when a new order is placed.
240
+ */
241
+ export function buildPurchaseAnnouncementMessage(input) {
242
+ const { buyerName, firstItemName, additionalItemCount, totalAmount, orderId } = input;
243
+ const extra = additionalItemCount > 0 ? ` + ${additionalItemCount} more item${additionalItemCount > 1 ? "s" : ""}` : "";
244
+ const amount = totalAmount.toLocaleString("en-IN");
245
+ return `🛍️ New order! ${buyerName} purchased ${firstItemName}${extra} for ₹${amount}. Order #${orderId}`;
246
+ }
247
+ /**
248
+ * Build a wa.me share link with a pre-filled message.
249
+ * Opens WhatsApp contact picker — user selects group manually.
250
+ * This is the only API-compliant way to target a WhatsApp group.
251
+ */
252
+ export function buildGroupShareLink(message) {
253
+ return `https://wa.me/?text=${encodeURIComponent(message)}`;
254
+ }
255
+ /*
256
+ * `buildPaymentProofReviewMessage` lives in `features/orders/utils/`, not
257
+ * beside these senders. It is a pure wa.me link-body builder with no crypto
258
+ * and no network, and it belongs to the orders domain — the move happened
259
+ * during the 2026-10-20 removal and is kept, because re-homing it here would
260
+ * drag this file's top-level `crypto` import into the client barrel's graph
261
+ * (Root Cause #24).
262
+ */
263
+ export { buildPaymentProofReviewMessage } from "../../orders/utils/payment-proof-message.js";
264
+ /**
265
+ * Send an outbound WhatsApp message via Twilio REST API.
266
+ * Returns true on success, false on failure.
267
+ */
268
+ export async function sendWhatsAppMessage(input) {
269
+ const { toPhone, message, accountSid, authToken, fromNumber } = input;
270
+ const cleanPhone = toPhone.replace(/\D/g, "");
271
+ if (!cleanPhone)
272
+ return false;
273
+ const url = `https://api.twilio.com/2010-04-01/Accounts/${accountSid}/Messages.json`;
274
+ const body = new URLSearchParams({
275
+ From: fromNumber,
276
+ To: `whatsapp:+${cleanPhone}`,
277
+ Body: message,
278
+ });
279
+ const res = await fetch(url, {
280
+ method: "POST",
281
+ headers: {
282
+ "Content-Type": "application/x-www-form-urlencoded",
283
+ Authorization: `Basic ${Buffer.from(`${accountSid}:${authToken}`).toString("base64")}`,
284
+ },
285
+ body: body.toString(),
286
+ });
287
+ return res.ok;
288
+ }
@@ -0,0 +1,3 @@
1
+ export * from "./types";
2
+ export * from "./components";
3
+ export { manifest } from "./manifest";
@@ -0,0 +1,3 @@
1
+ export * from "./types/index.js";
2
+ export * from "./components/index.js";
3
+ export { manifest } from "./manifest.js";
@@ -0,0 +1,2 @@
1
+ import type { FeatureManifest } from "../../contracts";
2
+ export declare const manifest: FeatureManifest;
@@ -0,0 +1,13 @@
1
+ export const manifest = {
2
+ name: "whatsappBot",
3
+ i18nNamespace: "whatsappBot",
4
+ envKeys: [
5
+ "WHATSAPP_BOT_NUMBER",
6
+ "WHATSAPP_WEBHOOK_SECRET",
7
+ ],
8
+ routes: [],
9
+ apiRoutes: [
10
+ { segment: "api/whatsapp/webhook", methods: ["POST"] },
11
+ { segment: "api/whatsapp/send-status", methods: ["POST"] },
12
+ ],
13
+ };
@@ -0,0 +1,7 @@
1
+ {
2
+ "whatsappBot": {
3
+ "chatNow": "Chat on WhatsApp",
4
+ "orderViaWhatsapp": "Order via WhatsApp",
5
+ "supportViaWhatsapp": "Contact support"
6
+ }
7
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @mohasinac/appkit/features/whatsapp-bot/server
3
+ *
4
+ * Server-only entry point for WhatsApp bot helpers (crypto operations).
5
+ */
6
+ export * from "./helpers/whatsapp";
7
+ export * from "./types";
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @mohasinac/appkit/features/whatsapp-bot/server
3
+ *
4
+ * Server-only entry point for WhatsApp bot helpers (crypto operations).
5
+ */
6
+ export * from "./helpers/whatsapp.js";
7
+ export * from "./types/index.js";