Files
gochat/internal/service/shopify_integration_service.go
T
2026-06-04 15:44:48 +08:00

115 lines
4.1 KiB
Go

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
}