kyon147 / laravel-shopify
Shopify package for Laravel to aide in app development
Requires
- php: ^8.2
- ext-json: *
- funeralzone/valueobjects: ^0.5
- gnikyt/basic-shopify-api: ^9.0 || ^10.0 || ^11.0
- jenssegers/agent: ^2.6
- laravel/framework: ^12.0 || ^13.0
Requires (Dev)
- ergebnis/composer-normalize: ^2.8
- friendsofphp/php-cs-fixer: ^3.0
- laravel/legacy-factories: ^1.3.0
- mockery/mockery: ^1.0
- orchestra/testbench: ^10.0 || ^11.0
- phpstan/phpstan: ^2.1
- phpunit/phpunit: ^8.0 || ^9.0 || ^10.0 || ^11.0
This package is auto-updated.
Last update: 2026-06-20 16:02:55 UTC
README
This is a maintained version of the wonderful but now deprecated original Laravel Shopify App. To keep things clean, this has been detached from the original.
To install this package run:
composer require kyon147/laravel-shopify
Publish the config file:
php artisan vendor:publish --tag=shopify-config
A full-featured Laravel package for aiding in Shopify App development, similar to shopify_app for Rails. Works for Laravel 8 and up.
Table of Contents
* Wiki pages
For more information, tutorials, etc., please view the project's wiki.
Goals
- Per User Auth Working
- Better support for SPA apps using VueJS
- Getting "Blade" templates working better with Shopify's new auth process???
Documentation
For full resources on this package, see the wiki.
Expiring offline access tokens
Shopify requires expiring offline access tokens for new public apps created on or after April 1, 2026. This package supports them when enabled:
- Run package migrations so your shops table includes
shopify_offline_refresh_token,shopify_offline_access_token_expires_at, andshopify_offline_refresh_token_expires_at. - Set
SHOPIFY_EXPIRING_OFFLINE_TOKENS=truein.env(seeexpiring_offline_tokens,auto_migrate_legacy, andoffline_access_token_refresh_skew_secondsinconfig/shopify-app.php). - Keep
APP_KEYstable: refresh tokens are stored encrypted with Laravel’s encrypter.
Authorization code exchange, session-token exchange, and refresh_token grants are handled inside this package (Osiset\ShopifyApp\Services\ApiHelper and OfflineAccessTokenRefresher), not via gnikyt/basic-shopify-api updates. A valid access token is refreshed automatically before apiHelper() builds the API session when the offline token is expired or within the configured skew.
Migrating existing installs (optional): Shops that already have a non-expiring offline token are not upgraded automatically when you enable the flag alone. Options:
- Passive (default): With
SHOPIFY_AUTO_MIGRATE_LEGACY=true(default), the firstapiHelper()call for a legacy shop runs Shopify Step 4 token exchange synchronously. Failures are logged and the request continues with the legacy token (fail-open). - Batch CLI (serverless-safe):
php artisan shopify-app:migrate-expiring-offline-tokens(--dry-run,--shop=example.myshopify.com) chunks matching shops and dispatchesMigrateShopTokenJobto the queue — no HTTP in the command itself (suitable for Laravel Vapor). - Action:
MigrateShopToExpiringOfflineAccessToken— call per shop from your own code; returnsmigrated,skipped,reason, anderrorkeys. - API:
ApiHelper::exchangeNonExpiringOfflineTokenForExpiring($shopDomain, $currentOfflineToken)then persist withShopCommand::setAccessToken.
Migration is one-way: Shopify revokes the old token on successful exchange.
Long-running jobs: Token refresh runs when the API client is first built ($shop->api() / $shop->apiHelper()). If a job reuses the same shop model for hours, the cached client keeps the old access token even after expiry. Options:
- Opt-in config:
SHOPIFY_REFRESH_OFFLINE_TOKEN_BEFORE_API_CALL=truere-checks expiry before eachapi()/apiHelper()call and rebuilds the client when needed. - Manual helpers on your shop model:
$shop->offlineAccessTokenIsFresh()—truewhen the token is outside the refresh skew window$shop->refreshOfflineAccessTokenIfNeeded()— refreshes via Shopify and clears the cached client$shop->resetApiClient()— clears the cached client so the next API call rebuilds it
// Per loop iteration in a bulk job if (! $shop->offlineAccessTokenIsFresh()) { $shop->refreshOfflineAccessTokenIfNeeded(); } $shop->api()->graph('...');
If your User model overrides $casts, merge datetime casts for the two *_expires_at columns (the ShopModel trait uses mergeCasts when initializeShopModel runs).
Longer term, consider replacing or forking gnikyt/basic-shopify-api for REST/Graph traffic if you need an actively maintained HTTP client; expiring offline OAuth is already decoupled from that dependency.
Issue or request?
If you have found a bug or would like to request a feature for discussion, please use the ISSUE_TEMPLATE in this repo when creating your issue. Any issue submitted without this template will be closed.
License
This project is released under the MIT license.
Misc
Repository
Contributors
Contributions are always welcome! Contibutors are updated each release, pulled from Github. See CONTRIBUTORS.txt.
If you're looking to become a contributor, please see CONTRIBUTING.md.
Maintainers
Maintainers are users who manage the repository itself, whether it's managing the issues, assisting in releases, or helping with pull requests.
Currently this repository is maintained by:
- @kyon147
@gnikytOriginal author of the package. See announcement for details.
Looking to become a maintainer? E-mail @kyon147 directly.
Special Note
I develop this package in my spare time, with a busy family/work life like many of you! So, I would like to thank everyone who's helped me out from submitting PRs, to assisting on issues, and plain using the package (I hope its useful). Cheers.

