Direct tracking code injection to extension - Wildfire Support Center

Direct tracking code injection to extension

Overview

Before we get started, it's important to understand what a tracking code is and why it is used.

When a user installs your extension, we need a way to identify the user to affiliate when they activate cash back through your extension, in order to attribute them properly when the sale completes its lifecycle. This is done at Wildfire by attaching a tc query parameter at the end of your affiliate URL. Once the sale completes its lifecycle, you can retrieve the value within the tc parameter. By going into the admin tool, you can post the data to your callback URL.

Example URLs

Vanity URL with tc parameter:

https://wild.link/lovepop/AIuL-AI?tc=ee5f1a61-a2f1-4cde-be78-293fa339e592

Offline Vanity URL with tc parameter:

https://wild.link/e?d=0&c=0&tc=2d4109a6-c5f6-4028-a206-c26b1379ea82&url=https://example.com

Many extensions use different models, whether it's an OAuth flow or logging in directly through the extension. They all serve the purpose of having the user identify themselves, and saving that identifier in the extension's storage. This way is different because it doesn't require making an HTTP request within the extension after authenticating, and not storing any secrets in the extension when not needed.

Important Notes

Example Code Location

The example code can be found at the bottom of this document.

Example Project Directory Structure

The extension directory contains the extension code, the website directory contains the code for your website. Your structure may look different.

Update manifest.json

Update your manifest.json with the following:

  1. Specify your web page as an externally_connectable web page. This enables the extension to communicate with tabs that have URLs matching the matches property.
  2. Add a key in your manifest.json to create a permanent extension ID. This ID will be used to communicate from your web page to your extension. For information on how to generate your own key see here. The extension ID in our example is aokkbecooohkgppimfffkongjlpihbol.

manifest.json Example

{
  "manifest_version": 2,
  "name": "Example",
  "description": "Example",
  "version": "1.0.0",
  "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5yYHytHDwMWWn3pVWriKJdycAYXScUNWpQqxatgbz8+835xfFBLFjDjjzqYO2rautzCUhp3CxunzJ7Ljm2WIvfsNcKTau/u23MfGf92gk0XZnj+DE0fHBUZDwaA649jqzrB5gm2V49SgROJAyy6KtwmVMj13A1+kOVrm3z1U33yN6NUYHLxtBT6BjHbXxmoE+LDT/owe2Tl5eAuvC37mUYpgJG9/DlGwW2UCTeYfmQ9gl/HymQtofWtl8oFNjR0cyrPoJNL+9bnFy2yeXbz98kuiM8g6iBM/ZnwZfYybf8z+iRpQMVXA7iIcu0qn2pcTAv4yJ6AVMGcNMY0HaPN7iQIDAQAB",
  "background": {
    "scripts": ["background.js"]
  },
  "content_scripts": [{
    "js": ["content.js"],
    "matches": ["<all_urls>"]
  }],
  "browser_action": {
    "default_popup": "popup.html",
    "default_icon": {
      "16": "images/toolbar-icon-16.png",
      "19": "images/toolbar-icon-19.png",
      "32": "images/toolbar-icon-32.png",
      "38": "images/toolbar-icon-38.png"
    }
  },
  "externally_connectable": {
    "matches": [
      "https://*.yoursite.com/*",
      "http://127.0.0.1:5500/*"
    ]
  }
}

The https://*.yoursite.com/* value is a placeholder for you to enter your own URL. The http://127.0.0.1:5500/* is for testing purposes. You may be using something like Webpack Dev Server or another local server; make sure to update the port number accordingly.

Background Script Example

This is our background.js file.

let trackingCode = null;

chrome.runtime.onMessageExternal.addListener((message, sender, sendResponse) => {
  switch (message.status) {
    case "PING":
      console.log("Received ping from", sender.origin);
      sendResponse(true);
      break;
    case "SET_TRACKING_CODE":
      const { code } = message.payload;
      console.log("Received: ", code);
      trackingCode = code;
      sendResponse(true);
      break;
  }
});

chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  switch (message.status) {
    case "IS_LOGGED_IN":
      sendResponse(!!trackingCode);
      break;
  }
});

Popup Script Example

This is our popup.js file.

chrome.runtime.sendMessage({status: "IS_LOGGED_IN"}, (isLoggedIn) => {
  const button = document.querySelector("button");
  button.textContent = isLoggedIn ? "Logged In" : "Not Logged In";
  button.style.backgroundColor = isLoggedIn ? "green" : "red";
});

When the popup is rendered, we check to see if the tracking code has been stored in the background. The objective is to ensure that users identify themselves before using the extension.

Webpage Example

Use Live Server to run your webpage using website/index.html:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <link rel="stylesheet" href="style.css" />
  <title>Login</title>
</head>
<body>
  <main>
    <h1>Send Code</h1>
    <input type="text" disabled value="ac7222b1-bf17-461e-9f52-c53d4d58a1c4" />
    <button disabled>Submit</button>
  </main>
  <script src="main.js "></script>
</body>
</html>

If your button and input fields are greyed out, then you did not set your key properly. You can change the extension ID on line 8 of the website/main.js file.

Main.js Example

Here is the main.js file:

/**
 * Your extension ID to specify your extension when sending the message.
 * For information on how to generate your own extension ID see here: <https://stackoverflow.com/a/46739698>
 * Once you have your key generated, you can add the key to your manifest.json under the "key" property.
 * If you have not set the ID properly, or made this webpage externally connectable you will get the following error:
 * `Unchecked runtime.lastError: Could not establish connection. Receiving end does not exist.`
 */
const extensionID = "aokkbecooohkgppimfffkongjlpihbol";

const isChromeApiAccessible = typeof window.chrome !== "undefined" &&
                               typeof window.chrome.runtime !== "undefined" &&
                               typeof window.chrome.runtime.sendMessage !== "undefined";

const submitButton = document.querySelector("button");
const inputElement = document.querySelector("input");

/**
 * Wrapped in an IFFE to stop execution when needed
 */
(() => {
  try {
    if (!isChromeApiAccessible) {
      return console.log("Stopping execution, chrome api is not accessible.");
    }

window.chrome.runtime.sendMessage(
      extensionID,
      { status: "PING" },
      (isConnected) => {
        if (!isConnected) {
          return console.log("Stopping execution, failed to connect to the extension");
        }

console.log("Successfully pinged extension, ready to accept code");
        submitButton.disabled = false;
        inputElement.disabled = false;

submitButton.onclick = () => {
          const code = inputElement.value;
          window.chrome.runtime.sendMessage(
            extensionID,
            { status: "SET_TRACKING_CODE", payload: { code } },
            (wasSuccessful) => {
              if (!wasSuccessful) {
                return console.log("Failed to send code");
              }
              console.log("Successfully sent code");
            }
          );
        };
      }
    );
  } catch (err) {
    console.error("Failed to communicate with the extension", err);
  }
})();

This file checks to see if the Chrome API is accessible, selects the input and button elements, pings the extension to see if communication is possible, and enables the input/button for interaction.

Conclusion

Incorporate this logic in your own web pages, ensuring users identify before using the extension.

Additional Resources

Example Project.zip