Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

App Php Sdk Laravel Package

shopware/app-php-sdk

A PHP SDK for building Shopware apps. It handles app registration and lifecycle, webhook verification, HMAC request signing, and authenticated communication with the Shopware API, giving PHP developers ready-made tools for secure, reliable app development on the Shopware platform.

View on GitHub
Deep Wiki
Context7

Getting Started

The SDK is deliberately thin: it handles Shopware app protocol mechanics (signatures, registration proof, lifecycle payloads, API client plumbing, context hydration) and leaves HTTP framework, persistence, and routing to you. Expect to write more glue code than with a framework bundle.

  1. Install the package plus PSR implementations, since the SDK depends on PSR-7/17/18 rather than a concrete HTTP stack:
composer require shopware/app-php-sdk \
    nyholm/psr7 \
    guzzlehttp/guzzle
  1. First place to look: ShopRepositoryInterface. You cannot use the SDK without implementing persistence for shops. A shop is identified by its Shopware shop ID and stores at minimum the shop URL, shop secret, and API credentials. Start with a simple Doctrine/Eloquent/Redis-backed implementation.
use Shopware\App\SDK\Shop\ShopInterface;
use Shopware\App\SDK\Shop\ShopRepositoryInterface;

final class ShopRepository implements ShopRepositoryInterface
{
    public function __construct(private \PDO $pdo) {}

    public function createShop(ShopInterface $shop): void { /* INSERT ... ON CONFLICT UPDATE */ }
    public function updateShop(ShopInterface $shop): void { /* ... */ }
    public function deleteShop(string $shopId): void { /* ... */ }
    public function getShop(string $shopId): ?ShopInterface { /* ... */ }
}
  1. Construct the AppLifecycle with your repository and app secret, then expose the lifecycle endpoints. Your first useful endpoint is registration (POST /registration): it validates the shopware-app-signature query parameter, gives you a RegistrationRequest containing shop metadata, and produces the proof/secret response Shopware expects.
$lifecycle = new AppLifecycle($shopRepository, $_ENV['SHOPWARE_APP_SECRET']);

$registration = $lifecycle->register($request);   // validates app signature
$shopRepository->createShop($registration->getShop());
return $registration->getResponse();              // JSON with proof/secret
  1. After registration works, wire activate, deactivate, uninstall, and the webhook endpoint. Registration plus one webhook is the smallest end-to-end slice that proves signatures, persistence, and the API client all work.

Implementation Patterns

Single verifier in middleware. Put signature verification in one PSR-15 middleware (or Laravel middleware), resolve the shop once, and attach it to the request as an attribute. Every downstream handler then receives a trusted ShopInterface instead of re-parsing headers. This mirrors how the SDK's own context resolution is intended to be used and avoids duplicated HMAC logic across controllers.

Registration as upsert. Registration can be retried and can re-register an existing shop (for example after a secret rotation). Treat shop-id as the primary key and upsert; never assume a shop is new. Persist the shopware-version too — it is your only reliable feature gate for API differences.

Webhook handling: verify, resolve, enqueue, return 200. Shopware retries failed webhooks aggressively. Do the minimum synchronously:

$webhook = $lifecycle->webhook($request);       // verifies shopware-shop-signature
$shop = $webhook->getShop();
$queue->push(new HandleShopwareEvent($shop->getShopId(), $webhook->getPayload()));
return new Response(200);

Make the job idempotent. Shopware may deliver the same event more than once, and the payload does not carry a globally unique delivery ID you can rely on across versions, so derive idempotency from event name + entity ID + updatedAt, or store the raw payload hash.

Use ShopwareClient for both APIs. Build the client from the persisted shop; it carries the credentials and gives you PSR-7 request builders. Use Admin API credentials (client_id/client_secret) for /api/... and Store API credentials (sw-access-key) for /store-api/.... Do not hand-roll OAuth token fetching unless you need to share a token cache across processes.

$client = ShopwareClient::createFromShop($shop, $httpClient, $requestFactory);
$response = $client->send($client->createGetRequest('/api/product/' . $id));

Typed contexts instead of arrays. For payment and checkout flows, let ContextResolver turn the raw request into typed action objects (PaymentValidateAction, PaymentPayAction, PaymentCaptureAction, etc.). This is the SDK's biggest day-to-day win: you avoid $payload['order']['lineItems'][0]['...'] spelunking and get IDE support. Payment handlers become small match-style dispatch methods.

Framework integration. For Laravel/Symfony, register AppLifecycle, the repository, the PSR-18 client, and the request factory as singletons. Keep the SDK objects stateless; the only per-request state should be the resolved shop. Route registration/activation/webhook endpoints outside CSRF and auth middleware, but inside your normal logging and error-handling middleware.

Testing. Because the SDK is framework-agnostic, you can test handlers without booting an app: construct signed PSR-7 requests with a known app secret or shop secret, call AppLifecycle, and assert on the returned response and repository calls. Keep a small fixture factory for signed registration and webhook requests — you will use it constantly.

Gotchas and Tips

  • Sign the raw request, not a rebuilt one. Registration signatures cover the raw query string, and webhook signatures cover the raw body. Re-encoding query parameters, normalizing JSON, or decoding/re-encoding a form body will break hash_equals verification. Read php://input or the PSR-7 body once and verify before any middleware mutates it.
  • Two different secrets. Registration uses the app secret (shopware-app-signature query param). Webhooks and lifecycle calls use the shop secret (shopware-shop-signature header). Mixing them up is the single most common integration bug. Log which verifier failed, not just "invalid signature".
  • Unknown shops must not 500. After uninstall, in-flight webhooks can still arrive. If the repository returns null, respond 200 (or 410) and log; throwing gives Shopware a retry loop you cannot satisfy.
  • Uninstall is destructive and final. Delete shop credentials and cached tokens in the uninstall handler. Do not keep the shop secret "just in case" — a re-install generates a new one, and stale secrets cause confusing verification failures.
  • Shop URL normalization. Persisted shop-url values can include or omit trailing slashes and may be http in local dev. Normalize once at persistence time and build API URLs from a single helper; never concatenate shopUrl . '/api' inline.
  • Token caching matters. Admin API tokens expire quickly (minutes). Cache per shop with an expiry buffer, and key the cache by shop ID, not by URL — URLs can change while the shop identity does not. Without caching you will noticeably slow down every API call.
  • PSR-18 clients throw. ClientInterface::sendRequest throws on transport errors and sometimes on 4xx/5xx depending on configuration. Wrap calls and translate to your domain exceptions; do not let a Guzzle exception escape a webhook handler.
  • Debugging signatures. Reproduce the HMAC in a scratch script using the exact raw bytes from the request and the correct secret. Check header/query name spelling (shopware-shop-signature vs shopware-app-signature), check that a proxy is not rewriting the body, and always compare with hash_equals.
  • Version pinning. The package is small (low star count, niche audience), so community answers are scarce and the API has moved between releases. Pin an exact version in composer.json, read the source for the classes you use, and treat upgrades as minor migrations rather than drop-ins. The source is readable in an afternoon — that is the intended debugging strategy.
  • Extension points to prefer: a custom ShopInterface implementation for extra persisted fields (tenant ID, locale, flags), a decorator around your repository for caching, a decorator around the PSR-18 client for logging/retries, and custom context resolvers for your own webhook payload types. Avoid subclassing AppLifecycle; compose it in a small application service instead, which keeps upgrades tractable.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource