If you are enabling expiring tokens for an existing installation, you have three migration options:
1. Passive Migration (Default)
Set SHOPIFY_AUTO_MIGRATE_LEGACY=true in your environment. The first time apiHelper() is called for a legacy shop, the package will attempt a synchronous token exchange. If it fails, it logs the error and continues using the legacy token (fail-open).
2. Batch CLI Migration
Run the following command to chunk shops and dispatch MigrateShopTokenJob to your queue. This is safe for serverless environments like Laravel Vapor because it performs no HTTP requests in the command itself.
php artisan shopify-app:migrate-expiring-offline-tokens [--dry-run] [--shop=example.myshopify.com] [--queue=] [--connection=]
Use SHOPIFY_MIGRATE_OFFLINE_TOKENS_JOB_QUEUE or SHOPIFY_MIGRATE_OFFLINE_TOKENS_JOB_CONNECTION to target specific workers.
3. Programmatic Migration
Use the MigrateShopToExpiringOfflineAccessToken action per shop in your own code. It returns an array containing migrated, skipped, reason, and error keys.
Alternatively, use the ApiHelper directly:
ApiHelper::exchangeNonExpiringOfflineTokenForExpiring($shopDomain, $currentOfflineToken);
// Then persist using:
ShopCommand::setAccessToken(...);
Note: Migration is one-way. Shopify revokes the old token upon a successful exchange.