package service import ( "context" "encoding/json" "fmt" "github.com/gochat/gochat/internal/model" "github.com/gochat/gochat/internal/repository" applogger "github.com/gochat/gochat/pkg/logger" ) // ShopifyIntegrationService implements Shopify integration business logic. // Reference: Chatwoot Integrations::ShopifyController // Shopify integration links customer order information to conversations. type ShopifyIntegrationService struct { hookRepo *repository.IntegrationHookRepo } // NewShopifyIntegrationService creates a new ShopifyIntegrationService. func NewShopifyIntegrationService(hookRepo *repository.IntegrationHookRepo) *ShopifyIntegrationService { return &ShopifyIntegrationService{hookRepo: hookRepo} } // CreateShopifyAuthRequest is the DTO for Shopify OAuth auth. type CreateShopifyAuthRequest struct { ShopDomain string `json:"shop_domain" validate:"required"` AccessToken string `json:"access_token,omitempty"` } // Delete removes a Shopify integration hook for an account. func (s *ShopifyIntegrationService) Delete(ctx context.Context, accountID uint) error { hooks, err := s.hookRepo.FindByAccountAndType(ctx, accountID, model.HookTypeShopify) if err != nil || len(hooks) == 0 { return fmt.Errorf("Shopify integration not found for account %d", accountID) } for _, hook := range hooks { if err := s.hookRepo.Delete(ctx, hook.ID); err != nil { return fmt.Errorf("failed to delete Shopify integration: %w", err) } } applogger.L().Infof("Shopify integration deleted: account=%d", accountID) return nil } // Auth creates/updates a Shopify integration with OAuth credentials. // POST /api/v1/accounts/:account_id/integrations/shopify/auth func (s *ShopifyIntegrationService) Auth(ctx context.Context, accountID uint, req CreateShopifyAuthRequest) (*model.IntegrationHook, error) { settings := model.ShopifySettings{ ShopDomain: req.ShopDomain, AccessToken: req.AccessToken, } settingsJSON, err := json.Marshal(settings) if err != nil { return nil, fmt.Errorf("failed to marshal Shopify settings: %w", err) } // Check if Shopify hook already exists for this account hooks, err := s.hookRepo.FindByAccountAndType(ctx, accountID, model.HookTypeShopify) if err != nil { return nil, fmt.Errorf("failed to find existing Shopify integration: %w", err) } if len(hooks) > 0 { // Update existing hook hook := &hooks[0] hook.Settings = settingsJSON hook.URL = fmt.Sprintf("https://%s/admin/api/webhooks.json", req.ShopDomain) if err := s.hookRepo.Update(ctx, hook); err != nil { return nil, fmt.Errorf("failed to update Shopify integration: %w", err) } applogger.L().Infof("Shopify integration updated: account=%d, shop=%s", accountID, req.ShopDomain) return hook, nil } // Create new hook hook := &model.IntegrationHook{ AccountID: accountID, HookType: model.HookTypeShopify, Status: model.HookStatusActive, URL: fmt.Sprintf("https://%s/admin/api/webhooks.json", req.ShopDomain), Settings: settingsJSON, } if err := s.hookRepo.Create(ctx, hook); err != nil { return nil, fmt.Errorf("failed to create Shopify integration: %w", err) } applogger.L().Infof("Shopify integration created: account=%d, shop=%s", accountID, req.ShopDomain) return hook, nil } // GetOrders retrieves Shopify orders for an account (proxy to Shopify API). // GET /api/v1/accounts/:account_id/integrations/shopify/orders func (s *ShopifyIntegrationService) GetOrders(ctx context.Context, accountID uint) ([]map[string]interface{}, error) { hooks, err := s.hookRepo.FindByAccountAndType(ctx, accountID, model.HookTypeShopify) if err != nil || len(hooks) == 0 { return nil, fmt.Errorf("Shopify integration not found for account %d", accountID) } var settings model.ShopifySettings if err := json.Unmarshal(hooks[0].Settings, &settings); err != nil { return nil, fmt.Errorf("failed to unmarshal Shopify settings: %w", err) } // In production, this would call the Shopify API using settings.AccessToken. applogger.L().Infof("Listing Shopify orders for account=%d, shop=%s", accountID, settings.ShopDomain) return []map[string]interface{}{ {"shop_domain": settings.ShopDomain, "status": "configured"}, }, nil }