Zum Inhalt springen

Shopware 6 App System vs. Plugin: What's the Difference and Which Should You Use?

Veröffentlicht am May 15, 2025 | ca. 1 Min. Lesezeit |

Since Shopware 6.4, there are officially two ways to extend the shop: the classic plugin and the app system. Both can add functionality, manage data and automate processes -- but they do so in fundamentally different ways. When planning a project, you need to decide early which path to take, as the architectures are not compatible.

The Plugin Model

Plugins are PHP packages that are loaded directly into the Shopware process. They have access to all Symfony services, the dependency injection container, the database and all Shopware internals.

Typical Plugin Structure

MyPlugin/
├── composer.json
├── src/
│   ├── MyPlugin.php          # Plugin class
│   ├── Core/
│   │   └── Content/          # Custom Entities, DAL extensions
│   ├── Service/              # Business Logic
│   └── Resources/
│       ├── config/
│       │   └── services.xml  # Service definitions
│       └── views/            # Twig templates
└── Migration/                # Database migrations

Plugin Class

<?php

declare(strict_types=1);

namespace MyPlugin;

use Shopware\Core\Framework\Plugin;
use Shopware\Core\Framework\Plugin\Context\InstallContext;
use Shopware\Core\Framework\Plugin\Context\UninstallContext;

class MyPlugin extends Plugin
{
    public function install(InstallContext $installContext): void
    {
        // One-time initialization during installation
    }

    public function uninstall(UninstallContext $uninstallContext): void
    {
        if ($uninstallContext->keepUserData()) {
            return;
        }

        // Database cleanup
    }
}

What Plugins Can Do

  • Create custom entities and DAL definitions
  • Extend existing entities with custom fields
  • Register and override Symfony services
  • Event subscribers for all Shopware events
  • Integrate custom admin components (Vue.js) into the backend
  • Custom storefront templates with template inheritance
  • Direct database operations with full access

Plugins run in the same PHP process as Shopware. This means: direct access to all resources, but also the risk of bringing down the entire shop with a single error.

The App System

The app system was introduced to enable extensions that run outside the Shopware process. An app is an external service that communicates via webhooks and the admin API.

Typical App Structure

MyApp/
├── manifest.xml              # App manifest (no PHP!)
├── Resources/
│   ├── views/                # Twig templates (for storefront blocks)
│   └── app/
│       └── administration/   # Admin extensions
└── (external web server)     # Your own service

manifest.xml

The central file of an app defines everything declaratively:

<?xml version="1.0" encoding="UTF-8"?>
<manifest xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/shopware/shopware/trunk/src/Core/Framework/App/Manifest/Schema/manifest-2.0.xsd">
    <meta>
        <name>MyApp</name>
        <label>Meine App</label>
        <description>Eine Demo-App</description>
        <author>Wunner Software</author>
        <copyright>(c) 2025 Wunner Software</copyright>
        <version>1.0.0</version>
        <license>proprietary</license>
    </meta>

    <permissions>
        <read>order</read>
        <read>customer</read>
        <create>order_tag</create>
    </permissions>

    <webhooks>
        <webhook name="order-placed"
                 url="https://my-app.example.com/webhook/order-placed"
                 event="checkout.order.placed"/>
    </webhooks>

    <setup>
        <registrationUrl>https://my-app.example.com/register</registrationUrl>
        <!-- Hardcoded secrets are for local development only.
             In production/Store apps, the secret is exchanged automatically during the registration handshake. -->
        <secret>my-secret-for-hmac-validation</secret>
    </setup>
</manifest>

Webhook Handler (External Service, e.g. Symfony)

<?php

declare(strict_types=1);

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;

class WebhookController extends AbstractController
{
    #[Route('/webhook/order-placed', methods: ['POST'])]
    public function orderPlaced(Request $request): Response
    {
        // shopware-shop-signature is the correct header for incoming webhooks.
        // (shopware-app-signature is used for responses FROM the app, not incoming requests.)
        $signature = $request->headers->get('shopware-shop-signature');
        $body = $request->getContent();

        if (!$this->validateSignature($signature, $body)) {
            return new Response('Unauthorized', Response::HTTP_UNAUTHORIZED);
        }

        $data = json_decode($body, true);
        $orderId = $data['data']['payload']['id'] ?? null;

        if ($orderId !== null) {
            // Custom business logic: e.g. ERP integration
            $this->erpService->syncOrder($orderId);
        }

        return new Response('', Response::HTTP_OK);
    }

    private function validateSignature(?string $signature, string $body): bool
    {
        if ($signature === null) {
            return false;
        }

        $expected = hash_hmac('sha256', $body, 'my-secret-for-hmac-validation');

        return hash_equals($expected, $signature);
    }
}

Admin API Calls from the App

The app receives an API key during the registration process and can then use the Shopware admin API:

<?php

declare(strict_types=1);

namespace App\Service;

use Symfony\Contracts\HttpClient\HttpClientInterface;

class ShopwareApiClient
{
    public function __construct(
        private readonly HttpClientInterface $httpClient,
        private readonly string $shopUrl,
        private readonly string $accessToken,
    ) {
    }

    public function getOrder(string $orderId): array
    {
        $response = $this->httpClient->request('GET', sprintf('%s/api/order/%s', $this->shopUrl, $orderId), [
            'headers' => [
                'Authorization' => 'Bearer ' . $this->accessToken,
                'Content-Type' => 'application/json',
            ],
        ]);

        return $response->toArray();
    }

    public function addTagToOrder(string $orderId, string $tagId): void
    {
        $this->httpClient->request('POST', sprintf('%s/api/order/%s/tags', $this->shopUrl, $orderId), [
            'headers' => [
                'Authorization' => 'Bearer ' . $this->accessToken,
                'Content-Type' => 'application/json',
            ],
            'json' => [['id' => $tagId]],
        ]);
    }
}

Direct Comparison

Criterion Plugin App
Runtime environment In the Shopware process External service
Programming language PHP (enforced) Any (PHP, Node.js, Python...)
Database access Direct (DAL/SQL) Only via admin API
Performance impact Directly on the shop Asynchronous via webhooks
Deployment On the shop server Own server/cloud
Shopware Cloud compatible No Yes
Template overrides Full Limited (blocks)
Admin extensions Full Vue.js components Limited (iFrame, modules)
Error risk for shop High (same process) Low (isolated)
Development complexity Medium-high Medium (more infrastructure)

When to Choose Plugin vs. App?

Plugin is the right choice when...

  • deep intervention in Shopware core logic is needed (e.g., price calculation, checkout flow)
  • custom DAL entities are required that Shopware should manage directly
  • the shop runs on a self-managed server
  • extensive storefront customizations (template inheritance) are needed
  • no external server should be operated

App is the right choice when...

  • the shop runs on Shopware Cloud or with a host that does not allow plugins
  • the extension communicates with external systems (ERP, CRM, warehouse management)
  • the logic runs in another language or an existing service
  • isolation and fault tolerance are important (an app error does not slow down the shop)
  • the product is sold as SaaS to multiple shop operators

Hybrid Approaches

In practice, both models are often combined:

  • A lightweight plugin registers custom fields or flow actions
  • The actual business service runs as an app on its own server
  • Communication runs via the admin API and webhooks

This is particularly useful for complex B2B integration projects where Shopware serves as the frontend and ordering system, but ERP and warehouse are managed externally.

Conclusion

The plugin model remains the more powerful option for local Shopware installations because it has direct access to all Shopware internals. The app system is more flexible in technology choice, more isolated and cloud-compatible -- but limited to the admin API. When planning new projects, you should seriously evaluate the app system, especially if an external service architecture is already being considered.

Thomas Wunner

Thomas Wunner

Certified IT specialist for application development with an instructor qualification and over 14 years of experience building scalable web applications with Symfony and Shopware. When not coding, Thomas volunteers as a lifeguard with the Wasserwacht, performs as a DJ, and explores the countryside on his motorbike.

Kommentare

Kommentare werden von Remark42 bereitgestellt. Beim Laden werden Daten an unseren Kommentar-Server übertragen.