PHP code example of sequra / integration-core

1. Go to this page and download the library: Download sequra/integration-core library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

sequra / integration-core example snippets


ServiceRegister::registerService(
    EncryptorInterface::class,
    static function () {
        return new Encryptor();
    }
);

$encryptor = ServiceRegister::getService(EncryptorInterface::class);

RepositoryRegistry::registerRepository( ConfigEntity::class, EntityRepository::class );

$configRepository = RepositoryRegistry::getRepository(ConfigEntity::class);

namespace YourNamespace\Integration;

use SeQura\Core\BusinessLogic\BootstrapComponent;

class Bootstrap extends BootstrapComponent
{
    public static function init(): void
    {
        // Customize initialization logic here as needed. 
        // For example, register constant values   parent::initServices();
        // Register your services here
    }
}

// Must extend SeQura\Core\Infrastructure\Configuration\Configuration
class PlatformConfiguration extends Configuration 
{
    // Platform-specific configuration storage and retrieval
    public function getConfigValue($key, $defaultValue = null) { /* */ }
    public function setConfigValue($key, $value) { /* */ }
    public function getAsyncProcessUrl($guid) { /* */ }
    // ... other 

// Must implement SeQura\Core\Infrastructure\Logger\Interfaces\ShopLoggerAdapter
class PlatformLogger implements ShopLoggerAdapter
{
    public function logMessage($level, $message, array $context = []) { /* */ }
    public function isLoggingEnabled() { /* */ }
    // ... other 

// Must implement SeQura\Core\BusinessLogic\Utility\EncryptorInterface
class PlatformEncryptor implements EncryptorInterface
{
    public function encrypt($data) { /* */ }
    public function decrypt($data) { /* */ }
}

// Must implement StoreServiceInterface
class PlatformStoreService implements StoreServiceInterface
{
    public function getStoreId() { /* */ }
    public function getStoreName() { /* */ }
    public function getStores() { /* */ }
    public function getDefaultStore() { /* */ }
}

// Must implement VersionServiceInterface
class PlatformVersionService implements VersionServiceInterface
{
    public function getShopName() { /* */ }
    public function getShopVersion() { /* */ }
    public function getModuleVersion() { /* */ }
}

// Must implement CategoryServiceInterface
class PlatformCategoryService implements CategoryServiceInterface
{
    public function getCategory($categoryId) { /* */ }
    public function getAllCategories() { /* */ }
}

// Must implement ProductServiceInterface
class PlatformProductService implements ProductServiceInterface
{
    public function getProductById($productId) { /* */ }
    public function getProductsByIds(array $productIds) { /* */ }
}

// Must implement OrderCreationInterface
class PlatformOrderCreation implements OrderCreationInterface
{
    public function createOrder($cartData, $sequraOrderReference) { /* */ }
    public function updateOrderStatus($orderId, $status) { /* */ }
}

// Must implement MerchantDataProviderInterface
class PlatformMerchantDataProvider implements MerchantDataProviderInterface
{
    public function getMerchantData($orderId) { /* */ }
    public function getOrderItems($orderId) { /* */ }
    public function getShippingInfo($orderId) { /* */ }
}

// Must implement OrderReportServiceInterface
class PlatformOrderReportService implements OrderReportServiceInterface
{
    public function getOrdersForReporting($from, $to) { /* */ }
    public function markOrderAsReported($orderId) { /* */ }
}

// Must implement SellingCountriesServiceInterface
class PlatformSellingCountriesService implements SellingCountriesServiceInterface
{
    public function getSellingCountries() { /* */ }
    public function getCountryByCode($countryCode) { /* */ }
}

// Must implement DisconnectServiceInterface
class PlatformDisconnectService implements DisconnectServiceInterface
{
    public function disconnect() { /* */ }
    public function cleanup() { /* */ }
}

// Must implement WidgetConfiguratorInterface
class PlatformWidgetConfigurator implements WidgetConfiguratorInterface
{
    public function getWidgetAssetsHtml() { /* */ }
    public function getWidgetScriptUrl() { /* */ }
    public function getSelectors() { /* */ }
}

// Must implement MiniWidgetMessagesProviderInterface
class PlatformMiniWidgetMessagesProvider implements MiniWidgetMessagesProviderInterface
{
    public function getMessages($productId, $price) { /* */ }
}

protected static function initRepositories(): void
{
    parent::initRepositories();
    
    // Register entity repositories with your platform's storage implementation
    RepositoryRegistry::registerRepository(ConfigEntity::class, PlatformBaseRepository::class);
    RepositoryRegistry::registerRepository(QueueItem::class, PlatformQueueRepository::class);
    RepositoryRegistry::registerRepository(SeQuraOrder::class, PlatformOrderRepository::class);
    // ... register all 

namespace YourNamespace\Integration\Core\Infrastructure;

use SeQura\Core\Infrastructure\Configuration\Configuration;
use SeQura\Core\Infrastructure\Logger\Interfaces\ShopLoggerAdapter;
use SeQura\Core\Infrastructure\Logger\LogData;
use SeQura\Core\Infrastructure\Logger\Logger;
use SeQura\Core\Infrastructure\ServiceRegister;
use SeQura\Core\Infrastructure\Singleton;
use Sequra\Core\Services\BusinessLogic\ConfigurationService;
use Psr\Log\LoggerInterface;

class LoggerService extends Singleton implements ShopLoggerAdapter
{
    /**
     * Singleton instance of this class.
     *
     * @var static
     */
    protected static $instance;
    /**
     * Log level names for corresponding log level codes.
     *
     * @var array<string>
     */
    private static $logLevelName = [
        Logger::ERROR => 'error',
        Logger::WARNING => 'warning',
        Logger::INFO => 'info',
        Logger::DEBUG => 'debug',
    ];
    /**
     * Magento logger interface.
     *
     * @var LoggerInterface
     */
    private $logger;

    /**
     * Logger service constructor.
     *
     * @param LoggerInterface $logger Magento logger interface.
     */
    public function __construct(LoggerInterface $logger)
    {
        parent::__construct();

        $this->logger = $logger;

        static::$instance = $this;
    }

    /**
     * Logs message in the system.
     *
     * @param LogData $data
     */
    public function logMessage(LogData $data): void
    {
        /** @var ConfigurationService $configService */
        $configService = ServiceRegister::getService(Configuration::CLASS_NAME);
        $minLogLevel = $configService->getMinLogLevel();
        $logLevel = $data->getLogLevel();

        if (($logLevel > $minLogLevel) && !$configService->isDebugModeEnabled()) {
            return;
        }

        $message = 'SEQURA LOG: 
            Date: ' . date('d/m/Y') . '
            Time: ' . date('H:i:s') . '
            Log level: ' . self::$logLevelName[$logLevel] . '
            Message: ' . $data->getMessage();
        $context = $data->getContext();
        if (!empty($context)) {
            $message .= '
            Context data: [';
            foreach ($context as $item) {
                $message .= '"' . $item->getName() . '" => "' . print_r($item->getValue(), true) . '", ';
            }

            $message .= ']';
        }
        \call_user_func([$this->logger, self::$logLevelName[$logLevel]], $message);
    }
}

namespace YourNamespace\Integration\Core\Infrastructure;

use Magento\Framework\Exception\NoSuchEntityException;
use Sequra\Core\Helper\UrlHelper;
use SeQura\Core\Infrastructure\Configuration\Configuration;

class ConfigurationService extends Configuration
{
    public const MIN_LOG_LEVEL = 1;
    private const INTEGRATION_NAME = 'Magento2';

    /**
     * @var UrlHelper
     */
    private $urlHelper;

    /**
     * @param UrlHelper $urlHelper
     */
    public function __construct(UrlHelper $urlHelper)
    {
        parent::__construct();

        $this->urlHelper = $urlHelper;
    }

    /**
     * @inheritDoc
     */
    public function getIntegrationName(): string
    {
        return self::INTEGRATION_NAME;
    }

    /**
     * @inheritDoc
     *
     * @throws NoSuchEntityException
     */
    public function getAsyncProcessUrl($guid): string
    {
        $params = [
            'guid' => $guid,
            'ajax' => 1,
            '_nosid' => true,
        ];

        return $this->urlHelper->getFrontendUrl('sequra/asyncprocess/asyncprocess', $params);
    }
}


use SeQura\Core\Infrastructure\ServiceRegister;
use SeQura\Core\Infrastructure\Utility\RegexProvider;
// Get the RegexProvider service
$regexProvider = ServiceRegister::getService(RegexProvider::CLASS_NAME);
// Initialize the SequraFE object as an array to be injected in the frontend later as JavaScript object
$sequraFe = array(
    // Other config options...
    'regex' => $regexProvider->toArray()
);


// Magento Admin Controller using AdminAPI
class GeneralSettingsController extends AbstractConfigurationAction
{
    /**
     * Get shop categories for configuration dropdown
     */
    protected function getShopCategories(): Json
    {
        // Use AdminAPI with store context and error handling
        $response = AdminAPI::get()
            ->generalSettings($this->storeId)  // Store context applied
            ->getShopCategories();             // Business logic call
        
        $this->addResponseCode($response);     // Handle HTTP response codes
        return $this->result->setData($response->toArray());
    }

    /**
     * Get current general settings
     */
    protected function getGeneralSettings(): Json
    {
        $response = AdminAPI::get()
            ->generalSettings($this->storeId)
            ->getGeneralSettings();
        
        $this->addResponseCode($response);
        return $this->result->setData($response->toArray());
    }

    /**
     * Save new general settings
     */
    protected function setGeneralSettings(): Json
    {
        // Extract and validate POST data
        $data = $this->getSequraPostData();
        
        // Create strongly-typed request object
        $request = new GeneralSettingsRequest(
            $data['sendOrderReportsPeriodicallyToSeQura'] ?? true,
            $data['showSeQuraCheckoutAsHostedPage'] ?? false,
            $data['allowedIPAddresses'] ?? [],
            $data['excludedProducts'] ?? [],
            $data['excludedCategories'] ?? []
        );
        
        // Execute through AdminAPI
        $response = AdminAPI::get()
            ->generalSettings($this->storeId)
            ->saveGeneralSettings($request);
        
        $this->addResponseCode($response);
        return $this->result->setData($response->toArray());
    }
}

// Example of the underlying flow:
class GeneralSettingsService
{
    public function saveGeneralSettings(GeneralSettings $settings): void
    {
        // Validate settings
        $this->validateSettings($settings);
        
        // Transform to entity
        $entity = $this->transformToEntity($settings);
        
        // Persist through repository
        $this->repository->save($entity);
        
        // Trigger events
        $this->eventBus->publish(new SettingsUpdatedEvent($settings));
    }
}

// Automatic validation in widget controllers
if (
    !$this->widgetValidatorService->isCurrencySupported($request->getCurrentCurrency()) ||
    !$this->widgetValidatorService->isIpAddressValid($request->getCurrentIpAddress()) ||
    !$this->widgetValidatorService->isProductSupported($request->getProductId())
) {
    return new GetWidgetsCheckoutResponse([]);
}


// Magento Product Block using CheckoutAPI
class ProductWidgetBlock extends Template
{
    private $storeManager;
    private $checkoutSession;
    private $request;

    /**
     * Get available widgets for the current product page
     */
    public function getAvailableWidgets(): array
    {
        try {
            // Get current product ID from request
            $productId = $this->request->getParam('id');
            if (!$productId) {
                return [];
            }

            $storeId = (string)$this->storeManager->getStore()->getId();

            // Use CheckoutAPI to get widgets with context validation
            $widgets = CheckoutAPI::get()
                ->promotionalWidgets($storeId)
                ->getAvailableWidgetsForProductPage(new PromotionalWidgetsCheckoutRequest(
                    $this->getShippingAddressCountry(),  // Customer's shipping country
                    $this->getCurrentCountry(),          // Store's country
                    $this->getCurrentCurrency(),         // Current currency
                    $this->getCustomerIpAddress(),       // For geo-validation
                    (string)$productId                   // Current product
                ));

            if (!$widgets->isSuccessful()) {
                return [];
            }

            return $widgets->toArray()['widgets'];
            
        } catch (Exception $e) {
            // CheckoutAPI aspects handle logging automatically
            return [];
        }
    }

    /**
     * Get shipping country from customer session
     */
    private function getShippingAddressCountry(): string
    {
        $quote = $this->checkoutSession->getQuote();
        $shippingAddress = $quote->getShippingAddress();
        
        return $shippingAddress->getCountryId() ?: 'ES';
    }

    /**
     * Get current store country
     */
    private function getCurrentCountry(): string
    {
        return $this->scopeConfig->getValue(
            'general/country/default',
            ScopeInterface::SCOPE_STORE
        );
    }

    /**
     * Get customer IP for geo-validation
     */
    private function getCustomerIpAddress(): string
    {
        return $this->request->getClientIp() ?: '';
    }
}

// Template usage (product.phtml)
 $widgets = $block->getAvailableWidgets(); 


// Payment method service using CheckoutAPI
class PaymentMethodsService
{
    /**
     * Get available SeQura payment methods for current cart
     */
    public function getAvailablePaymentMethods(Quote $quote): array
    {
        try {
            // Build order request from cart data
            $builder = new CreateOrderRequestBuilder($quote);
            $builder->setDeliveryAddress($quote->getShippingAddress())
                   ->setBillingAddress($quote->getBillingAddress())
                   ->setItems($this->getCartItems($quote))
                   ->setTotalWithTax($quote->getGrandTotal());

            // Check if SeQura is allowed for this configuration
            $generalSettings = AdminAPI::get()
                ->generalSettings((string)$quote->getStore()->getId())
                ->getGeneralSettings();
                
            if (!$generalSettings->isSuccessful() || !$builder->isAllowedFor($generalSettings)) {
                return [];
            }

            // Solicit available payment methods from SeQura
            $response = CheckoutAPI::get()
                ->solicitation((string)$quote->getStore()->getId())
                ->solicitFor($builder);

            if (!$response->isSuccessful()) {
                return [];
            }

            return $response->toArray()['availablePaymentMethods'];
            
        } catch (Exception $e) {
            return [];
        }
    }

    /**
     * Get identification form for specific payment method
     */
    public function getIdentificationForm(string $cartId, string $paymentMethod): array
    {
        $response = CheckoutAPI::get()
            ->solicitation($this->getStoreId())
            ->getIdentificationForm($cartId, $paymentMethod, null, true);

        return $response->isSuccessful() ? $response->toArray() : [];
    }
}


// Cart block using CheckoutAPI
class CartWidgetBlock extends Template
{
    public function getCartWidget(): array
    {
        try {
            $storeId = (string)$this->storeManager->getStore()->getId();

            $widget = CheckoutAPI::get()
                ->promotionalWidgets($storeId)
                ->getAvailableWidgetForCartPage(new PromotionalWidgetsCheckoutRequest(
                    $this->getShippingCountry(),
                    $this->getCurrentCountry(),
                    $this->getCurrentCurrency(),
                    $this->getCustomerIpAddress()
                ));

            if (!$widget->isSuccessful()) {
                return [];
            }

            $widgets = $widget->toArray()['widgets'];
            return !empty($widgets) ? $widgets[0] : [];
            
        } catch (Exception $e) {
            return [];
        }
    }
}

// In cart template
 $cartWidget = $block->getCartWidget(); 

// Cached payment methods for performance
$cachedMethods = CheckoutAPI::get()
    ->cachedPaymentMethods($storeId)
    ->getCachedPaymentMethods(new GetCachedPaymentMethodsRequest($merchantId));

// Early validation prevents unnecessary API calls
if (!$this->widgetValidatorService->isCurrencySupported($currency)) {
    return new GetWidgetsCheckoutResponse([]); // Return empty immediately
}

// Widgets adapt to customer context automatically
$request = new PromotionalWidgetsCheckoutRequest(
    $shippingCountry,  // Determines available financing options
    $currentCountry,   // Determines legal compliance
    $currency,         // Determines payment method availability
    $ipAddress,        // Geo-validation and fraud prevention
    $productId         // Product-specific exclusions
);


// Magento Webhook Controller
class WebhookController extends Action
{
    private $invoiceService;
    private $transactionFactory;
    private $orderRepository;
    
    /**
     * Execute webhook endpoint
     */
    public function execute(): void
    {
        // Only accept POST requests
        if (!$this->getRequest()->isPost()) {
            return;
        }

        // Get webhook payload from SeQura
        $payload = $this->getRequest()->getPostValue();
        
        // Transform SeQura payload format to internal format
        $modifiedPayload = $this->transformPayload($payload);
        
        if (empty($modifiedPayload['storeId'])) {
            return;
        }

        // Set webhook processing flag to prevent interference
        self::setIsWebhookProcessing(true);
        
        try {
            // Process webhook through WebhookAPI
            $response = WebhookAPI::webhookHandler($modifiedPayload['storeId'])
                ->handleRequest($modifiedPayload);
            
            if ($response->isSuccessful()) {
                // Handle post-processing for approved orders
                if ($modifiedPayload['sq_state'] === OrderStates::STATE_APPROVED) {
                    $this->createInvoiceForOrder($modifiedPayload['order_ref']);
                }
                return; // HTTP 200 response
            }
            
            // Handle errors with appropriate HTTP codes
            $responseData = $response->toArray();
            $httpCode = $this->getErrorCode($responseData);
            $this->getResponse()->setHttpResponseCode($httpCode);
            
        } finally {
            self::setIsWebhookProcessing(false);
        }
    }

    /**
     * Transform SeQura webhook payload to internal format
     */
    private function transformPayload(array $payload): array
    {
        $modifiedPayload = [];
        
        foreach ($payload as $key => $value) {
            // Transform 'event' to 'sq_state' and remove prefixes
            $newKey = $key === 'event' ? 'sq_state' : $this->trimPrefixFromKey($key);
            $modifiedPayload[$newKey] = $value;
        }
        
        return $modifiedPayload;
    }

    /**
     * Create invoice for approved orders
     */
    private function createInvoiceForOrder(string $orderRef): void
    {
        try {
            // Find Magento order by SeQura reference
            $order = $this->findOrderBySeQuraReference($orderRef);
            
            if ($order && $order->canInvoice()) {
                // Create invoice
                $invoice = $this->invoiceService->prepareInvoice($order);
                $invoice->setRequestedCaptureCase(Invoice::CAPTURE_ONLINE);
                $invoice->register();
                
                // Save transaction
                $transaction = $this->transactionFactory->create()
                    ->addObject($invoice)
                    ->addObject($invoice->getOrder());
                $transaction->save();
                
                Logger::logInfo('Invoice created for SeQura order: ' . $orderRef);
            }
        } catch (Exception $e) {
            Logger::logError('Failed to create invoice for order: ' . $orderRef, [
                'error' => $e->getMessage()
            ]);
        }
    }

    /**
     * Get appropriate HTTP error code for webhook response
     */
    private function getErrorCode(array $responseData): int
    {
        // 409 = Conflict (temporary issue, SeQura should retry)
        // 410 = Gone (permanent issue, SeQura should not retry)
        return isset($responseData['errorCode']) && $responseData['errorCode'] === 409 
            ? 409 
            : 410;
    }
}


// Platform implementation of ShopOrderService
class MagentoShopOrderService implements ShopOrderService
{
    private $orderRepository;
    private $orderStatusService;
    
    /**
     * Update order status based on SeQura webhook
     */
    public function updateStatus(
        Webhook $webhook, 
        string $status, 
        ?int $reasonCode = null, 
        ?string $message = null
    ): void {
        try {
            // Find platform order by SeQura reference
            $order = $this->findOrderBySeQuraReference($webhook->getOrderRef());
            
            if (!$order) {
                throw new OrderNotFoundException(
                    "Order not found: {$webhook->getOrderRef()}"
                );
            }

            // Map SeQura state to platform status
            $platformStatus = $this->mapSeQuraStateToPlatformStatus(
                $webhook->getSqState(), 
                $status
            );
            
            // Update order status
            $order->setStatus($platformStatus);
            $order->setState($this->getStateForStatus($platformStatus));
            
            // Add status history comment
            $comment = $this->buildStatusComment($webhook, $reasonCode, $message);
            $order->addStatusHistoryComment($comment, $platformStatus);
            
            // Save order
            $this->orderRepository->save($order);
            
            Logger::logInfo("Order status updated", [
                'order_ref' => $webhook->getOrderRef(),
                'sq_state' => $webhook->getSqState(),
                'platform_status' => $platformStatus
            ]);
            
        } catch (Exception $e) {
            Logger::logError("Failed to update order status", [
                'order_ref' => $webhook->getOrderRef(),
                'sq_state' => $webhook->getSqState(),
                'error' => $e->getMessage()
            ]);
            throw $e;
        }
    }

    /**
     * Map SeQura states to platform-specific statuses
     */
    private function mapSeQuraStateToPlatformStatus(string $sqState, string $status): string
    {
        $mapping = [
            OrderStates::STATE_APPROVED => 'processing',
            OrderStates::STATE_CANCELLED => 'canceled',
            OrderStates::STATE_NEEDS_REVIEW => 'payment_review'
        ];
        
        return $mapping[$sqState] ?? $status;
    }

    /**
     * Build informative status history comment
     */
    private function buildStatusComment(
        Webhook $webhook, 
        ?int $reasonCode, 
        ?string $message
    ): string {
        $comment = "SeQura payment status updated to: {$webhook->getSqState()}";
        
        if ($reasonCode) {
            $comment .= " (Reason code: {$reasonCode})";
        }
        
        if ($message) {
            $comment .= " - {$message}";
        }
        
        return $comment;
    }

    /**
     * Get order IDs for reporting
     */
    public function getReportOrderIds(int $page, int $limit = 5000): array
    {
        // Implementation to get orders for delivery reporting
        $searchCriteria = $this->searchCriteriaBuilder
            ->addFilter('status', 'shipped')
            ->addFilter('sequra_order_ref', null, 'neq')
            ->setPageSize($limit)
            ->setCurrentPage($page)
            ->create();
            
        $orders = $this->orderRepository->getList($searchCriteria);
        
        return array_map(function($order) {
            return $order->getIncrementId();
        }, $orders->getItems());
    }
}

// The WebhookValidator ensures security and data integrity
class WebhookValidator
{
    const ALLOWED_STATES = ['approved', 'cancelled', 'needs_review'];

    public function validate(Webhook $webhook): void
    {
        // 1. Verify order exists
        $order = $this->getSeQuraOrderByOrderReference($webhook->getOrderRef());
        if (!$order) {
            throw new OrderNotFoundException("Order not found", 404);
        }

        // 2. Validate signature
        $expectedSignature = $order->getMerchant()
            ->getNotificationParameters()['signature'];
        if ($webhook->getSignature() !== $expectedSignature) {
            throw new InvalidSignatureException("Signature mismatch", 400);
        }

        // 3. Validate state
        if (!in_array($webhook->getSqState(), self::ALLOWED_STATES)) {
            throw new InvalidStateException("Unknown event", 400);
        }
    }
}

// OrderUpdateTask for reliable background processing
class OrderUpdateTask extends Task
{
    protected $webhook;
    protected $storeId;

    public function execute(): void
    {
        // Execute in proper store context
        StoreContext::doWithStore($this->storeId, function () {
            $this->doExecute();
        });
    }

    protected function doExecute(): void
    {
        // Get platform-specific status mapping
        $shopStatus = $this->getOrderStatusMappingService()
            ->getMapping($this->webhook->getSqState());
        
        // Update order in platform
        $this->getShopOrderService()
            ->updateStatus($this->webhook, $shopStatus);

        $this->reportProgress(100);
    }
}


// Custom task for processing bulk order updates
class BulkOrderSyncTask extends Task
{
    private $orderIds;
    private $storeId;
    private $syncType;

    public function __construct(array $orderIds, string $syncType = 'status')
    {
        parent::__construct();
        $this->orderIds = $orderIds;
        $this->syncType = $syncType;
        // Capture store context when task is created
        $this->storeId = StoreContext::getInstance()->getStoreId();
    }

    /**
     * Main task execution logic
     */
    public function execute(): void
    {
        // Execute in proper store context
        StoreContext::doWithStore($this->storeId, function() {
            $this->doExecute();
        });
    }

    private function doExecute(): void
    {
        $totalOrders = count($this->orderIds);
        $processed = 0;

        foreach ($this->orderIds as $orderId) {
            try {
                // Report progress periodically
                $this->reportAlive();
                
                // Process individual order
                $this->syncOrderData($orderId);
                $processed++;
                
                // Update progress (0-100%)
                $progress = ($processed / $totalOrders) * 100;
                $this->reportProgress($progress);
                
            } catch (Exception $e) {
                Logger::logError("Failed to sync order: {$orderId}", [
                    'error' => $e->getMessage(),
                    'task_type' => $this->getType()
                ]);
                
                // Decide whether to fail entire task or continue
                if ($this->isCriticalError($e)) {
                    throw $e; // This will fail the entire task
                }
                // Otherwise continue with next order
            }
        }
        
        // Final progress report
        $this->reportProgress(100);
    }

    /**
     * Task serialization for queue storage
     */
    public function toArray(): array
    {
        return [
            'storeId' => $this->storeId,
            'orderIds' => $this->orderIds,
            'syncType' => $this->syncType
        ];
    }

    /**
     * Task deserialization from queue storage
     */
    public static function fromArray(array $data): Serializable
    {
        return StoreContext::doWithStore($data['storeId'], function() use ($data) {
            return new static($data['orderIds'], $data['syncType']);
        });
    }

    /**
     * Set task priority (webhooks should be HIGH)
     */
    public function getPriority(): int
    {
        return $this->syncType === 'webhook' ? Priority::HIGH : Priority::NORMAL;
    }

    /**
     * Handle task failure
     */
    public function onFail(): void
    {
        Logger::logWarning("Bulk order sync task failed", [
            'store_id' => $this->storeId,
            'order_count' => count($this->orderIds),
            'sync_type' => $this->syncType
        ]);
    }

    private function syncOrderData(string $orderId): void
    {
        // Implementation depends on sync type
        switch ($this->syncType) {
            case 'status':
                $this->syncOrderStatus($orderId);
                break;
            case 'webhook':
                $this->processWebhookData($orderId);
                break;
            default:
                throw new InvalidArgumentException("Unknown sync type: {$this->syncType}");
        }
    }

    private function isCriticalError(Exception $e): bool
    {
        // Define which errors should fail the entire task
        return $e instanceof AuthenticationException 
            || $e instanceof InvalidConfigurationException;
    }
}


// Service for managing background operations
class BackgroundTaskManager
{
    private $queueService;

    public function __construct()
    {
        $this->queueService = ServiceRegister::getService(QueueService::class);
    }

    /**
     * Enqueue order synchronization task
     */
    public function scheduleOrderSync(array $orderIds, string $priority = 'normal'): QueueItem
    {
        $task = new BulkOrderSyncTask($orderIds, 'status');
        
        $priorityLevel = $priority === 'high' ? Priority::HIGH : Priority::NORMAL;
        
        return $this->queueService->enqueue(
            'order-sync',           // Queue name
            $task,                  // Task instance
            'bulk-operation',       // Context for filtering
            $priorityLevel         // Priority level
        );
    }

    /**
     * Schedule webhook processing (high priority)
     */
    public function scheduleWebhookProcessing(Webhook $webhook): QueueItem
    {
        $task = new WebhookProcessingTask($webhook);
        
        return $this->queueService->enqueue(
            'webhooks',             // Dedicated webhook queue
            $task,
            "webhook-{$webhook->getOrderRef()}",
            Priority::HIGH          // Webhooks are always high priority
        );
    }

    /**
     * Schedule order reporting (background)
     */
    public function scheduleOrderReporting(string $merchantId, array $orderIds): QueueItem
    {
        $task = new OrderReportTask($merchantId, $orderIds);
        
        return $this->queueService->enqueue(
            'reporting',
            $task,
            "report-{$merchantId}",
            Priority::LOW           // Reporting can wait
        );
    }

    /**
     * Check task status
     */
    public function getTaskStatus(int $taskId): ?array
    {
        $queueItem = $this->queueService->find($taskId);
        
        if (!$queueItem) {
            return null;
        }

        return [
            'id' => $queueItem->getId(),
            'status' => $queueItem->getStatus(),
            'progress' => $queueItem->getProgressPercent(),
            'created_at' => $queueItem->getCreateTimestamp(),
            'started_at' => $queueItem->getStartTimestamp(),
            'finished_at' => $queueItem->getFinishTimestamp(),
            'retries' => $queueItem->getRetries(),
            'failure_description' => $queueItem->getFailureDescription()
        ];
    }

    /**
     * Get running tasks for monitoring
     */
    public function getRunningTasks(): array
    {
        $runningItems = $this->queueService->findRunningItems();
        
        return array_map(function(QueueItem $item) {
            return [
                'id' => $item->getId(),
                'type' => $item->getTaskType(),
                'progress' => $item->getProgressPercent(),
                'started_at' => $item->getStartTimestamp(),
                'last_activity' => $item->getLastUpdateTimestamp()
            ];
        }, $runningItems);
    }

    /**
     * Find failed tasks for retry
     */
    public function getFailedTasks(int $limit = 50): array
    {
        $filter = new QueryFilter();
        $filter->where('status', Operators::EQUALS, QueueItem::FAILED)
               ->where('retries', Operators::LESS_THAN, QueueService::MAX_RETRIES)
               ->setLimit($limit);

        $repository = RepositoryRegistry::getRepository(QueueItem::getClassName());
        return $repository->select($filter);
    }

    /**
     * Retry failed task
     */
    public function retryTask(int $taskId): bool
    {
        $queueItem = $this->queueService->find($taskId);
        
        if (!$queueItem || $queueItem->getStatus() !== QueueItem::FAILED) {
            return false;
        }

        try {
            $this->queueService->requeue($queueItem);
            return true;
        } catch (Exception $e) {
            Logger::logError("Failed to retry task {$taskId}", [
                'error' => $e->getMessage()
            ]);
            return false;
        }
    }
}


// Task system health monitoring
class TaskHealthMonitor
{
    private $queueService;

    public function __construct()
    {
        $this->queueService = ServiceRegister::getService(QueueService::class);
    }

    /**
     * Get system health overview
     */
    public function getSystemHealth(): array
    {
        return [
            'queue_stats' => $this->getQueueStatistics(),
            'long_running_tasks' => $this->getLongRunningTasks(),
            'failed_tasks_summary' => $this->getFailedTasksSummary(),
            'queue_health' => $this->assessQueueHealth()
        ];
    }

    private function getQueueStatistics(): array
    {
        $filter = new QueryFilter();
        $repository = RepositoryRegistry::getRepository(QueueItem::getClassName());

        $stats = [];
        foreach ([QueueItem::QUEUED, QueueItem::IN_PROGRESS, QueueItem::COMPLETED, QueueItem::FAILED] as $status) {
            $filter->where('status', Operators::EQUALS, $status);
            $stats[$status] = $repository->count($filter);
            $filter->reset();
        }

        return $stats;
    }

    private function getLongRunningTasks(): array
    {
        $runningItems = $this->queueService->findRunningItems();
        $longRunning = [];

        foreach ($runningItems as $item) {
            $runningTime = time() - $item->getStartTimestamp();
            if ($runningTime > 300) { // More than 5 minutes
                $longRunning[] = [
                    'id' => $item->getId(),
                    'type' => $item->getTaskType(),
                    'running_time_seconds' => $runningTime,
                    'progress' => $item->getProgressPercent()
                ];
            }
        }

        return $longRunning;
    }

    private function assessQueueHealth(): string
    {
        $stats = $this->getQueueStatistics();
        $longRunning = $this->getLongRunningTasks();

        // Health assessment logic
        if (count($longRunning) > 5) {
            return 'CRITICAL';
        }

        if ($stats[QueueItem::FAILED] > 10) {
            return 'WARNING';
        }

        if ($stats[QueueItem::QUEUED] > 100) {
            return 'BUSY';
        }

        return 'HEALTHY';
    }
}

// Standard task usage pattern:
$task = new CustomTask($data);                    // Create task with data
$queueItem = $queueService->enqueue(              // Enqueue for processing
    'queue-name', 
    $task, 
    'context', 
    Priority::NORMAL
);

// Monitor execution
$status = $queueService->find($queueItem->getId());
echo "Task status: {$status->getStatus()}, Progress: {$status->getProgressPercent()}%";


// Custom entity for storing order synchronization status
class OrderSyncStatus extends Entity
{
    const CLASS_NAME = __CLASS__;

    // Entity fields
    protected $sequraOrderRef;
    protected $platformOrderId;
    protected $lastSyncTimestamp;
    protected $syncStatus;
    protected $errorMessage;
    protected $retryCount;
    protected $storeId;

    // Define all fields for ORM
    protected $fields = [
        'id',
        'sequraOrderRef',
        'platformOrderId', 
        'lastSyncTimestamp',
        'syncStatus',
        'errorMessage',
        'retryCount',
        'storeId'
    ];

    /**
     * Entity configuration defines database schema
     */
    public function getConfig(): EntityConfiguration
    {
        $config = new EntityConfiguration();
        
        // Primary key
        $config->setTable('sequra_order_sync_status')
               ->setPrimaryKey('id');

        // Indexes for performance
        $config->addIndex('idx_sequra_ref', ['sequraOrderRef'])
               ->addIndex('idx_platform_order', ['platformOrderId'])
               ->addIndex('idx_store_id', ['storeId'])
               ->addIndex('idx_status', ['syncStatus']);

        return $config;
    }

    // Getters and setters
    public function getSequraOrderRef(): ?string
    {
        return $this->sequraOrderRef;
    }

    public function setSequraOrderRef(string $sequraOrderRef): void
    {
        $this->sequraOrderRef = $sequraOrderRef;
    }

    public function getPlatformOrderId(): ?string
    {
        return $this->platformOrderId;
    }

    public function setPlatformOrderId(string $platformOrderId): void
    {
        $this->platformOrderId = $platformOrderId;
    }

    public function getLastSyncTimestamp(): ?int
    {
        return $this->lastSyncTimestamp;
    }

    public function setLastSyncTimestamp(int $timestamp): void
    {
        $this->lastSyncTimestamp = $timestamp;
    }

    public function getSyncStatus(): ?string
    {
        return $this->syncStatus;
    }

    public function setSyncStatus(string $status): void
    {
        $this->syncStatus = $status;
    }

    public function getErrorMessage(): ?string
    {
        return $this->errorMessage;
    }

    public function setErrorMessage(?string $message): void
    {
        $this->errorMessage = $message;
    }

    public function getRetryCount(): int
    {
        return $this->retryCount ?? 0;
    }

    public function setRetryCount(int $count): void
    {
        $this->retryCount = $count;
    }

    public function getStoreId(): ?string
    {
        return $this->storeId;
    }

    public function setStoreId(string $storeId): void
    {
        $this->storeId = $storeId;
    }

    /**
     * Business logic methods
     */
    public function markAsSuccessful(): void
    {
        $this->setSyncStatus('completed');
        $this->setLastSyncTimestamp(time());
        $this->setErrorMessage(null);
    }

    public function markAsFailed(string $errorMessage): void
    {
        $this->setSyncStatus('failed');
        $this->setLastSyncTimestamp(time());
        $this->setErrorMessage($errorMessage);
        $this->setRetryCount($this->getRetryCount() + 1);
    }

    public function canRetry(int $maxRetries = 3): bool
    {
        return $this->getRetryCount() < $maxRetries;
    }
}


// Magento implementation of repository
class MagentoOrderSyncStatusRepository implements RepositoryInterface
{
    private $connection;
    private $entityClass;
    private $tableName = 'sequra_order_sync_status';

    public function __construct()
    {
        // Get Magento database connection
        $this->connection = ObjectManager::getInstance()
            ->get(ResourceConnection::class)
            ->getConnection();
    }

    public function setEntityClass(string $entityClass): void
    {
        $this->entityClass = $entityClass;
    }

    /**
     * Find entities matching filter criteria
     */
    public function select(QueryFilter $filter = null): array
    {
        $select = $this->connection->select()->from($this->tableName);
        
        if ($filter) {
            $this->applyFilter($select, $filter);
        }

        $rows = $this->connection->fetchAll($select);
        
        return array_map(function($row) {
            return $this->entityClass::fromArray($row);
        }, $rows);
    }

    /**
     * Find single entity
     */
    public function selectOne(QueryFilter $filter = null): ?Entity
    {
        $results = $this->select($filter);
        return $results ? $results[0] : null;
    }

    /**
     * Save new entity
     */
    public function save(Entity $entity): int
    {
        $data = $this->entityToArray($entity);
        unset($data['id']); // Auto-increment

        $this->connection->insert($this->tableName, $data);
        $id = $this->connection->lastInsertId();
        
        $entity->setId($id);
        return $id;
    }

    /**
     * Update existing entity
     */
    public function update(Entity $entity): bool
    {
        $data = $this->entityToArray($entity);
        $id = $data['id'];
        unset($data['id']);

        $affected = $this->connection->update(
            $this->tableName,
            $data,
            ['id = ?' => $id]
        );

        return $affected > 0;
    }

    /**
     * Delete entity
     */
    public function delete(Entity $entity): bool
    {
        $affected = $this->connection->delete(
            $this->tableName,
            ['id = ?' => $entity->getId()]
        );

        return $affected > 0;
    }

    /**
     * Count entities matching filter
     */
    public function count(QueryFilter $filter = null): int
    {
        $select = $this->connection->select()
            ->from($this->tableName, 'COUNT(*) as count');
            
        if ($filter) {
            $this->applyFilter($select, $filter);
        }

        return (int)$this->connection->fetchOne($select);
    }

    /**
     * Apply QueryFilter to database select
     */
    private function applyFilter(Select $select, QueryFilter $filter): void
    {
        // Apply WHERE conditions
        foreach ($filter->getConditions() as $condition) {
            $column = $condition->getColumn();
            $operator = $condition->getOperator();
            $value = $condition->getValue();

            switch ($operator) {
                case Operators::EQUALS:
                    $select->where("{$column} = ?", $value);
                    break;
                case Operators::NOT_EQUALS:
                    $select->where("{$column} != ?", $value);
                    break;
                case Operators::GREATER_THAN:
                    $select->where("{$column} > ?", $value);
                    break;
                case Operators::LESS_THAN:
                    $select->where("{$column} < ?", $value);
                    break;
                case Operators::IN:
                    $select->where("{$column} IN (?)", $value);
                    break;
                case Operators::LIKE:
                    $select->where("{$column} LIKE ?", $value);
                    break;
            }
        }

        // Apply ORDER BY
        if ($filter->getOrderBy()) {
            $select->order($filter->getOrderBy() . ' ' . $filter->getOrderDirection());
        }

        // Apply LIMIT and OFFSET
        if ($filter->getLimit()) {
            $select->limit($filter->getLimit(), $filter->getOffset());
        }
    }

    /**
     * Convert entity to database array
     */
    private function entityToArray(Entity $entity): array
    {
        $array = $entity->toArray();
        
        // Handle date/time conversions
        if (isset($array['lastSyncTimestamp']) && $array['lastSyncTimestamp']) {
            $array['lastSyncTimestamp'] = date('Y-m-d H:i:s', $array['lastSyncTimestamp']);
        }

        return $array;
    }
}


// Service using ORM for data operations
class OrderSyncService
{
    private $repository;

    public function __construct()
    {
        $this->repository = RepositoryRegistry::getRepository(OrderSyncStatus::class);
    }

    /**
     * Record successful order synchronization
     */
    public function recordSuccessfulSync(string $sequraRef, string $platformOrderId): void
    {
        $syncStatus = $this->findBySequraRef($sequraRef);
        
        if (!$syncStatus) {
            $syncStatus = new OrderSyncStatus();
            $syncStatus->setSequraOrderRef($sequraRef);
            $syncStatus->setPlatformOrderId($platformOrderId);
            $syncStatus->setStoreId(StoreContext::getInstance()->getStoreId());
        }

        $syncStatus->markAsSuccessful();
        
        if ($syncStatus->getId()) {
            $this->repository->update($syncStatus);
        } else {
            $this->repository->save($syncStatus);
        }
    }

    /**
     * Record failed synchronization
     */
    public function recordFailedSync(string $sequraRef, string $errorMessage): void
    {
        $syncStatus = $this->findBySequraRef($sequraRef);
        
        if (!$syncStatus) {
            $syncStatus = new OrderSyncStatus();
            $syncStatus->setSequraOrderRef($sequraRef);
            $syncStatus->setStoreId(StoreContext::getInstance()->getStoreId());
        }

        $syncStatus->markAsFailed($errorMessage);
        
        if ($syncStatus->getId()) {
            $this->repository->update($syncStatus);
        } else {
            $this->repository->save($syncStatus);
        }
    }

    /**
     * Find sync status by SeQura reference
     */
    public function findBySequraRef(string $sequraRef): ?OrderSyncStatus
    {
        $filter = new QueryFilter();
        $filter->where('sequraOrderRef', Operators::EQUALS, $sequraRef);
        
        return $this->repository->selectOne($filter);
    }

    /**
     * Get failed syncs that can be retried
     */
    public function getRetryableFailed(int $maxRetries = 3): array
    {
        $filter = new QueryFilter();
        $filter->where('syncStatus', Operators::EQUALS, 'failed')
               ->where('retryCount', Operators::LESS_THAN, $maxRetries)
               ->orderBy('lastSyncTimestamp', QueryFilter::ORDER_ASC)
               ->setLimit(50);

        return $this->repository->select($filter);
    }

    /**
     * Get sync statistics
     */
    public function getSyncStatistics(string $storeId): array
    {
        $baseFilter = new QueryFilter();
        $baseFilter->where('storeId', Operators::EQUALS, $storeId);

        $stats = [];
        foreach (['completed', 'failed', 'pending'] as $status) {
            $filter = clone $baseFilter;
            $filter->where('syncStatus', Operators::EQUALS, $status);
            $stats[$status] = $this->repository->count($filter);
        }

        return $stats;
    }

    /**
     * Clean up old sync records
     */
    public function cleanupOldRecords(int $daysOld = 30): int
    {
        $cutoffTimestamp = time() - ($daysOld * 24 * 60 * 60);
        
        $filter = new QueryFilter();
        $filter->where('lastSyncTimestamp', Operators::LESS_THAN, $cutoffTimestamp)
               ->where('syncStatus', Operators::EQUALS, 'completed');

        $oldRecords = $this->repository->select($filter);
        
        $deleted = 0;
        foreach ($oldRecords as $record) {
            if ($this->repository->delete($record)) {
                $deleted++;
            }
        }

        return $deleted;
    }
}


// Complex queries using QueryFilter
class AdvancedOrderQueries
{
    private $repository;

    public function __construct()
    {
        $this->repository = RepositoryRegistry::getRepository(OrderSyncStatus::class);
    }

    /**
     * Find orders by multiple criteria
     */
    public function findOrdersByDateRange(
        DateTime $startDate, 
        DateTime $endDate, 
        array $statuses = []
    ): array {
        $filter = new QueryFilter();
        
        // Date range
        $filter->where('lastSyncTimestamp', Operators::GREATER_THAN, $startDate->getTimestamp())
               ->where('lastSyncTimestamp', Operators::LESS_THAN, $endDate->getTimestamp());
        
        // Status filter
        if (!empty($statuses)) {
            $filter->where('syncStatus', Operators::IN, $statuses);
        }

        // Ordering and pagination
        $filter->orderBy('lastSyncTimestamp', QueryFilter::ORDER_DESC)
               ->setLimit(100);

        return $this->repository->select($filter);
    }

    /**
     * Find orders with high retry counts
     */
    public function findProblematicOrders(): array
    {
        $filter = new QueryFilter();
        $filter->where('retryCount', Operators::GREATER_THAN, 2)
               ->where('syncStatus', Operators::EQUALS, 'failed')
               ->orderBy('retryCount', QueryFilter::ORDER_DESC);

        return $this->repository->select($filter);
    }

    /**
     * Search orders by pattern
     */
    public function searchOrdersByReference(string $pattern): array
    {
        $filter = new QueryFilter();
        $filter->where('sequraOrderRef', Operators::LIKE, "%{$pattern}%")
               ->orderBy('lastSyncTimestamp', QueryFilter::ORDER_DESC)
               ->setLimit(50);

        return $this->repository->select($filter);
    }

    /**
     * Get aggregated statistics
     */
    public function getDetailedStatistics(): array
    {
        // Since ORM doesn't support aggregation, use platform-specific queries
        // This would typically be implemented in the repository layer
        
        $stats = [
            'total_orders' => $this->repository->count(),
            'by_status' => [],
            'retry_distribution' => []
        ];

        // Get counts by status
        foreach (['completed', 'failed', 'pending'] as $status) {
            $filter = new QueryFilter();
            $filter->where('syncStatus', Operators::EQUALS, $status);
            $stats['by_status'][$status] = $this->repository->count($filter);
        }

        return $stats;
    }
}

// Standard ORM usage pattern:
$repository = RepositoryRegistry::getRepository(EntityClass::class);
$entity = new EntityClass();
$entity->setField($value);
$repository->save($entity);                    // Create
$found = $repository->selectOne($filter);      // Read
$found->updateField($newValue);
$repository->update($found);                   // Update
$repository->delete($found);                   // Delete


// Basic store context switching
class MultiStoreOrderService
{
    private $orderRepository;
    private $configService;

    public function __construct()
    {
        $this->orderRepository = RepositoryRegistry::getRepository(SeQuraOrder::class);
        $this->configService = ServiceRegister::getService(Configuration::class);
    }

    /**
     * Process order in specific store context
     */
    public function processOrderForStore(string $storeId, array $orderData): SeQuraOrder
    {
        // Execute operation with store context
        return StoreContext::doWithStore($storeId, function() use ($orderData) {
            // All operations inside this closure run with $storeId context
            
            // 1. Configuration is store-specific
            $merchantId = $this->configService->getConfigValue('merchant_id');
            
            // 2. Repository queries are automatically filtered by store
            $existingOrder = $this->findExistingOrder($orderData['reference']);
            
            // 3. API calls use store-specific credentials
            $orderService = ServiceRegister::getService(OrderService::class);
            $newOrder = $orderService->createOrder($orderData);
            
            // 4. Saved data is automatically tagged with store context
            $this->orderRepository->save($newOrder);
            
            return $newOrder;
        });
    }

    /**
     * Process orders for multiple stores
     */
    public function processOrdersForMultipleStores(array $storeOrders): array
    {
        $results = [];
        
        foreach ($storeOrders as $storeId => $orderDataArray) {
            try {
                // Switch context for each store
                $storeResults = StoreContext::doWithStore($storeId, function() use ($orderDataArray) {
                    $orders = [];
                    
                    foreach ($orderDataArray as $orderData) {
                        // Each order processed in correct store context
                        $orders[] = $this->processStoreOrder($orderData);
                    }
                    
                    return $orders;
                });
                
                $results[$storeId] = [
                    'success' => true,
                    'orders' => $storeResults
                ];
                
            } catch (Exception $e) {
                $results[$storeId] = [
                    'success' => false,
                    'error' => $e->getMessage()
                ];
                
                Logger::logError("Failed to process orders for store {$storeId}", [
                    'error' => $e->getMessage(),
                    'store_id' => $storeId
                ]);
            }
        }
        
        return $results;
    }

    /**
     * Get current store context
     */
    public function getCurrentStoreOperations(): array
    {
        $currentStoreId = StoreContext::getInstance()->getStoreId();
        
        return [
            'store_id' => $currentStoreId,
            'merchant_id' => $this->configService->getConfigValue('merchant_id'),
            'environment' => $this->configService->getConfigValue('environment'),
            'orders_count' => $this->getOrderCountForCurrentStore()
        ];
    }

    private function processStoreOrder(array $orderData): SeQuraOrder
    {
        // This runs within store context - all operations are store-specific
        $orderService = ServiceRegister::getService(OrderService::class);
        return $orderService->createOrder($orderData);
    }

    private function getOrderCountForCurrentStore(): int
    {
        // Repository automatically filters by current store context
        return $this->orderRepository->count();
    }
}


// Store-aware repository implementation
class StoreAwareOrderRepository implements RepositoryInterface
{
    private $connection;
    private $entityClass;

    public function select(QueryFilter $filter = null): array
    {
        // Automatically add store context to all queries
        $storeFilter = $this->addStoreContextFilter($filter);
        
        $select = $this->connection->select()->from('sequra_orders');
        $this->applyFilter($select, $storeFilter);
        
        $rows = $this->connection->fetchAll($select);
        
        return array_map(function($row) {
            return $this->entityClass::fromArray($row);
        }, $rows);
    }

    public function save(Entity $entity): int
    {
        $data = $entity->toArray();
        
        // Automatically add store context to saved entities
        $data['store_id'] = StoreContext::getInstance()->getStoreId();
        
        $this->connection->insert('sequra_orders', $data);
        $id = $this->connection->lastInsertId();
        
        $entity->setId($id);
        return $id;
    }

    /**
     * Automatically add store context filter to all queries
     */
    private function addStoreContextFilter(?QueryFilter $filter): QueryFilter
    {
        if (!$filter) {
            $filter = new QueryFilter();
        }
        
        $currentStoreId = StoreContext::getInstance()->getStoreId();
        if ($currentStoreId) {
            $filter->where('store_id', Operators::EQUALS, $currentStoreId);
        }
        
        return $filter;
    }

    /**
     * Count entities for current store only
     */
    public function count(QueryFilter $filter = null): int
    {
        $storeFilter = $this->addStoreContextFilter($filter);
        
        $select = $this->connection->select()
            ->from('sequra_orders', 'COUNT(*) as count');
            
        $this->applyFilter($select, $storeFilter);
        
        return (int)$this->connection->fetchOne($select);
    }
}


// Store-aware task implementation
class MultiStoreReportTask extends Task
{
    private $storeConfigurations;
    private $reportType;

    public function __construct(array $storeConfigurations, string $reportType)
    {
        parent::__construct();
        $this->storeConfigurations = $storeConfigurations;
        $this->reportType = $reportType;
    }

    public function execute(): void
    {
        $totalStores = count($this->storeConfigurations);
        $processed = 0;

        foreach ($this->storeConfigurations as $storeId => $config) {
            try {
                // Execute each store's processing in its context
                StoreContext::doWithStore($storeId, function() use ($config) {
                    $this->processStoreReport($config);
                });
                
                $processed++;
                $progress = ($processed / $totalStores) * 100;
                $this->reportProgress($progress);
                
            } catch (Exception $e) {
                Logger::logError("Failed to process report for store {$storeId}", [
                    'store_id' => $storeId,
                    'report_type' => $this->reportType,
                    'error' => $e->getMessage()
                ]);
                
                // Continue with other stores
                $processed++;
            }
        }
    }

    private function processStoreReport(array $config): void
    {
        // All operations here run in correct store context
        $orderService = ServiceRegister::getService(OrderService::class);
        $reportService = ServiceRegister::getService(OrderReportService::class);
        
        // Get orders for current store (context filtering automatic)
        $orders = $orderService->getOrdersForReporting($config['date_from'], $config['date_to']);
        
        // Generate report for current store
        $reportService->generateReport($orders, $this->reportType);
    }

    public function toArray(): array
    {
        return [
            'storeConfigurations' => $this->storeConfigurations,
            'reportType' => $this->reportType
        ];
    }

    public static function fromArray(array $data): Serializable
    {
        return new static($data['storeConfigurations'], $data['reportType']);
    }
}


// Store-specific configuration service
class MultiStoreConfigurationService
{
    private $configService;

    public function __construct()
    {
        $this->configService = ServiceRegister::getService(Configuration::class);
    }

    /**
     * Get configuration for specific store
     */
    public function getStoreConfiguration(string $storeId): array
    {
        return StoreContext::doWithStore($storeId, function() {
            return [
                'merchant_id' => $this->configService->getConfigValue('merchant_id'),
                'environment' => $this->configService->getConfigValue('environment'),
                'api_username' => $this->configService->getConfigValue('api_username'),
                'webhook_url' => $this->configService->getConfigValue('webhook_url'),
                'enabled' => $this->configService->getConfigValue('enabled', false)
            ];
        });
    }

    /**
     * Update configuration for specific store
     */
    public function updateStoreConfiguration(string $storeId, array $config): void
    {
        StoreContext::doWithStore($storeId, function() use ($config) {
            foreach ($config as $key => $value) {
                $this->configService->setConfigValue($key, $value);
            }
        });
    }

    /**
     * Get configurations for all stores
     */
    public function getAllStoreConfigurations(array $storeIds): array
    {
        $configurations = [];
        
        foreach ($storeIds as $storeId) {
            try {
                $configurations[$storeId] = $this->getStoreConfiguration($storeId);
            } catch (Exception $e) {
                $configurations[$storeId] = [
                    'error' => $e->getMessage(),
                    'enabled' => false
                ];
            }
        }
        
        return $configurations;
    }

    /**
     * Validate store configuration
     */
    public function validateStoreConfiguration(string $storeId): array
    {
        return StoreContext::doWithStore($storeId, function() {
            $config = $this->getStoreConfiguration($storeId);
            $errors = [];

            if (empty($config['merchant_id'])) {
                $errors[] = 'Merchant ID is 


// Context-aware API usage
class ContextAwareAPIService
{
    /**
     * AdminAPI automatically applies store context
     */
    public function getStoreSettings(string $storeId): array
    {
        // Context is automatically applied to all AdminAPI calls
        $response = AdminAPI::get()
            ->generalSettings($storeId)  // Store context applied here
            ->getGeneralSettings();

        return $response->toArray();
    }

    /**
     * CheckoutAPI with store context
     */
    public function getPaymentMethodsForStore(string $storeId, array $cartData): array
    {
        // Context ensures correct merchant credentials are used
        $response = CheckoutAPI::get()
            ->solicitation($storeId)     // Store context applied here
            ->solicitFor($cartData);

        return $response->toArray();
    }

    /**
     * WebhookAPI with store context
     */
    public function processWebhookForStore(string $storeId, array $webhookData): bool
    {
        // Context ensures webhook is processed for correct store
        $response = WebhookAPI::webhookHandler($storeId)  // Store context applied here
            ->handleRequest($webhookData);

        return $response->isSuccessful();
    }

    /**
     * Process operations for multiple stores
     */
    public function bulkStoreOperations(array $storeOperations): array
    {
        $results = [];

        foreach ($storeOperations as $storeId => $operation) {
            try {
                switch ($operation['type']) {
                    case 'settings':
                        $results[$storeId] = $this->getStoreSettings($storeId);
                        break;
                    case 'payment_methods':
                        $results[$storeId] = $this->getPaymentMethodsForStore($storeId, $operation['data']);
                        break;
                    case 'webhook':
                        $results[$storeId] = $this->processWebhookForStore($storeId, $operation['data']);
                        break;
                }
            } catch (Exception $e) {
                $results[$storeId] = ['error' => $e->getMessage()];
            }
        }

        return $results;
    }
}

// Standard multi-store usage pattern:
StoreContext::doWithStore($storeId, function() {
    // All operations here are automatically store-scoped:
    // - Configuration access
    // - Database queries  
    // - API calls
    // - Task execution
    // - Event handling
});


// Custom API service using proxies
class SeQuraAPIService
{
    private $authorizedProxyFactory;
    private $connectionService;

    public function __construct()
    {
        $this->authorizedProxyFactory = ServiceRegister::getService(AuthorizedProxyFactory::class);
        $this->connectionService = ServiceRegister::getService(ConnectionService::class);
    }

    /**
     * Create order using OrderProxy
     */
    public function createOrder(string $merchantId, array $orderData): SeQuraOrder
    {
        try {
            // Build order request
            $orderRequest = $this->buildOrderRequest($orderData);
            
            // Get authenticated proxy for merchant
            $orderProxy = new OrderProxy($this->authorizedProxyFactory);
            
            // Create order via API
            $sequraOrder = $orderProxy->createOrder($orderRequest);
            
            Logger::logInfo('Order created successfully', [
                'merchant_id' => $merchantId,
                'order_ref' => $sequraOrder->getReference(),
                'amount' => $sequraOrder->getTotalWithTax()
            ]);
            
            return $sequraOrder;
            
        } catch (HttpApiUnauthorizedException $e) {
            Logger::logError('Authentication failed for order creation', [
                'merchant_id' => $merchantId,
                'error' => $e->getMessage()
            ]);
            throw new AuthenticationException('Invalid credentials for merchant');
            
        } catch (HttpApiNotFoundException $e) {
            Logger::logError('API endpoint not found', [
                'merchant_id' => $merchantId,
                'error' => $e->getMessage()
            ]);
            throw new APIException('SeQura API endpoint not available');
            
        } catch (HttpRequestException $e) {
            Logger::logError('HTTP request failed for order creation', [
                'merchant_id' => $merchantId,
                'error' => $e->getMessage()
            ]);
            throw new CommunicationException('Failed to communicate with SeQura API');
        }
    }

    /**
     * Get available payment methods
     */
    public function getPaymentMethods(string $merchantId, array $requestData): array
    {
        try {
            $request = new GetAvailablePaymentMethodsRequest(
                $merchantId,
                $requestData['amount'],
                $requestData['currency'],
                $requestData['country']
            );
            
            $orderProxy = new OrderProxy($this->authorizedProxyFactory);
            $paymentMethods = $orderProxy->getAvailablePaymentMethods($request);
            
            Logger::logInfo('Payment methods retrieved', [
                'merchant_id' => $merchantId,
                'method_count' => count($paymentMethods),
                'amount' => $requestData['amount']
            ]);
            
            return $paymentMethods;
            
        } catch (Exception $e) {
            Logger::logError('Failed to get payment methods', [
                'merchant_id' => $merchantId,
                'error' => $e->getMessage()
            ]);
            
            // Return empty array for graceful degradation
            return [];
        }
    }

    /**
     * Update order status
     */
    public function updateOrder(string $merchantId, string $orderRef, array $updateData): bool
    {
        try {
            $updateRequest = new UpdateOrderRequest($orderRef, $updateData);
            
            $orderProxy = new OrderProxy($this->authorizedProxyFactory);
            $success = $orderProxy->updateOrder($updateRequest);
            
            if ($success) {
                Logger::logInfo('Order updated successfully', [
                    'merchant_id' => $merchantId,
                    'order_ref' => $orderRef,
                    'update_type' => $updateData['type'] ?? 'unknown'
                ]);
            }
            
            return $success;
            
        } catch (Exception $e) {
            Logger::logError('Failed to update order', [
                'merchant_id' => $merchantId,
                'order_ref' => $orderRef,
                'error' => $e->getMessage()
            ]);
            
            return false;
        }
    }

    /**
     * Test API connection
     */
    public function testConnection(string $merchantId): array
    {
        try {
            // Test basic authentication
            $connectionProxy = new ConnectionProxy();
            $connectionData = $this->connectionService->getConnectionDataByMerchantId($merchantId);
            
            $isValid = $connectionProxy->validateCredentials(
                $connectionData->getAuthorizationCredentials()
            );
            
            if (!$isValid) {
                return [
                    'success' => false,
                    'error' => 'Invalid credentials'
                ];
            }
            
            // Test order API access
            $testRequest = new GetAvailablePaymentMethodsRequest(
                $merchantId,
                100, // Test amount
                'EUR',
                'ES'
            );
            
            $orderProxy = new OrderProxy($this->authorizedProxyFactory);
            $methods = $orderProxy->getAvailablePaymentMethods($testRequest);
            
            return [
                'success' => true,
                'payment_methods_count' => count($methods),
                'environment' => $connectionData->getEnvironment()
            ];
            
        } catch (Exception $e) {
            return [
                'success' => false,
                'error' => $e->getMessage()
            ];
        }
    }

    /**
     * Submit order report
     */
    public function submitOrderReport(string $merchantId, array $orderIds): bool
    {
        try {
            $reportProxy = new OrderReportProxy($this->authorizedProxyFactory);
            
            // Build report data
            $reportData = $this->buildReportData($orderIds);
            
            $success = $reportProxy->submitDeliveryReport($merchantId, $reportData);
            
            if ($success) {
                Logger::logInfo('Order report submitted', [
                    'merchant_id' => $merchantId,
                    'order_count' => count($orderIds)
                ]);
            }
            
            return $success;
            
        } catch (Exception $e) {
            Logger::logError('Failed to submit order report', [
                'merchant_id' => $merchantId,
                'order_count' => count($orderIds),
                'error' => $e->getMessage()
            ]);
            
            return false;
        }
    }

    private function buildOrderRequest(array $orderData): CreateOrderRequest
    {
        // Build CreateOrderRequest from platform order data
        // This would 


// Extended proxy factory with additional features
class ExtendedProxyFactory extends AuthorizedProxyFactory
{
    private $cache;
    private $retryPolicy;

    public function __construct(
        HttpClient $httpClient,
        ConnectionService $connectionService, 
        DeploymentsService $deploymentsService,
        CacheInterface $cache = null
    ) {
        parent::__construct($httpClient, $connectionService, $deploymentsService);
        $this->cache = $cache;
        $this->retryPolicy = new RetryPolicy();
    }

    /**
     * Build proxy with caching
     */
    public function buildWithCache(string $merchantId): AuthorizedProxy
    {
        $cacheKey = "proxy_{$merchantId}";
        
        if ($this->cache && $this->cache->has($cacheKey)) {
            return $this->cache->get($cacheKey);
        }
        
        $proxy = $this->build($merchantId);
        
        if ($this->cache) {
            $this->cache->set($cacheKey, $proxy, 3600); // Cache for 1 hour
        }
        
        return $proxy;
    }

    /**
     * Build proxy with retry capabilities
     */
    public function buildWithRetry(string $merchantId): AuthorizedProxy
    {
        $proxy = $this->build($merchantId);
        
        // Wrap proxy with retry logic
        return new RetryableProxy($proxy, $this->retryPolicy);
    }

    /**
     * Build proxy for specific environment
     */
    public function buildForEnvironment(string $merchantId, string $environment): AuthorizedProxy
    {
        $connectionData = $this->connectionService->getConnectionDataByMerchantId($merchantId);
        
        // Override environment
        $connectionData->setEnvironment($environment);
        
        $deployment = $this->deploymentsService->getDeploymentById(
            $connectionData->getDeployment()
        );
        
        return new AuthorizedProxy($this->client, $connectionData, $deployment, $merchantId);
    }
}


// API error handling and retry service
class APIErrorHandler
{
    private $maxRetries = 3;
    private $retryDelay = 1000; // milliseconds

    /**
     * Execute API call with retry logic
     */
    public function executeWithRetry(callable $apiCall, array $context = []): mixed
    {
        $attempt = 0;
        $lastException = null;

        while ($attempt < $this->maxRetries) {
            try {
                return $apiCall();
                
            } catch (HttpApiUnauthorizedException $e) {
                // Don't retry authentication errors
                Logger::logError('Authentication error - not retrying', array_merge($context, [
                    'error' => $e->getMessage(),
                    'attempt' => $attempt + 1
                ]));
                throw $e;
                
            } catch (HttpApiNotFoundException $e) {
                // Don't retry 404 errors
                Logger::logError('Resource not found - not retrying', array_merge($context, [
                    'error' => $e->getMessage(),
                    'attempt' => $attempt + 1
                ]));
                throw $e;
                
            } catch (HttpRequestException $e) {
                $lastException = $e;
                $attempt++;
                
                if ($attempt < $this->maxRetries) {
                    Logger::logWarning('API call failed - retrying', array_merge($context, [
                        'error' => $e->getMessage(),
                        'attempt' => $attempt,
                        'retry_in_ms' => $this->retryDelay
                    ]));
                    
                    // Exponential backoff
                    usleep($this->retryDelay * 1000 * $attempt);
                } else {
                    Logger::logError('API call failed after all retries', array_merge($context, [
                        'error' => $e->getMessage(),
                        'total_attempts' => $attempt
                    ]));
                }
            }
        }

        throw $lastException ?? new Exception('API call failed');
    }

    /**
     * Handle different types of API errors
     */
    public function handleAPIError(Exception $e, array $context = []): array
    {
        switch (get_class($e)) {
            case HttpApiUnauthorizedException::class:
                return [
                    'type' => 'authentication',
                    'message' => 'Invalid credentials or expired token',
                    'retryable' => false,
                    'action' => 'check_credentials'
                ];
                
            case HttpApiNotFoundException::class:
                return [
                    'type' => 'not_found',
                    'message' => 'Resource or endpoint not found',
                    'retryable' => false,
                    'action' => 'check_endpoint'
                ];
                
            case HttpApiInvalidRequestBodyException::class:
                return [
                    'type' => 'invalid_request',
                    'message' => 'Request data is invalid',
                    'retryable' => false,
                    'action' => 'validate_data'
                ];
                
            case HttpRequestException::class:
                return [
                    'type' => 'network',
                    'message' => 'Network or server error',
                    'retryable' => true,
                    'action' => 'retry_later'
                ];
                
            default:
                return [
                    'type' => 'unknown',
                    'message' => $e->getMessage(),
                    'retryable' => false,
                    'action' => 'investigate'
                ];
        }
    }
}


// API health monitoring service
class APIHealthMonitor
{
    private $proxyFactory;
    private $metrics;

    public function __construct()
    {
        $this->proxyFactory = ServiceRegister::getService(AuthorizedProxyFactory::class);
        $this->metrics = new APIMetrics();
    }

    /**
     * Check API health for all merchants
     */
    public function checkAPIHealth(array $merchantIds): array
    {
        $results = [];
        
        foreach ($merchantIds as $merchantId) {
            $results[$merchantId] = $this->checkMerchantAPIHealth($merchantId);
        }
        
        return [
            'overall_health' => $this->calculateOverallHealth($results),
            'merchant_results' => $results,
            'timestamp' => time()
        ];
    }

    /**
     * Check API health for specific merchant
     */
    public function checkMerchantAPIHealth(string $merchantId): array
    {
        $startTime = microtime(true);
        
        try {
            // Test basic connectivity
            $proxy = $this->proxyFactory->build($merchantId);
            
            // Test payment methods endpoint (lightweight)
            $testRequest = new GetAvailablePaymentMethodsRequest(
                $merchantId, 
                100, 
                'EUR', 
                'ES'
            );
            
            $orderProxy = new OrderProxy($this->proxyFactory);
            $methods = $orderProxy->getAvailablePaymentMethods($testRequest);
            
            $responseTime = (microtime(true) - $startTime) * 1000; // ms
            
            $this->metrics->recordSuccess($merchantId, $responseTime);
            
            return [
                'status' => 'healthy',
                'response_time_ms' => round($responseTime, 2),
                'payment_methods_count' => count($methods),
                'last_check' => time()
            ];
            
        } catch (HttpApiUnauthorizedException $e) {
            $this->metrics->recordError($merchantId, 'authentication');
            
            return [
                'status' => 'authentication_error',
                'error' => 'Invalid credentials',
                'last_check' => time()
            ];
            
        } catch (Exception $e) {
            $this->metrics->recordError($merchantId, 'error');
            
            return [
                'status' => 'error',
                'error' => $e->getMessage(),
                'last_check' => time()
            ];
        }
    }

    /**
     * Get API performance metrics
     */
    public function getAPIMetrics(string $merchantId): array
    {
        return [
            'success_rate' => $this->metrics->getSuccessRate($merchantId),
            'average_response_time' => $this->metrics->getAverageResponseTime($merchantId),
            'error_breakdown' => $this->metrics->getErrorBreakdown($merchantId),
            'uptime_percentage' => $this->metrics->getUptimePercentage($merchantId)
        ];
    }

    private function calculateOverallHealth(array $results): string
    {
        $total = count($results);
        $healthy = 0;
        
        foreach ($results as $result) {
            if ($result['status'] === 'healthy') {
                $healthy++;
            }
        }
        
        $healthPercentage = ($healthy / $total) * 100;
        
        if ($healthPercentage >= 95) return 'excellent';
        if ($healthPercentage >= 80) return 'good';
        if ($healthPercentage >= 60) return 'warning';
        return 'critical';
    }
}

// Standard API proxy usage pattern:
$proxy = $authorizedProxyFactory->build($merchantId);    // Get authenticated proxy
$response = $proxy->post($request);                      // Make API call
$data = $response->decodeBodyToArray();                  // Parse response



namespace YourNamespace;

use SeQura\Core\Infrastructure\ServiceRegister;
use SeQura\Core\Infrastructure\BootstrapComponent;
use SeQura\Core\BusinessLogic\Domain\GeneralSettings\Services\GeneralSettingsService;
use YourNamespace\Controllers\CustomGeneralSettingsController;

class Bootstrap extends BootstrapComponent
{
    /**
	 * Initializes controllers.
	 */
	protected static function initControllers(): void {
		parent::initControllers();

		// Extend GeneralSettingsController
		ServiceRegister::registerService(
			GeneralSettingsController::class,
			static function () {
				return new CustomGeneralSettingsController();
			}
		);
    }
}