0
Đang tải...
Đang tải...
Mã SKU: Đang tải...
|
Đã bán 0
|
|
Truy xuất nguồn gốc
0
Đang tải...
Đảm bảo chất lượng
Giao hàng nhanh
Đóng gói cẩn thận
Thông số kỹ thuật
Thông tin chi tiết về sản phẩm
Đang tải thông số kỹ thuật...
Không thể tải thông số kỹ thuật
Đang tải...
Voucher của cửa hàng
Chọn voucher và nhấn Lưu để dùng khi thanh toán
Chưa có voucher nào cho
cửa hàng này.
0
Đang tải...
Đang tải...
Mã SKU: Đang tải...
0
Đang tải...
Đảm bảo chất lượng
Giao hàng nhanh
Đóng gói cẩn thận
Thông số kỹ thuật
Thông tin chi tiết về sản phẩm
Đang tải thông số kỹ thuật...
Không thể tải thông số kỹ thuật
Đang tải...
Voucher của cửa hàng
Chọn voucher và nhấn Lưu để dùng khi thanh toán
Chưa có voucher nào cho
cửa hàng này.
Đánh giá sản phẩm
(function () {
// Đợi Liferay sẵn sàng (đặc biệt là Liferay.authToken)
function waitForLiferay(callback, maxAttempts) {
maxAttempts = maxAttempts || 50; // Tối đa 5 giây (50 * 100ms)
var attempts = 0;
function check() {
attempts++;
if (typeof Liferay !== 'undefined' && Liferay.authToken) {
callback();
} else if (attempts < maxAttempts) {
setTimeout(check, 100);
} else {
console.warn('[AFF] ⚠️ Liferay.authToken không sẵn sàng sau ' + maxAttempts + ' lần thử, vẫn tiếp tục...');
callback(); // Vẫn chạy callback để thử (có thể backend không cần token)
}
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', check);
} else {
check();
}
}
waitForLiferay(async function () {
// ═══════════════════════════════════════════════════════════════
// 1️⃣ Lấy param từ URL: ưu tiên trackingCode, fallback về affiliate
// URL dạng: https://domain/en/p/product-slug?affiliate=AFF-20260203-K34EWN&trackingCode=aff_1_123_abcd1234
// Hoặc: https://domain/en/p/product-slug?affiliate=AFF-20260203-K34EWN (legacy)
// ═══════════════════════════════════════════════════════════════
const params = new URLSearchParams(window.location.search);
// Ưu tiên trackingCode (mã riêng cho từng sản phẩm), nếu không có thì dùng affiliate (mã chung)
const trackingCode = params.get('trackingCode') || params.get('affiliate');
if (!trackingCode) {
return;
}
// ═══════════════════════════════════════════════════════════════
// Chỉ ghi 1 click / session (tránh trùng khi refresh)
// ═══════════════════════════════════════════════════════════════
if (sessionStorage.getItem('AFF_CLICKED_' + trackingCode)) {
// Vẫn lưu lại trackingCode cho các bước sau (duyệt site, checkout)
try {
sessionStorage.setItem('AFF_TRACK', trackingCode);
document.cookie = 'AFF_TRACK=' + encodeURIComponent(trackingCode) + '; path=/; max-age=' + (7 * 24 * 60 * 60);
} catch (e) { }
return;
}
// ═══════════════════════════════════════════════════════════════
// Lấy productId từ context trang sản phẩm (cùng cách với getProductInfo trong template)
// ═══════════════════════════════════════════════════════════════
function getProductIdFromPage() {
var el = document.querySelector('[data-product-id]');
if (!el) return null;
var raw = (el.getAttribute('data-product-id') || '').toString().replace(/[^\d]/g, '');
if (!raw || raw === '0') return null;
var n = parseInt(raw, 10);
return isNaN(n) ? null : n + 1;
}
var productId = getProductIdFromPage();
if (!productId) {
try {
var Utils = window.CommerceUtils;
if (Utils && typeof Utils.getApiProduct === 'function') {
var api = await Utils.getApiProduct();
if (api && api.productId) {
productId = api.productId;
}
}
} catch (e) { }
}
var clickedAt = new Date().toISOString();
var userAgent = typeof navigator !== 'undefined' ? (navigator.userAgent || '') : '';
// Parse device type từ userAgent (cho field device - Picklist)
function parseDeviceType(ua) {
if (!ua) return null;
ua = ua.toLowerCase();
if (/mobile|android|iphone|ipod|blackberry|iemobile|opera mini/i.test(ua)) {
return 'MOBILE';
}
if (/tablet|ipad|playbook|silk/i.test(ua)) {
return 'TABLET';
}
return 'DESKTOP';
}
var device = parseDeviceType(userAgent);
// Kiểm tra CSRF token
var csrfToken = typeof Liferay !== 'undefined' && Liferay.authToken ? Liferay.authToken : '';
// Ghi click qua server: server lấy IP, kiểm mã còn hiệu lực, chống spam (thay POST /o/c/affiliateclicks từ trình duyệt)
fetch('/o/gocgac-affiliate/v1/clicks', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'x-csrf-token': csrfToken },
credentials: 'include',
body: JSON.stringify({ trackingCode: trackingCode, productId: productId || 0, sub1: new URLSearchParams(window.location.search).get('sub1') || '', sub2: new URLSearchParams(window.location.search).get('sub2') || '', sub3: new URLSearchParams(window.location.search).get('sub3') || '' })
})
.then(function (res) {
return res.json().catch(function () { return {}; }).then(function (data) {
if (!res.ok || !data || !data.ok) {
console.warn('[AFF] Mã tiếp thị liên kết không hợp lệ hoặc đã ngừng:', trackingCode);
}
return { ok: res.ok && !!(data && data.ok) };
});
})
.then(function (r) {
if (!r || !r.ok) {
console.warn('[AFF] ⚠️ Response không OK, không lưu sessionStorage');
return;
}
sessionStorage.setItem('AFF_CLICKED_' + trackingCode, 'true');
// 3️⃣ Lưu trackingCode để dùng cho các bước sau (giữ khi user duyệt site, gán đơn hàng lúc checkout)
try {
sessionStorage.setItem('AFF_TRACK', trackingCode);
document.cookie = 'AFF_TRACK=' + encodeURIComponent(trackingCode) + '; path=/; max-age=' + (7 * 24 * 60 * 60);
} catch (e) {
console.warn('[AFF] ⚠️ Lỗi lưu sessionStorage/cookie:', e);
}
})
.catch(function (err) {
console.error('[AFF] ❌ Lỗi trong quá trình tracking:', err);
console.error('[AFF] Error details:', {
message: err.message,
stack: err.stack
});
});
}); // End waitForLiferay callback
})();
0
Dựa trên 0 đánh giá
lucide.createIcons();
<script src="https://cdn.tailwindcss.com">
// ==================== SKU VARIANT SELECTOR ====================
(function () {
'use strict';
function parseNum(v) {
var n = Number(v);
return isNaN(n) ? 0 : n;
}
function formatVnd(n) {
var num = parseNum(n);
if (num <= 0) return 'Liên hệ';
return Math.round(num).toLocaleString('vi-VN') + 'đ';
}
function resolveSkuPrices(sku) {
function pickPositive(candidates) {
for (var i = 0; i < candidates.length; i++) {
var n = Number(candidates[i]);
if (!isNaN(n) && n > 0) return n;
}
return 0;
}
var primaryUom = null;
if (sku && Array.isArray(sku.skuUnitOfMeasures) && sku.skuUnitOfMeasures.length > 0) {
primaryUom = sku.skuUnitOfMeasures.find(function (u) { return u && u.primary === true; }) || sku.skuUnitOfMeasures[0];
}
// Giá trực tiếp từ skuStockLevels (có thể = 0 → Liên hệ), không nhảy sang UOM/field khác
var directList = sku && sku.price !== undefined && sku.price !== null ? Number(sku.price) : NaN;
var directPromo = sku && sku.promoPrice !== undefined && sku.promoPrice !== null ? Number(sku.promoPrice) : NaN;
var hasDirect = (!isNaN(directList) && directList >= 0) || (!isNaN(directPromo) && directPromo >= 0);
var listPrice;
var promoPrice;
if (hasDirect) {
listPrice = !isNaN(directList) && directList >= 0 ? directList : 0;
promoPrice = !isNaN(directPromo) && directPromo >= 0 ? directPromo : 0;
} else {
listPrice = pickPositive([
primaryUom && primaryUom.basePrice,
primaryUom && primaryUom.price,
sku.basePrice,
sku.listPrice,
sku.priceModel && sku.priceModel.price,
sku.priceModel && sku.priceModel.basePrice,
sku.priceInfo && sku.priceInfo.price,
sku.priceInfo && sku.priceInfo.basePrice
]);
promoPrice = pickPositive([
primaryUom && primaryUom.promoPrice,
primaryUom && primaryUom.finalPrice,
sku.discountedPrice,
sku.finalPrice,
sku.priceModel && sku.priceModel.finalPrice,
sku.priceInfo && sku.priceInfo.finalPrice
]);
}
if (listPrice <= 0 && promoPrice > 0) {
listPrice = promoPrice;
promoPrice = 0;
}
return {
listPrice: listPrice,
promoPrice: promoPrice
};
}
function syncNoVariantBadge(hasVariants) {
var tagsContainer = document.getElementById('product-category-tags');
if (!tagsContainer) return;
var spans = tagsContainer.querySelectorAll('span');
Array.prototype.forEach.call(spans, function (sp) {
var txt = (sp.textContent || '').trim();
if (txt === 'Chưa có phân loại' && hasVariants) {
sp.remove();
}
});
}
function getStockFromSku(sku) {
var candidates = [sku.inventoryLevel, sku.stockQuantity, sku.stockLevel, sku.quantity, sku.availableQuantity];
for (var i = 0; i < candidates.length; i++) {
var v = parseInt(candidates[i], 10);
if (!isNaN(v)) return v;
}
return null;
}
function enrichSkuInventoryLevels(skus) {
if (!Array.isArray(skus) || skus.length === 0) return skus || [];
var output = [];
for (var i = 0; i < skus.length; i++) {
var sku = skus[i];
var merged = sku;
if (window.CommerceUtils && window.CommerceUtils.apiProduct && window.CommerceUtils.apiProduct.skuStockLevels && typeof window.getSkuDataFromSkuStockLevels === 'function') {
var meta = window.getSkuDataFromSkuStockLevels(window.CommerceUtils.apiProduct.skuStockLevels, sku.id);
if (meta) {
var mp = meta.price;
var mpp = meta.promoPrice;
merged = Object.assign({}, sku, {
stockLevel: meta.stockLevel,
price: (mp !== undefined && mp !== null && !isNaN(Number(mp))) ? Number(mp) : sku.price,
promoPrice: (mpp !== undefined && mpp !== null && !isNaN(Number(mpp))) ? Number(mpp) : sku.promoPrice,
skuUnitOfMeasureKey: meta.skuUnitOfMeasureKey || sku.skuUnitOfMeasureKey
});
if ((!merged.skuUnitOfMeasures || merged.skuUnitOfMeasures.length === 0) && meta.skuUnitOfMeasureKey) {
merged.skuUnitOfMeasures = [{
key: meta.skuUnitOfMeasureKey,
name: meta.unitOfMeasure || meta.skuUnitOfMeasureKey,
primary: true
}];
}
}
}
var stock = getStockFromSku(merged);
if (stock != null) {
output.push(merged);
continue;
}
// Dữ liệu đã có đủ trong apiProduct.skuStockLevels, không cần fetch thêm
output.push(merged);
}
return output;
}
function getSkuLabel(sku, idx) {
try {
if (sku.skuOptions && Array.isArray(sku.skuOptions) && sku.skuOptions.length > 0) {
var parts = sku.skuOptions.map(function (opt) {
return opt.value || opt.key || opt.optionValueKey || '';
}).filter(Boolean);
if (parts.length) return parts.join(' / ');
}
if (sku.optionValueKey) return String(sku.optionValueKey);
if (sku.sku) return String(sku.sku);
} catch (e) { }
return 'Phân loại ' + (idx + 1);
}
function normalizeUOM(rawUOM) {
if (!rawUOM) return null;
var nameValue = 'Piece';
if (rawUOM.name) {
if (typeof rawUOM.name === 'string') {
nameValue = rawUOM.name;
} else if (typeof rawUOM.name === 'object') {
nameValue = rawUOM.name.vi_VN || rawUOM.name.en_US || Object.values(rawUOM.name)[0] || 'Piece';
}
}
return {
key: rawUOM.key,
name: nameValue,
precision: rawUOM.precision !== undefined ? rawUOM.precision : 0,
primary: rawUOM.primary !== undefined ? rawUOM.primary : true,
priority: rawUOM.priority !== undefined ? rawUOM.priority : 0,
rate: rawUOM.rate !== undefined ? rawUOM.rate : 1,
incrementalOrderQuantity: rawUOM.incrementalOrderQuantity !== undefined ? rawUOM.incrementalOrderQuantity : 1
};
}
function applySkuToUi(sku) {
var resolvedPrices = resolveSkuPrices(sku || {});
var listPrice = parseNum(resolvedPrices.listPrice);
var promoPrice = parseNum(resolvedPrices.promoPrice);
var finalPrice = promoPrice > 0 ? promoPrice : listPrice;
var stock = getStockFromSku(sku);
if (stock == null && window.CommerceUtils && window.CommerceUtils.apiProduct && window.CommerceUtils.apiProduct.skuStockLevels && typeof window.getStockFromSkuStockLevels === 'function') {
var gsUi = window.getStockFromSkuStockLevels(window.CommerceUtils.apiProduct.skuStockLevels, sku.id);
if (gsUi != null) {
stock = gsUi;
}
}
var inStock = stock == null || stock > 0;
var canPurchase = inStock && finalPrice > 0;
var priceDesktop = document.getElementById('product-total-price');
var priceMobile = document.getElementById('product-total-price-mobile');
var unitPriceDesktop = document.getElementById('product-unit-price');
var unitPriceMobile = document.getElementById('product-unit-price-mobile');
var stockInfo = document.getElementById('stock-info');
var btnIds = ['btn-add-to-cart-desktop', 'btn-add-to-cart-mobile', 'btn-buy-now-desktop', 'btn-buy-now-mobile'];
// (Removed fallback to avoid mutating apiProduct and displaying previous price instead of 'Liên hệ' when price is 0)
var priceHtml = finalPrice > 0 ? formatVnd(finalPrice) : 'Liên hệ';
if (priceDesktop) {
priceDesktop.innerHTML = priceHtml;
priceDesktop.setAttribute('data-sku-id', String(sku.id || 0));
priceDesktop.setAttribute('data-list-price', String(listPrice || 0));
priceDesktop.setAttribute('data-promo-price', String(promoPrice || 0));
priceDesktop.setAttribute('data-unit-of-measure-key', String(sku.skuUnitOfMeasureKey || ''));
}
if (priceMobile) {
priceMobile.innerHTML = priceHtml;
priceMobile.setAttribute('data-sku-id', String(sku.id || 0));
priceMobile.setAttribute('data-list-price', String(listPrice || 0));
priceMobile.setAttribute('data-promo-price', String(promoPrice || 0));
priceMobile.setAttribute('data-unit-of-measure-key', String(sku.skuUnitOfMeasureKey || ''));
}
if (unitPriceDesktop) {
unitPriceDesktop.setAttribute('data-unit-price', String(finalPrice > 0 ? finalPrice : 0));
unitPriceDesktop.textContent = String(finalPrice > 0 ? finalPrice : 0);
}
if (unitPriceMobile) {
unitPriceMobile.setAttribute('data-unit-price', String(finalPrice > 0 ? finalPrice : 0));
unitPriceMobile.textContent = String(finalPrice > 0 ? finalPrice : 0);
}
// Đồng bộ gạch giá / % khi đổi phân loại (tránh vướng giá SP trước)
var hasDiscount = listPrice > 0 && finalPrice > 0 && listPrice > finalPrice;
var discountPercent = hasDiscount ? Math.round(((listPrice - finalPrice) / listPrice) * 100) : 0;
var listLineText = listPrice > 0 ? (Math.round(listPrice).toLocaleString('vi-VN') + '₫') : '';
var discountLineText = '-' + discountPercent + '%';
var listElM = document.getElementById('product-list-price-mobile');
var pctElM = document.getElementById('product-discount-percent-mobile');
if (listElM && pctElM) {
if (hasDiscount) {
listElM.style.display = '';
listElM.textContent = listLineText;
pctElM.style.display = '';
pctElM.textContent = discountLineText;
} else {
listElM.style.display = 'none';
pctElM.style.display = 'none';
}
}
var listElD = document.getElementById('product-list-price');
var pctElD = document.getElementById('product-discount-percent');
if (listElD && pctElD) {
if (hasDiscount) {
listElD.style.display = '';
listElD.textContent = listLineText;
pctElD.style.display = '';
pctElD.textContent = discountLineText;
} else {
listElD.style.display = 'none';
pctElD.style.display = 'none';
}
}
if (stockInfo) {
if (stock != null) {
stockInfo.setAttribute('data-max-quantity', String(Math.max(0, stock)));
stockInfo.innerHTML = stock > 0
? 'Còn ' + Math.round(stock).toLocaleString('vi-VN') + ' sản phẩm'
: 'Hết hàng';
} else {
stockInfo.removeAttribute('data-max-quantity');
stockInfo.innerHTML = 'Đang cập nhật tồn kho theo phân loại';
}
}
btnIds.forEach(function (id) {
var btn = document.getElementById(id);
if (btn) btn.disabled = !canPurchase;
});
if (window.CommerceUtils && window.CommerceUtils.apiProduct) {
var rawUomList = sku.skuUnitOfMeasures || [];
var primaryUom = rawUomList.find(function (u) { return u.primary === true; }) || rawUomList[0];
window.CommerceUtils.apiProduct.skuId = sku.id || null;
window.CommerceUtils.apiProduct.skuUnitOfMeasureKey = sku.skuUnitOfMeasureKey || (primaryUom ? primaryUom.key : '');
window.CommerceUtils.apiProduct.skuUnitOfMeasure = normalizeUOM(primaryUom);
window.CommerceUtils.apiProduct.stockLevel = stock;
window.CommerceUtils.apiProduct.price = listPrice;
window.CommerceUtils.apiProduct.promoPrice = promoPrice;
window.CommerceUtils.apiProduct.finalPrice = finalPrice;
}
var quantityInput = document.getElementById('quantity-input');
var quantityInputMobile = document.getElementById('quantity-input-mobile');
if (quantityInput) {
// Khi đổi SKU thì reset về 1 để tính giá/tồn theo biến thể mới
quantityInput.value = '1';
if (quantityInputMobile) quantityInputMobile.value = '1';
quantityInput.dispatchEvent(new Event('change', { bubbles: true }));
}
}
function renderSkuOptions(skus) {
var section = document.getElementById('sku-variant-section');
var container = document.getElementById('sku-variant-options');
if (!section || !container) return;
if (!Array.isArray(skus) || skus.length === 0) {
syncNoVariantBadge(false);
section.classList.remove('hidden');
container.innerHTML = 'Chưa có dữ liệu phân loại.';
return;
}
if (skus.length === 1) {
syncNoVariantBadge(false);
section.classList.add('hidden');
return;
}
syncNoVariantBadge(true);
container.innerHTML = '';
section.classList.remove('hidden');
var currentSkuId = window.CommerceUtils && window.CommerceUtils.apiProduct
? parseInt(window.CommerceUtils.apiProduct.skuId, 10)
: null;
var selectedIdx = 0;
if (currentSkuId) {
var found = skus.findIndex(function (s) { return parseInt(s.id, 10) === currentSkuId; });
if (found >= 0) selectedIdx = found;
}
skus.forEach(function (sku, idx) {
var btn = document.createElement('button');
btn.type = 'button';
btn.className = 'px-3 py-1.5 rounded border text-sm transition-colors ' +
(idx === selectedIdx ? 'border-emerald-600 text-emerald-700 bg-emerald-50' : 'border-gray-300 text-gray-700 hover:border-emerald-400');
btn.textContent = getSkuLabel(sku, idx);
btn.addEventListener('click', function () {
Array.prototype.forEach.call(container.children, function (el) {
el.className = 'px-3 py-1.5 rounded border text-sm transition-colors border-gray-300 text-gray-700 hover:border-emerald-400';
});
btn.className = 'px-3 py-1.5 rounded border text-sm transition-colors border-emerald-600 text-emerald-700 bg-emerald-50';
applySkuToUi(sku);
});
container.appendChild(btn);
});
applySkuToUi(skus[selectedIdx]);
}
/** Đồng bộ: map từ apiProduct (by-id) — không gọi API SKU / Admin. */
function buildVariantSkusFromApiProduct(ap) {
if (!ap) {
return { skus: [], resolvedProductId: null };
}
try {
if (ap.skuStockLevels && ap.skuStockLevels.length > 0) {
return {
skus: ap.skuStockLevels.map(function (row) {
// Giá từng SKU độc lập: 0 = chưa niêm yết, không gộp giá cấp sản phẩm (tránh SIZE S hiện giá SIZE M)
var rp = row.price;
var rpp = row.promoPrice;
var rowList = (rp !== undefined && rp !== null && !isNaN(Number(rp))) ? Number(rp) : NaN;
var rowPromo = (rpp !== undefined && rpp !== null && !isNaN(Number(rpp))) ? Number(rpp) : NaN;
return {
id: row.skuId,
sku: row.sku || ('SKU ' + row.skuId),
price: !isNaN(rowList) ? rowList : (Number(ap.listPrice) || Number(ap.price) || 0),
promoPrice: !isNaN(rowPromo) ? rowPromo : (Number(ap.promoPrice) || 0),
stockLevel: row.stockLevel,
skuUnitOfMeasureKey: row.skuUnitOfMeasureKey || ap.skuUnitOfMeasureKey,
skuUnitOfMeasures: (row.skuUnitOfMeasureKey || ap.skuUnitOfMeasureKey)
? [{
key: row.skuUnitOfMeasureKey || ap.skuUnitOfMeasureKey,
name: row.unitOfMeasure || row.skuUnitOfMeasureKey || ap.skuUnitOfMeasureKey,
primary: true
}]
: (ap.skuUnitOfMeasure ? [ap.skuUnitOfMeasure] : [])
};
}),
resolvedProductId: ap.productId || null
};
}
if (ap.skuId) {
return {
skus: [{
id: ap.skuId,
sku: 'SKU ' + ap.skuId,
price: ap.price,
promoPrice: ap.promoPrice,
stockLevel: ap.stockLevel,
skuUnitOfMeasureKey: ap.skuUnitOfMeasureKey,
skuUnitOfMeasures: ap.skuUnitOfMeasure ? [ap.skuUnitOfMeasure] : []
}],
resolvedProductId: ap.productId || null
};
}
} catch (e) { }
return { skus: [], resolvedProductId: null };
}
async function initVariantSelector() {
try {
if (!window.CommerceUtils || typeof window.CommerceUtils.getApiProduct !== 'function') return;
var apiProduct = await window.CommerceUtils.getApiProduct();
if (!apiProduct || !apiProduct.productId) return;
var result = buildVariantSkusFromApiProduct(apiProduct);
var enrichedSkus = enrichSkuInventoryLevels(result.skus || []);
renderSkuOptions(enrichedSkus);
} catch (e) {
console.warn('initVariantSelector error:', e);
var section = document.getElementById('sku-variant-section');
var container = document.getElementById('sku-variant-options');
if (section && container) {
section.classList.remove('hidden');
container.innerHTML = 'Không tải được phân loại.';
}
}
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initVariantSelector);
} else {
initVariantSelector();
}
})();
Đánh giá sản phẩm
(function () { // Đợi Liferay sẵn sàng (đặc biệt là Liferay.authToken) function waitForLiferay(callback, maxAttempts) { maxAttempts = maxAttempts || 50; // Tối đa 5 giây (50 * 100ms) var attempts = 0; function check() { attempts++; if (typeof Liferay !== 'undefined' && Liferay.authToken) { callback(); } else if (attempts < maxAttempts) { setTimeout(check, 100); } else { console.warn('[AFF] ⚠️ Liferay.authToken không sẵn sàng sau ' + maxAttempts + ' lần thử, vẫn tiếp tục...'); callback(); // Vẫn chạy callback để thử (có thể backend không cần token) } } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', check); } else { check(); } } waitForLiferay(async function () { // ═══════════════════════════════════════════════════════════════ // 1️⃣ Lấy param từ URL: ưu tiên trackingCode, fallback về affiliate // URL dạng: https://domain/en/p/product-slug?affiliate=AFF-20260203-K34EWN&trackingCode=aff_1_123_abcd1234 // Hoặc: https://domain/en/p/product-slug?affiliate=AFF-20260203-K34EWN (legacy) // ═══════════════════════════════════════════════════════════════ const params = new URLSearchParams(window.location.search); // Ưu tiên trackingCode (mã riêng cho từng sản phẩm), nếu không có thì dùng affiliate (mã chung) const trackingCode = params.get('trackingCode') || params.get('affiliate'); if (!trackingCode) { return; } // ═══════════════════════════════════════════════════════════════ // Chỉ ghi 1 click / session (tránh trùng khi refresh) // ═══════════════════════════════════════════════════════════════ if (sessionStorage.getItem('AFF_CLICKED_' + trackingCode)) { // Vẫn lưu lại trackingCode cho các bước sau (duyệt site, checkout) try { sessionStorage.setItem('AFF_TRACK', trackingCode); document.cookie = 'AFF_TRACK=' + encodeURIComponent(trackingCode) + '; path=/; max-age=' + (7 * 24 * 60 * 60); } catch (e) { } return; } // ═══════════════════════════════════════════════════════════════ // Lấy productId từ context trang sản phẩm (cùng cách với getProductInfo trong template) // ═══════════════════════════════════════════════════════════════ function getProductIdFromPage() { var el = document.querySelector('[data-product-id]'); if (!el) return null; var raw = (el.getAttribute('data-product-id') || '').toString().replace(/[^\d]/g, ''); if (!raw || raw === '0') return null; var n = parseInt(raw, 10); return isNaN(n) ? null : n + 1; } var productId = getProductIdFromPage(); if (!productId) { try { var Utils = window.CommerceUtils; if (Utils && typeof Utils.getApiProduct === 'function') { var api = await Utils.getApiProduct(); if (api && api.productId) { productId = api.productId; } } } catch (e) { } } var clickedAt = new Date().toISOString(); var userAgent = typeof navigator !== 'undefined' ? (navigator.userAgent || '') : ''; // Parse device type từ userAgent (cho field device - Picklist) function parseDeviceType(ua) { if (!ua) return null; ua = ua.toLowerCase(); if (/mobile|android|iphone|ipod|blackberry|iemobile|opera mini/i.test(ua)) { return 'MOBILE'; } if (/tablet|ipad|playbook|silk/i.test(ua)) { return 'TABLET'; } return 'DESKTOP'; } var device = parseDeviceType(userAgent); // Kiểm tra CSRF token var csrfToken = typeof Liferay !== 'undefined' && Liferay.authToken ? Liferay.authToken : ''; // Ghi click qua server: server lấy IP, kiểm mã còn hiệu lực, chống spam (thay POST /o/c/affiliateclicks từ trình duyệt) fetch('/o/gocgac-affiliate/v1/clicks', { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-csrf-token': csrfToken }, credentials: 'include', body: JSON.stringify({ trackingCode: trackingCode, productId: productId || 0, sub1: new URLSearchParams(window.location.search).get('sub1') || '', sub2: new URLSearchParams(window.location.search).get('sub2') || '', sub3: new URLSearchParams(window.location.search).get('sub3') || '' }) }) .then(function (res) { return res.json().catch(function () { return {}; }).then(function (data) { if (!res.ok || !data || !data.ok) { console.warn('[AFF] Mã tiếp thị liên kết không hợp lệ hoặc đã ngừng:', trackingCode); } return { ok: res.ok && !!(data && data.ok) }; }); }) .then(function (r) { if (!r || !r.ok) { console.warn('[AFF] ⚠️ Response không OK, không lưu sessionStorage'); return; } sessionStorage.setItem('AFF_CLICKED_' + trackingCode, 'true'); // 3️⃣ Lưu trackingCode để dùng cho các bước sau (giữ khi user duyệt site, gán đơn hàng lúc checkout) try { sessionStorage.setItem('AFF_TRACK', trackingCode); document.cookie = 'AFF_TRACK=' + encodeURIComponent(trackingCode) + '; path=/; max-age=' + (7 * 24 * 60 * 60); } catch (e) { console.warn('[AFF] ⚠️ Lỗi lưu sessionStorage/cookie:', e); } }) .catch(function (err) { console.error('[AFF] ❌ Lỗi trong quá trình tracking:', err); console.error('[AFF] Error details:', { message: err.message, stack: err.stack }); }); }); // End waitForLiferay callback })();0
Dựa trên 0 đánh giá