PHPackages                             acefolio/laravel-shopify - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [API Development](/categories/api)
4. /
5. acefolio/laravel-shopify

ActiveLibrary[API Development](/categories/api)

acefolio/laravel-shopify
========================

A comprehensive Laravel package replicating Shopify CLI core functionality with App Bridge 4 and Token Exchange support.

v1.2.3(2mo ago)02MITPHPPHP ^8.2

Since Mar 5Pushed 2mo agoCompare

[ Source](https://github.com/acefolioDev/laravel-shopify)[ Packagist](https://packagist.org/packages/acefolio/laravel-shopify)[ RSS](/packages/acefolio-laravel-shopify/feed)WikiDiscussions master Synced 1mo ago

READMEChangelogDependencies (10)Versions (9)Used By (0)

Laravel Shopify App
===================

[](#laravel-shopify-app)

A comprehensive Laravel package that replicates the core functionality of the Shopify CLI (Node/Remix version). Handles the entire Shopify app lifecycle within the Laravel framework, adhering to 2026 Shopify standards — **App Bridge 4** and **Token Exchange**.

Requirements
------------

[](#requirements)

- PHP 8.2+
- Laravel 10 or 11
- Shopify Partner Account

Installation
------------

[](#installation)

```
composer require acefolio/laravel-shopify
```

### Publish Assets

[](#publish-assets)

```
# Publish everything
php artisan vendor:publish --provider="LaravelShopify\ShopifyAppServiceProvider"

# Or publish individually
php artisan vendor:publish --tag=shopify-config
php artisan vendor:publish --tag=shopify-migrations
php artisan vendor:publish --tag=shopify-views
php artisan vendor:publish --tag=shopify-stubs
php artisan vendor:publish --tag=shopify-vite-plugin
```

### Run Migrations

[](#run-migrations)

```
php artisan migrate
```

Configuration
-------------

[](#configuration)

Add these to your `.env` file:

```
SHOPIFY_API_KEY=your-api-key
SHOPIFY_API_SECRET=your-api-secret
SHOPIFY_SCOPES=read_products,write_products,read_orders
SHOPIFY_APP_URL=https://your-app-url.com
SHOPIFY_API_VERSION=2025-01

# Tunnel (for development)
SHOPIFY_TUNNEL_DRIVER=ngrok
NGROK_AUTH_TOKEN=your-ngrok-token

# Billing (optional)
SHOPIFY_BILLING_ENABLED=true
SHOPIFY_BILLING_REQUIRED=true
SHOPIFY_BILLING_TEST=true

# Partners Dashboard Auto-Update (optional)
SHOPIFY_PARTNERS_AUTO_UPDATE=false
SHOPIFY_CLI_TOKEN=your-cli-token
SHOPIFY_APP_ID=your-app-id
```

See `config/shopify-app.php` for all available options.

---

Authentication &amp; Session Management
---------------------------------------

[](#authentication--session-management)

### Token Exchange (No OAuth Redirects)

[](#token-exchange-no-oauth-redirects)

This package uses Shopify's **Token Exchange** flow — the 2026 standard that replaces legacy OAuth redirect loops. The frontend obtains a session token from App Bridge, and the backend exchanges it for an access token.

```
[App Bridge 4] → Session Token (JWT) → [Laravel Backend] → Token Exchange → [Shopify] → Access Token

```

No redirect pages, no flashing, no auth callback routes needed for the primary flow.

### Middleware

[](#middleware)

#### `verify.shopify`

[](#verifyshopify)

Validates the session token from the `Authorization: Bearer` header, performs token exchange or refresh as needed, and binds the shop context to the request.

```
Route::middleware('verify.shopify')->group(function () {
    Route::get('/api/products', [ProductController::class, 'index']);
});
```

Access the shop context in your controllers:

```
use LaravelShopify\Traits\ShopifyRequestContext;

class ProductController extends Controller
{
    use ShopifyRequestContext;

    public function index(Request $request)
    {
        $shopDomain = $this->getShopDomain($request);
        $accessToken = $this->getAccessToken($request);
        $session = $this->getShopifySession($request);
        $shop = $this->getShop($request);

        // ... your logic
    }
}
```

#### `verify.billing`

[](#verifybilling)

Checks for an active billing plan. If none exists, returns a 402 with the App Bridge redirect header pointing to Shopify's checkout page.

```
Route::middleware(['verify.shopify', 'verify.billing'])->group(function () {
    Route::get('/api/dashboard', [DashboardController::class, 'index']);
});

// Or require a specific plan
Route::middleware(['verify.shopify', 'verify.billing:pro'])->group(function () {
    Route::get('/api/advanced', [AdvancedController::class, 'index']);
});
```

#### `verify.webhook.hmac`

[](#verifywebhookhmac)

Validates the HMAC signature on incoming Shopify webhooks.

#### `verify.app.proxy`

[](#verifyappproxy)

Validates the signature on Shopify App Proxy requests.

### Expiring Offline Access Tokens

[](#expiring-offline-access-tokens)

The package fully supports Shopify's expiring offline tokens with automatic refresh token rotation. When a token is about to expire (within the configurable buffer window), the middleware automatically refreshes it.

```
// config/shopify-app.php
'offline_tokens' => [
    'expiring' => true,
    'refresh_buffer_seconds' => 300, // Refresh 5 min before expiry
],
```

---

Artisan Commands
----------------

[](#artisan-commands)

### `shopify:app:dev`

[](#shopifyappdev)

Start a full development environment — tunnel, app URL update, Laravel server, and Vite dev server.

```
php artisan shopify:app:dev

# Options
php artisan shopify:app:dev --tunnel=cloudflare
php artisan shopify:app:dev --port=8000 --vite-port=5173
php artisan shopify:app:dev --no-tunnel
php artisan shopify:app:dev --no-update
```

### `shopify:app:deploy`

[](#shopifyappdeploy)

Bundle assets, optimize Laravel, and prepare for production.

```
php artisan shopify:app:deploy

# Options
php artisan shopify:app:deploy --skip-build
php artisan shopify:app:deploy --skip-optimize
```

### `shopify:generate:webhook`

[](#shopifygeneratewebhook)

Scaffold a webhook Job class and register it in the config.

```
php artisan shopify:generate:webhook PRODUCTS_UPDATE
php artisan shopify:generate:webhook APP_UNINSTALLED --force
```

### `shopify:generate:extension`

[](#shopifygenerateextension)

Scaffold Theme App Extensions or UI Extensions.

```
# Theme App Extension
php artisan shopify:generate:extension my-theme-block --type=theme

# UI Extension
php artisan shopify:generate:extension my-admin-block --type=ui
```

---

API Client
----------

[](#api-client)

### GraphQL Client (with Leaky Bucket Rate Limiting)

[](#graphql-client-with-leaky-bucket-rate-limiting)

```
use LaravelShopify\Services\GraphQLClient;

$graphql = app(GraphQLClient::class);

$result = $graphql->query($shopDomain, $accessToken, '
    {
        products(first: 10) {
            edges {
                node {
                    id
                    title
                }
            }
        }
    }
');

// Mutations
$result = $graphql->mutate($shopDomain, $accessToken, '
    mutation productCreate($input: ProductInput!) {
        productCreate(input: $input) {
            product { id title }
            userErrors { field message }
        }
    }
', ['input' => ['title' => 'New Product']]);
```

### REST Client

[](#rest-client)

```
use LaravelShopify\Services\ShopifyApiClient;

$api = app(ShopifyApiClient::class);

$products = $api->get($shopDomain, $accessToken, 'products.json', ['limit' => 10]);
$product = $api->post($shopDomain, $accessToken, 'products.json', ['product' => [...]]);
$api->put($shopDomain, $accessToken, 'products/123.json', ['product' => [...]]);
$api->delete($shopDomain, $accessToken, 'products/123.json');
```

### Facade

[](#facade)

```
use LaravelShopify\Facades\ShopifyApp;

$result = ShopifyApp::query($shopDomain, $accessToken, $query);
```

Both clients implement the **Leaky Bucket algorithm** for automatic rate-limit handling with configurable retry logic.

---

Billing
-------

[](#billing)

### Configuration

[](#configuration-1)

```
// config/shopify-app.php
'billing' => [
    'enabled' => true,
    'required' => true,

    'plans' => [
        'basic' => [
            'name' => 'Basic Plan',
            'type' => 'recurring',
            'price' => 9.99,
            'currency' => 'USD',
            'interval' => 'EVERY_30_DAYS',
            'trial_days' => 7,
            'test' => true,
        ],
        'pro' => [
            'name' => 'Pro Plan',
            'type' => 'recurring',
            'price' => 29.99,
            'currency' => 'USD',
            'interval' => 'EVERY_30_DAYS',
            'trial_days' => 14,
            'test' => true,
            'capped_amount' => 100.00,
            'terms' => 'Usage charges for API calls',
        ],
        'lifetime' => [
            'name' => 'Lifetime Access',
            'type' => 'one_time',
            'price' => 199.99,
            'currency' => 'USD',
            'test' => true,
        ],
    ],
],
```

### Programmatic Usage

[](#programmatic-usage)

```
use LaravelShopify\Services\BillingService;

$billing = app(BillingService::class);

// Create a charge and get the confirmation URL
$confirmationUrl = $billing->createCharge($shopDomain, $accessToken, 'pro');

// Check active subscription
$subscription = $billing->checkActiveSubscription($shopDomain, $accessToken);

// Confirm after merchant approves
$plan = $billing->confirmCharge($shopDomain, 'pro', $chargeId);
```

### App Bridge Redirect Pattern

[](#app-bridge-redirect-pattern)

All billing redirects use the `Link` header pattern for App Bridge iframe breakout:

```
Link: ; rel="app-bridge-redirect-endpoint"

```

---

Webhooks
--------

[](#webhooks)

### Declarative Registration

[](#declarative-registration)

```
// config/shopify-app.php
'webhooks' => [
    'APP_UNINSTALLED' => \App\Jobs\Shopify\AppUninstalledJob::class,
    'PRODUCTS_UPDATE' => \App\Jobs\Shopify\ProductsUpdateJob::class,
],
```

Webhooks are automatically registered with Shopify when a shop installs your app.

### Webhook Jobs

[](#webhook-jobs)

```
php artisan shopify:generate:webhook PRODUCTS_UPDATE
```

Generated job:

```
class ProductsUpdateJob implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public string $shopDomain;
    public array $data;
    public string $topic;
    public string $apiVersion;

    public function handle(): void
    {
        // Your webhook handling logic
    }
}
```

### CSRF Exemption

[](#csrf-exemption)

Add the webhook path to your `VerifyCsrfToken` middleware exceptions:

```
protected $except = [
    'shopify/webhooks',
];
```

---

Frontend Integration
--------------------

[](#frontend-integration)

### Blade Layout (App Bridge 4)

[](#blade-layout-app-bridge-4)

```
@extends('shopify-app::layouts.shopify-app')

@section('content')
    My Shopify App

        // Use the global authenticatedFetch helper
        authenticatedFetch('/api/products')
            .then(res => res.json())
            .then(data => console.log(data));

@endsection
```

### React/Inertia Layout

[](#reactinertia-layout)

Use the React-optimized layout:

```
@extends('shopify-app::layouts.shopify-react')
```

Add the Inertia data-sharing middleware to your stack:

```
// app/Http/Kernel.php
'web' => [
    // ...
    \LaravelShopify\Http\Middleware\ShareShopifyInertiaData::class,
],
```

### Vite Plugin

[](#vite-plugin)

Publish and use the included Vite plugin:

```
php artisan vendor:publish --tag=shopify-vite-plugin
```

```
// vite.config.js
import shopifyAppBridge from './vite-plugin-shopify/index.js';

export default defineConfig({
    plugins: [
        shopifyAppBridge({
            apiKey: process.env.SHOPIFY_API_KEY,
        }),
        laravel({
            input: ['resources/js/app.jsx'],
            refresh: true,
        }),
    ],
});
```

The plugin automatically:

- Injects the App Bridge 4 CDN script
- Configures HMR for embedded iframe context
- Provides `getSessionToken()` and `authenticatedFetch()` helpers

### Navigation Bridge

[](#navigation-bridge)

Sync Laravel routes with the Shopify Admin address bar:

```
use LaravelShopify\Navigation\NavigationBridge;

// Generate nav items
$menu = NavigationBridge::buildMenu([
    ['label' => 'Dashboard', 'route' => 'dashboard'],
    ['label' => 'Products', 'route' => 'products.index'],
    ['label' => 'Settings', 'route' => 'settings'],
], Route::currentRouteName());

// For Inertia apps
$sharedData = NavigationBridge::inertiaSharedData($menuItems, $currentRoute);
```

In Blade views, include the sync script:

```
{!! \LaravelShopify\Navigation\NavigationBridge::syncScript() !!}
```

---

Events
------

[](#events)

The package dispatches lifecycle events you can listen for:

EventWhen`ShopInstalled`First-time token exchange for a shop`ShopUninstalled`(Dispatch in your APP\_UNINSTALLED webhook job)`ShopTokenRefreshed`Token refreshed for an existing shop```
// EventServiceProvider
protected $listen = [
    \LaravelShopify\Events\ShopInstalled::class => [
        \App\Listeners\SetupNewShop::class,
    ],
];
```

---

Helpers
-------

[](#helpers)

### Shop Domain Utilities

[](#shop-domain-utilities)

```
use LaravelShopify\Support\ShopifyHelper;

ShopifyHelper::sanitizeShopDomain('my-store');
// → "my-store.myshopify.com"

ShopifyHelper::adminUrl('my-store.myshopify.com', 'products');
// → "https://my-store.myshopify.com/admin/products"

ShopifyHelper::embeddedAppUrl('my-store.myshopify.com');
// → "https://admin.shopify.com/store/my-store/apps/{api_key}"

ShopifyHelper::shopFromHost($encodedHost);
// → "my-store.myshopify.com"
```

### HMAC Verification

[](#hmac-verification)

```
use LaravelShopify\Support\HmacVerifier;

HmacVerifier::verifyWebhook($body, $hmacHeader);
HmacVerifier::verifyProxy($request);
HmacVerifier::verifyOAuth($queryParams);
HmacVerifier::isValidShopDomain('my-store.myshopify.com'); // true
```

---

Package Structure
-----------------

[](#package-structure)

```
├── config/
│   └── shopify-app.php              # Full configuration
├── database/migrations/
│   ├── create_shopify_shops_table
│   ├── create_shopify_sessions_table
│   └── create_shopify_plans_table
├── resources/views/layouts/
│   ├── shopify-app.blade.php        # Blade + App Bridge 4
│   └── shopify-react.blade.php      # React/Inertia + App Bridge 4
├── routes/
│   └── shopify.php                  # Token exchange, webhooks, billing
├── src/
│   ├── Auth/
│   │   ├── SessionToken.php         # JWT validation
│   │   └── TokenExchange.php        # Token exchange + refresh
│   ├── Console/Commands/
│   │   ├── AppDevCommand.php        # shopify:app:dev
│   │   ├── AppDeployCommand.php     # shopify:app:deploy
│   │   ├── GenerateWebhookCommand.php
│   │   └── GenerateExtensionCommand.php
│   ├── Events/
│   │   ├── ShopInstalled.php
│   │   ├── ShopUninstalled.php
│   │   └── ShopTokenRefreshed.php
│   ├── Exceptions/
│   │   ├── ShopifyApiException.php
│   │   └── TokenExchangeException.php
│   ├── Facades/
│   │   └── ShopifyApp.php
│   ├── Http/
│   │   ├── Controllers/
│   │   │   ├── BillingController.php
│   │   │   ├── TokenExchangeController.php
│   │   │   └── WebhookController.php
│   │   └── Middleware/
│   │       ├── ShareShopifyInertiaData.php
│   │       ├── VerifyAppProxy.php
│   │       ├── VerifyBilling.php
│   │       ├── VerifyShopify.php
│   │       └── VerifyWebhookHmac.php
│   ├── Models/
│   │   ├── Plan.php
│   │   ├── Session.php
│   │   └── Shop.php
│   ├── Navigation/
│   │   └── NavigationBridge.php
│   ├── Services/
│   │   ├── BillingService.php
│   │   ├── GraphQLClient.php
│   │   ├── RateLimiter.php
│   │   ├── ShopifyApiClient.php
│   │   └── WebhookRegistrar.php
│   ├── Support/
│   │   ├── HmacVerifier.php
│   │   └── ShopifyHelper.php
│   ├── Traits/
│   │   └── ShopifyRequestContext.php
│   └── ShopifyAppServiceProvider.php
├── stubs/
│   └── webhook-job.stub
├── vite-plugin/
│   ├── index.js
│   └── package.json
├── composer.json
└── README.md

```

License
-------

[](#license)

MIT

###  Health Score

39

—

LowBetter than 86% of packages

Maintenance88

Actively maintained with recent releases

Popularity3

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity51

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~1 days

Total

6

Last Release

62d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/ed629cf0b1fe73713d979d2002733702dcb66d1dbe2b7c39c52100b40d00b7e9?d=identicon)[acofolio.dev](/maintainers/acofolio.dev)

---

Top Contributors

[![muhammad-atif-unitedsol](https://avatars.githubusercontent.com/u/195889647?v=4)](https://github.com/muhammad-atif-unitedsol "muhammad-atif-unitedsol (7 commits)")

---

Tags

laravelshopifyshopify-apptoken-exchangeapp-bridge

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/acefolio-laravel-shopify/health.svg)

```
[![Health](https://phpackages.com/badges/acefolio-laravel-shopify/health.svg)](https://phpackages.com/packages/acefolio-laravel-shopify)
```

###  Alternatives

[laravel/passport

Laravel Passport provides OAuth2 server support to Laravel.

3.4k85.0M531](/packages/laravel-passport)[laravel/cashier

Laravel Cashier provides an expressive, fluent interface to Stripe's subscription billing services.

2.5k25.9M107](/packages/laravel-cashier)[roots/acorn

Framework for Roots WordPress projects built with Laravel components.

9682.1M97](/packages/roots-acorn)[laravel/pulse

Laravel Pulse is a real-time application performance monitoring tool and dashboard for your Laravel application.

1.7k12.1M99](/packages/laravel-pulse)[laravel/cashier-paddle

Cashier Paddle provides an expressive, fluent interface to Paddle's subscription billing services.

264778.4k3](/packages/laravel-cashier-paddle)[flat3/lodata

OData v4.01 Producer for Laravel

96320.9k](/packages/flat3-lodata)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
