package ironlicensing import ( "bytes" "context" "encoding/json" "fmt" "io" "net/http" "os" "path/filepath" "runtime" "time" "github.com/google/uuid" ) type Transport struct { baseURL string publicKey string productSlug string debug bool httpClient *http.Client machineID string } func NewTransport(baseURL, publicKey, productSlug string, timeout time.Duration, debug bool) *Transport { t := &Transport{ baseURL: baseURL, publicKey: publicKey, productSlug: productSlug, debug: debug, httpClient: &http.Client{Timeout: timeout}, } t.machineID = t.getMachineID() return t } func (t *Transport) log(msg string) { if t.debug { fmt.Printf("[IronLicensing] %s\n", msg) } } func (t *Transport) getMachineID() string { homeDir, _ := os.UserHomeDir() idPath := filepath.Join(homeDir, ".ironlicensing", "machine_id") if data, err := os.ReadFile(idPath); err == nil { return string(data) } id := uuid.New().String() os.MkdirAll(filepath.Dir(idPath), 0755) os.WriteFile(idPath, []byte(id), 0644) return id } func (t *Transport) request(ctx context.Context, method, path string, body any) (*http.Response, error) { var reqBody io.Reader if body != nil { data, _ := json.Marshal(body) reqBody = bytes.NewReader(data) } req, err := http.NewRequestWithContext(ctx, method, t.baseURL+path, reqBody) if err != nil { return nil, err } req.Header.Set("Content-Type", "application/json") req.Header.Set("X-Public-Key", t.publicKey) req.Header.Set("X-Product-Slug", t.productSlug) return t.httpClient.Do(req) } func (t *Transport) Validate(ctx context.Context, licenseKey string) LicenseResult { t.log(fmt.Sprintf("Validating: %s...", licenseKey[:min(10, len(licenseKey))])) resp, err := t.request(ctx, "POST", "/api/v1/validate", map[string]string{ "licenseKey": licenseKey, "machineId": t.machineID, }) if err != nil { return LicenseResult{Valid: false, Error: err.Error()} } defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) if resp.StatusCode == 200 { var result LicenseResult json.Unmarshal(body, &result) return result } var errResp struct{ Error string } json.Unmarshal(body, &errResp) return LicenseResult{Valid: false, Error: errResp.Error} } func (t *Transport) Activate(ctx context.Context, licenseKey, machineName string) LicenseResult { t.log(fmt.Sprintf("Activating: %s...", licenseKey[:min(10, len(licenseKey))])) if machineName == "" { machineName, _ = os.Hostname() } resp, err := t.request(ctx, "POST", "/api/v1/activate", map[string]string{ "licenseKey": licenseKey, "machineId": t.machineID, "machineName": machineName, "platform": runtime.GOOS, }) if err != nil { return LicenseResult{Valid: false, Error: err.Error()} } defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) if resp.StatusCode == 200 { var result LicenseResult json.Unmarshal(body, &result) return result } var errResp struct{ Error string } json.Unmarshal(body, &errResp) return LicenseResult{Valid: false, Error: errResp.Error} } func (t *Transport) Deactivate(ctx context.Context, licenseKey string) bool { resp, err := t.request(ctx, "POST", "/api/v1/deactivate", map[string]string{ "licenseKey": licenseKey, "machineId": t.machineID, }) if err != nil { return false } defer resp.Body.Close() return resp.StatusCode == 200 } func (t *Transport) StartTrial(ctx context.Context, email string) LicenseResult { t.log(fmt.Sprintf("Starting trial for: %s", email)) resp, err := t.request(ctx, "POST", "/api/v1/trial", map[string]string{ "email": email, "machineId": t.machineID, }) if err != nil { return LicenseResult{Valid: false, Error: err.Error()} } defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) if resp.StatusCode == 200 { var result LicenseResult json.Unmarshal(body, &result) return result } var errResp struct{ Error string } json.Unmarshal(body, &errResp) return LicenseResult{Valid: false, Error: errResp.Error} } func (t *Transport) GetTiers(ctx context.Context) []ProductTier { resp, err := t.request(ctx, "GET", "/api/v1/tiers", nil) if err != nil { return nil } defer resp.Body.Close() if resp.StatusCode == 200 { var result struct{ Tiers []ProductTier } json.NewDecoder(resp.Body).Decode(&result) return result.Tiers } return nil } func (t *Transport) StartCheckout(ctx context.Context, tierID, email string) CheckoutResult { resp, err := t.request(ctx, "POST", "/api/v1/checkout", map[string]string{ "tierId": tierID, "email": email, }) if err != nil { return CheckoutResult{Success: false, Error: err.Error()} } defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) if resp.StatusCode == 200 { var result CheckoutResult json.Unmarshal(body, &result) result.Success = true return result } var errResp struct{ Error string } json.Unmarshal(body, &errResp) return CheckoutResult{Success: false, Error: errResp.Error} } func min(a, b int) int { if a < b { return a } return b }