Combined Sourcepoint CCPA and GDPR TCFv2 Code

Overview

Setting up Sourcepoint’s CCPA solution is a simple process that publisher teams can follow to get up and running quickly. This document is a quick start guide on how to implement a Do Not Sell (my data) experience on your website using Sourcepoint’s Javascript code-snippet along with Sourcepoint's GDPR TCFv2 solution. The JS code-snippet needs to be placed on your site, preferably in the head tag before any advertising technology scripts implemented on your site. The goal of the implementation is to render a Do Not Sell (my data) notification required under CCPA and syndicate the user's privacy settings to any relevant third party technology running on your site along with a GDPR TCFv2 message when appropriate. The Sourcepoint script implementation has been optimized for high performance and fast delivery.

Below are the tags and libraries necessary to serve a CCPA "Do Not Sell" message and a GDPR TCFv2 message.

<script type="text/javascript">
//GDPR Stub file
!function (t) { var e = {}; function n(r) { if (e[r]) return e[r].exports; var o = e[r] = { i: r, l: !1, exports: {} }; return t[r].call(o.exports, o, o.exports, n), o.l = !0, o.exports } n.m = t, n.c = e, n.d = function (t, e, r) { n.o(t, e) || Object.defineProperty(t, e, { enumerable: !0, get: r }) }, n.r = function (t) { "undefined" != typeof Symbol && Symbol.toStringTag && Object.defineProperty(t, Symbol.toStringTag, { value: "Module" }), Object.defineProperty(t, "__esModule", { value: !0 }) }, n.t = function (t, e) { if (1 & e && (t = n(t)), 8 & e) return t; if (4 & e && "object" == typeof t && t && t.__esModule) return t; var r = Object.create(null); if (n.r(r), Object.defineProperty(r, "default", { enumerable: !0, value: t }), 2 & e && "string" != typeof t) for (var o in t) n.d(r, o, function (e) { return t[e] }.bind(null, o)); return r }, n.n = function (t) { var e = t && t.__esModule ? function () { return t.default } : function () { return t }; return n.d(e, "a", e), e }, n.o = function (t, e) { return Object.prototype.hasOwnProperty.call(t, e) }, n.p = "", n(n.s = 3) }([function (t, e) { t.exports = function (t) { return "object" == typeof t ? null !== t : "function" == typeof t } }, function (t, e, n) { t.exports = !n(2)(function () { return 7 != Object.defineProperty({}, "a", { get: function () { return 7 } }).a }) }, function (t, e) { t.exports = function (t) { try { return !!t() } catch (t) { return !0 } } }, function (t, e, n) { "use strict"; n.r(e); n(4); !function () { if ("function" != typeof window.__tcfapi) { var t, e = [], n = window, r = n.document; !n.__tcfapi && function t() { var e = !!n.frames.__tcfapiLocator; if (!e) if (r.body) { var o = r.createElement("iframe"); o.style.cssText = "display:none", o.name = "__tcfapiLocator", r.body.appendChild(o) } else setTimeout(t, 5); return !e }() && (n.__tcfapi = function () { for (var n = arguments.length, r = new Array(n), o = 0; o < n; o++)r[o] = arguments[o]; if (!r.length) return e; if ("setGdprApplies" === r[0]) r.length > 3 && 2 === parseInt(r[1], 10) && "boolean" == typeof r[3] && (t = r[3], "function" == typeof r[2] && r[2]("set", !0)); else if ("ping" === r[0]) { var i = { gdprApplies: t, cmpLoaded: !1, apiVersion: "2.0" }; "function" == typeof r[2] && r[2](i, !0) } else e.push(r) }, n.addEventListener("message", function (t) { var e = "string" == typeof t.data, r = {}; try { r = e ? JSON.parse(t.data) : t.data } catch (t) { } var o = r.__tcfapiCall; o && n.__tcfapi(o.command, o.parameter, o.version, function (n, r) { var i = { __tcfapiReturn: { returnValue: n, success: r, callId: o.callId } }; e && (i = JSON.stringify(i)), t.source.postMessage(i, "*") }) }, !1)) } }() }, function (t, e, n) { var r = n(5).f, o = Function.prototype, i = /^\s*function ([^ (]*)/; "name" in o || n(1) && r(o, "name", { configurable: !0, get: function () { try { return ("" + this).match(i)[1] } catch (t) { return "" } } }) }, function (t, e, n) { var r = n(6), o = n(7), i = n(10), f = Object.defineProperty; e.f = n(1) ? Object.defineProperty : function (t, e, n) { if (r(t), e = i(e, !0), r(n), o) try { return f(t, e, n) } catch (t) { } if ("get" in n || "set" in n) throw TypeError("Accessors not supported!"); return "value" in n && (t[e] = n.value), t } }, function (t, e, n) { var r = n(0); t.exports = function (t) { if (!r(t)) throw TypeError(t + " is not an object!"); return t } }, function (t, e, n) { t.exports = !n(1) && !n(2)(function () { return 7 != Object.defineProperty(n(8)("div"), "a", { get: function () { return 7 } }).a }) }, function (t, e, n) { var r = n(0), o = n(9).document, i = r(o) && r(o.createElement); t.exports = function (t) { return i ? o.createElement(t) : {} } }, function (t, e) { var n = t.exports = "undefined" != typeof window && window.Math == Math ? window : "undefined" != typeof self && self.Math == Math ? self : Function("return this")(); "number" == typeof __g && (__g = n) }, function (t, e, n) { var r = n(0); t.exports = function (t, e) { if (!r(t)) return t; var n, o; if (e && "function" == typeof (n = t.toString) && !r(o = n.call(t))) return o; if ("function" == typeof (n = t.valueOf) && !r(o = n.call(t))) return o; if (!e && "function" == typeof (n = t.toString) && !r(o = n.call(t))) return o; throw TypeError("Can't convert object to primitive value") } }]);
</script>
<script type="text/javascript">
//CCPA Stub file
(function () { var e = false; var c = window; var t = document; function r() { if (!c.frames["__uspapiLocator"]) { if (t.body) { var a = t.body; var e = t.createElement("iframe"); e.style.cssText = "display:none"; e.name = "__uspapiLocator"; a.appendChild(e) } else { setTimeout(r, 5) } } } r(); function p() { var a = arguments; __uspapi.a = __uspapi.a || []; if (!a.length) { return __uspapi.a } else if (a[0] === "ping") { a[2]({ gdprAppliesGlobally: e, cmpLoaded: false }, true) } else { __uspapi.a.push([].slice.apply(a)) } } function l(t) { var r = typeof t.data === "string"; try { var a = r ? JSON.parse(t.data) : t.data; if (a.__cmpCall) { var n = a.__cmpCall; c.__uspapi(n.command, n.parameter, function (a, e) { var c = { __cmpReturn: { returnValue: a, success: e, callId: n.callId } }; t.source.postMessage(r ? JSON.stringify(c) : c, "*") }) } } catch (a) { } } if (typeof __uspapi !== "function") { c.__uspapi = p; __uspapi.msgHandler = l; c.addEventListener("message", l, false) } })();
</script>
<script type="text/javascript">
window._sp_ = {
config: {
mmsDomain: "https://message.sp-prod.net",
accountId: ACCOUNT_ID_HERE,
wrapperAPIOrigin: "https://wrapper-api.sp-prod.net/tcfv2"
}
}
window._sp_ccpa = {
config: {
mmsDomain: "https://message.sp-prod.net",
ccpaOrigin: "https://ccpa-service.sp-prod.net",
accountId: ACCOUNT_ID_HERE,
getDnsMsgMms: true,
alwaysDisplayDns: false,
}
}
</script>
<script src="https://gdpr-tcfv2.sp-prod.net/wrapperMessagingWithoutDetection.js"></script>
<script src="https://ccpa.sp-prod.net/ccpa.js"></script>

Setting up The Messaging Domain (optional)

Setting up a messaging domain (mmsDomain) is optional. The Dialogue Javascript communicates with the Sourcepoint messaging server on a subdomain of the site. The benefit of doing that is to allow messaging cookies to be “first party” and thus, circumventing Safari’s web browser Intelligent Tracking Prevention (ITP). This creates a discrete messaging channel between the publisher’s messaging subdomain and the Dialogue messaging server. Once you have created the subdomain, you should create a DNS CNAME record to direct traffic to the Sourcepoint messaging endpoint message<account id>.sp-prod.net where the account id refers to your account ID in the Sourcepoint user interface. If you are unsure of what your account ID is, please contact your Sourcepoint account manager.

Once you have created the CNAME record, please inform your Sourcepoint Account manager so that they can create an SSL certificate for the subdomain. This will ensure that both secure and non-secure traffic are handled properly.

You can utilize the same CNAME for both CCPA & GDPR

We also included additional information on how to leverage our key-value pair targeting features and our data callbacks. Key-value pairs are used to create highly targeted messages based on data points from external systems like DMPs and CRM platforms. Our callbacks can be used to set cookie values based on the messages a user have seen and to send data to external systems such as website analytics platforms and/or DMPs.