ironlicensing-go/client.go

267 lines
5.9 KiB
Go

package ironlicensing
import (
"context"
"errors"
"fmt"
"sync"
)
var (
ErrNotInitialized = errors.New("ironlicensing: client not initialized")
ErrPublicKeyRequired = errors.New("ironlicensing: public key is required")
ErrProductSlugRequired = errors.New("ironlicensing: product slug is required")
)
type LicenseRequiredError struct {
Feature string
}
func (e *LicenseRequiredError) Error() string {
return fmt.Sprintf("Feature '%s' requires a valid license", e.Feature)
}
type Client struct {
options Options
transport *Transport
currentLicense *License
licenseKey string
mu sync.RWMutex
}
var globalClient *Client
var globalMu sync.RWMutex
func NewClient(options Options) (*Client, error) {
if options.PublicKey == "" {
return nil, ErrPublicKeyRequired
}
if options.ProductSlug == "" {
return nil, ErrProductSlugRequired
}
opts := options.WithDefaults()
c := &Client{
options: opts,
transport: NewTransport(opts.APIBaseURL, opts.PublicKey, opts.ProductSlug, opts.HTTPTimeout, opts.Debug),
}
if opts.Debug {
fmt.Println("[IronLicensing] Client initialized")
}
return c, nil
}
func Init(publicKey, productSlug string, opts ...func(*Options)) error {
options := DefaultOptions()
options.PublicKey = publicKey
options.ProductSlug = productSlug
for _, opt := range opts {
opt(&options)
}
client, err := NewClient(options)
if err != nil {
return err
}
globalMu.Lock()
globalClient = client
globalMu.Unlock()
return nil
}
func WithDebug(debug bool) func(*Options) {
return func(o *Options) { o.Debug = debug }
}
func getGlobalClient() (*Client, error) {
globalMu.RLock()
defer globalMu.RUnlock()
if globalClient == nil {
return nil, ErrNotInitialized
}
return globalClient, nil
}
func (c *Client) Validate(licenseKey string) LicenseResult {
return c.ValidateContext(context.Background(), licenseKey)
}
func (c *Client) ValidateContext(ctx context.Context, licenseKey string) LicenseResult {
result := c.transport.Validate(ctx, licenseKey)
if result.Valid && result.License != nil {
c.mu.Lock()
c.currentLicense = result.License
c.licenseKey = licenseKey
c.mu.Unlock()
}
return result
}
func (c *Client) Activate(licenseKey string) LicenseResult {
return c.ActivateContext(context.Background(), licenseKey, "")
}
func (c *Client) ActivateContext(ctx context.Context, licenseKey, machineName string) LicenseResult {
result := c.transport.Activate(ctx, licenseKey, machineName)
if result.Valid && result.License != nil {
c.mu.Lock()
c.currentLicense = result.License
c.licenseKey = licenseKey
c.mu.Unlock()
}
return result
}
func (c *Client) Deactivate() bool {
return c.DeactivateContext(context.Background())
}
func (c *Client) DeactivateContext(ctx context.Context) bool {
c.mu.RLock()
key := c.licenseKey
c.mu.RUnlock()
if key == "" {
return false
}
if c.transport.Deactivate(ctx, key) {
c.mu.Lock()
c.currentLicense = nil
c.licenseKey = ""
c.mu.Unlock()
return true
}
return false
}
func (c *Client) StartTrial(email string) LicenseResult {
return c.StartTrialContext(context.Background(), email)
}
func (c *Client) StartTrialContext(ctx context.Context, email string) LicenseResult {
result := c.transport.StartTrial(ctx, email)
if result.Valid && result.License != nil {
c.mu.Lock()
c.currentLicense = result.License
c.licenseKey = result.License.Key
c.mu.Unlock()
}
return result
}
func (c *Client) HasFeature(featureKey string) bool {
c.mu.RLock()
defer c.mu.RUnlock()
if c.currentLicense == nil {
return false
}
for _, f := range c.currentLicense.Features {
if f.Key == featureKey && f.Enabled {
return true
}
}
return false
}
func (c *Client) RequireFeature(featureKey string) error {
if !c.HasFeature(featureKey) {
return &LicenseRequiredError{Feature: featureKey}
}
return nil
}
func (c *Client) GetFeature(featureKey string) *Feature {
c.mu.RLock()
defer c.mu.RUnlock()
if c.currentLicense == nil {
return nil
}
for _, f := range c.currentLicense.Features {
if f.Key == featureKey {
return &f
}
}
return nil
}
func (c *Client) License() *License {
c.mu.RLock()
defer c.mu.RUnlock()
return c.currentLicense
}
func (c *Client) Status() LicenseStatus {
c.mu.RLock()
defer c.mu.RUnlock()
if c.currentLicense != nil {
return c.currentLicense.Status
}
return StatusNotActivated
}
func (c *Client) IsLicensed() bool {
c.mu.RLock()
defer c.mu.RUnlock()
return c.currentLicense != nil && (c.currentLicense.Status == StatusValid || c.currentLicense.Status == StatusTrial)
}
func (c *Client) IsTrial() bool {
c.mu.RLock()
defer c.mu.RUnlock()
return c.currentLicense != nil && (c.currentLicense.Status == StatusTrial || c.currentLicense.Type == TypeTrial)
}
func (c *Client) GetTiers() []ProductTier {
return c.transport.GetTiers(context.Background())
}
func (c *Client) StartPurchase(tierID, email string) CheckoutResult {
return c.transport.StartCheckout(context.Background(), tierID, email)
}
// Global functions
func Validate(licenseKey string) LicenseResult {
c, err := getGlobalClient()
if err != nil {
return LicenseResult{Valid: false, Error: err.Error()}
}
return c.Validate(licenseKey)
}
func Activate(licenseKey string) LicenseResult {
c, err := getGlobalClient()
if err != nil {
return LicenseResult{Valid: false, Error: err.Error()}
}
return c.Activate(licenseKey)
}
func Deactivate() bool {
c, err := getGlobalClient()
if err != nil {
return false
}
return c.Deactivate()
}
func StartTrial(email string) LicenseResult {
c, err := getGlobalClient()
if err != nil {
return LicenseResult{Valid: false, Error: err.Error()}
}
return c.StartTrial(email)
}
func HasFeature(featureKey string) bool {
c, err := getGlobalClient()
if err != nil {
return false
}
return c.HasFeature(featureKey)
}
func RequireFeature(featureKey string) error {
c, err := getGlobalClient()
if err != nil {
return err
}
return c.RequireFeature(featureKey)
}