portside-labs/laravel-shopify
Laravel Shopify by Portside Labs is a lightweight integration for building Shopify apps with Laravel. It streamlines API client setup, OAuth authentication, webhook routing, and configuration so you can connect your app to Shopify stores with less boilerplate and focus on business logic.
composer create-project portside-labs/laravel-shopify my-app, then cd my-app and set your .env (Shopify API key/secret, SHOPIFY_APP_CONFIG=dev is written for you locally).php artisan shopify:install once. It links your development app, writes shopify.app.dev.toml, and subscribes the default webhooks (app/uninstalled, app/scopes_update, plus the three compliance topics). It ends by telling you the next step.php artisan shopify:dev. It installs npm dependencies, starts the Shopify CLI, and points application_url, redirect_urls, and APP_URL at the tunnel. Press p in the CLI when prompted to open the app in your dev store.Look first at: shopify.app.dev.toml (local config), shopify.app.toml (production config), the README's billing section (Shopify App Pricing, Partner API client, each .env value), and php artisan shopify:doctor for a pre-flight check.
First real use case: get a tunnel-backed dev app running with webhooks intact, then verify billing via a route using the subscribed middleware.
shopify:dev. It is idempotent-ish by design: it always runs npm install, re-links an unlinked app through shopify:install, and repairs webhook subscriptions and the webhook API version if the configuration drifted.shopify.app.dev.toml (git-ignored). Production uses shopify.app.toml. A config you name yourself (SHOPIFY_APP_CONFIG=staging) is always respected regardless of environment. Use this to keep staging/production app definitions separate without branching the CLI invocation.shopify:deploy (dev config won't do here): --production deploys shopify.app.toml; --config=staging deploys a named config; --env=production makes Artisan read .env.production. It runs shopify:doctor first and always names the configuration so the CLI can't deploy the wrong one.--no-interaction, which adds --allow-updates for you; pass --message/--app-version for traceability, --no-release to stage a version without releasing, and --skip-checks only when you already ran the doctor elsewhere. --allow-deletes is opt-in, so destructive config changes fail loudly by default.shopify:install after manual CLI fiddling — shopify app dev alone rewrites config from the Dev Dashboard and drops the kit's webhooks/settings, which is exactly why shopify:dev links first.shopify:doctor as an advisory (it reports missing billing settings with links but doesn't fail), and on the subscribed middleware as the enforcement point for routes that need a plan.shopify:install, shopify:dev, and shopify:deploy now operate on shopify.app.dev.toml, not shopify.app.toml. If you've been developing against shopify.app.toml, either copy it to shopify.app.dev.toml or run php artisan shopify:install once to link your dev app. Otherwise you'll see config changes seemingly ignored.SHOPIFY_APP_CONFIG wins. Setting it explicitly overrides the environment-based default, including in local — don't set it to production on a dev machine.shopify:deploy refuses unlinked configs and refuses to deploy the production config while its URL is a tunnel, localhost, *.test, or example.com. If a deploy "mysteriously" aborts, check application_url in the target TOML first.shopify:dev mutates .env. It rewrites APP_URL to the tunnel each run; redirect URLs keep their paths but swap host/port. Committing .env after a dev session is a bad idea, and any hardcoded absolute URLs in your app will drift.shopify:dev down; if that becomes painful, keep a warm node_modules and be aware the step is unconditional rather than conditional.shopify:doctor doesn't mean billing is configured — only that nothing failed. Routes behind subscribed still enforce billing settings, so test those routes explicitly.--config=staging over renaming files; the CLI is always passed an explicit config name on deploy, which is the safety property you want to preserve in any wrapper scripts you write.How can I help you explore Laravel packages today?