1/** 2 * Client-side Tracking Service 3 * Makes API calls to tracking service 4 * Migrated from src/services/tracking/index.ts 5 */ 6 7const TrackingService = { 8 /** 9 * Record tracking data 10 * @param {Object} data - Tracking data to record 11 * @returns {Promise<{success: boolean, message: string, ip: string}>} 12 */ 13 recordData: async function (data) { 14 try { 15 const response = await fetch( 16 'https://tracking.simplywise.com/record', 17 { 18 method: 'POST', 19 headers: { 20 'Content-Type': 'application/json', 21 }, 22 body: JSON.stringify(data), 23 // A download click navigates to the store right after 24 // this; keepalive lets the request finish anyway. 25 keepalive: true, 26 } 27 ); 28 29 const result = await response.json(); 30 Logger.log('[TrackingService] Data recorded:', result.success); 31 return result; 32 } catch (error) { 33 Logger.error('[TrackingService] Error recording data:', error); 34 return { 35 success: false, 36 message: error.message, 37 ip: data.ip_address || '', 38 }; 39 } 40 }, 41 42 /** 43 * Check tracking data 44 * @param {Object} data - Data to check 45 * @returns {Promise<{success: boolean, message: string, ip: string}>} 46 */ 47 checkData: async function (data) { 48 try { 49 const response = await fetch( 50 'https://tracking.simplywise.com/check', 51 { 52 method: 'POST', 53 headers: { 54 'Content-Type': 'application/json', 55 }, 56 body: JSON.stringify(data), 57 } 58 ); 59 60 const result = await response.json(); 61 return result; 62 } catch (error) { 63 Logger.error('[TrackingService] Error checking data:', error); 64 return { 65 success: false, 66 message: error.message, 67 ip: data.ip_address || '', 68 }; 69 } 70 }, 71 72 /** 73 * Get IP address. Looked up once per page: the same address is what 74 * the download record is keyed on, so callers may warm it early. 75 * @returns {Promise<string>} 76 */ 77 getIpAddress: function () { 78 if (!this._ipAddress) { 79 this._ipAddress = fetch('https://api.ipify.org?format=json') 80 .then(function (response) { 81 return response.json(); 82 }) 83 .then(function (data) { 84 return data.ip; 85 }) 86 .catch( 87 function (error) { 88 Logger.error( 89 '[TrackingService] Error getting IP address:', 90 error 91 ); 92 // Let the next caller try again. 93 this._ipAddress = null; 94 return ''; 95 }.bind(this) 96 ); 97 } 98 return this._ipAddress; 99 }, 100 101 /** 102 * Send a Facebook conversion event server-side (Conversions API). 103 * Used on pages where the Facebook Pixel is not loaded - the backend 104 * forwards the event to Facebook on our behalf. 105 * @param {string} eventName - Facebook event name (e.g. 'AddToCart') 106 * @param {Object} options 107 * @param {string|null} [options.gaId] 108 * @param {string|null} [options.facebookClickId] - fbclid from the URL 109 * @param {string|null} [options.facebookBrowserId] 110 * @param {string|null} [options.ipAddress] 111 * @param {string|null} [options.userAgent] 112 * @param {Object|null} [options.eventData] 113 * @param {string} [options.phoneNumber] 114 * @returns {Promise<Object>} 115 */ 116 sendFbEvent: async function (eventName, options) { 117 const opts = options || {}; 118 try { 119 const response = await fetch( 120 'https://api.simplywise.com/api/v2/tracking/send-fb-event', 121 { 122 method: 'POST', 123 headers: { 124 'Content-Type': 'application/json', 125 }, 126 body: JSON.stringify({ 127 event_name: eventName, 128 gaid: opts.gaId || null, 129 facebook_click_id: opts.facebookClickId || null, 130 facebook_browser_id: opts.facebookBrowserId || null, 131 ip_address: opts.ipAddress || null, 132 user_agent: opts.userAgent || null, 133 event_data: opts.eventData || null, 134 phone_number: opts.phoneNumber || null, 135 }), 136 } 137 ); 138 139 const result = await response.json(); 140 Logger.log('[TrackingService] FB event sent:', eventName); 141 return result; 142 } catch (error) { 143 Logger.error('[TrackingService] Error sending FB event:', error); 144 return { success: false, message: error.message }; 145 } 146 }, 147 148 /** 149 * Send an Amplitude event server-side via the backend endpoint. 150 * Used on ad landing pages when the visitor has not granted statistics 151 * consent, so the client-side Amplitude SDK is blocked by Cookiebot. 152 * The backend forwards the event to Amplitude's HTTP API. 153 * @param {string} eventName - Amplitude event name (maps to event_type) 154 * @param {Object} options 155 * @param {Object} [options.eventProperties] 156 * @param {string} options.deviceId - Consent-aware visitor id 157 * @param {string|null}
157 [options.userAgent] 158 * @returns {Promise<Object>} 159 */ 160 sendAmplitudeEvent: async function (eventName, options) { 161 const opts = options || {}; 162 try { 163 const response = await fetch( 164 'https://api.simplywise.com/api/v2/tracking/amplitude-event', 165 { 166 method: 'POST', 167 headers: { 168 'Content-Type': 'application/json', 169 }, 170 body: JSON.stringify({ 171 event_type: eventName, 172 event_properties: opts.eventProperties || {}, 173 device_id: opts.deviceId || null, 174 user_agent: opts.userAgent || null, 175 platform: 'web', 176 time: Date.now(), 177 }), 178 } 179 ); 180 181 const result = await response.json(); 182 Logger.log('[TrackingService] Amplitude event sent:', eventName); 183 return result; 184 } catch (error) { 185 Logger.error( 186 '[TrackingService] Error sending Amplitude event:', 187 error 188 ); 189 return { success: false, message: error.message }; 190 } 191 }, 192}; 193 194// Export for use in browser 195if (typeof window !== 'undefined') { 196 window.TrackingService = TrackingService; 197}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.