Issue:
Hi, I'm trying to activate a Shopify web pixel extension on a production store. The web pixel extension is configured in the Shopify Remix app template. The backend is wired to Express.js.
What I've tried:
Approach_1:
I was able to activate it on the dev/test store using the Shopify app dev GraphQL by following this guide https://shopify.dev/docs/apps/build/marketing-analytics/build-web-pixels#:~:text=To%20activate%20a%20web%20pixel,extension.
However, I couldn't apply it on the production store since i cannot run shopify app dev on production store to open up the shopify GraphQL console.
mutation {
# This mutation creates a web pixel, and sets the `accountID` declared in `shopify.extension.toml` to the value `123`.
webPixelCreate(webPixel: { settings: "{\"accountID\":\"123\"}" }) {
userErrors {
code
field
message
}
webPixel {
settings
id
}
}
}
Approach_2:
I've also tried this guide that activates the pixel using shopify remix app's loader function. Unfortunately it didn't work as well. https://community.shopify.com/c/extensions/how-do-you-actually-activate-the-web-pixel/m-p/2496617
shopify remix app template > app app._index.jsx
export const loader = async ({ request }) => { // Authenticate with Shopify const { admin } = await authenticate.admin(request);
const mutationResponse = await admin.graphql( #graphql
mutation webPixelCreate($webPixel: WebPixelInput!) {
webPixelCreate(webPixel: $webPixel) {
userErrors {
field
message
}
webPixel {
settings
id
}
}
}
, { variables: { webPixel: { settings: { "accountID": 'Account_ID_45466_this_is_as_per_toml'
}, }, }, } );
if (!mutationResponse.ok) { console.error('Request failed', mutationResponse); return; }
const data = await mutationResponse.json(); console.log(data);
return mutationResponse;
};
const loaderDataForWebPixel = useLoaderData();
Approach_3: Oauth redirect and Token Exchange on Express.js endpoint routes>shopifyRouter.js file
import { shopify } from '../shopifyApi.js';
import { Router } from 'express';
import dotenv from 'dotenv';
import { shopify, RequestedTokenType } from '../shopifyApi.js';
import cookieParser from 'cookie-parser';
import axios from 'axios';
dotenv.config();
const router = Router();
// Middleware to set CSP headers for embedded apps
const setCSPHeaders = (req, res, next) => {
const shop = req.query.shop;
const shopDomain = shop ? `https://${shop}` : null;
const shopifyAdminDomain = "https://admin.shopify.com";
if (shopDomain) {
res.setHeader("Content-Security-Policy", `frame-ancestors ${shopDomain} ${shopifyAdminDomain}`);
} else {
res.setHeader("Content-Security-Policy", `frame-ancestors ${shopifyAdminDomain}`);
}
next();
};
router.use((req, res, next) => {
console.log(`Incoming request: ${req.method} ${req.url}`);
next();
});
// Apply middleware
router.use(cookieParser());
router.use(setCSPHeaders);
// Route to handle the initial OAuth
router.get('/install', async (req, res) => {
try {
const shop = req.query.shop;
if (!shop) {
return res.status(400).json({ error: 'Missing "shop" query parameter' });
}
await shopify.auth.begin({
shop,
callbackPath: '/auth/callback',
isOnline: false,
rawRequest: req,
rawResponse: res,
});
} catch (error) {
console.error('Error during install:', error.message);
console.error('Stack trace:', error.stack);
res.status(500).json({ error: 'Error during install', message: error.message });
}
});
// Route to handle the OAuth callback and activate web pixel
router.get('/auth/callback', async (req, res) => {
try {
const callbackResponse = await shopify.auth.callback({
rawRequest: req,
rawResponse: res,
});
const { session } = callbackResponse;
const accessToken = session.accessToken;
// Activate web pixel
const graphqlUrl = `https://${session.shop}/admin/api/2023-07/graphql.json`;
const graphqlHeaders = {
'Content-Type': 'application/json',
'X-Shopify-Access-Token': accessToken,
};
const graphqlMutation = {
query: `
mutation {
webPixelCreate(webPixel: { settings: "{\\"accountID\\":\\"88888888\\"}" }) {
userErrors {
code
field
message
}
webPixel {
settings
id
}
}
}
`,
};
const graphqlResponse = await axios.post(graphqlUrl, graphqlMutation, { headers: graphqlHeaders });
if (graphqlResponse.data.errors) {
console.error('GraphQL errors:', graphqlResponse.data.errors);
return res.status(500).json({ error: graphqlResponse.data.errors });
}
console.log('Web pixel activated:', graphqlResponse.data.data);
res.json(graphqlResponse.data.data);
} catch (error) {
console.error('Error during OAuth callback:', error.message);
console.error('Stack trace:', error.stack);
res.status(500).json({ error: 'Error during OAuth callback', message: error.message });
}
});
// Route get access token and activate web pixel
router.get('/auth', async (req, res) => {
try {
const shop = shopify.utils.sanitizeShop(req.query.shop, true);
const headerSessionToken = getSessionTokenHeader(req);
const searchParamSessionToken = getSessionTokenFromUrlParam(req);
const sessionToken = headerSessionToken || searchParamSessionToken;
if (!sessionToken) {
return res.status(400).json({ error: 'Missing session token' });
}
const session = await shopify.auth.tokenExchange({
sessionToken,
shop,
requestedTokenType: RequestedTokenType.OfflineAccessToken, // or RequestedTokenType.OnlineAccessToken
});
// Activate web pixel
const accessToken = session.accessToken;
console.log("🚀 ~ file: shopifyRouter.js:132 ~ router.get ~ accessToken:", accessToken);
const graphqlUrl = `https://${shop}/admin/api/2023-07/graphql.json`;
const graphqlHeaders = {
'Content-Type': 'application/json',
'X-Shopify-Access-Token': accessToken,
};
const graphqlMutation = {
query: `
mutation {
webPixelCreate(webPixel: { settings: "{\\"accountID\\":\\"88888888\\"}" }) {
userErrors {
code
field
message
}
webPixel {
settings
id
}
}
}
`,
};
const graphqlResponse = await axios.post(graphqlUrl, graphqlMutation, { headers: graphqlHeaders });
if (graphqlResponse.data.errors) {
console.error('GraphQL errors:', graphqlResponse.data.errors);
return res.status(500).json({ error: graphqlResponse.data.errors });
}
console.log('Web pixel activated:', graphqlResponse.data.data);
res.json(graphqlResponse.data.data);
} catch (error) {
console.error('Error during token exchange:', error.message);
console.error('Stack trace:', error.stack);
res.status(500).json({ error: 'Error during token exchange', message: error.message });
}
});
// Helper functions to get session token
function getSessionTokenHeader(request) {
const authHeader = request.headers.authorization;
if (authHeader && authHeader.startsWith('Bearer ')) {
return authHeader.substring(7);
}
return null;
}
function getSessionTokenFromUrlParam(request) {
return request.query.id_token || null;
}
export default router;
shopifyApi.js file
import '@shopify/shopify-api/adapters/node';
import { shopifyApi, LATEST_API_VERSION, RequestedTokenType } from '@shopify/shopify-api';
import dotenv from 'dotenv';
dotenv.config();
const myAppsLogFunction = (severity, message) => {
console.log(`[${severity}] ${message}`);
};
let shopify;
try {
shopify = shopifyApi({
apiKey: process.env.SHOPIFY_API_KEY,
apiSecretKey: process.env.SHOPIFY_API_SECRET,
scopes: ['read_products', 'write_products', 'read_customer_events', 'write_pixels'],
hostName: process.env.SHOPIFY_APP_HOST,
hostScheme: 'https',
apiVersion: LATEST_API_VERSION,
isEmbeddedApp: true,
isCustomStoreApp: false,
userAgentPrefix: 'Custom prefix',
logger: {
log: (severity, message) => {
myAppsLogFunction(severity, message);
},
level: 'info',
httpRequests: true,
timestamps: true,
},
future: {
unstable_newEmbeddedAuthStrategy: true,
}
});
} catch (error) {
console.log('shopifyApi.js error', error);
}
export { shopify, RequestedTokenType };
Here are the approach_3 's results
express-url-endpoint/install results to:
{
"error":
"Error during Oauth callback",
"message": Cannot complete Oauth process.
Could not find an Oauth cookie for shop url: the-shop.myshopify.com"
}
express-url-endpoint/auth token exchange results to:
{
"error:":
"Error during token exchange",
"message": "Request failed with status code 401"
}
Issue:
Hi, I'm trying to activate a Shopify web pixel extension on a production store. The web pixel extension is configured in the Shopify Remix app template. The backend is wired to Express.js.
What I've tried:
Approach_1:
I was able to activate it on the dev/test store using the Shopify app dev GraphQL by following this guide https://shopify.dev/docs/apps/build/marketing-analytics/build-web-pixels#:~:text=To%20activate%20a%20web%20pixel,extension.
However, I couldn't apply it on the production store since i cannot run
shopify app devon production store to open up the shopify GraphQL console.Approach_2:
I've also tried this guide that activates the pixel using shopify remix app's loader function. Unfortunately it didn't work as well. https://community.shopify.com/c/extensions/how-do-you-actually-activate-the-web-pixel/m-p/2496617
shopify remix app template > app app._index.jsx
Approach_3: Oauth redirect and Token Exchange on Express.js endpoint routes>shopifyRouter.js file
shopifyApi.js file
Here are the approach_3 's results
express-url-endpoint/installresults to:express-url-endpoint/authtoken exchange results to: