From feb22885d0ef2ba18265129ef236c3bf4d97ed83 Mon Sep 17 00:00:00 2001 From: Nitheesh D R Date: Mon, 7 Sep 2026 21:36:25 +0530 Subject: [PATCH 1/2] PUL-16: minimal WhatsApp Cloud API pilot, for App Review App Review's screencast and API-test-call requirements need a real, working feature to demonstrate -- nothing in the Cloud API epic (PUL-13 through PUL-20) existed in code yet, only Jira tracking and Meta-side account setup. This is a deliberately minimal, real slice: one working send (whatsapp_business_messaging) and one working template list (whatsapp_business_management), against Meta's own free test business number (temporary token from the App Dashboard's API Setup page, in .env.local, never committed). Not the merchant-facing send path -- that's PUL-18, gated on the billing decision (PUL-15) and template workflow (PUL-17). Not reachable from the main sidebar nav. - src/lib/whatsapp-cloud/client.ts: new, parallel to (not replacing) src/lib/whatsapp/client.ts's gateway client, per PUL-16's own scope note in the epic. - src/app/api/whatsapp-cloud/{test-send,templates}: two thin routes, same requireTenant + hand-typed-recipient-only pattern as the existing /api/whatsapp/test route. - src/app/(app)/whatsapp-cloud-pilot: one page to record the App Review screencast against. - src/proxy.ts: added to PROTECTED_PAGES. - src/lib/openapi.ts: both new routes documented, new tag added. This is a partial slice of PUL-16's full scope (no webhook receiver, no template-message sending yet) -- staying open after this merges, not moving to Done. Co-Authored-By: Claude Sonnet 5 --- src/app/(app)/whatsapp-cloud-pilot/page.tsx | 137 ++++++++++++++ src/app/api/whatsapp-cloud/templates/route.ts | 26 +++ src/app/api/whatsapp-cloud/test-send/route.ts | 52 ++++++ src/lib/openapi.ts | 74 ++++++++ src/lib/whatsapp-cloud/client.ts | 170 ++++++++++++++++++ src/proxy.ts | 1 + 6 files changed, 460 insertions(+) create mode 100644 src/app/(app)/whatsapp-cloud-pilot/page.tsx create mode 100644 src/app/api/whatsapp-cloud/templates/route.ts create mode 100644 src/app/api/whatsapp-cloud/test-send/route.ts create mode 100644 src/lib/whatsapp-cloud/client.ts diff --git a/src/app/(app)/whatsapp-cloud-pilot/page.tsx b/src/app/(app)/whatsapp-cloud-pilot/page.tsx new file mode 100644 index 0000000..b515c9b --- /dev/null +++ b/src/app/(app)/whatsapp-cloud-pilot/page.tsx @@ -0,0 +1,137 @@ +"use client"; + +import { Loader2, Send, ListChecks } from "lucide-react"; +import { useState } from "react"; +import { toast } from "sonner"; +import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { Input } from "@/components/ui/input"; +import { Textarea } from "@/components/ui/textarea"; + +interface CloudMessageTemplate { + id: string; + name: string; + status: string; + category: string; + language: string; +} + +/** + * PUL-16 pilot: a minimal, real exercise of the Meta WhatsApp Cloud API, + * against the temporary test business number from the App Dashboard's own + * API Setup page — not the merchant-facing send path (that's PUL-18) and + * not reachable from the main sidebar nav. Exists so App Review's + * screencast requirement has a genuine, working flow to record: a real + * send via whatsapp_business_messaging, and a real template list via + * whatsapp_business_management. + */ +export default function WhatsAppCloudPilotPage() { + const [to, setTo] = useState(""); + const [message, setMessage] = useState("Hello from the PulseCommerce Cloud API pilot."); + const [sending, setSending] = useState(false); + + const [templates, setTemplates] = useState(null); + const [loadingTemplates, setLoadingTemplates] = useState(false); + + const sendTest = async () => { + setSending(true); + try { + const res = await fetch("/api/whatsapp-cloud/test-send", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ to, message }), + }); + const body = await res.json(); + if (!res.ok) { + toast.error(body.error ?? "The send failed."); + return; + } + toast.success(`Sent — message id ${body.messageId}`); + } finally { + setSending(false); + } + }; + + const loadTemplates = async () => { + setLoadingTemplates(true); + try { + const res = await fetch("/api/whatsapp-cloud/templates", { cache: "no-store" }); + const body = await res.json(); + if (!res.ok) { + toast.error(body.error ?? "Could not list templates."); + return; + } + setTemplates(body.templates); + } finally { + setLoadingTemplates(false); + } + }; + + return ( +
+ + + + Send a test message + + + Uses whatsapp_business_messaging against Meta's test business number. The + recipient must be one of the up to 5 numbers verified as a test recipient in the App + Dashboard. + + + + setTo(e.target.value)} + /> +