import { useEffect, useState } from "react"; import crypto from "crypto"; import { AdminButton } from "@/components/admin/design-system"; import { getWebhookSettings, saveWebhookSettings } from "@/actions/wholesale"; import type { MsgFn } from "./types"; interface WebhookSettingsSectionProps { brandId: string; onMsg: MsgFn; } // Section inside SettingsTab that lets admins configure an outbound HMAC-signed // webhook for wholesale order events. Includes a "Send Test Webhook" button that // dispatches a signed payload to the configured URL. export default function WebhookSettingsSection({ brandId, onMsg }: WebhookSettingsSectionProps) { const [settings, setSettings] = useState<{ url: string; secret: string; enabled: boolean; }>({ url: "", secret: "", enabled: false }); const [loading, setLoading] = useState(true); const [saving, setSaving] = useState(false); const [testing, setTesting] = useState(false); useEffect(() => { getWebhookSettings(brandId).then((s: { url: string; secret: string; enabled: boolean } | null) => { if (s) { setSettings({ url: s.url ?? "", secret: s.secret ?? "", enabled: s.enabled }); } else { setSettings({ url: "", secret: "", enabled: false }); } setLoading(false); }); }, [brandId]); async function handleSave() { setSaving(true); const result = await saveWebhookSettings({ brandId, url: settings.url, secret: settings.secret, enabled: settings.enabled, }); setSaving(false); if (result.success) { onMsg("success", "Webhook settings saved."); } else { onMsg("error", result.error ?? "Failed to save webhook settings."); } } async function handleTestDispatch() { if (!settings.url || !settings.secret) { onMsg("error", "Enter both a webhook URL and signing secret before testing."); return; } setTesting(true); onMsg("success", "Sending test webhook..."); const testPayload = { event: "order_created", test: true, brand_id: brandId, message: "Test webhook from Route Commerce wholesale portal", timestamp: new Date().toISOString(), order_id: null, }; const payloadString = JSON.stringify(testPayload); const signature = crypto .createHmac("sha256", settings.secret) .update(payloadString) .digest("hex"); try { const res = await fetch(settings.url, { method: "POST", headers: { "Content-Type": "application/json", "X-Webhook-Signature": `sha256=${signature}`, "X-Webhook-Event": "order_created", }, body: payloadString, }); const responseText = await res.text().catch(() => ""); if (res.ok) { onMsg("success", `Test delivered — ${res.status} response from your endpoint. Check wholesale_sync_log for the logged entry.`); } else { onMsg("error", `Webhook endpoint returned ${res.status}: ${responseText.slice(0, 120)}`); } } catch (err) { onMsg("error", `Connection failed: ${err instanceof Error ? err.message : "Network error"}`); } setTesting(false); } if (loading) { return
Loading webhook settings...
; } return (Webhook Enabled
When disabled, no events are queued or sent.
The receiver should verify the X-Webhook-Signature header using this secret.