Shopify / shopify_python_api

ShopifyAPI library allows Python developers to programmatically access the admin section of stores
http://shopify.github.io/shopify_python_api
MIT License
1.28k stars 354 forks source link

shopify.api_version.VersionNotFoundError #324

Closed ammarA94 closed 1 year ago

ammarA94 commented 5 years ago

i am following 'https://medium.com/@dernis/shopify-embedded-sdk-with-python-flask-6af197e88c63' this tutorial, after everything when i go to link https://localhost:5000/shopify?shop=.myshopify.com by putting my shop_name it gives me that error

shopify.api_version.VersionNotFoundError shopify.api_version.VersionNotFoundError

Traceback (most recent call last) File "C:\Users\92344\Anaconda3\lib\site-packages\flask\app.py", line 2309, in call return self.wsgi_app(environ, start_response) File "C:\Users\92344\Anaconda3\lib\site-packages\flask\app.py", line 2295, in wsgi_app response = self.handle_exception(e) File "C:\Users\92344\Anaconda3\lib\site-packages\flask\app.py", line 1741, in handle_exception reraise(exc_type, exc_value, tb) File "C:\Users\92344\Anaconda3\lib\site-packages\flask_compat.py", line 35, in reraise raise value File "C:\Users\92344\Anaconda3\lib\site-packages\flask\app.py", line 2292, in wsgi_app response = self.full_dispatch_request() File "C:\Users\92344\Anaconda3\lib\site-packages\flask\app.py", line 1815, in full_dispatch_request rv = self.handle_user_exception(e) File "C:\Users\92344\Anaconda3\lib\site-packages\flask\app.py", line 1718, in handle_user_exception reraise(exc_type, exc_value, tb) File "C:\Users\92344\Anaconda3\lib\site-packages\flask_compat.py", line 35, in reraise raise value File "C:\Users\92344\Anaconda3\lib\site-packages\flask\app.py", line 1813, in full_dispatch_request rv = self.dispatch_request() File "C:\Users\92344\Anaconda3\lib\site-packages\flask\app.py", line 1799, in dispatch_request return self.view_functionsrule.endpoint File "C:\Users\92344\Downloads\HelloShopify-master\helloshopify\shopify_bp\views.py", line 36, in install session = shopify.Session(shop_url) File "C:\Users\92344\Anaconda3\lib\site-packages\shopify\session.py", line 47, in init self.version = ApiVersion.coerce_to_version(version) File "C:\Users\92344\Anaconda3\lib\site-packages\shopify\api_version.py", line 18, in coerce_to_version raise VersionNotFoundError shopify.api_version.VersionNotFoundError

nwtn commented 5 years ago

That tutorial is not up-to-date with this library. Please see the !! Breaking change notice for version 5.0.0 !! notice in the README and follow instructions there for setting an api_version.

ammarA94 commented 5 years ago

this thing is already added in session.py file. [session.py]

import time import hmac import json from hashlib import sha256 try: import simplejson as json except ImportError: import json import re from contextlib import contextmanager from six.moves import urllib from shopify.api_version import ApiVersion, Release, Unstable import six

class ValidationException(Exception): pass

class Session(object): api_key = None secret = None protocol = 'https' myshopify_domain = 'myshopify.com' port = None

@classmethod
def setup(cls, **kwargs):
    for k, v in six.iteritems(kwargs):
        setattr(cls, k, v)

@classmethod
@contextmanager
def temp(cls, domain, version, token):
    import shopify
    original_domain = shopify.ShopifyResource.url
    original_token = shopify.ShopifyResource.get_headers().get('X-Shopify-Access-Token')
    original_version = shopify.ShopifyResource.get_version() or version
    original_session = shopify.Session(original_domain, original_version, original_token)
    **session = Session(domain, version, token)**
    shopify.ShopifyResource.activate_session(session)
    yield
    shopify.ShopifyResource.activate_session(original_session)

def __init__(self, shop_url, version=None, token=None):
    self.url = self.__prepare_url(shop_url)
    self.token = token
    self.version = ApiVersion.coerce_to_version(version)
    return

def create_permission_url(self, scope, redirect_uri, state=None):
    query_params = dict(client_id=self.api_key, scope=",".join(scope), redirect_uri=redirect_uri)
    if state: query_params['state'] = state
    return "https://%s/admin/oauth/authorize?%s" % (self.url, urllib.parse.urlencode(query_params))

def request_token(self, params):
    if self.token:
        return self.token

    if not self.validate_params(params):
        raise ValidationException('Invalid HMAC: Possibly malicious login')

    code = params['code']

    url = "https://%s/admin/oauth/access_token?" % self.url
    query_params = dict(client_id=self.api_key, client_secret=self.secret, code=code)
    request = urllib.request.Request(url, urllib.parse.urlencode(query_params).encode('utf-8'))
    response = urllib.request.urlopen(request)

    if response.code == 200:
        self.token = json.loads(response.read().decode('utf-8'))['access_token']
        return self.token
    else:
        raise Exception(response.msg)

@property
def api_version(self):
    return self.version

@property
def site(self):
    return self.version.api_path("%s://%s" % (self.protocol, self.url))

@property
def valid(self):
    return self.url is not None and self.token is not None

@classmethod
def __prepare_url(cls, url):
    if not url or (url.strip() == ""):
        return None
    url = re.sub("^https?://", "", url)
    shop = urllib.parse.urlparse("https://" + url).hostname
    if shop is None:
        return None
    idx = shop.find(".")
    if idx != -1:
        shop = shop[0:idx]
    if len(shop) == 0:
        return None
    shop += "." + cls.myshopify_domain
    if cls.port:
        shop += ":" + str(cls.port)
    return shop

@classmethod
def validate_params(cls, params):
    # Avoid replay attacks by making sure the request
    # isn't more than a day old.
    one_day = 24 * 60 * 60
    if int(params.get('timestamp', 0)) < time.time() - one_day:
        return False

    return cls.validate_hmac(params)

@classmethod
def validate_hmac(cls, params):
    if 'hmac' not in params:
        return False

    hmac_calculated = cls.calculate_hmac(params).encode('utf-8')
    hmac_to_verify = params['hmac'].encode('utf-8')

    # Try to use compare_digest() to reduce vulnerability to timing attacks.
    # If it's not available, just fall back to regular string comparison.
    try:
        return hmac.compare_digest(hmac_calculated, hmac_to_verify)
    except AttributeError:
        return hmac_calculated == hmac_to_verify

@classmethod
def calculate_hmac(cls, params):
    """
    Calculate the HMAC of the given parameters in line with Shopify's rules for OAuth authentication.
    See http://docs.shopify.com/api/authentication/oauth#verification.
    """
    encoded_params = cls.__encoded_params_for_signature(params)
    # Generate the hex digest for the sorted parameters using the secret.
    return hmac.new(cls.secret.encode(), encoded_params.encode(), sha256).hexdigest()

@classmethod
def __encoded_params_for_signature(cls, params):
    """
    Sort and combine query parameters into a single string, excluding those that should be removed and joining with '&'
    """
    def encoded_pairs(params):
        for k, v in six.iteritems(params):
            if k == 'hmac':
                continue

            if k.endswith('[]'):
                #foo[]=1&foo[]=2 has to be transformed as foo=["1", "2"] note the whitespace after comma
                k = k.rstrip('[]')
                v = json.dumps(list(map(str, v)))

            # escape delimiters to avoid tampering
            k = str(k).replace("%", "%25").replace("=", "%3D")
            v = str(v).replace("%", "%25")
            yield '{0}={1}'.format(k, v).replace("&", "%26")

    return "&".join(sorted(encoded_pairs(params)))
saurabhkthakur commented 2 years ago

image

you need to add api_version while creating the session

session = shopify.Session(shop_url,"2022-04")

github-actions[bot] commented 1 year ago

This issue is stale because it has been open for 60 days with no activity. It will be closed if no further action occurs in 14 days.

github-actions[bot] commented 1 year ago

We are closing this issue because it has been inactive for a few months. This probably means that it is not reproducible or it has been fixed in a newer version. If it’s an enhancement and hasn’t been taken on since it was submitted, then it seems other issues have taken priority.

If you still encounter this issue with the latest stable version, please reopen using the issue template. You can also contribute directly by submitting a pull request– see the CONTRIBUTING.md file for guidelines

Thank you!