Skip to content

How to Activate Shopify Web Pixel Extension on Production Store? #34

Description

@oneskynevergiveup

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"
}

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions