Search code examples
pythondjangoshopifyshopify-appshopify-api

Error After Uninstall in Shopify App Using Django


I am taking reference from https://github.com/Shopify/shopify_django_app repository to make a shopify app. The problem is that the app installs properly and works fine but the problem is that if we access App URL after it is uninstalled from shopify store, it returns the following error.

I believe the error is because of the 'shop_login_required' decorator in shopify_app.decorator.py file. This decorator is used in login view in home.views.py file.

Full Error Log:-

Internal Server Error: /
Traceback (most recent call last):
  File "D:\app\venv\lib\site-packages\pyactiveresource\connection.py", line 286, in _open
    http_response = self._handle_error(self._urlopen(request))
  File "D:\app\venv\lib\site-packages\pyactiveresource\connection.py", line 316, in _urlopen
    return urllib.request.urlopen(request, timeout=self.timeout)
  File "C:\Users\royal\miniconda3\lib\urllib\request.py", line 216, in urlopen
    return opener.open(url, data, timeout)
  File "C:\Users\royal\miniconda3\lib\urllib\request.py", line 525, in open
    response = meth(req, response)
  File "C:\Users\royal\miniconda3\lib\urllib\request.py", line 634, in http_response
    response = self.parent.error(
  File "C:\Users\royal\miniconda3\lib\urllib\request.py", line 563, in error
    return self._call_chain(*args)
  File "C:\Users\royal\miniconda3\lib\urllib\request.py", line 496, in _call_chain
    result = func(*args)
  File "C:\Users\royal\miniconda3\lib\urllib\request.py", line 643, in http_error_default
    raise HTTPError(req.full_url, code, msg, hdrs, fp)
urllib.error.HTTPError: HTTP Error 401: Unauthorized

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "D:\app\venv\lib\site-packages\django\core\handlers\exception.py", line 55, in inner
    response = get_response(request)
  File "D:\app\venv\lib\site-packages\django\core\handlers\base.py", line 197, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "D:\app\shopify_django_app\shopify_app\decorators.py", line 12, in wrapper
    return fn(request, *args, **kwargs)
  File "D:\app\shopify_django_app\home\views.py", line 16, in index
    products = shopify.Product.find()
  File "D:\app\venv\lib\site-packages\shopify\base.py", line 196, in find
    collection = super(ShopifyResource, cls).find(id_=id_, from_=from_, **kwargs)
  File "D:\app\venv\lib\site-packages\pyactiveresource\activeresource.py", line 386, in find
    return cls._find_every(from_=from_, **kwargs)
  File "D:\app\venv\lib\site-packages\pyactiveresource\activeresource.py", line 525, in _find_every
    response = cls.connection.get(path, cls.headers)
  File "D:\app\venv\lib\site-packages\pyactiveresource\connection.py", line 329, in get
    return self._open('GET', path, headers=headers)
  File "D:\app\venv\lib\site-packages\shopify\base.py", line 23, in _open
    self.response = super(ShopifyConnection, self)._open(*args, **kwargs)
  File "D:\app\venv\lib\site-packages\pyactiveresource\connection.py", line 288, in _open
    http_response = self._handle_error(err)
  File "D:\app\venv\lib\site-packages\pyactiveresource\connection.py", line 415, in _handle_error
    raise UnauthorizedAccess(err)
pyactiveresource.connection.UnauthorizedAccess: Response(code=401, body="b'{"errors":"[API] Invalid API key or access token (unrecognized login or wrong password)"}'", headers={'Date': 'Fri, 11 Aug 2023 11:46:01 GMT', 'Content-Type': 'application/json; charset=utf-8', 'Transfer-Encoding': 'chunked', 'Connection': 'close', 'X-Sorting-Hat-PodId': '319', 'X-Sorting-Hat-ShopId': '72702755136', 'Referrer-Policy': 'origin-when-cross-origin', 'X-Frame-Options': 'DENY', 'X-ShopId': '72702755136', 'X-ShardId': '319', 'WWW-Authenticate': 'Basic Realm="Shopify API Authentication"', 'Strict-Transport-Security': 'max-age=7889238', 'Server-Timing': 'processing;dur=29', 'X-Shopify-Stage': 'production', 'Content-Security-Policy': "default-src 'self' data: blob: 'unsafe-inline' 'unsafe-eval' https://* shopify-pos://*; block-all-mixed-content; child-src 'self' https://* shopify-pos://*; connect-src 'self' wss://* https://*; frame-ancestors 'none'; img-src 'self' data: blob: https:; script-src https://cdn.shopify.com https://cdn.shopifycdn.net https://checkout.shopifycs.com https://api.stripe.com https://mpsnare.iesnare.com https://appcenter.intuit.com https://www.paypal.com https://js.braintreegateway.com https://c.paypal.com https://maps.googleapis.com https://www.google-analytics.com https://v.shopify.com 'self' 'unsafe-inline' 'unsafe-eval'; upgrade-insecure-requests; report-uri /csp-report?source%5Baction%5D=index&source%5Bapp%5D=Shopify&source%5Bcontroller%5D=admin%2Fproducts&source%5Bsection%5D=admin_api&source%5Buuid%5D=b46c2afc-80f3-4566-90e3-0fe20c35d3ba", 'X-Content-Type-Options': 'nosniff', 'X-Download-Options': 'noopen', 'X-Permitted-Cross-Domain-Policies': 'none', 'X-XSS-Protection': '1; mode=block; report=/xss-report?source%5Baction%5D=index&source%5Bapp%5D=Shopify&source%5Bcontroller%5D=admin%2Fproducts&source%5Bsection%5D=admin_api&source%5Buuid%5D=b46c2afc-80f3-4566-90e3-0fe20c35d3ba', 'X-Dc': 'gcp-asia-south1,gcp-us-central1,gcp-us-central1', 'X-Request-ID': 'b46c2afc-80f3-4566-90e3-0fe20c35d3ba', 'CF-Cache-Status': 'DYNAMIC', 'Report-To': '{"endpoints":[{"url":"https:\\/\\/a.nel.cloudflare.com\\/report\\/v3?s=5ia78nHKzIhToOvDIQVkvrHwaxDNNvCQXSPxnqHQZ0n%2FosdCZTI2E9ACFETWfr0eyz4%2Bu7pi9JAdJWJCz5I4oMnj0AbooqopOxi5bxA%2B%2FhMkGOs2ivWCsIQfja10K7EnBQyEg%2BKb%2FuBsuw%3D%3D"}],"group":"cf-nel","max_age":604800}', 'NEL': '{"success_fraction":0.01,"report_to":"cf-nel","max_age":604800}', 'Server': 'cloudflare', 'CF-RAY': '7f503c547e451798-MAA', 'alt-svc': 'h3=":443"; ma=86400'}, msg="Unauthorized")```

Solution

  • This problem was solved. I created an app/uninstall webhook on app installation.

    def create_uninstall_webhook(shop_name, access_token):
        with shopify_session(shop_name, access_token):
            app_url = apps.get_app_config("shopify_app").APP_URL
            webhook = shopify.Webhook()
            webhook.topic = "app/uninstalled"
            webhook.address = app_url + '/shopify/uninstall/'
            webhook.format = "json"
            webhook.save()
    
    def shopify_session(shopify_domain, access_token):
        api_version = apps.get_app_config("shopify_app").SHOPIFY_API_VERSION
    
        return shopify.Session.temp(shopify_domain, api_version, access_token)
    
    
    def finalize(request):
        ...
        ...
        create_uninstall_webhook(shop_name, access_token)
        ...
        ...
    

    In addition to this if someone doesn't want to do this, shopify requires us to subscribe to mandatory webhooks in partner's dashboard. One of the mandatory webhooks is 'App uninstallation' which notifies us within 48 hours after the app uninstalled and we can perform the mandatory action accordingly.