${affiliateName ? decodeURIComponent(affiliateName) : "We've"} sent you ${
discountType === 'percentage' ? `${discountValue}%` : `$${discountValue}`
} off!
Click below to redeem your discount code
Redeem Coupon
Your discount will automatically be applied at checkout
`;
overlay.appendChild(modal);
document.body.appendChild(overlay);
// Close handler
document.getElementById('__growi-close-modal').onclick = () => {
window.growiCookieUtils.setCookie('discount_seen', `${affiliateId.toLowerCase()}`, -1);
overlay.remove();
};
// Redeem handler
document.getElementById('__growi-redeem-btn').onclick = () => {
const redeemBtn = document.getElementById('__growi-redeem-btn');
// Set loading state
redeemBtn.disabled = true;
redeemBtn.style.cursor = 'not-allowed';
redeemBtn.innerHTML = `
Loading...
`;
// Add spinning animation
const style = document.createElement('style');
style.textContent = `
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
`;
document.head.appendChild(style);
// Simulate loading delay then show discount code
setTimeout(() => {
// Replace button with discount code
redeemBtn.style.display = 'none';
// Create discount code container
const discountCodeDiv = document.createElement('div');
discountCodeDiv.id = '__growi-discount-code';
discountCodeDiv.style.cssText = `
background-color:#ecf0ff;
display:block;
width:90%;
margin:20px auto 0 auto;
text-align:center;
color:#3860fb;
font-size:25px;
padding:8px;
cursor:pointer;
border-radius:5px;
font-weight:bold;
font-family:-apple-system, Inter, BlinkMacSystemFont, Segoe UI, Roboto,
Helvetica Neue, Arial, Noto Sans, sans-serif;
`;
discountCodeDiv.innerHTML = `
${discountCode.toUpperCase()}
`;
// Add copy functionality
discountCodeDiv.onclick = () => {
navigator.clipboard
.writeText(discountCode.toUpperCase())
.then(() => {
// Show copied feedback under the code
let copiedText = document.getElementById('__growi-copied-text');
if (!copiedText) {
copiedText = document.createElement('div');
copiedText.id = '__growi-copied-text';
copiedText.style.cssText = `
font-size:0.9em;
color:#28a745;
margin-top:8px;
text-align:center;
`;
discountCodeDiv.parentNode.insertBefore(copiedText, discountCodeDiv.nextSibling);
}
copiedText.innerHTML = '✓ Discount applied!';
})
.catch(() => {
// Fallback for older browsers
const textArea = document.createElement('textarea');
textArea.value = discountCode.toUpperCase();
document.body.appendChild(textArea);
textArea.select();
document.execCommand('copy');
document.body.removeChild(textArea);
let copiedText = document.getElementById('__growi-copied-text');
if (!copiedText) {
copiedText = document.createElement('div');
copiedText.id = '__growi-copied-text';
copiedText.style.cssText = `
font-size:0.9em;
color:#28a745;
margin-top:8px;
text-align:center;
`;
discountCodeDiv.parentNode.insertBefore(copiedText, discountCodeDiv.nextSibling);
}
copiedText.innerHTML = '✓ Discount applied!';
});
};
// Insert the discount code div after the button
redeemBtn.parentNode.insertBefore(discountCodeDiv, redeemBtn.nextSibling);
}, 1500);
window.growiCookieUtils.setCookie('discount_seen', `${affiliateId.toLowerCase()}`);
};
}
async function showDiscountModalV2(discountCode, affiliateName, discountValue, discountType, affiliateId) {
console.log('showDiscountModalV2', discountCode, affiliateName, discountValue, discountType, affiliateId);
if (
!discountValue ||
discountValue === '0' ||
discountValue === 0 ||
discountValue === null ||
discountValue === undefined ||
discountValue === 'null'
)
return;
// Fetch customization from API
const customization = await fetchCustomization(affiliateId);
// If no customization exists, fall back to the original showDiscountModal
if (!customization) {
console.log('ℹ️ No customization available, falling back to default modal');
showDiscountModal(discountCode, affiliateName, discountValue, discountType, affiliateId);
return;
}
// Prevent duplicate modals
if (document.getElementById('__growi-modal-overlay')) return;
// Merge customization with defaults
const config = { ...defaultCustomization, ...customization };
// Check if popup is enabled
if (!config.popup_enabled) {
console.log('ℹ️ Popup is disabled by customization settings');
if (config.banner_enabled) {
window.growiCookieUtils.setCookie('discount_seen', affiliateId.toLowerCase());
showBanner(affiliateId, discountCode, customization);
}
return;
}
const headingText =
config.heading_text ||
`${affiliateName ? decodeURIComponent(affiliateName) : "We've"} sent you ${
discountType === 'percentage' ? `${discountValue}%` : `$${discountValue}`
} off!`;
const bodyText = config.body_text || 'Click below to redeem your discount code';
const buttonText = config.button_text || 'Redeem Coupon';
const helperText = config.helper_text || 'Your discount will automatically be applied at checkout';
const buttonMarkup = config.hide_button
? ''
: `
${buttonText}
`;
const helperMarkup = `
${helperText}
`;
const overlay = document.createElement('div');
overlay.id = '__growi-modal-overlay';
overlay.style.cssText = `
display:block;
position:fixed;
top:0; left:0;
width:100%; height:100%;
background-color:rgba(0,0,0,0.5);
z-index:9998;
`;
const modal = document.createElement('div');
modal.className = '__growi_modal_content';
modal.style.cssText = `
display:block;
background-color:${config.background_color};
z-index:9999;
border-top:10px solid #2C2C2C;
border-radius:10px;
text-align:center;
margin:auto;
min-height:400px;
height:auto;
position:fixed;
top:50%; left:50%;
transform:translate(-50%, -50%);
width:90%; max-width:500px;
`;
// Add mobile-specific styles
const mobileStyles = document.createElement('style');
mobileStyles.textContent = `
@media (max-width: 768px) {
.__growi_modal_content {
top: calc(50% + 20px) !important;
}
}
@media (max-width: 480px) {
.__growi_modal_content {
top: calc(50% + 30px) !important;
}
}
`;
document.head.appendChild(mobileStyles);
modal.innerHTML = `
${config.emoji || '🎉'}
${headingText}
${bodyText}
${buttonMarkup}
${helperMarkup}
`;
overlay.appendChild(modal);
document.body.appendChild(overlay);
// Close handler
const closeButton = document.getElementById('__growi-close-modal');
if (closeButton) {
closeButton.onclick = () => {
window.growiCookieUtils.setCookie('discount_seen', affiliateId.toLowerCase(), -1);
overlay.remove();
};
}
// Redeem handler
const redeemBtn = document.getElementById('__growi-redeem-btn');
if (redeemBtn) {
redeemBtn.onclick = () => {
// Set loading state (preserve custom button colors)
redeemBtn.disabled = true;
redeemBtn.style.cursor = 'not-allowed';
redeemBtn.style.backgroundColor = config.button_color;
redeemBtn.style.color = config.button_text_color;
redeemBtn.innerHTML = `
Loading...
`;
// Add spinning animation
const style = document.createElement('style');
style.textContent = `
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
`;
document.head.appendChild(style);
// Simulate loading delay then show discount code
setTimeout(() => {
// Replace button with discount code
redeemBtn.style.display = 'none';
// Create discount code container
const discountCodeDiv = document.createElement('div');
discountCodeDiv.id = '__growi-discount-code';
discountCodeDiv.style.cssText = `
background-color:#ecf0ff;
display:block;
width:90%;
margin:20px auto 0 auto;
text-align:center;
color:#3860fb;
font-size:25px;
padding:8px;
cursor:pointer;
border-radius:5px;
font-weight:bold;
font-family:-apple-system, Inter, BlinkMacSystemFont, Segoe UI, Roboto,
Helvetica Neue, Arial, Noto Sans, sans-serif;
`;
discountCodeDiv.innerHTML = `
${discountCode.toUpperCase()}
`;
// Add copy functionality
discountCodeDiv.onclick = () => {
navigator.clipboard
.writeText(discountCode.toUpperCase())
.then(() => {
// Show copied feedback under the code
let copiedText = document.getElementById('__growi-copied-text');
if (!copiedText) {
copiedText = document.createElement('div');
copiedText.id = '__growi-copied-text';
copiedText.style.cssText = `
font-size:0.9em;
color:#28a745;
margin-top:8px;
text-align:center;
`;
discountCodeDiv.parentNode.insertBefore(copiedText, discountCodeDiv.nextSibling);
}
copiedText.innerHTML = '✓ Discount applied!';
})
.catch(() => {
// Fallback for older browsers
const textArea = document.createElement('textarea');
textArea.value = discountCode.toUpperCase();
document.body.appendChild(textArea);
textArea.select();
document.execCommand('copy');
document.body.removeChild(textArea);
let copiedText = document.getElementById('__growi-copied-text');
if (!copiedText) {
copiedText = document.createElement('div');
copiedText.id = '__growi-copied-text';
copiedText.style.cssText = `
font-size:0.9em;
color:#28a745;
margin-top:8px;
text-align:center;
`;
discountCodeDiv.parentNode.insertBefore(copiedText, discountCodeDiv.nextSibling);
}
copiedText.innerHTML = '✓ Discount applied!';
});
};
// Insert the discount code div after the button
redeemBtn.parentNode.insertBefore(discountCodeDiv, redeemBtn.nextSibling);
}, 1500);
window.growiCookieUtils.setCookie('discount_seen', affiliateId.toLowerCase());
};
}
}
async function showBanner(affiliateId, discountCode, customizationData = null) {
// Prevent duplicate banners
if (document.getElementById('__growi-banner')) return;
// Fetch customization from API if not provided
const customization = customizationData || (await fetchCustomization(affiliateId));
// If no customization exists or banner not enabled, return
if (!customization || !customization.banner_enabled) {
console.log('ℹ️ Banner is disabled or no customization available');
return;
}
// Merge customization with defaults
const config = { ...defaultCustomization, ...customization };
const affiliateName = window.growiCookieUtils.getCookie('affiliate_name');
const discountValue = window.growiCookieUtils.getCookie('discount_value');
const discountType = window.growiCookieUtils.getCookie('discount_type');
const bannerHeading =
config.heading_text ||
`${affiliateName ? decodeURIComponent(affiliateName) : 'We'} sent you ${
discountType === 'percentage' ? `${discountValue}%` : `$${discountValue}`
} off!`;
const bannerSubtext = config.body_text || `Click below to redeem your discount code`;
const bannerButtonText = config.button_text || 'Redeem Coupon';
const bannerButtonMarkup = config.hide_button
? ''
: `
${bannerButtonText}
`;
// Create banner element
const banner = document.createElement('div');
banner.id = '__growi-banner';
banner.style.cssText = `
display: flex;
align-items: center;
justify-content: space-between;
padding: 16px 20px;
background-color: ${config.background_color};
color: ${config.text_color};
position: fixed;
top: 0;
left: 0;
right: 0;
width: 100%;
z-index: 9997;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
border-bottom: 1px solid rgba(0,0,0,0.08);
font-family: -apple-system, Inter, BlinkMacSystemFont, Segoe UI, Roboto,
Helvetica Neue, Arial, Noto Sans, sans-serif;
gap: 20px;
box-sizing: border-box;
`;
banner.innerHTML = `
${config.emoji || '🎉'}
${bannerHeading}
${bannerSubtext}
${bannerButtonMarkup}
`;
// Add responsive styles and hover effects
const bannerStyles = document.createElement('style');
bannerStyles.textContent = `
#__growi-banner-btn:hover {
opacity: 0.9;
}
#__growi-banner-close:hover {
opacity: 1;
}
@media (max-width: 768px) {
#__growi-banner {
flex-direction: column;
align-items: stretch !important;
padding: 16px;
width: 100%;
}
#__growi-banner > div:first-child {
margin-bottom: 12px;
}
#__growi-banner-btn {
width: 100%;
}
#__growi-banner-close {
position: absolute;
top: 16px;
right: 16px;
}
}
`;
document.head.appendChild(bannerStyles);
// Append to body
document.body.appendChild(banner);
// Add body padding to prevent content from being hidden behind banner
const originalPaddingTop = document.body.style.paddingTop;
// Wait for banner to render to get actual height
setTimeout(() => {
const bannerBottom = banner.getBoundingClientRect().bottom;
document.body.style.paddingTop = `${bannerBottom}px`;
// Push down any existing sticky announcement bars so they appear below our banner
const stickyAnnouncementBars = document.querySelectorAll(
'[class*="announcement-bar"], [class*="announcement_bar"], [id*="announcement-bar"]'
);
stickyAnnouncementBars.forEach((el) => {
const style = getComputedStyle(el);
if (style.position === 'sticky') {
el.style.top = `${bannerBottom}px`;
}
});
}, 0);
// Store original padding for cleanup
banner.dataset.originalPadding = originalPaddingTop;
// Button click handler - always copy code to clipboard, and optionally open modal
const bannerBtn = document.getElementById('__growi-banner-btn');
if (bannerBtn) {
bannerBtn.onclick = () => {
const discountCode = window.growiCookieUtils.getCookie('discount_code');
const affiliateName = window.growiCookieUtils.getCookie('affiliate_name');
const discountType = window.growiCookieUtils.getCookie('discount_type');
const discountValue = window.growiCookieUtils.getCookie('discount_value');
if (discountCode && discountValue) {
// Always copy discount code to clipboard
const originalText = bannerBtn.innerHTML;
navigator.clipboard
.writeText(discountCode.toUpperCase())
.then(() => {
bannerBtn.innerHTML = '✓ Discount applied!';
bannerBtn.style.backgroundColor = '#28a745';
setTimeout(() => {
bannerBtn.innerHTML = originalText;
bannerBtn.style.backgroundColor = config.button_color;
}, 2000);
})
.catch(() => {
// Fallback for older browsers
const textArea = document.createElement('textarea');
textArea.value = discountCode.toUpperCase();
textArea.style.position = 'fixed';
textArea.style.opacity = '0';
document.body.appendChild(textArea);
textArea.select();
document.execCommand('copy');
document.body.removeChild(textArea);
bannerBtn.innerHTML = '✓ Discount applied!';
bannerBtn.style.backgroundColor = '#28a745';
setTimeout(() => {
bannerBtn.innerHTML = originalText;
bannerBtn.style.backgroundColor = config.button_color;
}, 2000);
});
// Additionally show modal if enabled
if (config.popup_enabled) {
showDiscountModalV2(discountCode, affiliateName, discountValue, discountType, affiliateId);
}
}
};
}
// Close button handler
document.getElementById('__growi-banner-close').onclick = () => {
// Restore original padding
const originalPadding = banner.dataset.originalPadding || '';
document.body.style.paddingTop = originalPadding;
banner.remove();
// Store in cookie that banner was dismissed
window.growiCookieUtils.setCookie('banner_dismissed', affiliateId.toLowerCase(), 1); // 1 day
};
}
// ---- PAGE VIEW (exact referrer only) ----
function hasFiredPageView(visitorUid) {
const key = `growi_pv_sent::${visitorUid || 'anon'}::${location.pathname}`;
try {
if (sessionStorage.getItem(key)) return true;
} catch {}
if (window.growiCookieUtils) {
const ck = window.growiCookieUtils.getCookie(encodeURIComponent(key));
if (ck === '1') return true;
}
return false;
}
function markFiredPageView(visitorUid) {
const key = `growi_pv_sent::${visitorUid || 'anon'}::${location.pathname}`;
try {
sessionStorage.setItem(key, '1');
} catch {}
// ~30 minutes lock across tabs
window.growiCookieUtils && window.growiCookieUtils.setCookie(encodeURIComponent(key), '1', 0.02);
}
async function sendOneTimePageView(externalId) {
console.log('sendOneTimePageView', externalId);
if (!window.growiCookieUtils) return;
console.log('sendOneTimePageView1', externalId);
if (!externalId) return;
const affiliateId = window.growiCookieUtils.getCookie('growi_affiliate_id');
const visitorUid = window.growiCookieUtils.getCookie('growi_visitor_uid');
if (!affiliateId || affiliateId === 'null') return; // keep your current rule
console.log('sendOneTimePageView2', affiliateId);
if (hasFiredPageView(visitorUid)) return;
console.log('sendOneTimePageView3', visitorUid);
markFiredPageView(visitorUid);
// EXACT referrer only
const referrerRaw = document.referrer || null;
let referrerHost = null;
try {
referrerHost = referrerRaw ? new URL(referrerRaw).host : null;
} catch {}
// Referrer captured at script-execute time, before any same-host nav
// could blank document.referrer. Survives pushState / SPA-style nav
// inside in-app browsers.
const referrerInitialRaw =
(typeof window !== 'undefined' && typeof window.__growi_initial_referrer !== 'undefined'
? window.__growi_initial_referrer
: null) || null;
let referrerInitialHost = null;
try {
referrerInitialHost = referrerInitialRaw ? new URL(referrerInitialRaw).host : null;
} catch {}
// Get UTM parameters for Meta/Facebook ad tracking
const utmParams = window.growiCookieUtils.getUtmParams ? window.growiCookieUtils.getUtmParams() : {};
// Check if traffic is from Facebook/Meta based on utm_source
const isFacebookSource = utmParams.utm_source &&
['fb', 'facebook', 'meta'].includes(utmParams.utm_source.toLowerCase());
// Click IDs + first-touch snapshot for paid-search fraud detection
const clickIds = window.growiCookieUtils.getClickIds ? window.growiCookieUtils.getClickIds() : {};
const firstTouch = window.growiCookieUtils.getFirstTouch ? window.growiCookieUtils.getFirstTouch() : null;
// In-app-browser / platform inference from UA. Always safe to call —
// returns { platform: 'browser', in_app_browser: false, ... } when
// nothing matches.
const clientPlatform = window.growiCookieUtils.detectClientPlatform
? window.growiCookieUtils.detectClientPlatform()
: { platform: 'browser', in_app_browser: false, user_agent: '', is_mobile: false };
// Prefer the value stamped onto first_touch (computed when the affiliate
// cookie was set / backfilled — most accurate). Fall back to recomputing
// live so payloads without a first_touch still carry signal.
let inferredSource = (firstTouch && firstTouch.inferred_source) || null;
if (!inferredSource) {
if (clientPlatform && clientPlatform.platform && clientPlatform.platform !== 'browser') {
inferredSource = clientPlatform.platform;
} else if (referrerHost) {
inferredSource = referrerHost;
} else if (clickIds.gclid) {
inferredSource = 'google_ads';
} else if (clickIds.msclkid) {
inferredSource = 'bing_ads';
} else if (clickIds.fbclid) {
inferredSource = 'facebook';
} else if (clickIds.ttclid) {
inferredSource = 'tiktok';
} else {
inferredSource = 'direct';
}
}
const event = {
name: 'page_viewed',
id: (crypto.randomUUID && crypto.randomUUID()) || `${Date.now()}-${Math.random().toString(16).slice(2)}`,
timestamp: new Date().toISOString(),
data: {
title: document.title || null,
path: location.pathname,
url: location.href,
referrer_raw: referrerRaw,
referrer_host: referrerHost,
referrer_initial_raw: referrerInitialRaw,
referrer_initial_host: referrerInitialHost,
utm_source: utmParams.utm_source || null,
utm_medium: utmParams.utm_medium || null,
utm_campaign: utmParams.utm_campaign || null,
utm_content: utmParams.utm_content || null,
utm_term: utmParams.utm_term || null,
utm_id: utmParams.utm_id || null,
// Meta Ad ID (only set if utm_source is fb/facebook/meta)
meta_ad_id: isFacebookSource ? (utmParams.tw_id || utmParams.utm_id || utmParams.utm_content || null) : null,
click_ids: clickIds,
first_touch: firstTouch,
inferred_source: inferredSource,
client_platform: clientPlatform,
},
clientId: visitorUid,
};
const body = {
tracking_event: {
event_name: event.name,
occurred_at: event.timestamp,
visitor_uid: visitorUid,
event_data: {
pageEventId: event.id,
timeStamp: event.timestamp,
data: { ...event.data, clientId: event.clientId },
},
},
campaign_affiliate_id: affiliateId,
external_id: `gid://shopify/Shop/${externalId}`,
conversion_platform: 'shopify',
};
// Optional server dedupe
const idempKey = `${visitorUid || 'anon'}::${location.pathname}::${Math.floor(Date.now() / 60000)}`;
try {
await fetch('https://api.growi.io/api/v1/tracking_events', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Idempotency-Key': idempKey,
},
body: JSON.stringify(body),
});
} catch (err) {
console.warn('page_viewed send failed', err);
}
}
function waitForUrlParams(retries = 10, delay = 200) {
// Check if URL has any parameters yet
const hasParams = window.location.search.length > 0;
const affiliateParam =
new URLSearchParams(window.location.search).get('a') ||
new URLSearchParams(window.location.search).get('snowball') ||
new URLSearchParams(window.location.search).get('growi');
// If we found affiliate params or exhausted retries, proceed
if (affiliateParam || retries <= 0 || hasParams) {
console.log(`✅ URL stabilized after ${10 - retries} attempts. Affiliate param:`, affiliateParam);
initializeAffiliate();
return;
}
// Wait and retry - Instagram WebView needs time to stabilize
console.log(`⏳ Waiting for URL params to stabilize... (${retries} retries left)`);
setTimeout(() => waitForUrlParams(retries - 1, delay), delay);
}
function waitForUtils(retries = 5) {
console.log(`🔍 [WAIT-FOR-UTILS] Attempt ${6 - retries}/5 - Checking for window.growiCookieUtils...`);
console.log(
`🔍 [WAIT-FOR-UTILS] window.growiCookieUtils exists?`,
typeof window.growiCookieUtils !== 'undefined'
);
console.log(`🔍 [WAIT-FOR-UTILS] Document readyState:`, document.readyState);
console.log(`🔍 [WAIT-FOR-UTILS] Timestamp:`, new Date().toISOString());
if (window.growiCookieUtils) {
console.log('✅ [WAIT-FOR-UTILS] Cookie utils found! Available methods:', Object.keys(window.growiCookieUtils));
// Don't immediately read params - wait for URL to stabilize first
waitForUrlParams();
} else if (retries > 0) {
console.log(`⏳ [WAIT-FOR-UTILS] Not found yet, retrying in 100ms... (${retries} retries left)`);
setTimeout(() => waitForUtils(retries - 1), 100);
} else {
console.error('❌ [WAIT-FOR-UTILS] Growi cookie utilities not loaded after 5 retries (500ms total)');
console.error('❌ [WAIT-FOR-UTILS] All window properties:', Object.keys(window));
console.error('❌ [WAIT-FOR-UTILS] Check if cookie-utils.js is loading correctly');
}
}
function initializeAffiliate() {
console.log('🎯 [INIT-AFFILIATE] initializeAffiliate() called');
console.log(
'🎯 [INIT-AFFILIATE] window.growiCookieUtils exists?',
typeof window.growiCookieUtils !== 'undefined'
);
if (!window.growiCookieUtils) {
console.error('❌ [INIT-AFFILIATE] Cookie utils not available - CANNOT PROCEED');
console.error('❌ [INIT-AFFILIATE] This usually means cookie-utils.js failed to load or execute');
return;
}
console.log('✅ [INIT-AFFILIATE] Cookie utils available, proceeding...');
// Now read affiliate params after URL has stabilized
const urlAffiliateId =
new URLSearchParams(window.location.search).get('a') ||
new URLSearchParams(window.location.search).get('snowball') ||
new URLSearchParams(window.location.search).get('growi');
// Get affiliate info from cookies
const cookieInfo = window.growiCookieUtils.manageAffiliateCookies();
// Determine which affiliate ID to use (URL param takes precedence)
const affiliateId = urlAffiliateId || cookieInfo.affiliateId;
const visitorUid = cookieInfo.visitorUid;
// Brand-built Meta ads carry only a gcs_id (no affiliate param/cookie).
// gcs_id alone is enough to stamp the cart so the backend can attribute
// the sale.
const gcsId =
window.growiCookieUtils.getQueryParam('gcs_id') ||
window.growiCookieUtils.getCookie('growi_gcs_id');
const autoResponderId =
window.growiCookieUtils.getQueryParam('igar_id') ||
cookieInfo.autoResponderId ||
window.growiCookieUtils.getCookie('growi_auto_responder_id');
// Creator-set sub-ID for platform-level tracking (e.g. ?sub_id=instagram)
const subId =
window.growiCookieUtils.getQueryParam('sub_id') ||
cookieInfo.subId ||
window.growiCookieUtils.getCookie('growi_sub_id');
console.log('🔍 Final affiliate detection:', {
urlAffiliateId,
cookieAffiliateId: cookieInfo.affiliateId,
finalAffiliateId: affiliateId,
gcsId,
autoResponderId,
subId,
});
// Run if we have an affiliate from either source OR a gcs_id (brand-built
// Meta ad). visitorUid is always created by manageAffiliateCookies.
if ((affiliateId || gcsId || autoResponderId) && visitorUid) {
// Get UTM parameters to pass to order
const utmParams = window.growiCookieUtils.getUtmParams ? window.growiCookieUtils.getUtmParams() : {};
// Check if traffic is from Facebook/Meta based on utm_source
const isFacebookSource = utmParams.utm_source &&
['fb', 'facebook', 'meta'].includes(utmParams.utm_source.toLowerCase());
// Build cart attributes
const cartAttributes = {
__growi_visitor_uid: visitorUid,
};
if (affiliateId) cartAttributes.__growi_affiliate_id = affiliateId;
if (gcsId) cartAttributes.__growi_gcs_id = gcsId;
if (autoResponderId) cartAttributes.__growi_auto_responder_id = autoResponderId;
if (subId) cartAttributes.__growi_sub_id = subId;
if (utmParams.utm_source) cartAttributes.__growi_utm_source = utmParams.utm_source;
if (utmParams.utm_medium) cartAttributes.__growi_utm_medium = utmParams.utm_medium;
if (utmParams.utm_campaign) cartAttributes.__growi_utm_campaign = utmParams.utm_campaign;
if (utmParams.utm_content) cartAttributes.__growi_utm_content = utmParams.utm_content;
if (utmParams.utm_term) cartAttributes.__growi_utm_term = utmParams.utm_term;
if (utmParams.utm_id) cartAttributes.__growi_utm_id = utmParams.utm_id;
if (utmParams.campaign_id) cartAttributes.__growi_campaign_id = utmParams.campaign_id;
if (utmParams.user_id) cartAttributes.__growi_user_id = utmParams.user_id;
if (utmParams.campaign_affiliate_id) cartAttributes.__growi_campaign_affiliate_id = utmParams.campaign_affiliate_id;
if (utmParams.asset_id) cartAttributes.__growi_asset_id = utmParams.asset_id;
if (utmParams.tw_id) cartAttributes.__growi_tw_id = utmParams.tw_id;
if (isFacebookSource && (utmParams.tw_id || utmParams.utm_id || utmParams.utm_content)) {
cartAttributes.__growi_meta_ad_id = utmParams.tw_id || utmParams.utm_id || utmParams.utm_content;
}
fetch('/cart/update.js', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
attributes: cartAttributes,
}),
})
.then((res) => {
// Extract x-shopid from response headers
const externalId = res.headers.get('x-shopid');
console.log('🏪 Shop ID extracted from headers:', externalId);
return res.json().then((data) => ({ data, externalId }));
})
.then(({ data, externalId }) => {
console.log('✅ Cart attributes updated with affiliate & visitor ID', data);
// --- Discount code logic start ---
// Only fetch if we have an affiliateId and no discount_code cookie
// Only fetch if we have an affiliateId and no discount_code cookie
const discountCode = window.growiCookieUtils.getCookie('discount_code');
const affiliateName = window.growiCookieUtils.getCookie('affiliate_name');
const discountType = window.growiCookieUtils.getCookie('discount_type');
const discountValue = window.growiCookieUtils.getCookie('discount_value');
const campaignType = window.growiCookieUtils.getCookie('campaign_type');
const discountSeen = window.growiCookieUtils.getCookie('discount_seen');
const isDiscountSeen = discountSeen && discountSeen === `${affiliateId}`.toLowerCase();
console.log('Test', affiliateId, discountCode, affiliateId !== 'null', externalId);
if (affiliateId && !!discountCode && affiliateId !== 'null' && isDiscountSeen) {
const bannerDismissed = window.growiCookieUtils.getCookie('banner_dismissed');
const isBannerDismissed = bannerDismissed && bannerDismissed === `${affiliateId}`.toLowerCase();
if (!isBannerDismissed) {
// Check if banner is enabled before showing
fetchCustomization(affiliateId).then((customization) => {
if (customization && customization.banner_enabled) {
showBanner(affiliateId, discountCode, customization);
}
});
}
sendOneTimePageView(externalId);
} else if (
affiliateId &&
(!discountCode || (!!discountCode && !isDiscountSeen)) &&
affiliateId !== 'null'
) {
const currentUrl = window.location.href;
// gcs_id (Growi Content Submission ID) lets the backend attribute
// brand-built Meta ads. Prefer the live URL param, fall back to the
// cookie captured on the landing page.
const gcsId =
window.growiCookieUtils.getQueryParam('gcs_id') ||
window.growiCookieUtils.getCookie('growi_gcs_id');
const gcsQuery = gcsId ? `&gcs_id=${encodeURIComponent(gcsId)}` : '';
fetch(
`https://api.growi.io/api/v1/campaign_affiliates/${encodeURIComponent(
affiliateId
)}/discount_code?url=${encodeURIComponent(currentUrl)}${gcsQuery}`,
{
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
}
)
.then((response) => {
if (!response.ok) throw new Error('Network response was not ok');
return response.json();
})
.then((data) => {
if (data.success) {
cookieDuration = data.cookie_duration;
if (!!cookieDuration) {
updateAffiliateCookieDuration(cookieDuration);
console.log('✅ Affiliate cookie duration updated:', cookieDuration);
}
}
if (data.success && data.discount_code) {
// Set discount_code cookie for 30 days
window.growiCookieUtils.setCookie('discount_code', data.discount_code, 30);
// Set new response format cookies
if (data.affiliate_name) {
window.growiCookieUtils.setCookie('affiliate_name', data.affiliate_name, 30);
}
if (data.discount_type) {
window.growiCookieUtils.setCookie('discount_type', data.discount_type, 30);
}
if (data.discount_value) {
window.growiCookieUtils.setCookie('discount_value', data.discount_value, 30);
}
if (data.campaign_type) {
window.growiCookieUtils.setCookie('campaign_type', data.campaign_type, 30);
}
console.log('✅ Discount code cookies set:', data.discount_code);
// Show discount modal only for ambassador programs and if not seen yet
const discountSeen = window.growiCookieUtils.getCookie('discount_seen');
const isDiscountSeen = discountSeen && discountSeen === `${affiliateId}`.toLowerCase();
if (!isDiscountSeen) {
if (data.is_v2) {
showDiscountModalV2(
data.discount_code,
data.affiliate_name,
data.discount_value,
data.discount_type,
affiliateId
);
} else {
showDiscountModal(
data.discount_code,
data.affiliate_name,
data.discount_value,
data.discount_type,
affiliateId
);
}
} else {
// If modal was already seen, show banner instead (if not dismissed)
const bannerDismissed = window.growiCookieUtils.getCookie('banner_dismissed');
const isBannerDismissed = bannerDismissed && bannerDismissed === `${affiliateId}`.toLowerCase();
if (!isBannerDismissed) {
// Check if banner is enabled before showing
fetchCustomization(affiliateId).then((customization) => {
if (customization && customization.banner_enabled) {
showBanner(affiliateId, data.discount_code, customization);
}
});
}
}
}
})
.catch((error) => {
console.error('There has been a problem with your fetch operation:', error);
});
}
// --- Discount code logic end ---
sendOneTimePageView(externalId);
})
.catch((err) => {
console.error('❌ Failed to update cart attributes', err);
});
} else {
console.log('ℹ️ No affiliate data to process');
}
}
// Start the process - wait for utils first, then URL stability
console.log('🚀 [GROWI-EMBED] Starting waitForUtils() immediately...');
console.log('🚀 [GROWI-EMBED] Current timestamp:', new Date().toISOString());
waitForUtils();
// Also try on window.load as a fallback for very slow WebViews
window.addEventListener('load', () => {
setTimeout(() => {
console.log('🔄 [WINDOW-LOAD] Window load backup trigger fired (500ms after window.load)');
console.log('🔄 [WINDOW-LOAD] window.growiCookieUtils exists?', typeof window.growiCookieUtils !== 'undefined');
initializeAffiliate();
}, 500);
});
})();
Skip to main content
It all started with
A Simple Wish To Feel a Little Better Every Day
We weren’t trying to start a company. We were just trying to feel better. We loved the warmth and comfort of our morning coffee ritual, but not the crash, jitters, or gut issues that came with it. There had to be a better way. So, we looked to nature, ancient practices, and time-tested traditions. That’s when we found mushrooms. They’d been used for centuries to support energy, immunity, clarity, and calm. We didn’t need to overcomplicate it. We just needed to make them easier to enjoy.
What Started with Coffee Became a Movement
As people shared how RYZE was changing their mornings, they started asking for more. Something with a different taste. Something without caffeine. Something to wind down with. So we listened. We launched matcha and chicory. Then cocoa. Then chai. Every scoop, every blend, every idea we bring to life is shaped by the people who drink it. Because we’re not just building better products. We’re building a better way to feel.
Millions Strong and Growing Every Day
What began as a personal quest has become a thriving community. Today, millions start their day with RYZE—not just for the energy, but for the way it makes them feel. What we’ve built together is more than a product. It’s a community of people who care about how they live and how they show up—for themselves and each other. Whether you’re here for better focus, deeper sleep, or just a calmer start to your day, you’re in good company.
Meet Rye
Our Mushroom Mascot
He’s small but mighty—the kind of presence that makes you feel better just by being there. Rye is your gentle guide, always arriving right on time with a quiet boost of support. No words needed. Just good energy.
Founders’ note
A Note From Our Founders
Hi, we’re Andrée and Rashad, and we’re so happy you’re here. What started as a small shift in our own mornings has turned into something so much bigger, and that’s because of you. You gave RYZE a place in your morning, your routine, your life. For that, we’re endlessly grateful. Keep sipping, keep sharing, keep making it your own. We’ll be right here, cheering you on.
Andrée + Rashad
Meet the team
The Team Behind The Blend
We're a small but mighty crew—creatives, strategists, scientists, and more—brought together by a shared belief: that something as simple as your morning ritual can change how you feel, and maybe even how you live. Some of us work on the blends. Some of us talk to customers. Some of us obsess over every word, image, and email. But we all show up with the same goal: to make sure that every cup of RYZE helps you feel just a little more like yourself.
“This wellness startup recently came to market with a product that mixes a healthy blend of functional mushrooms with MCT oil and coffee to form a healthy alternative to the conventional coffee blend.”
“RYZE is showing us that energy can come without jitters and irritability, and that calm focus with a cup of coffee is possible.”
“Best overall instant: RYZE Mushroom Coffee”
“Ryze plants itself firmly in the middle of 'mushroom' and 'coffee' with its both funky and roasty flavor, but not too far into either territory”
“For those seeking a streamlined morning routine, Maeng recommends this instant coffee, as it 'provides 48 mg of caffeine per serving and also contains MCT oil and coconut oil,' she says.”
“Ryze is another coffee mixed with functional mushrooms recommended by Amidor. 'This mushroom coffee contains a blend of six different mushrooms with a lower amount of caffeine at 48mg,' says Amidor.”
“This wellness startup recently came to market with a product that mixes a healthy blend of functional mushrooms with MCT oil and coffee to form a healthy alternative to the conventional coffee blend.”
“RYZE is showing us that energy can come without jitters and irritability, and that calm focus with a cup of coffee is possible.”
“Best overall instant: RYZE Mushroom Coffee”
“Ryze plants itself firmly in the middle of 'mushroom' and 'coffee' with its both funky and roasty flavor, but not too far into either territory”
“For those seeking a streamlined morning routine, Maeng recommends this instant coffee, as it 'provides 48 mg of caffeine per serving and also contains MCT oil and coconut oil,' she says.”
“Ryze is another coffee mixed with functional mushrooms recommended by Amidor. 'This mushroom coffee contains a blend of six different mushrooms with a lower amount of caffeine at 48mg,' says Amidor.”
Choosing a selection results in a full page refresh.
Opens in a new window.