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
- The identifiers in the example URLs are UUIDs; your platform may have different ways of identifying a user, but the premise is the same.
- This method is known to work in Chrome and Firefox. Safari does not allow the
onMessageExternalmethod to be exposed for extensions. - During this tutorial, we will assume you have some knowledge of how Chrome extensions work. Please check the documentation for basic information on setting up a Chrome extension.
- We will use the v2 manifest version in the examples.
- The example code will contain a directory depicting an example web page that is run locally on your computer. You can run a web server from VSCode using the Live Server extension.
- In the example extension, we depict that the user is logged in based on whether or not the tracking code is stored within the extension. This behavior is common among our cashback extensions, primarily because we don't want the user activating cashback if we have no way of identifying who the user is.
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:
- Specify your web page as an
externally_connectableweb page. This enables the extension to communicate with tabs that have URLs matching thematchesproperty. - Add a key in your
manifest.jsonto 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 isaokkbecooohkgppimfffkongjlpihbol.
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.