christophergregory / shopify-node-api Goto Github PK
View Code? Open in Web Editor NEWOAuth2 Module for Shopify API
License: MIT License
OAuth2 Module for Shopify API
License: MIT License
Received the following error: BODY: {"errors":"[API] Invalid API key or access token (unrecognized login or wrong password)"}
My code
var Shopify = new shopifyAPI({
shop: 'SHOP_NAME',
shopify_api_key: 'API_KEY', // Your API key
shopify_shared_secret: 'API_SHARED_SECRET'// Your Shared Secret
});
// More secure if we get the user email through the server
var activeUserEmail = getCurentUser.email;
var findCustomerByEmailURL = "/admin/customers/search.json?query=" + activeUserEmail;
Shopify.get(findCustomerByEmailURL, function(err, data, headers){
console.log(data); // Data contains customer json information
});`
Pretty sure I have the correct API_KEY and API_SHARED_SECRET. Im guessing this has something to do with the customer endpoint, I'm trying to find a customer by email.
You invoke a callback inside a try-catch
block on line 160
which catches errors thrown by passed callbacks. As a consequence errors occurring in the callback appear to be errors from the API calls and your code invokes the callback a second time passing the error thrown by themselves.
Hi Chris,
Thanks for setting up this module. It's been great so far, however, I have an issue using it.
Once I get my permanent token (it's automatically saved to the access_token property in the config object), I try the following code:
var Shopify = new shopifyAPI(config)
config is:
{ shop: 'second-opinions-store',
shopify_api_key: '167922d6d9ace8e71795cb4c10074cf4',
shopify_shared_secret: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
shopify_scope: 'write_products',
redirect_uri: 'http://localhost:5000/finish_auth',
nonce: '150281256017000',
verbose: true,
access_token: 'xxxxxxxxxxxxxxxxxxxxxxxxxx' }
I'm sure api_key, shared_secret and access_token are correct for my store, but I get a 401 error:
"[API] Invalid API key or access token (unrecognized login or wrong password)"
What am I doing wrong??
Hi there again...
Sorry for making another issue.. This is more a logic problem on my side, but wondering if shopify-node-api has anything built in that could help me.
I'm trying to grab all customers + each customer's metafield.
Shopify doesn't have an API that I could easily grab all at once with,so I need multiple calls sadly.
I'm using express + need to get my data from my innermost for loop API call, and send it to the outer API call so I can bundle it all up and send it as a response to the client.
Here's my code
router.get('/customers', (req, res) => {
Shopify.get('/admin/customers.json', function(err, data, headers){
for (var i = 0; i < data.customers.length; i++) {
Shopify.get('/admin/metafields.json?metafield[owner_id]=' + data.customers[i].id + '&metafield[owner_resource]=customer', function(err, data, headers){
for (var i = 0; i < data.metafields.length; i++) {
console.log(data.metafields[i]);
//THIS WORKS!!!
// return callback(data.metafields[i]);
}
});
}
});
});
Any helpers here?
I'm trying to just show a list of all customers with ther metafields.Anyone have a better solution?
Hi, I am new to shopify and node as well. I have successfully created auth url. Now i am stuck in res.redirect(url). I guess it needs login credentials of my shop. When i am hitting this auth url on browser, it is redirecting to login page.
I'm using your module and it works just fine. However, I noticed that in your signature validation you are not using the "state" parameter (which is strongly recommended by Shopify).
Why is that and does that make my app more vulnerable to hacking ?
I'm trying to put a get to the products.json based on its documentation and passing the options but it's not working.
Shopify API products.json endpoint
Shopify.get('/admin/products.json', {fields: 'id, title', page: 1, limit: 5 }, function(err, data, headers){
console.log(data); // Data contains product json information
console.log(headers); // Headers returned from request
});
This return ALL the products, not only 5 from page 1. What am I doing wrong?
If i do the below, it works.
Shopify.get('/admin/products.json?limit=5', function(err, data, headers){
console.log(data); // Data contains product json information
console.log(headers); // Headers returned from request
}))
Thanks
Is this change taken into account on the lib?
Shopify’s OAuth flow is changing
This is a reminder about a change that we are making to Shopify’s OAuth functionality.
Effective tomorrow, June 1, the signature URL parameter will not be included in OAuth redirects that come from Shopify. We announced this deprecation in December, 2013 because of security problems with the MD5 algorithm.
Please take a moment to make sure that your app uses the newer HMAC URL parameter to verify OAuth requests.
You can find more information about this change in our OAuth documentation and in Shopify’s API Announcements forum.
If you have any questions or feedback, just reply to this message and we’ll be glad to help.
Thanks for reading,
Shopify Apps Team
thanks for the best shopify node module
Sometimes, Shopify doesn't pass the state parameter as a query string, even though it is set in the authorize call.
Several others reported this:
https://ecommerce.shopify.com/c/shopify-apis-and-technology/t/oauth2-state-parameter-is-not-passed-262706
https://ecommerce.shopify.com/c/shopify-apis-and-technology/t/oauth-state-parameter-207445
In this case, it's impossible to use exchange_temporary_token(), as that forces check of the state
if (!self.is_valid_signature(query_params)) {
return callback(new Error("Signature is not authentic!"));
}
Would it be possible to ignore that instead, e.g.
if (!self.is_valid_signature(query_params, true)) {
and let developers call is_valid_signature(query_params) if desired?
Thanks
I am usingversion : 2.1.0
Module not found: Error: Cannot resolve 'file' or 'directory' ./package in /Users/Path/to/Project/node_modules/shopify-api-node
@ ./~/shopify-api-node/index.js 10:12-32
Shouldnt it specially use json
extension? I am missing something in my webpack config?
var webpack = require('webpack');
module.exports = {
entry: "./index.js",
target:"node",
output: {
path: __dirname,
filename: "bundle.js"
},
resolve: {
extensions: ['', '.js'],
modules: [
'node_modules'
],
},
module: {
loaders: [
{test: /\.js$/,
exclude: [/node_modules/, /.+\.config.js/],
loader: 'babel-loader',
query: {
presets: ['es2015']
}
},
{test: /\.json$/,loader:'json-loader'},
]
}
};
README has
Shopify.post('/admin/products/1234567.json', post_data, function(err, data, headers){
console.log(data);
});
should be
Shopify.post('/admin/products.json', ... )
I was just wondering if there were any complete open source app examples made with this, it would certainly help us get up to speed quickly :)
The checkout URI makes use of a 202 response.statusCode and a location header.
shopify-node-api appears to swallow non-error codes.
a simple "fix" would be to add a header in your response.on('end') event handler
var headers = response.headers; headers.x-shopify-status = response.statusCode; callback(error, json, headers, options);
It appears that any datetime string is automatically parsed into a Date object, then rewritten out as a string without the timezone.
Using the stock JSON.parse can fail, since shopify uses bigint types for its ids, and javascript can only natively handle 2^53 - 1, whereas the bigint can go to 2^64 - 1. As such is the case, a number larger than Number.MAX_SAFE_INTEGER would be incorrectly parsed as a different, smaller number, and would cause untold issues. I have not seen them send back such a large number yet, but I want to plan for the future.
I'm sending a pull request to use https://github.com/sidorares/json-bigint as the JSON parsing library, it turns numbers larger than the max safe integer into "BigNumber" objects, which can easily be represented as strings. Coming shortly.
Shopify API occasionally times out. It would be useful to catch & retry.
When making API requests, one has to check both err and data.errors to verify if there was an error.
What do you think about making it simpler for clients by checking only err?
To make it possible, this
var json = JSON.parse(body);
callback(null, json, response.headers);
should be changed to
var json = JSON.parse(body);
if (typeof json.errors != "undefined") {
callback(JSON.stringify(json.errors)); //could be string or array?
} else {
callback(null, json, response.headers);
}
Hello,
Great module, very helpful. One question, is there a reason you don't use the native "Error" object? This module doesn't play well with koa.js, since it expects a native Error object. Would you be opposed to a P.R. that uses a native Error object? I would still attach the same properties. Thanks!
Please see http://docs.shopify.com/api/tutorials/using-webhooks#verify-webhook
Would be nice to have this built into the library, similar to Shopify.is_valid_signature(req.query)
Cannot seem to get a permanent token. 'State' in query_params is returning undefined. Could this be the reason?
I have been pulling my hair out for the past day trying to figure out where I am going wrong, but when i try to get my app setup for public use I just get errors when I try to finish doing the auth, see the relevant code below..
router.get('/install', function(req, res, next) {
shop = req.param('shop');
Shopify = new shopifyAPI({
shop: shop, // MYSHOP.myshopify.com
shopify_api_key: apikey, // Your API key
shopify_shared_secret: sharedkey, // Your Shared Secret
shopify_scope: 'read_orders,read_products,read_customers',
redirect_uri: appurl + '/finish_auth',
nonce: n() // you must provide a randomly selected value unique for each authorization request
});
writeConfigData(shop, Shopify.config); //writes the config to firebase, worried the config was wrong
var auth_url = Shopify.buildAuthURL();
res.redirect(auth_url);
});
router.get('/finish_auth', function(req, res){
var theshop = req.param('shop').replace(/\./g, "_"); //this is just to find the saved shopconfig in firebase
database.ref('configs/' + theshop).once('value').then(function(snapshot) {
var config = snapshot.val();
var Shopify = new shopifyAPI(config), // You need to pass in your config here
query_params = req.query;
Shopify.exchange_temporary_token(query_params, function (err, data) {
// This will return successful if the request was authentic from Shopify
// Otherwise err will be non-null.
// The module will automatically update your config with the new access token
// It is also available here as data['access_token']
console.log("HERE IS THE EXCHANGE: " + err + data);
});
});
});
The current Docs are a little fuzzy with is_valid_signature and the way that Shopify uses hmac now instead, I have tried about a thousand different workarounds but always get that same error... I am starting to think it might actually be Shopify.
Any help with this issue would be awesome
Requests to the shopify api which contain umlauts (äüö) do not work.
795: unexpected token at '{"product":{"title":"Bl��d"}'
As far as i can tell, the encoding is fine up to the point where you pass it to the request from the https
module. This is not a problem of the api itself, it works using other clients such as Postman.
Hello again,
I am getting stuck on what to pass in for config in
app.get('/finish_auth', function(req, res){
var Shopify = new shopifyAPI(config), // You need to pass in your config here
query_params = req.query;
Shopify.exchange_temporary_token(query_params, function(err, data){
// This will return successful if the request was authentic from Shopify
// Otherwise err will be non-null.
// The module will automatically update your config with the new access token
// It is also available here as data['access_token']
});
});
Thank you for your help.
Hi,
Does anyone ever had this error : "SyntaxError: Unexpected token <" in the callback "err" after trying to
create a webhook? Here's my code
var topics = ['app/uninstalled', 'orders/fulfilled', 'orders/paid'];
var requesturl = "https://THESHOPURL/admin/webhooks";
Shopify.post(requesturl, { webhook : { address : config.shopify_webhook_url + topics[0] , topic : topics[0]}},
function(err, data, headers) {
if(err) {
console.log("HEADERS : ", headers);
console.error("ERROR IN CREATING SHOPIFY WEBHOOKS FOR THE SHOP: ", shop.url);
console.error("ERR : ", err);
} else {
console.log("webhook created with ", shop.url);
shop.webhooks.push(data.webhook);
}
}
})
ERROR IN CREATING SHOPIFY WEBHOOKS FOR THE SHOP: kollectionk.com
ERR : SyntaxError: Unexpected token <
I've used your API to do some cool things, and some of them are things everybody might want. I'm wondering if you want me to create a pull request to implement them into the api.
For example, I have a function which gets all the products on a store:
function getAllProducts() {
let products = [];
function getpage(pagenum) {
return new Promise((resolve, reject) => {
Shopify.get('/admin/products.json', {limit: 250, page: pagenum}, function (err, data, headers) {
if (err) {
reject(err);
}
resolve(data.products);
});
});
}
let pagenum = 1;
return new Promise((resolve, reject) => {
function getNextPage() {
getpage(pagenum)
.then((data) => {
products = products.concat(data);
if (data.length < 250) {
resolve(products);
} else {
pagenum += 1;
getNextPage();
}
})
}
getNextPage();
});
}
If you like I can work on implementing this into a pull request. Let me know 😄
I'm creating an embedded app and this wrapper is working well. I don't see any special instructions in the documentation about escaping the iframe. Does this app automatically handle the oAuth redirect for embedded apps through the function res.redirect(auth_url)? Or do I need to add code within my app to escape the iframe?
Per Shopify's documentation "it is critical that the initial OAuth request redirect escapes the iframe to make the requests."
HI,
In my application, when a shop download my app, I create some webhooks. Sometimes it works, sometimes not. I recently had this error with a shop that I tried to create webhook { error: 'Not Found', code: 404 }. I don't know why I receive this because I have the correct access_token. I'm using this var to make request.
var Shopify = new shopifyAPI({
shopify_api_key: process.env.SHOPIFY_API_KEY, // Your API key
shopify_shared_secret: process.env.SHOPIFY_SHARED_SECRET, // Your Shared Secret
access_token: shop.access_token,
shop: shopGoodUrl,
verbose: false
});
I think the problem is when in the library, when I make a request, the method "hostname()" is called and this method add .myshopify to the shop. For example "whatyouvape" download my app and www.whatyouvap.com works but not www.whatyouvape.myshopify.com. When I type that in the url it's says "Sorry, this shop is currently unavailable."
If someone could help me with this. This would be very appreciated.
Thank you very much.
Hi ! I've installed this module to my local app built on angular. I am trying to get some data from my shopify store ( Private App ) to my local http://localhost:4200 . I get this error in console :
"Fetch API cannot load https://mephistoshoesseattle.myshopify.com/admin/products.json. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:4200' is therefore not allowed access. The response had HTTP status code 404. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled."
I've read a tonn of doc about CORS , but nothing helped.
You are my only hope, please help!
I think that package should not auto log, if needed developers will config at somewhere or them will do it.
Thanks a lot for advice to use proxy on my local server! It works, now i can get the data from the remote server.
There is one more question : after i will build my app on local and push site files to some hosting, how can i setup that proxy there? is that possible?
It will be great if you can give me an advice, thanks for you time!
is_valid_signature()
should get a spot in the Docs. It's a very helpful method. I've been using my own, until I just noticed that it was included in shopify-node-api
.
Hi there, not sure if this is specific to your library... but I'm trying to query customers with a certain tag.
I can do the following -
/admin/customers/search.json?query=tag:singleWord
But obviously, once I try and add a tag with spaces in it I get errors -
/admin/customers/search.json?query=tag:Waiting - 5kg Single Bag
TypeError: Request path contains unescaped characters
So I figured, okay - I'll escape the spaces and turn it into something like this:
Waiting%20-%205kg%20Single%20Bag
The request then goes through, but no customer is returned.
Is this something on the library end, or on Shopify's end?
Thanks so much! Love the library.
Hi,
In this module, seems like you are setting the Shop: 'MYSHOP' when you create a new shopifyAPI object. However, for a public app, how and where does the application get the shop name of the actual shop trying to install the app? I would assume the buildAuthURL function, exchange temporary token and all of the requests would require the actual shop name, not a hardcoded one?
Thanks for your help.
A
This is just a feature request.
I could not find an official error response format from Shopify, but the error responses I receive look like this:
{"error":"invalid_request","error_description":"Could not find Shopify API application with api_key: "}
Currently the library uses the 'error' param as the error message when thrown. I believe all 400 requests are 'invalid_request' errors, and it would be a more descriptive message if it contained the 'error_description' if available.
Sometimes Shopify errors on Shopify.get() responses (e.g. a HTML page saying Something went wrong), and we need to re-try that.
However, as we issue multiple Shopify.get() calls asynchronously, we can't tell which of the original requests failed once we get the response, as the included data doesn't mention the original url.
In
ShopifyAPI.prototype.makeRequest
only the headers are passed to callback:
callback(null, json, response.headers);
Would you please pass the original request URL too?
Thanks
Hi,
is it possible to listen for Shopify Events ?
see: https://help.shopify.com/api/reference/event
Cheers Marc
Hi,
I'm using is_valid_signature()
before doing API calls with shopify-node-api. But it won't work multiple times, since it deletes the "signature" parameter from req.query array.
Instead of
delete params['signature']; // Not needed to build hash
for (var key in params){
calculated_signature.push(key + '=' + params[key]);
}
it would work better to do
for (var key in params){
if (key != "signature") { //signature must not be included in hash
calculated_signature.push(key + '=' + params[key]);
}
}
As a side note, saw that exchange_temporary_token()
calls is_valid_signature()
after making the oauth request. Verification should be moved at the top.
Hi,
After the OAuth update of shopify my app has stopped working. I checked the issue and its when i call the "exchange_temporary_token" method and I get the error "Signature is not authentic!" now as my code is deployed on github without the node modules I am simply updating my version in the package.json so shouldnt it work?? If not then what is required at my end?? My current code is this
function verifyAndGetToken(req){
query_params = req.query;
shopifyApi.exchange_temporary_token(query_params, function(err, data) {
console.log("REsponse from auth token", err, data);// This line prints the error "Signature is not authentic!"
AccountDb.findOneAndUpdate({
"stores.name": req.query.shop
}, {
"stores": [{
name: req.query.shop,
token: data.access_token
}]
}, {
upsert: true
}, function(err, account) {
console.log("Got auth token and updated account", err, account)
req.session.shopify = query_params;
console.log("auth token set session",req.session)
res.redirect('/shopify/')
return;
});
});
}
Shopify.exchange_temporary_token
and saves token to databasePOST
creates uninstall webhookPOST
from webhooktoken
below, after shop, api key, secret are input correctlyvar Shopify = new shopifyAPI({
shop: 'MYSHOP', // MYSHOP.myshopify.com
shopify_api_key: '', // Your API key
shopify_shared_secret: '', // Your Shared Secret
access_token: 'token', //permanent token
});
DELETE
Shopify.delete
the uninstall webhook
401 Invalid API key or access token (unrecognized login or wrong password)
My question is did I mess up or miss anything on any of the steps I listed to get the 401 error?
Request: allow user to optionally specify https agent in options. Added with test in #67
API requests don't always return an err for unsuccessful responses. A simple case is a GET request to an asset that doesn't exist. For example:
shopifyAPI.get('https://<store>.myshopify.com/admin/themes/<themeid>/assets.json?asset[key]=doesnotexist', function(err, data, headers) {});
An API call like this results in Shopify sending a response with a 404 status code and an empty body. This should result in the callback being invoked with an err, but instead it gets called with no err and data={}.
The cause of the issue appears to be in shopify.js#makeRequest. This function is relying on JSON body inspection to determine if an error is returned.
Instead, the code should use response.statusCode
to determine if the request was a success. And then inspect the JSON body just for the purpose of pulling out any additional messages.
Some API calls accept a X-Shopify-Api-Features header
(not documented, but google it)
As a new feature request, would you please consider a way to pass this header when making API calls?
Thanks
When going over the API limits (http://docs.shopify.com/api/tutorials/learning-to-respect-the-api-call-limit), it would be great if the library would auto-retry several times at a time interval, instead of passing directly the error to the client.
This way, the client worries less about handling usage spikes. It just knows that at some point it will get data originally asked for, even if it takes longer.
Thanks
I'm using this module with React / Webpack and it works well with exception of the Shopify.exchange_temporary_token() fails because crypto.createHmac('SHA256') is used instead of crypto.createHmac('sha256'). React / Weback uses the crypto-browserify module which requires the hash name to be lowercase.
Hello,
Does the library need to be updated for
https://ecommerce.shopify.com/c/api-announcements/t/oauth-api-endpoint-changes-to-signature-verification-222656
?
(didn't find "hmac" in code)
Thanks
Hello guys,
We at ghostmonitor rely on this repo and want to keep it in good health :) If you you open we could make a refactor using promises generators and standard style guide. What do you think?
After initial install, a few days later one will instantiate as follows:
var Shopify = new shopifyAPI({
shop: 'MYSHOP', // MYSHOP.myshopify.com
shopify_api_key: '', // Your API key
shopify_shared_secret: '', // Your Shared Secret
access_token: 'token', //permanent token
});
Please document this type of usage too.
Thanks
Hello,
We had an issue with Shopify auth sending more query params, and resulting in an invalid HMAC.
That being said, you should consider almost all url query params (*see below) to re-generate the hmac value. Not just shop and timestamp. This allows us to send you extra params when they may be required, without you needing to make a code change.
* Your code should remove hmac and signature from the url query params.
This
https://github.com/christophergregory/shopify-node-api/blob/master/lib/shopify.js#L54
should be changed to exclude hmac and signature, rather than including only a pre-defined list of params.
Thanks!
Hello,
I am learning how to build a private app and on the auth_url I am getting an error that "Oauth error invalid_request: The Shopify API application does not support oauth"
Is there something that I am missing that is different for private apps vs public apps?
Thanks for your help.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.