Kyon147 / laravel-shopify

A full-featured Laravel package for aiding in Shopify App development
MIT License
363 stars 107 forks source link

OrderUpdatedJob and OrderCreatedJob is not firing. #232

Closed tariqbilal closed 10 months ago

tariqbilal commented 10 months ago

The OrdersUpdatedJob and OrdersCreatedJob are not functioning as expected, while other jobs like ProductCreateJob and ProductUpdateJob are working fine. The jobs were created using the following commands:

OrdersUpdatedJob: php artisan shopify-app:make:webhook OrdersUpdateJob orders/updated OrdersCreatedJob: php artisan shopify-app:make:webhook OrdersCreateJob orders/create These jobs have been successfully created in a Laravel project with the specified versions and setup. However, the events associated with OrdersUpdatedJob and OrdersCreatedJob are not triggering. There are no logs generated, and the webhook is not firing.

Setup Information:

Kyon147 commented 10 months ago

@tariqbilal do you have the correct scopes for the webhooks? You will need to be able to read_orders to be able to receive the webhooks.

Can you also share your config file with the webhook array so I can see the format of it.

tariqbilal commented 10 months ago

@Kyon147 yes, I have added all the scopes. I am sharing the config file

<?php

return [
    /*
    |--------------------------------------------------------------------------
    | Debug Mode
    |--------------------------------------------------------------------------
    |
    | (Not yet complete) A verbose logged output of processes.
    |
    */

    'debug' => (bool) env('SHOPIFY_DEBUG', false),

    /*
    |--------------------------------------------------------------------------
    | Manual migrations
    |--------------------------------------------------------------------------
    |
    | This option allows you to use:
    | `php artisan vendor:publish --tag=shopify-migrations` to push migrations
    | to your app's folder so you're free to modify before migrating.
    |
    */

    'manual_migrations' => (bool) env('SHOPIFY_MANUAL_MIGRATIONS', false),

    /*
    |--------------------------------------------------------------------------
    | Manual routes
    |--------------------------------------------------------------------------
    |
    | This option allows you to ignore the package's built-in routes.
    | Use `false` (default) for allowing the built-in routes. Otherwise, you
    | can list out which route "names" you would like excluded.
    | See `resources/routes/shopify.php` and `resources/routes/api.php`
    | for a list of available route names.
    | Example: `home,billing` would ignore both "home" and "billing" routes.
    |
    | Please note that if you override the route names
    | (see "route_names" below), the route names that are used in this
    | option DO NOT change!
    |
    */

    'manual_routes' => env('SHOPIFY_MANUAL_ROUTES', false),

    /*
    |--------------------------------------------------------------------------
    | Route names
    |--------------------------------------------------------------------------
    |
    | This option allows you to override the package's built-in route names.
    | This can help you avoid collisions with your existing route names.
    |
    */

    'route_names' => [
        'home' => env('SHOPIFY_ROUTE_NAME_HOME', 'home'),
        'authenticate' => env('SHOPIFY_ROUTE_NAME_AUTHENTICATE', 'authenticate'),
        'authenticate.token' => env('SHOPIFY_ROUTE_NAME_AUTHENTICATE_TOKEN', 'authenticate.token'),
        'billing' => env('SHOPIFY_ROUTE_NAME_BILLING', 'billing'),
        'billing.process' => env('SHOPIFY_ROUTE_NAME_BILLING_PROCESS', 'billing.process'),
        'billing.usage_charge' => env('SHOPIFY_ROUTE_NAME_BILLING_USAGE_CHARGE', 'billing.usage_charge'),
        'webhook' => env('SHOPIFY_ROUTE_NAME_WEBHOOK', 'webhook'),
    ],

    /*
    |--------------------------------------------------------------------------
    | Shop auth guard
    |--------------------------------------------------------------------------
    |
    | This option allows you to override auth guard used by package middlewares
    |
    */
    'shop_auth_guard' => env('SHOPIFY_SHOP_AUTH_GUARD', null),

    /*
    |--------------------------------------------------------------------------
    | Shop auth provider
    |--------------------------------------------------------------------------
    |
    | This option allows you to override package's build-in auth model
    | If you need to keep User model intact, add custom auth provider and route middlewares for it
    |
    */
    'shop_auth_provider' => env('SHOPIFY_SHOP_AUTH_PROVIDER', 'users'),

    /*
    |--------------------------------------------------------------------------
    | App Namespace
    |--------------------------------------------------------------------------
    |
    | This option allows you to set a namespace for the users in the DB.
    | Useful for running multiple apps using the same database instance.
    | Meaning, one shop can be part of many apps on the same database.
    |
    */

    'namespace' => env('SHOPIFY_APP_NAMESPACE', null),

    /*
    |--------------------------------------------------------------------------
    | Shopify Jobs Namespace
    |--------------------------------------------------------------------------
    |
    | This option allows you to change out the default job namespace
    | which is \App\Jobs. This option is mainly used if any custom
    | configuration is done in autoload and does not need to be changed
    | unless required.
    |
    */

    'job_namespace' => env('SHOPIFY_JOB_NAMESPACE', '\\App\\Jobs\\'),

    /*
    |--------------------------------------------------------------------------
    | Prefix
    |--------------------------------------------------------------------------
    |
    | This option allows you to set a prefix for URLs.
    | Useful for multiple apps using the same database instance.
    |
    */

    'prefix' => env('SHOPIFY_APP_PREFIX', ''),

    /*
    |--------------------------------------------------------------------------
    | AppBridge Mode
    |--------------------------------------------------------------------------
    |
    | AppBridge (embedded apps) are enabled by default. Set to false to use legacy
    | mode and host the app inside your own container.
    |
    */

    'appbridge_enabled' => (bool) env('SHOPIFY_APPBRIDGE_ENABLED', true),

    // Use semver range to link to a major or minor version number.
    // Leaving empty will use the latest version - not recommended in production.
    'appbridge_version' => env('SHOPIFY_APPBRIDGE_VERSION', 'latest'),

    // Set a new CDN URL if you want to host the AppBridge JS yourself or unpkg goes down.
    // DO NOT include a trailing slash.
    'appbridge_cdn_url' => env('SHOPIFY_APPBRIDGE_CDN_URL', 'https://unpkg.com'),

    /*
    |--------------------------------------------------------------------------
    | Shopify App Name
    |--------------------------------------------------------------------------
    |
    | This option simply lets you display your app's name.
    |
    */

    'app_name' => env('SHOPIFY_APP_NAME', 'Shopify App'),

    /*
    |--------------------------------------------------------------------------
    | Shopify API Version
    |--------------------------------------------------------------------------
    |
    | This option is for the app's API version string.
    | Use "YYYY-MM" or "unstable". Refer to Shopify documentation
    | at https://shopify.dev/api/usage/versioning#release-schedule
    | for the current stable version.
    |
    */

    'api_version' => env('SHOPIFY_API_VERSION', '2023-10'),

    /*
    |--------------------------------------------------------------------------
    | Shopify API Key
    |--------------------------------------------------------------------------
    |
    | This option is for the app's API key.
    |
    */

    'api_key' => env('SHOPIFY_API_KEY', '****************************'),

    /*
    |--------------------------------------------------------------------------
    | Shopify API Secret
    |--------------------------------------------------------------------------
    |
    | This option is for the app's API secret.
    |
    */

    'api_secret' => env('SHOPIFY_API_SECRET', '*******************************'),

    /*
    |--------------------------------------------------------------------------
    | Shopify API Scopes
    |--------------------------------------------------------------------------
    |
    | This option is for the scopes your application needs in the API.
    |
    */

    'api_scopes' => env('SHOPIFY_API_SCOPES', 'read_products,write_products,read_orders,write_orders'),

    /*
    |--------------------------------------------------------------------------
    | Shopify API Grant Mode
    |--------------------------------------------------------------------------
    |
    | This option is for the grant mode when authenticating.
    | Default is "OFFLINE", "PERUSER" is available as well.
    | Note: Install will always be in offline mode.
    |
    */

    'api_grant_mode' => env('SHOPIFY_API_GRANT_MODE', 'OFFLINE'),

    /*
    |--------------------------------------------------------------------------
    | Shopify API Redirect
    |--------------------------------------------------------------------------
    |
    | This option is for the redirect after authentication.
    |
    */

    'api_redirect' => env('SHOPIFY_API_REDIRECT', '/authenticate'),

    /*
    |--------------------------------------------------------------------------
    | Shopify API Time Store
    |--------------------------------------------------------------------------
    |
    | This option is for the class which will hold the timestamps for
    | API calls.
    |
    */

    'api_time_store' => env('SHOPIFY_API_TIME_STORE', \Gnikyt\BasicShopifyAPI\Store\Memory::class),

    /*
    |--------------------------------------------------------------------------
    | Shopify API Limit Store
    |--------------------------------------------------------------------------
    |
    | This option is for the class which will hold the call limits for REST
    | and GraphQL.
    |
    */

    'api_limit_store' => env('SHOPIFY_API_LIMIT_STORE', \Gnikyt\BasicShopifyAPI\Store\Memory::class),

    /*
    |--------------------------------------------------------------------------
    | Shopify API Deferrer
    |--------------------------------------------------------------------------
    |
    | This option is for the class which will handle sleep deferrals for
    | API calls.
    |
    */

    'api_deferrer' => env('SHOPIFY_API_DEFERRER', \Gnikyt\BasicShopifyAPI\Deferrers\Sleep::class),

    /*
    |--------------------------------------------------------------------------
    | Shopify API Init Function
    |--------------------------------------------------------------------------
    |
    | This option is for initializing the BasicShopifyAPI package yourself.
    | The first param injected in is the current options.
    |    (\Gnikyt\BasicShopifyAPI\Options)
    | The second param injected in is the session (if available) .
    |    (\Gnikyt\BasicShopifyAPI\Session)
    | The third param injected in is the current request input/query array.
        (\Illuminate\Http\Request::all())
    | With all this, you can customize the options, change params, and more.
    |
    | Value for this option must be a callable (callable, Closure, etc).
    |
    */

    'api_init' => null,

    /*
    |--------------------------------------------------------------------------
    | Shopify "MyShopify" domain
    |--------------------------------------------------------------------------
    |
    | The internal URL used by shops. This will not change but in the future
    | it may.
    |
    */

    'myshopify_domain' => env('SHOPIFY_MYSHOPIFY_DOMAIN', 'myshopify.com'),

    /*
    |--------------------------------------------------------------------------
    | Enable Billing
    |--------------------------------------------------------------------------
    |
    | Enable billing component to the package.
    |
    */

    'billing_enabled' => (bool) env('SHOPIFY_BILLING_ENABLED', false),

    /*
    |--------------------------------------------------------------------------
    | Enable Freemium Mode
    |--------------------------------------------------------------------------
    |
    | Allow a shop use the app in "freemium" mode.
    | Shop will get a `freemium` flag on their record in the table.
    |
    */

    'billing_freemium_enabled' => (bool) env('SHOPIFY_BILLING_FREEMIUM_ENABLED', false),

    /*
    |--------------------------------------------------------------------------
    | Billing Redirect
    |--------------------------------------------------------------------------
    |
    | Required redirection URL for billing when
    | a customer accepts or declines the charge presented.
    |
    */

    'billing_redirect' => env('SHOPIFY_BILLING_REDIRECT', '/billing/process'),

    /*
    |--------------------------------------------------------------------------
    | Shopify Webhooks
    |--------------------------------------------------------------------------
    |
    | This option is for defining webhooks.
    | `topic` is the GraphQL value of the Shopify webhook event.
    | `address` is the endpoint to call.
    |
    | Valid values for `topic` can be found here:
    | https://shopify.dev/api/admin/graphql/reference/events/webhooksubscriptiontopic
    |
    */

    'webhooks' => [
        [
            'topic' => 'APP_UNINSTALLED',
            'address' => env('SHOPIFY_WEBHOOK_1_ADDRESS').'/webhook/app-uninstalled'
        ],
        [
            'topic' => 'PRODUCTS_CREATE',
            'address' => env('SHOPIFY_WEBHOOK_1_ADDRESS').'/webhook/products-create'
        ],
        [
            'topic' => 'PRODUCTS_UPDATE',
            'address' => env('SHOPIFY_WEBHOOK_1_ADDRESS').'/webhook/products-update'
        ],
        [
            'topic' => 'ORDERS_CREATE',
            'address' => env('SHOPIFY_WEBHOOK_1_ADDRESS').'/webhook/orders-create'
        ],
        [
            'topic' => 'ORDERS_UPDATED',
            'address' => env('SHOPIFY_WEBHOOK_1_ADDRESS').'/webhook/orders-updated'
        ],
    /** [
                'topic' => env('SHOPIFY_WEBHOOK_2_TOPIC', 'APP_PURCHASES_ONE_TIME_UPDATE'),
                'address' => env('SHOPIFY_WEBHOOK_2_ADDRESS', 'https://some-app.com/webhook/purchase'),
            ]
            // In certain situations you may wish to map the webhook to a specific class
            // To do this, change the array to an associative array with a 'class' key
            'orders-create' => [
                'topic' => env('SHOPIFY_WEBHOOK_3_TOPIC', 'ORDERS_PAID'),
                'address' => env('SHOPIFY_WEBHOOK_3_ADDRESS', 'https://some-app.com/webhook/orders-create'),
                'class' => \App\Shopify\Actions\ExampleAppJob::class
            ],
        */
    ],

    /*
    |--------------------------------------------------------------------------
    | Shopify ScriptTags
    |--------------------------------------------------------------------------
    |
    | This option is for defining scripttags.
    |
    */

    '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')
            ],
            ...
        */
    ],

    /*
    |--------------------------------------------------------------------------
    | After Authenticate Job
    |--------------------------------------------------------------------------
    |
    | This option is for firing a job after a shop has been authenticated.
    | This, like webhooks and scripttag jobs, will fire every time a shop
    | authenticates, not just once.
    |
    */

    'after_authenticate_job' => [
        /*
            [
                'job' => env('AFTER_AUTHENTICATE_JOB'), // example: \App\Jobs\AfterAuthorizeJob::class
                'inline' => env('AFTER_AUTHENTICATE_JOB_INLINE', false) // False = dispatch job for later, true = dispatch immediately
            ],
        */
    ],

    /*
    |--------------------------------------------------------------------------
    | Job Queues
    |--------------------------------------------------------------------------
    |
    | This option is for setting a specific job queue for webhooks, scripttags
    | and after_authenticate_job.
    |
    */

    'job_queues' => [
        'webhooks' => env('WEBHOOKS_JOB_QUEUE', null),
        'scripttags' => env('SCRIPTTAGS_JOB_QUEUE', null),
        'after_authenticate' => env('AFTER_AUTHENTICATE_JOB_QUEUE', null),
    ],

    /*
    |--------------------------------------------------------------------------
    | Config API Callback
    |--------------------------------------------------------------------------
    |
    | This option can be used to modify what returns when `getConfig('api_*')`
    | is used. A use-case for this is modifying the return of `api_secret`
    | or something similar.
    |
    | A closure/callable is required.
    | The first argument will be the key string.
    | The second argument will be something to help identify the shop.
    |
    */

    'config_api_callback' => null,

    /*
    |--------------------------------------------------------------------------
    | Enable Turbolinks or Hotwire Turbo
    |--------------------------------------------------------------------------
    |
    | If you use Turbolinks/Turbo and Livewire, turn on this setting to get
    | the token assigned automatically.
    |
    */

    'turbo_enabled' => (bool) env('SHOPIFY_TURBO_ENABLED', false),

    /*
    |--------------------------------------------------------------------------
    | Customize Models and Table Name
    |--------------------------------------------------------------------------
    |
    | You can customize you model and extend them
    | also you can customize tables name for charge and plan models.
    |
    */

    'models' => [
        /*
        * The fully qualified class name of the Charge model.
        */
        'charge' => Osiset\ShopifyApp\Storage\Models\Charge::class,

        /*
        * The fully qualified class name of the Plan model.
        */
        'plan' => Osiset\ShopifyApp\Storage\Models\Plan::class,
    ],

    'table_names' => [
        /*
        * The table name for Charge model.
        */
        'charges' => 'charges',

        /*
        * The table name for Plan model.
        */
        'plans' => 'plans',

        /*
         * The table name for the Shop.
         */
        'shops' => 'users',
    ],

    /*
    |--------------------------------------------------------------------------
    | Checking theme compatibility
    |--------------------------------------------------------------------------
    |
    | It is necessary to check if your application is compatible with
    | the theme app blocks.
    |
    */

    'theme_support' => [
        /**
         * Specify the name of the template the app will integrate with
         */
        'templates' => ['product', 'collection', 'index'],
        /**
         * Interval for caching the request: minutes, seconds, hours, days, etc.
         */
        'cache_interval' => 'hours',
        /**
         * Cache duration
         */
        'cache_duration' => '12',
         /**
         * At which levels of theme support the use of "theme app extension" is not available
         * and script tags will be installed.
         * Available levels: FULL, PARTIAL, UNSUPPORTED.
         */
        'unacceptable_levels' => [
            Osiset\ShopifyApp\Objects\Enums\ThemeSupportLevel::UNSUPPORTED
        ]
    ],

    /*
    |--------------------------------------------------------------------------
    | Session token refresh
    |--------------------------------------------------------------------------
    |
    | For AppBridge, how often to refresh the session token for the user.
    |
    */

    'session_token_refresh_interval' => env('SESSION_TOKEN_REFRESH_INTERVAL', 2000),

    /*
    |--------------------------------------------------------------------------
    | Frontend engine used
    |--------------------------------------------------------------------------
    |
    | Available engines: "BLADE", "VUE", or "REACT".
    | For example, if you use React, you do not need to be redirected to a separate page to get the JWT token.
    | No changes are made for Vue.js and Blade.
    |
    */
    'frontend_engine' => env('SHOPIFY_FRONTEND_ENGINE', 'BLADE'),
];
tariqbilal commented 10 months ago

@Kyon147 I have also flushed my queues and restarted it. Lastly I have not set any variables in .env just the SHOPIFY_WEBHOOK_1_ADDRESS. So all other env are set in shopify-app.php

Kyon147 commented 10 months ago

What have you set the SHOPIFY_WEBHOOK_1_ADDRESS to? The config looks fine to me, so something else on your set up is likely causing the webhooks to not come through.

We do only install webhooks on authenticate I believe from memory. So you might not to go back through the /authenticate route again to install any new ones.

tariqbilal commented 10 months ago

@Kyon147

SHOPIFY_WEBHOOK_1_ADDRESS=https://20b4-2407-aa80-116-93e8-e0ad-a5a4-5ac4-b3d9.ngrok-free.app This is an ngrok address as I am working locally right now, funny thing is all other webhooks are working I have successfully updated Products and created them. ![Uploading Screenshot 2023-11-13 at 1.42.56 PM.png…]()

tariqbilal commented 10 months ago

root@5fcd19010dcd:/var/www# php artisan queue:work

INFO Processing jobs from the [default] queue.

2023-11-12 19:32:01 App\Jobs\ProductsUpdateJob ................................................................................................................................................................................... RUNNING 2023-11-12 19:32:01 App\Jobs\ProductsUpdateJob .............................................................................................................................................................................. 26.32ms DONE 2023-11-12 19:32:28 App\Jobs\ProductsUpdateJob ................................................................................................................................................................................... RUNNING 2023-11-12 19:32:28 App\Jobs\ProductsUpdateJob .............................................................................................................................................................................. 13.53ms DONE 2023-11-12 19:34:10 App\Jobs\AppUninstalledJob ................................................................................................................................................................................... RUNNING 2023-11-12 19:34:11 App\Jobs\AppUninstalledJob ............................................................................................................................................................................. 988.96ms DONE 2023-11-12 19:36:05 Osiset\ShopifyApp\Messaging\Jobs\WebhookInstaller ............................................................................................................................................................ RUNNING 2023-11-12 19:36:09 Osiset\ShopifyApp\Messaging\Jobs\WebhookInstaller ............................................................................................................................................................ 3s DONE 2023-11-12 20:08:43 App\Jobs\AppUninstalledJob ................................................................................................................................................................................... RUNNING 2023-11-12 20:08:44 App\Jobs\AppUninstalledJob ............................................................................................................................................................................. 966.69ms DONE 2023-11-12 20:09:11 Osiset\ShopifyApp\Messaging\Jobs\WebhookInstaller ............................................................................................................................................................ RUNNING 2023-11-12 20:09:15 Osiset\ShopifyApp\Messaging\Jobs\WebhookInstaller ............................................................................................................................................................ 3s DONE 2023-11-12 20:41:28 App\Jobs\AppUninstalledJob ................................................................................................................................................................................... RUNNING 2023-11-12 20:41:29 App\Jobs\AppUninstalledJob ............................................................................................................................................................................. 688.94ms DONE 2023-11-12 20:45:05 Osiset\ShopifyApp\Messaging\Jobs\WebhookInstaller ............................................................................................................................................................ RUNNING 2023-11-12 20:45:08 Osiset\ShopifyApp\Messaging\Jobs\WebhookInstaller ............................................................................................................................................................ 2s DONE 2023-11-13 07:02:34 App\Jobs\AppUninstalledJob ................................................................................................................................................................................... RUNNING 2023-11-13 07:02:34 App\Jobs\AppUninstalledJob ............................................................................................................................................................................. 826.78ms DONE

Kyon147 commented 10 months ago

@tariqbilal I can see you are using the free ngrok. Does this URL changes each time you run the ngrok cli?

tariqbilal commented 10 months ago

@Kyon147 yes correct, the URL changes everytime I turn it on for development. My normal practice is I turn on NGROK. Than I change the required URLS in app setup. That's how my jobs work locally but am I missing something? Like I say most of the jobs do work only orders jobs are having issues.

tariqbilal commented 10 months ago

@Kyon147 I have also deployed on server again same issue, I fired the orderUpdated queue but it's still not fired

root@wordpress-ubuntu-s-1vcpu-1gb-ams3-01:/var/www/orders_analytics# php artisan queue:work

INFO Processing jobs from the [default] queue.

2023-11-13 17:40:36 Osiset\ShopifyApp\Messaging\Jobs\WebhookInstaller ............................................................................................................................................................ RUNNING 2023-11-13 17:40:37 Osiset\ShopifyApp\Messaging\Jobs\WebhookInstaller ............................................................................................................................................................ 1s DONE 2023-11-13 17:42:01 App\Jobs\ProductsUpdateJob ................................................................................................................................................................................... RUNNING 2023-11-13 17:42:01 App\Jobs\ProductsUpdateJob .............................................................................................................................................................................. 21.34ms DONE

tariqbilal commented 10 months ago

@Kyon147 other people are also complaining so there is an issue to this.

https://github.com/Kyon147/laravel-shopify/discussions/199

Kyon147 commented 10 months ago

@tariqbilal webhooks can be easily dropped off due to set up errors or incorrect set up on local.

If you are using ngrok free and the url keeps changing, you probably have multiple webhooks registered under older urls which could also be a problem.

Have you tried receiving webhooks on a proper production-like environment? Local development for webhooks can be unreliable.

I've not seen any issues with webhooks recently with the package and v19 added in a new scopes middleware that makes sure to check if api_scopes have changed as the package did not auto push to /authenticate if they have changed since the initial install.

I'd check to make sure that your app has this middleware on the app routes and see if the app have the correct and most up to date permissions.

tariqbilal commented 10 months ago

@Kyon147 thank you so much for the response. I have also tested on server.

My domain was static this time

https://analytics.craftconcept.co

But again other jobs work but orders jobs are not working. I am curious about this issue as I have shown you my configuration too. Any help would be great as for now I have to run cronjobs to get the orders data but webhooks need to work as it will be much better solution for my app.

I would love to provide more information if we can diagnose this.

Kyon147 commented 10 months ago

Have you tried outputting what webhooks have been set up in your app?

Can you check using the webhook api endpoint and check to see if indeed you have a webhook registered?

matthewhaworth commented 10 months ago

You likely need to go to API access against your app and allow it to access customer protected data, otherwise you'll be getting this error silently: "This app is not approved to subscribe to webhook topics containing protected customer data.".

This module does not handle errors that are returned from GraphQL for failed webhooks subscriptions currently.

Kyon147 commented 10 months ago

@matthewhaworth that is great point - we should handle this better if we can. I'll take a look to see if we should throw an exception

tariqbilal commented 10 months ago

@Kyon147 @matthewhaworth let me try accessing it but as I am still in development mode plus I have filled the form to get protected customer data.

That is why I can still hit the order api and fetch the data from

/admin/api/2023-10/orders.json

Problem is webhook isn't firing even after registering the app.

If I am not wrong api won't return if the scope wasn't added already.

matthewhaworth commented 10 months ago

@tariqbilal, if you're able to hit the API, try hitting admin/api/2023-10/webhooks.json and seeing if the webhook was successfully created in the first place. Though I agree, I imagine the scope is also necessary.

As a quick 'hack', if you find the webhook isn't registered, you can put dd($response['body']) in vendor/kyon147/laravel-shopify/src/Services/ApiHelper.php around line 404 and observe the queue for the error message. Though we need to get a PR in to Log this properly of course.

tariqbilal commented 10 months ago

@matthewhaworth thanks for the response this one is a tricky issue. I have hit the /admin/api/2023-10/webhooks.json.

I am pasting the response. I am getting convinced it's a bug which we have to fix.

"body" => Gnikyt\BasicShopifyAPI\ResponseAccess^ {#1761
    +container: array:1 [
      "webhooks" => array:5 [
        0 => array:10 [
          "id" => 1420517507355
          "address" => "https://analytics.craftconcept.co/webhook/app-uninstalled"
          "topic" => "app/uninstalled"
          "created_at" => "2023-11-13T12:40:36-05:00"
          "updated_at" => "2023-11-13T12:40:36-05:00"
          "format" => "json"
          "fields" => []
          "metafield_namespaces" => []
          "api_version" => "2023-10"
          "private_metafield_namespaces" => []
        ]
        1 => array:10 [
          "id" => 1420517540123
          "address" => "https://analytics.craftconcept.co/webhook/products-create"
          "topic" => "products/create"
          "created_at" => "2023-11-13T12:40:36-05:00"
          "updated_at" => "2023-11-13T12:40:36-05:00"
          "format" => "json"
          "fields" => []
          "metafield_namespaces" => []
          "api_version" => "2023-10"
          "private_metafield_namespaces" => []
        ]
        2 => array:10 [
          "id" => 1420517572891
          "address" => "https://analytics.craftconcept.co/webhook/products-update"
          "topic" => "products/update"
          "created_at" => "2023-11-13T12:40:37-05:00"
          "updated_at" => "2023-11-13T12:40:37-05:00"
          "format" => "json"
          "fields" => []
          "metafield_namespaces" => []
          "api_version" => "2023-10"
          "private_metafield_namespaces" => []
        ]
        3 => array:10 [
          "id" => 1420517605659
          "address" => "https://analytics.craftconcept.co/webhook/orders-create"
          "topic" => "orders/create"
          "created_at" => "2023-11-13T12:40:37-05:00"
          "updated_at" => "2023-11-13T12:40:37-05:00"
          "format" => "json"
          "fields" => []
          "metafield_namespaces" => []
          "api_version" => "2023-10"
          "private_metafield_namespaces" => []
        ]
        4 => array:10 [
          "id" => 1420517638427
          "address" => "https://analytics.craftconcept.co/webhook/orders-updated"
          "topic" => "orders/updated"
          "created_at" => "2023-11-13T12:40:37-05:00"
          "updated_at" => "2023-11-13T12:40:37-05:00"
          "format" => "json"
          "fields" => []
          "metafield_namespaces" => []
          "api_version" => "2023-10"
          "private_metafield_namespaces" => []
        ]
      ]
    ]
    +position: 0
  }
Kyon147 commented 10 months ago

@tariqbilal if the webhook has been registered which is looks like it is, then the package is sending the request to create the webhook as intended.

What have you called your Job which is meant to handle this webhook?

From your config it should be called OrdersCreateJob is that right? In your first comment you mention it as OrdersCreatedJob which means the job and the webhook would not line up.

This might be your issue as mentioned in the wiki.

Kyon147 commented 10 months ago

@tariqbilal did this fix your issue?

tariqbilal commented 10 months ago

@Kyon147 no the issue has not been resolved and in my first comment I misspelled the job but the job is called OrdersCreateJob

Kyon147 commented 10 months ago

@tariqbilal i've updated one of my apps which I know order create / update works to the latest version of this package.

I can still get order webhook events coming through for an installed store and a new store on the app. So I feel at the moment this might be an issue with your set up rather than a package one until I get more information as I can't replicate it.

tariqbilal commented 10 months ago

@Kyon147 this is strange. I have followed all the steps provided in the documentation. Should I install a new app and try again?

What information do you think I can provide more so we can reach to the bottom of this. I will provide all the details.

Kyon147 commented 10 months ago

@tariqbilal have you tried a new shop install on the app - then seeing if that store can trigger the webhooks?

tariqbilal commented 10 months ago

@Kyon147 I can try that and see if things work. Will update you on this.

sumantaakeans2020 commented 10 months ago

Its not working, not sure what it is missing, app uninstall webhook is firing, but after orders create webhook not firing, I am trying to get the webhook once user placed the order in shopify and needs to get the order id in webhook with order details, Any other way i can try ?? Any suggestion

tariqbilal commented 10 months ago

@Kyon147 I have tested on multiple stores and I can confirm, the orders jobs are working perfectly fine, I believe it would be a reason for webhooks being attached to ngrok. I am testing on server and it works great. Do you think should I use ngrok for local development? @Kyon147 thank you so much :)

@sumantaakeans2020 please try to reinstall again on the store jobs do work and try to copy my configurations which is in this thread.

Kyon147 commented 10 months ago

@tariqbilal ngrok is fine to use most of the time, but as you are still pushing them to local sometimes there can be issues which you've seen.

I use ngrok (paid version) now mainly for app development locally, as it is much more consistent and you can have a single static url which makes testing webhooks a lot easier.

Glad you managed to get it sorted though 👍

sumantaakeans2020 commented 10 months ago

@tariqbilal my package version is 19.1, reinstall means i means i needs to reinstall the package again or create the order job again like running this php artisan shopify-app:make:webhook OrdersCreateJob orders/create and about the config i verified it same only as per your one. now i am running laravel code in server. So please suggest what to do? Thank you

Kyon147 commented 10 months ago

@sumantaakeans2020 I think @tariqbilal means reinstall the app on your store that you are testing with.

sumantaakeans2020 commented 10 months ago

@Kyon147 Yes tried same already but not luck, i dont know now how i will debug try to fix this or any other way i can get the webhook respose??

tariqbilal commented 10 months ago

@sumantaakeans2020 please share your configurations. check if the webhooks are connected already or not.

sumantaakeans2020 commented 10 months ago

Here is my Configuration `<?php

return [ /* -------------------------------------------------------------------------- Debug Mode
(Not yet complete) A verbose logged output of processes.
*/

'debug' => (bool) env('SHOPIFY_DEBUG', false),

/*
|--------------------------------------------------------------------------
| Manual migrations
|--------------------------------------------------------------------------
|
| This option allows you to use:
| `php artisan vendor:publish --tag=shopify-migrations` to push migrations
| to your app's folder so you're free to modify before migrating.
|
*/

'manual_migrations' => (bool) env('SHOPIFY_MANUAL_MIGRATIONS', false),

/*
|--------------------------------------------------------------------------
| Manual routes
|--------------------------------------------------------------------------
|
| This option allows you to ignore the package's built-in routes.
| Use `false` (default) for allowing the built-in routes. Otherwise, you
| can list out which route "names" you would like excluded.
| See `resources/routes/shopify.php` and `resources/routes/api.php`
| for a list of available route names.
| Example: `home,billing` would ignore both "home" and "billing" routes.
|
| Please note that if you override the route names
| (see "route_names" below), the route names that are used in this
| option DO NOT change!
|
*/

'manual_routes' => env('SHOPIFY_MANUAL_ROUTES', false),

/*
|--------------------------------------------------------------------------
| Route names
|--------------------------------------------------------------------------
|
| This option allows you to override the package's built-in route names.
| This can help you avoid collisions with your existing route names.
|
*/

'route_names' => [
    'home' => env('SHOPIFY_ROUTE_NAME_HOME', 'home'),
    'authenticate' => env('SHOPIFY_ROUTE_NAME_AUTHENTICATE', 'authenticate'),
    'authenticate.token' => env('SHOPIFY_ROUTE_NAME_AUTHENTICATE_TOKEN', 'authenticate.token'),
    'billing' => env('SHOPIFY_ROUTE_NAME_BILLING', 'billing'),
    'billing.process' => env('SHOPIFY_ROUTE_NAME_BILLING_PROCESS', 'billing.process'),
    'billing.usage_charge' => env('SHOPIFY_ROUTE_NAME_BILLING_USAGE_CHARGE', 'billing.usage_charge'),
    'webhook' => env('SHOPIFY_ROUTE_NAME_WEBHOOK', 'webhook'),
],

/*
|--------------------------------------------------------------------------
| Shop auth guard
|--------------------------------------------------------------------------
|
| This option allows you to override auth guard used by package middlewares
|
*/
'shop_auth_guard' => env('SHOPIFY_SHOP_AUTH_GUARD', null),

/*
|--------------------------------------------------------------------------
| Shop auth provider
|--------------------------------------------------------------------------
|
| This option allows you to override package's build-in auth model
| If you need to keep User model intact, add custom auth provider and route middlewares for it
|
*/
'shop_auth_provider' => env('SHOPIFY_SHOP_AUTH_PROVIDER', 'users'),

/*
|--------------------------------------------------------------------------
| App Namespace
|--------------------------------------------------------------------------
|
| This option allows you to set a namespace for the users in the DB.
| Useful for running multiple apps using the same database instance.
| Meaning, one shop can be part of many apps on the same database.
|
*/

'namespace' => env('SHOPIFY_APP_NAMESPACE', null),

/*
|--------------------------------------------------------------------------
| Shopify Jobs Namespace
|--------------------------------------------------------------------------
|
| This option allows you to change out the default job namespace
| which is \App\Jobs. This option is mainly used if any custom
| configuration is done in autoload and does not need to be changed
| unless required.
|
*/

'job_namespace' => env('SHOPIFY_JOB_NAMESPACE', '\\App\\Jobs\\'),

/*
|--------------------------------------------------------------------------
| Prefix
|--------------------------------------------------------------------------
|
| This option allows you to set a prefix for URLs.
| Useful for multiple apps using the same database instance.
|
*/

'prefix' => env('SHOPIFY_APP_PREFIX', ''),

/*
|--------------------------------------------------------------------------
| AppBridge Mode
|--------------------------------------------------------------------------
|
| AppBridge (embedded apps) are enabled by default. Set to false to use legacy
| mode and host the app inside your own container.
|
*/

'appbridge_enabled' => (bool) env('SHOPIFY_APPBRIDGE_ENABLED', true),

// Use semver range to link to a major or minor version number.
// Leaving empty will use the latest version - not recommended in production.
'appbridge_version' => env('SHOPIFY_APPBRIDGE_VERSION', 'latest'),

// Set a new CDN URL if you want to host the AppBridge JS yourself or unpkg goes down.
// DO NOT include a trailing slash.
'appbridge_cdn_url' => env('SHOPIFY_APPBRIDGE_CDN_URL', 'https://unpkg.com'),

/*
|--------------------------------------------------------------------------
| Shopify App Name
|--------------------------------------------------------------------------
|
| This option simply lets you display your app's name.
|
*/

'app_name' => env('SHOPIFY_APP_NAME', 'Shopify App'),

/*
|--------------------------------------------------------------------------
| Shopify API Version
|--------------------------------------------------------------------------
|
| This option is for the app's API version string.
| Use "YYYY-MM" or "unstable". Refer to Shopify documentation
| at https://shopify.dev/api/usage/versioning#release-schedule
| for the current stable version.
|
*/

'api_version' => env('SHOPIFY_API_VERSION', '2023-10'),

/*
|--------------------------------------------------------------------------
| Shopify API Key
|--------------------------------------------------------------------------
|
| This option is for the app's API key.
|
*/

'api_key' => env('SHOPIFY_API_KEY', ''),

/*
|--------------------------------------------------------------------------
| Shopify API Secret
|--------------------------------------------------------------------------
|
| This option is for the app's API secret.
|
*/

'api_secret' => env('SHOPIFY_API_SECRET', ''),

/*
|--------------------------------------------------------------------------
| Shopify API Scopes
|--------------------------------------------------------------------------
|
| This option is for the scopes your application needs in the API.
|
*/

'api_scopes' => env('SHOPIFY_API_SCOPES', 'read_products,write_products,read_product_listings,read_script_tags,write_script_tags,read_content,write_content,read_themes,write_themes,read_orders,write_orders'),

/*
|--------------------------------------------------------------------------
| Shopify API Grant Mode
|--------------------------------------------------------------------------
|
| This option is for the grant mode when authenticating.
| Default is "OFFLINE", "PERUSER" is available as well.
| Note: Install will always be in offline mode.
|
*/

'api_grant_mode' => env('SHOPIFY_API_GRANT_MODE', 'OFFLINE'),

/*
|--------------------------------------------------------------------------
| Shopify API Redirect
|--------------------------------------------------------------------------
|
| This option is for the redirect after authentication.
|
*/

'api_redirect' => env('SHOPIFY_API_REDIRECT', '/authenticate'),

/*
|--------------------------------------------------------------------------
| Shopify API Time Store
|--------------------------------------------------------------------------
|
| This option is for the class which will hold the timestamps for
| API calls.
|
*/

'api_time_store' => env('SHOPIFY_API_TIME_STORE', \Gnikyt\BasicShopifyAPI\Store\Memory::class),

/*
|--------------------------------------------------------------------------
| Shopify API Limit Store
|--------------------------------------------------------------------------
|
| This option is for the class which will hold the call limits for REST
| and GraphQL.
|
*/

'api_limit_store' => env('SHOPIFY_API_LIMIT_STORE', \Gnikyt\BasicShopifyAPI\Store\Memory::class),

/*
|--------------------------------------------------------------------------
| Shopify API Deferrer
|--------------------------------------------------------------------------
|
| This option is for the class which will handle sleep deferrals for
| API calls.
|
*/

'api_deferrer' => env('SHOPIFY_API_DEFERRER', \Gnikyt\BasicShopifyAPI\Deferrers\Sleep::class),

/*
|--------------------------------------------------------------------------
| Shopify API Init Function
|--------------------------------------------------------------------------
|
| This option is for initializing the BasicShopifyAPI package yourself.
| The first param injected in is the current options.
|    (\Gnikyt\BasicShopifyAPI\Options)
| The second param injected in is the session (if available) .
|    (\Gnikyt\BasicShopifyAPI\Session)
| The third param injected in is the current request input/query array.
    (\Illuminate\Http\Request::all())
| With all this, you can customize the options, change params, and more.
|
| Value for this option must be a callable (callable, Closure, etc).
|
*/

'api_init' => null,

/*
|--------------------------------------------------------------------------
| Shopify "MyShopify" domain
|--------------------------------------------------------------------------
|
| The internal URL used by shops. This will not change but in the future
| it may.
|
*/

'myshopify_domain' => env('SHOPIFY_MYSHOPIFY_DOMAIN', 'myshopify.com'),

/*
|--------------------------------------------------------------------------
| Enable Billing
|--------------------------------------------------------------------------
|
| Enable billing component to the package.
|
*/

'billing_enabled' => (bool) env('SHOPIFY_BILLING_ENABLED', false),

/*
|--------------------------------------------------------------------------
| Enable Freemium Mode
|--------------------------------------------------------------------------
|
| Allow a shop use the app in "freemium" mode.
| Shop will get a `freemium` flag on their record in the table.
|
*/

'billing_freemium_enabled' => (bool) env('SHOPIFY_BILLING_FREEMIUM_ENABLED', false),

/*
|--------------------------------------------------------------------------
| Billing Redirect
|--------------------------------------------------------------------------
|
| Required redirection URL for billing when
| a customer accepts or declines the charge presented.
|
*/

'billing_redirect' => env('SHOPIFY_BILLING_REDIRECT', '/billing/process'),

/*
|--------------------------------------------------------------------------
| Shopify Webhooks
|--------------------------------------------------------------------------
|
| This option is for defining webhooks.
| `topic` is the GraphQL value of the Shopify webhook event.
| `address` is the endpoint to call.
|
| Valid values for `topic` can be found here:
| https://shopify.dev/api/admin/graphql/reference/events/webhooksubscriptiontopic
|
*/

'webhooks' => [
    /*
        [
            'topic' => env('SHOPIFY_WEBHOOK_1_TOPIC', 'ORDERS_CREATE'),
            'address' => env('SHOPIFY_WEBHOOK_1_ADDRESS', 'https://some-app.com/webhook/orders-create')
        ], [
            'topic' => env('SHOPIFY_WEBHOOK_2_TOPIC', 'APP_PURCHASES_ONE_TIME_UPDATE'),
            'address' => env('SHOPIFY_WEBHOOK_2_ADDRESS', 'https://some-app.com/webhook/purchase'),
        ]
        // In certain situations you may wish to map the webhook to a specific class
        // To do this, change the array to an associative array with a 'class' key
        'orders-create' => [
            'topic' => env('SHOPIFY_WEBHOOK_3_TOPIC', 'ORDERS_PAID'),
            'address' => env('SHOPIFY_WEBHOOK_3_ADDRESS', 'https://some-app.com/webhook/orders-create'),
            'class' => \App\Shopify\Actions\ExampleAppJob::class
        ],
    */
    [
        'topic' => env('SHOPIFY_WEBHOOK_1_TOPIC', 'app/uninstalled'),
        'address' => env('SHOPIFY_WEBHOOK_1_ADDRESS', env('APP_URL').'/webhook/app-uninstalled')
    ],
    [
        'topic' => 'ORDERS_CREATE',
        'address' => env('SHOPIFY_WEBHOOK_1_ADDRESS', env('APP_URL').'/webhook/orders-create')
    ]

],

/*
|--------------------------------------------------------------------------
| Shopify ScriptTags
|--------------------------------------------------------------------------
|
| This option is for defining scripttags.
|
*/

'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')
        ],
        ...
    */
],

/*
|--------------------------------------------------------------------------
| After Authenticate Job
|--------------------------------------------------------------------------
|
| This option is for firing a job after a shop has been authenticated.
| This, like webhooks and scripttag jobs, will fire every time a shop
| authenticates, not just once.
|
*/

'after_authenticate_job' => [
    /*
        [
            'job' => env('AFTER_AUTHENTICATE_JOB'), // example: \App\Jobs\AfterAuthorizeJob::class
            'inline' => env('AFTER_AUTHENTICATE_JOB_INLINE', false) // False = dispatch job for later, true = dispatch immediately
        ],
    */
],

/*
|--------------------------------------------------------------------------
| Job Queues
|--------------------------------------------------------------------------
|
| This option is for setting a specific job queue for webhooks, scripttags
| and after_authenticate_job.
|
*/

'job_queues' => [
    'webhooks' => env('WEBHOOKS_JOB_QUEUE', null),
    'scripttags' => env('SCRIPTTAGS_JOB_QUEUE', null),
    'after_authenticate' => env('AFTER_AUTHENTICATE_JOB_QUEUE', null),
],

/*
|--------------------------------------------------------------------------
| Config API Callback
|--------------------------------------------------------------------------
|
| This option can be used to modify what returns when `getConfig('api_*')`
| is used. A use-case for this is modifying the return of `api_secret`
| or something similar.
|
| A closure/callable is required.
| The first argument will be the key string.
| The second argument will be something to help identify the shop.
|
*/

'config_api_callback' => null,

/*
|--------------------------------------------------------------------------
| Enable Turbolinks or Hotwire Turbo
|--------------------------------------------------------------------------
|
| If you use Turbolinks/Turbo and Livewire, turn on this setting to get
| the token assigned automatically.
|
*/

'turbo_enabled' => (bool) env('SHOPIFY_TURBO_ENABLED', false),

/*
|--------------------------------------------------------------------------
| Customize Models and Table Name
|--------------------------------------------------------------------------
|
| You can customize you model and extend them
| also you can customize tables name for charge and plan models.
|
*/

'models' => [
    /*
    * The fully qualified class name of the Charge model.
    */
    'charge' => Osiset\ShopifyApp\Storage\Models\Charge::class,

    /*
    * The fully qualified class name of the Plan model.
    */
    'plan' => Osiset\ShopifyApp\Storage\Models\Plan::class,
],

'table_names' => [
    /*
    * The table name for Charge model.
    */
    'charges' => 'charges',

    /*
    * The table name for Plan model.
    */
    'plans' => 'plans',

    /*
     * The table name for the Shop.
     */
    'shops' => 'users',
],

/*
|--------------------------------------------------------------------------
| Checking theme compatibility
|--------------------------------------------------------------------------
|
| It is necessary to check if your application is compatible with
| the theme app blocks.
|
*/

'theme_support' => [
    /**
     * Specify the name of the template the app will integrate with
     */
    'templates' => ['product', 'collection', 'index'],
    /**
     * Interval for caching the request: minutes, seconds, hours, days, etc.
     */
    'cache_interval' => 'hours',
    /**
     * Cache duration
     */
    'cache_duration' => '12',
     /**
     * At which levels of theme support the use of "theme app extension" is not available
     * and script tags will be installed.
     * Available levels: FULL, PARTIAL, UNSUPPORTED.
     */
    'unacceptable_levels' => [
        Osiset\ShopifyApp\Objects\Enums\ThemeSupportLevel::UNSUPPORTED
    ]
],

/*
|--------------------------------------------------------------------------
| Session token refresh
|--------------------------------------------------------------------------
|
| For AppBridge, how often to refresh the session token for the user.
|
*/

'session_token_refresh_interval' => env('SESSION_TOKEN_REFRESH_INTERVAL', 2000),

/*
|--------------------------------------------------------------------------
| Frontend engine used
|--------------------------------------------------------------------------
|
| Available engines: "BLADE", "VUE", or "REACT".
| For example, if you use React, you do not need to be redirected to a separate page to get the JWT token.
| No changes are made for Vue.js and Blade.
|
*/
'frontend_engine' => env('SHOPIFY_FRONTEND_ENGINE', 'BLADE'),

]; `

sumantaakeans2020 commented 10 months ago

@Kyon147 @tariqbilal i tried to call /admin/api/2023-10/webhooks.json and i saw only one webhook i.e app/uninstalled one, so i how i can register the order webhook?? so i think this is the issue

tariqbilal commented 10 months ago

@sumantaakeans2020 I can clearly see you have added alot of scopes.

Better to start with less scopes registered and also you have registered orders create webhook two times. Why is that so?

sumantaakeans2020 commented 10 months ago

@tariqbilal thanks for reply, i used other scopes because i need for some other purpose, but i used one create orders not which one you are saying as two, i used one for read and other is for write.

sumantaakeans2020 commented 10 months ago

@Kyon147 @tariqbilal hi any idea why webhook is not registering ?

Kyon147 commented 10 months ago

@sumantaakeans2020 webhooks are registered on /authenticate - so you probably need to go through the /authenticate route again.

If they still are not registering, then you probably don't have the right permissions to set them and will need to investigate why.

I plan to add some better error capturing in the webhook installer but I don't have an ETA so you will need to look yourself for the time being.

sumantaakeans2020 commented 10 months ago

@Kyon147 Ok thanks for your response, actually i uninstalled and reinstalled the app again but still its not registering

brunocoelhosilva commented 8 months ago

@Kyon147 Were you able to resolve this situation? I have the same problem with OrdersCreateJob and I don't know how to solve it.

Kyon147 commented 8 months ago

There is no issue the package I could find for the webhooks, they all work as expected on my apps and spinning up a test one.

Check your app permissions and also make sure you have the correct Customer Protected Data permission from Shopify for the fields.

tariqbilal commented 8 months ago

Hi @brunocoelhosilva package works great, most of the time issue is with the permissions or you have to reinstall the app. I have published a few apps after I raised this issue and all of them work great.