Ecommerce Shopify WordPress Discussion

Google Cloud Function between Shopify webhook and NetSuite working inconsistently

I'm attempting to relay a Shopify order/create webhook to NetSuite RESTlet using a 2nd Gen Google Cloud Function. For authentication into NetSuite, I'm using their TBA (token based authentication). It's driving me a bit mad because the RESTlet actually receives the payload sometimes, but it usually errors giving me a 403 (AxiosError: Request failed with status code 403 at settle). This tells me that the consumer key and consumer secret are correct at least. This is my first Google Cloud Function, so I may be missing something basic about how they work. Here is my latest version. The only difference between what has worked and this is that I'm storing the script ID as a environment variable. const axios = require('axios'); const crypto = require('crypto'); exports.shopifyToNetSuite = (req, res) => { let data = JSON.stringify(req.body); const realm = process.env.REALM; const consumerKey = process.env.CONSUMER_KEY; const consumerSecret = process.env.CONSUMER_SECRET; const tokenId = process.env.TOKEN_ID; const tokenSecret = process.env.TOKEN_SECRET; const scriptId = process.env.SCRIPT_ID; const timestamp = generateOAuthTimestamp(); const oauthNonce = crypto.randomBytes(16).toString('base64'); const oauthSignature = generateOAuthSignature(); function generateOAuthTimestamp() { const date = new Date(); return Math.floor(date.getTime() / 1000); } function generateOAuthSignature() { // Step 1: Generate Base String const httpMethod = 'POST'; const baseURL = `https://${realm}.restlets.api.netsuite.com/app/site/hosting/restlet.nl`; const parameters = { oauth_consumer_key: consumerKey, oauth_nonce: oauthNonce, oauth_signature_method: 'HMAC-SHA256', oauth_timestamp: timestamp, oauth_token: tokenId, oauth_version: '1.0', script: scriptId, deploy: '1' }; const parameterString = Object.keys(parameters) .sort() .map((key) => `${key}=${encodeURIComponent(parameters[key])}`) .join('&'); const baseString = `${httpMethod}&${encodeURIComponent(baseURL)}&${encodeURIComponent(parameterString)}`; // Step 2: Generate Signing Key const signingKey = `${encodeURIComponent(consumerSecret)}&${encodeURIComponent(tokenSecret)}`; // Step 3: Generate Signature const signature = crypto .createHmac('sha256', signingKey) .update(baseString) .digest('base64'); return signature; } let config = { method: 'post', maxBodyLength: Infinity, url: `https://${realm}.restlets.api.netsuite.com/app/site/hosting/restlet.nl?script=${scriptId}&deploy=1`, headers: { 'Content-Type': 'application/json', 'Authorization': `OAuth realm="${realm}",oauth_consumer_key="${consumerKey}",oauth_token="${tokenId}",oauth_signature_method="HMAC-SHA256",oauth_timestamp="${timestamp}",oauth_nonce="${oauthNonce}",oauth_version="1.0",oauth_signature="${oauthSignature}"`, }, data: data }; axios.request(config) .then((response) => { console.log(JSON.stringify(response.data)); }) .catch((error) => { console.log(error); }); }; I can't remember all the things I've tried, but here are a couple... I've tried changing the User-Agent header to 'Mozilla/5' since Shopify sends it as "Shopify-Captain-Hook," but that didn't seem to matter (I read in another forum that Suitelets won't accept Shopify-Captain-Hook as a header, so I thought this might also apply to RESTlets but I dunno). I tried using the oauth-1.0a package to build the request without success.
I have it working consistently now. For each request, I logged and documented the timestamps, signatures, nonces and the result. I noticed that for all failed authorizations, the nonce had a plus character (+) in it. Example nonce list: 1. ZMLVzjbsse5Y1dfBK+3O7g== (auth failed) 2. /lfKUZmJoySZxJalLNUUEA== (auth worked) 3. kMRJCJbXGGp4Tewu1BaHYw== (auth worked) 4. 5nOzBX4LVjLWnA7PqZJa6g== (auth worked) 5. MJFtqD8XVpDirTsL+5uvNA== (auth failed) Trying anything, I decided to change all "+" characters to "p." It hasn't failed since. const oauthNonce = crypto.randomBytes(16).toString('base64').replace(/\+/g, 'p'); I don't know why + signs are breaking it. Equal signs and slashes are going through without an issue. I'm running the signature through encodeURIComponent(), so it seems it's not an issue with it being URI friendly. It makes me believe that the URL version of +, which is "%2B" also makes it fail. Maybe it's just something about the way NetSuite is doing things. Whatever the case, it's working now. If anyone can help me make sense of this, I'd appreciate it.

December 30, 2023

TurboCommerce make the better internet purchasing globaly

Turbo Multi-language Translator

Make the better internet purchasing globaly

Turbosify SEO Speed Booster

5.0 (7) Free plan available
Get better conversions by improving store loading speed Installed

Turbo Multi-language Chat - AI Customer service in one hand

TurboCommerce make the better internet purchasing globaly
Our products

The help you need, when you need it

App by Turbo Engine

3 apps • 5.0 average rating

Turbosify Speed Booster

5.0 (7)
Get better conversions by optimizing shopify store Google page speed Installed

Turbosify Translator for Wordpress Woocommerce

5.0 (74) Free Wordpress Woocommerce Plugin
Translate your wordpress website to multiple language within 1 click, no configuration needed, no No technical required

Grow your business here

Whether you want to sell products down the street or around the world, we have all the tools you need.