Skip to content
Farid Movsumov edited this page Jun 12, 2024 · 19 revisions

For v17.4.x+


If you're migrating from the original [Laravel Shopify App](https://github.com/gnikyt/laravel-shopify/), you should remove that package first:

composer remove osiset/laravel-shopify

Then use Composer to grab the new package:

composer require kyon147/laravel-shopify

Note: You must install a laravel project first. [Learn how to install Laravel with the official documentation.](https://laravel.com/docs/11.x/installation)

Providers & Facades

With Laravel's auto-discover feature, this is handled for you on install.

Configuration

Package

php artisan vendor:publish --tag=shopify-config

You're now able to access config in config/shopify-app.php.

Essentially you will need to fill in the app_name, api_key, api_secret, and api_scopes to generate a working app. Items like webhooks and scripttags are completely optional depending on your app requirements. As well, anything to do with billing is also optional, and is disabled by default.

Its recommended you use an env file for the configuration.


Shopify App

In your app's settings on your Shopify Partner dashboard, you need to set the callback URL to be:

https://(your-domain).com/

And the redirect_uri to be:

https://(your-domain).com/authenticate

The callback URL will point to the home route, while the redirect_uri will point to the authentication route.

NOTE:Those two URLs must start with https, otherwise you will get an error message: "Oauth error invalid_request: The redirect_uri is not whitelisted"

Routing

This package expects a route named home to exist. By default, the package has this route defined which shows a simple welcome page. To enable it, you will need to open routes/web.php and comment out the default Laravel route for /.

Optionally, to make your own, edit routes/web.php and modify the default route to use the verify.shopify middleware with the home named, example:

Route::get('/', function () {
    return view('welcome');
})->middleware(['verify.shopify'])->name('home');

Next, modify resources/views/welcome.blade.php to extend this packages' layout for Shopify AppBridge abilities, example:

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

@section('content')
    <!-- You are: (shop domain name) -->
    <p>You are: {{ $shopDomain ?? Auth::user()->name }}</p>
@endsection

@section('scripts')
    @parent

    <script>
        actions.TitleBar.create(app, { title: 'Welcome' });
    </script>
@endsection

Middlewares

Middlewares are handled by the provider of this package for you.

verify.shopify, handles authentication of the shop, verification, and session token handling for both standard requests and AJAX.

verify.scopes, handles checking to make sure the api scopes the user has accepted match the ones set in the env

auth.webhook, handles verification of webhooks and their data.

auth.proxy, handles verification of proxy requests.


Models

You will need to modify your Laravel user model. Normally located in app/User.php or app/Models/User.php.

Open the file, add after the namespace:

use Osiset\ShopifyApp\Contracts\ShopModel as IShopModel;
use Osiset\ShopifyApp\Traits\ShopModel;

Next, modify the class line to become:

class User extends Authenticatable implements IShopModel

Next, inside the class, add:

use ShopModel;

A completed example:

<?php

namespace App;

use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Osiset\ShopifyApp\Contracts\ShopModel as IShopModel;
use Osiset\ShopifyApp\Traits\ShopModel;

class User extends Authenticatable implements IShopModel
{
    use Notifiable;
    use ShopModel;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name', 'email', 'password',
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password', 'remember_token',
    ];
}

IMPORTANT: From Laravel 10, the password column is automatically casted to a hash. You need to comment it out or remove it otherwise the shop's api key will be unusable.

    /**
     * The attributes that should be cast.
     *
     * @var array<string, string>
     */
    protected $casts = [
        'email_verified_at' => 'datetime',
        //'password' => 'hashed',
    ];

Jobs

Recommendations

By default, Laravel uses the sync driver to process jobs. These jobs run immediately and synchronously (blocking).

This package uses jobs to install webhooks, scripttags, and an option after-install hook if any are defined in the configuration. If you do not have any after-install hooks, scripttags, or webhooks to install on the shop, you may skip this section.

If you do however, you can leave the sync driver as default. But, it may impact load times for the customer accessing the app. It's recommended to set up Redis or database as your default driver in config/queue.php so jobs can run in the background and not affect the frontend performance. See [Laravel's docs on setting up queue drivers](https://laravel.com/docs/7.x/queues).

For more information on creating webhooks, see Creating Webhooks of this wiki or After Authentication Job.

ScriptTags Job

First, you need to get permission for write script tags to storefront. Add write_script_tags to api_scopes (or in SHOPIFY_API_SCOPES env variable) in config/shopify-app.php like the following:

'api_scopes' => env('SHOPIFY_API_SCOPES', 'read_products,write_products,write_script_tags'),

Second, add script tag values to scripttags in config/shopify-app.php:

'scripttags' => [
    [
        'src' => env('SHOPIFY_SCRIPTTAG_1_SRC', 'https://some-app.com/some-controller/js-method-response'),
        'event' => env('SHOPIFY_SCRIPTTAG_1_EVENT', 'onload'),
        'display_scope' => env('SHOPIFY_SCRIPTTAG_1_DISPLAY_SCOPE', 'online_store')
    ]
],

If you want to add local javascript file, you can do like the following:

'scripttags' => [
    [
        'src' => env('SHOPIFY_SCRIPTTAG_1_SRC', env('APP_URL') . '/scripttags/dummy.js'),
        'event' => env('SHOPIFY_SCRIPTTAG_1_EVENT', 'onload'),
        'display_scope' => env('SHOPIFY_SCRIPTTAG_1_DISPLAY_SCOPE', 'online_store')
    ]
],

dummy.js should be in public/scripttags directory.

Uninstalled Job (recommended)

There is a default job provided which soft deletes the shop, and its charges (if any) for you. You're able to install this job directly or extend it.

To install, first run:

php artisan vendor:publish --tag=shopify-jobs a job will be placed in App/Jobs/AppUninstalledJob.

Next, edit config/shopify-app.php to enable the job:

    'webhooks' => [
        [
            'topic' => env('SHOPIFY_WEBHOOK_1_TOPIC', 'APP_UNINSTALLED'),
            'address' => env('SHOPIFY_WEBHOOK_1_ADDRESS', 'https://(your-domain).com/webhook/app-uninstalled')
        ],
    ],

Use your environment file to replace the values, such as the domain, but set the topic to app/uninstalled and the path as /webhook/app-uninstalled will allow the webhook manager to do the heavy lifting for you.


Migrations

Automatic

By default, running php artisan migrate is enough. If you'd like to edit or review the migrations before running migrate, see Manual below.

Manual

Set env SHOPIFY_MANUAL_MIGRATIONS=1 or edit config/shopify-app.php, then run:

php artisan vendor:publish --tag=shopify-migrations

This will publish the migrations to your app's migration folder. When you're satisfied, run php artisan migrate to commit the changes.


CSRF

You must disable CSRF as there is currently no solution for verifying session tokens with CSRF, there is a conflict due to new login creation each request.

Open \App\Http\Middleware\VerifyCsrfToken.php, and add or edit:

protected $except = [
    '*',
];

This will exclude all routes from verifying CSRF.

In Laravel 11, you should add the following code to bootstrap/app.php file.

Check the official documentation for more details.

->withMiddleware(function (Middleware $middleware) {
        $middleware->validateCsrfTokens(except: [
            '*',
        ]);
    })

SPA Support

As of v17.3.0 the package has changed slightly to support using React, Vue and other Single Page Applications.

If you are using React, you can add to your env

SHOPIFY_FRONTEND_ENGINE="REACT"

If you are using Vue, you can add

~~SHOPIFY_FRONTEND_ENGINE="VUE" Please use REACT for all SPAs as VUE is not a valid constant value yet.

Note: At the moment, these two values do the same thing but have been separated in case there is logic that differs in the future.

If you are continuing to use the Blade templates then you can leave the ENV variable out of your .env file or just add it in as follows.

SHOPIFY_FRONTEND_ENGINE="BLADE" - This is the fallback value.

Once you have the SPA enabled, the main difference if authentication. With a frontend engine enabled you will no longer hit the auth/token in between routes or when the app is first installed. This means you will need to make sure your SPA gets the JWT token itself otherwise you will not be able to authenticate the frontend with the backend of the app.


Blade specific support

If you plan on using blade, usually you will be using the session cookie state within Laravel. This will affect things like putting data into the session for error messages and for submitting forms etc. To make sure this works you need to set up the cookie handling.

Secure has to be true 'secure' => env('SESSION_SECURE_COOKIE', true),

Same site needs to be none 'same_site' => 'none',

APP_URL in the ENV has to be the correct url set to the app you are loading.

Importantly, you have to be on https chrome and other browsers no longer allow insecure cookies being set even when you set secure to none it seems.

This might not be enough for your specific, set up but should help most people to get the session data working. If you still can not save state you'll need to look at your local development set up first.


AppBridge

Important: If you use the frontend_engine option above then you will no longer generate tokens in the blade templates. You will still need to remove the X-Frame-Options as described below.

By default Appbridge is enabled and required for session tokens.

X-Frame-Options header must be removed for AppBridge to function. This package attempts to remove this header through the responses in Laravel, but this does not work for all cases.

For Nginx, and Laravel Forge, you must specifically comment-out or remove the line below from the Nginx configuration:

add_header X-Frame-Options "SAMEORIGIN";

The Nginx configuration is found in Laravel Forge at the very bottom of the page for the site in question: Files->Edit Nginx Configuration.


Content Security Policy (CSP)

Shopify loads embedded apps within an iframe, implying that Shopify injects cross-site scripts on their end. Shopify mandates app developers to guard against XSS (Cross-Site Scripting) vulnerabilities. This is why they advocate for using Content Security Policy (CSP) to both prevent and identify XSS attacks. For further information on iframe protection, click here](https://shopify.dev/docs/apps/store/security/iframe-protection)

If your application is embedded, it is crucial to ensure that it is only frameable within the authenticated shop domain. Dynamically set the frame-ancestors directive based on the current shop domain and the Shopify admin domain. This directive ensures that your application can only be framed within the shop admin.

For instance, if the shop 'shopify-dev.myshopify.com' installs your application, the response headers from your application, when rendered by this shop, will include the following frame-ancestors declaration:

Content-Security-Policy: frame-ancestors https://shopify-dev.myshopify.com https://admin.shopify.com;

Note: The frame-ancestors declaration must be different for every shop, and these headers must be present in any routes that render HTML content.

This app comes with these features built-in. It incorporates the IframeProtection middleware into the web middleware group by default. However, there may be situations where you need to include an extra domain in the frame-ancestors. In such cases, you can configure additional sites in the configs/shopify-app.php file.

    'iframe_ancestors' => 'mysite.com subdomain.yoursite.net', // domain will be separated by space

Theme Block Support

As of 17.2.0 the package looks to see if the current main theme of the install store has full, partial or no support of the Shopify Theme 2.0.

A new field is included into the users table which will store the support level and if unsupported will fire of script tags. If fully support, script tags will not be fired as you will need to use Theme extensions to provide functionality on the storefront.


What's next? You can review the authentication process for a better understanding of how to work with Shopify session tokens.

Welcome to the wiki!

Please see the homepage for a list of relevant pages.

Clone this wiki locally