package tiktok // WebhookHandler processes incoming TikTok webhook HTTP requests. // Reference: gochat's Telegram WebhookHandler pattern // + TikTok Business API webhook documentation // // TikTok webhook flow: // 1. Verification: GET request with verify_token challenge (on webhook setup) // 2. Events: POST requests with JSON event payloads (message.received, etc.) // // The WebhookHandler: // - Validates webhook authenticity (verify_token for setup, signature for events) // - Parses the JSON payload into TikTokWebhookEvent // - Delegates to IncomingProcessor for message processing import ( "encoding/json" "fmt" "io" "net/http" applogger "github.com/gochat/gochat/pkg/logger" ) // WebhookHandler processes TikTok webhook HTTP requests. type WebhookHandler struct { service *TikTokService pipeline *IncomingProcessor } // NewWebhookHandler creates a TikTok webhook handler. func NewWebhookHandler(service *TikTokService, pipeline *IncomingProcessor) *WebhookHandler { return &WebhookHandler{ service: service, pipeline: pipeline, } } // HandleWebhookRequest processes an incoming TikTok webhook HTTP POST request. // Returns the parsed TikTokWebhookEvent for the pipeline to process. func (h *WebhookHandler) HandleWebhookRequest(r *http.Request) (*TikTokWebhookEvent, error) { // Read request body body, err := io.ReadAll(r.Body) if err != nil { return nil, fmt.Errorf("tiktok webhook: failed to read body: %w", err) } defer r.Body.Close() if len(body) == 0 { return nil, fmt.Errorf("tiktok webhook: empty request body") } // Parse the webhook event var event TikTokWebhookEvent if err := json.Unmarshal(body, &event); err != nil { return nil, fmt.Errorf("tiktok webhook: failed to parse JSON: %w", err) } applogger.L().Infof("TikTok webhook: received event type=%s biz_id=%s", event.Type, event.BizID) return &event, nil } // HandleVerifyRequest processes a TikTok webhook verification GET request. // TikTok sends this during webhook setup to verify the endpoint. func (h *WebhookHandler) HandleVerifyRequest(r *http.Request, expectedVerifyToken string) (string, error) { verifyToken := r.URL.Query().Get("verify_token") challenge := r.URL.Query().Get("challenge") if verifyToken != expectedVerifyToken { return "", fmt.Errorf("tiktok webhook verify: token mismatch (expected=%s, got=%s)", expectedVerifyToken, verifyToken) } applogger.L().Infof("TikTok webhook verify: challenge accepted, token=%s", verifyToken) return challenge, nil }