package internal import ( "encoding/json" "fmt" "io" "log" "net/http" "time" ) func (lc *LicenseChecker) Validate() error { url := fmt.Sprintf("%s/license/lookup?key=%s&format=json", lc.LicenseAPIURL, lc.APIKey) resp, err := http.Get(url) if err != nil { lc.setValid(false) return fmt.Errorf("license lookup failed: %w", err) } defer resp.Body.Close() if resp.StatusCode != 200 { lc.setValid(false) return fmt.Errorf("license lookup error: %s", resp.Status) } body, err := io.ReadAll(resp.Body) if err != nil { lc.setValid(false) return fmt.Errorf("reading response failed: %w", err) } var data struct { Revoked bool `json:"revoked"` ExpiresAt time.Time `json:"expires_at"` } if err := json.Unmarshal(body, &data); err != nil { lc.setValid(false) return fmt.Errorf("unmarshal error: %w", err) } if data.Revoked || time.Now().After(data.ExpiresAt) { lc.setValid(false) return fmt.Errorf("license expired or revoked") } lc.mu.Lock() lc.valid = true lc.lastGood = time.Now() lc.mu.Unlock() log.Printf("✅ License validated. Expires: %s", data.ExpiresAt) return nil } func (lc *LicenseChecker) StartBackgroundCheck() { go func() { for { time.Sleep(lc.PollInterval) err := lc.Validate() if err != nil { log.Printf("⚠️ License check failed: %v", err) } } }() } func (lc *LicenseChecker) IsValid() bool { lc.mu.RLock() defer lc.mu.RUnlock() return lc.valid }