giorgigrdzelidze/laravel-shopify-sdk
Laravel Shopify SDK provides a clean integration for building Shopify apps and store integrations in Laravel. It helps manage Shopify API access, authentication, webhooks, and common resources so you can focus on app logic instead of boilerplate.
This package wires a Shopify store into Laravel and ships a full Filament admin surface (orders, products, customers, draft orders, fulfillments, discounts, metafields, and an Analytics page). v2.1.0 completes the permission story, so the fastest path to value is: install it, register the plugin, then implement one method on your User model to turn gating on.
Minimal steps:
composer require giorgigrdzelidze/laravel-shopify-sdkphp artisan migrate, and add your Shopify credentials (SHOPIFY_API_KEY, SHOPIFY_API_SECRET, store domain) to .env.PanelProvider (or let it auto-register resources).User model — this is the single switch that activates RBAC across all 15 resources and the Analytics page:use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
public function hasShopifyPermission(string $permission): bool
{
return $this->hasPermissionTo($permission); // e.g. spatie/laravel-permission
}
}
DraftOrderResource, FulfillmentResource, DiscountResource, MetafieldResource) and the Analytics page are hidden without the matching *.view grant. If you were previously working around the gap, delete those workarounds now.Where to look first: the HasShopifyPermissions trait and its checkPermission() path, then the *.view / *.create / *.edit / *.delete prefixes in your permission seeder.
Map the prefix convention directly into your RBAC seeder. Every resource gates on <prefix>.<action>, so seeding is mechanical:
$prefixes = [
'orders', 'products', 'customers', 'draft_orders',
'fulfillments', 'discounts', 'metafields',
];
foreach ($prefixes as $prefix) {
foreach (['view', 'create', 'edit', 'delete'] as $action) {
Permission::firstOrCreate(['name' => "{$prefix}.{$action}"]);
}
}
Permission::firstOrCreate(['name' => 'analytics.view']);
Note the resource names are snake_case (draft_orders, not draft-orders), matching the trait's own prefixing.
Role composition per persona. Grant *.view broadly to support staff, *.create/*.edit to ops, *.delete sparingly — and reserve analytics.view for managers. Because each resource has its own prefix, you can shape roles without touching package code.
Reuse checkPermission() for your own UI. When you add custom actions, bulk buttons, or widgets around Shopify data, call the same path the trait uses so your custom code stays consistent with the built-in gating rather than re-implementing checks.
Scope grants per store for multi-store apps. The permission check runs against the authenticated user, so if a user should only touch one store's data, encode store in your permission names (e.g. orders.view.store_42) and resolve the matching string in hasShopifyPermission() based on the active store context.
Test both sides of the contract. v2.1.0 ships FilamentResourceGatingTest and ShopifyPermissionsTest — mirror that matrix in your app: no user (denied), user with no grant (denied), granted user (allowed), legacy user without the method (allowed). Add regression coverage whenever you extend a resource.
Extend, don't fork. Subclass or configure the resources and keep the trait in place; the gating is applied via the trait, so custom resource classes that also use HasShopifyPermissions inherit the same guarantees.
hasShopifyPermission() gets access to everything, unchanged from before. If gating appears not to work, the method is missing, misnamed, or your model is resolving to a different class — this is the first thing to check.checkPermission() short-circuits to false with no authenticated user, which is correct but can look like a broken panel in local testing against impersonation or API guards.draft_orders.view and metafields.edit are literal; a typo in your seeder yields a silent denial, not an error. Cross-prefix leakage is explicitly tested against, so don't expect orders.view to unlock draft orders.filament/filament is require-dev. Resource and page gating is verified against real Filament classes in the package's suite, but your app still needs Filament installed to render the panel. Keep Filament in require on the consuming app; the package's require-dev placement only affects its own test matrix.canAccess(), not a resource policy. Disabling the page or overriding its canAccess() now overrides real permission logic — prefer granting/revoking analytics.view instead.checkPermission() runs on every resource and page access; if hasShopifyPermission() hits the database each time, cache the resolved grant set per user/store and invalidate on role changes.hasShopifyPermission() with the incoming permission string and the resolved boolean; it will immediately reveal whether the miss is in prefix naming, the legacy fallback, or your role assignment.checkPermission() in a custom trait if you need tenant-aware or store-aware logic, and every gated resource plus the Analytics page inherits it uniformly.How can I help you explore Laravel packages today?