// Package webhook provides Gin-compatible HTTP handler adapters for external // channel webhook endpoints. Each adapter wraps the corresponding channel // sub-package's WebhookHandler and exposes methods that can be bound directly // to Gin router groups. package webhook import ( "context" "time" "github.com/gin-gonic/gin" "gorm.io/gorm" "github.com/gochat/gochat/internal/channel" "github.com/gochat/gochat/internal/channel/whatsapp" "github.com/gochat/gochat/internal/model" ) // WhatsAppWebhookHandler is a Gin adapter that wraps the WhatsApp // sub-package's WebhookHandler. It translates Gin context calls into the // underlying handler methods so that WhatsApp webhook routes can be registered // on a Gin engine with minimal glue code. // // Chatwoot-style routes: // // GET /webhooks/whatsapp/:phone_number → HandleWhatsAppVerification // POST /webhooks/whatsapp/:phone_number → HandleWhatsAppWebhook // // WhatsApp Cloud API webhook verification (GET): // // The Meta platform sends a GET request with query parameters: // - hub.mode = "subscribe" // - hub.verify_token = the token configured in the Meta dashboard // - hub.challenge = a string the endpoint must echo back verbatim // // The handler validates hub.verify_token against the stored token and // returns hub.challenge as the response body with HTTP 200, or returns // HTTP 403 on mismatch. // // WhatsApp Cloud API webhook events (POST): // // Meta delivers event payloads as JSON with structure: // { // "object": "whatsapp_business_account", // "entry": [ { "changes": [ ... ] } ] // } // // The handler parses the payload, dispatches incoming messages and status // updates, and must respond with HTTP 200 OK within 10 seconds to avoid // Meta retrying delivery. type WhatsAppWebhookHandler struct { provider *whatsapp.WhatsAppProvider waWebhook *whatsapp.WebhookHandler persister *IncomingPersister } // NewWhatsAppWebhookHandler creates a Gin adapter wrapping the WhatsApp // sub-package's WebhookHandler. The provider is stored for future use (e.g. // health checks or direct API calls) while waWebhook is the core handler that // processes verification and event requests. func NewWhatsAppWebhookHandler(provider *whatsapp.WhatsAppProvider, waWebhook *whatsapp.WebhookHandler, db *gorm.DB, dispatcher ...*channel.Dispatcher) *WhatsAppWebhookHandler { h := &WhatsAppWebhookHandler{ provider: provider, waWebhook: waWebhook, persister: NewIncomingPersister(db, dispatcher...), } if waWebhook != nil && h.persister != nil { waWebhook.SetIncomingPersister(whatsAppPersisterAdapter{persister: h.persister}) } return h } type whatsAppPersisterAdapter struct { persister *IncomingPersister } func (a whatsAppPersisterAdapter) PersistIncoming(ctx context.Context, inbox *model.Inbox, msg *channel.IncomingMessage) (interface{}, error) { return a.persister.PersistIncoming(ctx, inbox, msg) } func (a whatsAppPersisterAdapter) UpdateMessageStatus(ctx context.Context, inbox *model.Inbox, sourceID string, status model.MessageStatus, occurredAt *time.Time) error { return a.persister.UpdateMessageStatus(ctx, inbox, sourceID, status, occurredAt) } // HandleWhatsAppVerification handles GET requests for WhatsApp Cloud API // webhook verification. Meta sends this request during initial webhook setup // and periodic re-verification. It delegates to the underlying // waWebhook.HandleVerification method which validates hub.verify_token and // echoes hub.challenge on success. // // Expected Gin route: GET /webhooks/whatsapp/:phone_number func (h *WhatsAppWebhookHandler) HandleWhatsAppVerification(c *gin.Context) { h.waWebhook.HandleVerification(c) } // HandleWhatsAppWebhook handles POST requests carrying WhatsApp Cloud API // event payloads (messages, status updates, template events, etc.). It // delegates to the underlying waWebhook.HandleWebhookEvent method which // parses the JSON body and dispatches events to the appropriate processor. // // The endpoint must respond with HTTP 200 OK within 10 seconds; Meta retries // delivery on timeout or non-200 responses. // // Expected Gin route: POST /webhooks/whatsapp/:phone_number func (h *WhatsAppWebhookHandler) HandleWhatsAppWebhook(c *gin.Context) { h.waWebhook.HandleWebhookEvent(c) }