2-Piece Sweatshirt & Pants Set – Comfy Travel or Home Outfit
${function() {
const variantData = data.variant || {"id":"65be1a1c-8c5c-4923-a40a-06a732bed6f6","product_id":"61c60c5f-ce35-4f58-ac28-d1df39e862bc","title":"BLUE-4-6","weight_unit":"kg","inventory_quantity":-34,"sku":"","barcode":"","position":27,"option1":"BLUE","option2":"4-6","option3":"","note":"","image":{"src":"\/\/img.staticdj.com\/969b9c099723523bed71a4ac69a18be1.webp","path":"969b9c099723523bed71a4ac69a18be1.webp","width":800,"height":800,"alt":"","aspect_ratio":1},"wholesale_price":[{"price":4.26,"min_quantity":1}],"weight":"0","compare_at_price":"36.99","price":"4.26","retail_price":"36.99","available":true,"url":"\/products\/cnryt?variant=65be1a1c-8c5c-4923-a40a-06a732bed6f6","available_quantity":999999999,"options":[{"name":"color \uff08Want different sizes, leave color-size comment in cart\uff09","value":"BLUE"},{"name":"Top size\uff08Guaranteed return\/exchange if size not fit\uff09","value":"4-6"}],"off_ratio":88,"flashsale_info":[],"sales":52};
const saveType = "percentage";
const productLabelDiscountOn = true;
return `
-
${saveType == 'percentage'
? `-${variantData.off_ratio}% `
: `- `
}
`;
}()}
color (Want different sizes, leave color-size comment in cart):
BLUE
${function(){
return `${data.value} `;
}()}
${function(){
const tipText = "Please select a {{ name }}".replace(/\{\{\s+name\s+\}\}/g, data);
return `${tipText}
`
}()}
Top size(Guaranteed return/exchange if size not fit):
4-6
${function(){
return `${data.value} `;
}()}
${function(){
const tipText = "Please select a {{ name }}".replace(/\{\{\s+name\s+\}\}/g, data);
return `${tipText}
`
}()}
Add to cart
$4.26
${function(){
const wholesale_enabled = false;
const qty = data.quantity || 1;
const currentSelectVariant = data.variant;
const defaultVariant = (data.product && data.product.variants && data.product.variants[0]);
const productVariant = {"id":"65be1a1c-8c5c-4923-a40a-06a732bed6f6","product_id":"61c60c5f-ce35-4f58-ac28-d1df39e862bc","title":"BLUE-4-6","weight_unit":"kg","inventory_quantity":-34,"sku":"","barcode":"","position":27,"option1":"BLUE","option2":"4-6","option3":"","note":"","image":{"src":"\/\/img.staticdj.com\/969b9c099723523bed71a4ac69a18be1.webp","path":"969b9c099723523bed71a4ac69a18be1.webp","width":800,"height":800,"alt":"","aspect_ratio":1},"wholesale_price":[{"price":4.26,"min_quantity":1}],"weight":"0","compare_at_price":"36.99","price":"4.26","retail_price":"36.99","available":true,"url":"\/products\/cnryt?variant=65be1a1c-8c5c-4923-a40a-06a732bed6f6","available_quantity":999999999,"options":[{"name":"color \uff08Want different sizes, leave color-size comment in cart\uff09","value":"BLUE"},{"name":"Top size\uff08Guaranteed return\/exchange if size not fit\uff09","value":"4-6"}],"off_ratio":88,"flashsale_info":[],"sales":52};
const variantData = currentSelectVariant || defaultVariant || productVariant;
const wholesale_price = variantData.wholesale_price || [];
if(wholesale_enabled && wholesale_price.length > 0) {
let wholesaleIndex = wholesale_price.findIndex(item => {
return item.min_quantity > qty;
});
if(wholesaleIndex < 0){
wholesaleIndex = wholesale_price.length - 1;
}else if(wholesaleIndex > 0){
wholesaleIndex = wholesaleIndex - 1;
}
const wholesalePrice = wholesale_price[wholesaleIndex] || '';
return `
`
}else {
const price = variantData && variantData.price;
return price != undefined ? `
` : ' ';
}
}()}
Buy now
Product was out of stock.
Product is unavailable.
const getPluginI18nMessages = (message, replaceObj = {}) => {
const lang = document.documentElement.lang || "en-US";
const [form, key] = message.split('.')
let text = window.payment_plugin_message['en-US'][form][key];
if (window.payment_plugin_message[lang][form].hasOwnProperty(key)) {
text = window.payment_plugin_message[lang][form][key];
}
Object.keys(replaceObj).forEach(key => {
text = text.replace(new RegExp(`\{${key}\}`, 'gi'), replaceObj[key]);
})
return text;
}
const zhCN = {
ec: {
not_active_channel: "请到收款设置中{channelName}或在「快捷支付按钮」设置中选择其他的服务提供方,否则按钮将无法展示",
not_support_theme: "当前主题不支持添加「快捷支付按钮」",
more_button: "更多支付方式",
skeleton_layer_tips_title: "快捷支付按钮",
skeleton_layer_tips_content: "请点击左侧列表中的「快捷支付按钮」,在设置页面开启想要的展示的支付按钮",
mock_tips: "快捷支付按钮是否展示还取决于买家使用的浏览器以及商品的货币、金额",
not_find_form_tips: "快捷支付按钮组件仅支持配置到商品详情卡片内",
}
};
const zhTW = {
ec: {
not_active_channel: "请到收款设置中{channelName}或在「快捷支付按钮」设置中选择其他的服务提供方,否则按钮将无法展示",
not_support_theme: "当前主题不支持添加「快捷支付按钮」",
more_button: "更多付款方式",
}
};
const arSA = {
ec: {
not_active_channel: "Please activate {channelName} on B Admin or select another provider in the「Express checkout button」 settings, otherwise it will not display.",
not_support_theme: "This Theme doesn't support adding「Express checkout button」",
more_button: "المزيد من خيارات الدفع",
}
};
const deDE = {
ec: {
not_active_channel: "Please activate {channelName} on B Admin or select another provider in the「Express checkout button」 settings, otherwise it will not display.",
not_support_theme: "This Theme doesn't support adding「Express checkout button」",
more_button: "Weitere Bezahlmöglichkeiten",
}
};
const esES = {
ec: {
not_active_channel: "Please activate {channelName} on B Admin or select another provider in the「Express checkout button」 settings, otherwise it will not display.",
not_support_theme: "This Theme doesn't support adding「Express checkout button」",
more_button: "Más opciones de pago",
}
};
const frFR = {
ec: {
not_active_channel: "Please activate {channelName} on B Admin or select another provider in the「Express checkout button」 settings, otherwise it will not display.",
not_support_theme: "This Theme doesn't support adding「Express checkout button」",
more_button: "Plus d'options de paiement",
}
};
const idID = {
ec: {
not_active_channel: "Please activate {channelName} on B Admin or select another provider in the「Express checkout button」 settings, otherwise it will not display.",
not_support_theme: "This Theme doesn't support adding「Express checkout button」",
more_button: "Opsi pembayaran lainnya",
}
};
const itIT = {
ec: {
not_active_channel: "Please activate {channelName} on B Admin or select another provider in the「Express checkout button」 settings, otherwise it will not display.",
not_support_theme: "This Theme doesn't support adding「Express checkout button」",
more_button: "Altre opzioni di pagamento",
}
};
const jaJP = {
ec: {
not_active_channel: "Please activate {channelName} on B Admin or select another provider in the「Express checkout button」 settings, otherwise it will not display.",
not_support_theme: "This Theme doesn't support adding「Express checkout button」",
more_button: "その他の支払いオプション",
}
};
const koKR = {
ec: {
not_active_channel: "Please activate {channelName} on B Admin or select another provider in the「Express checkout button」 settings, otherwise it will not display.",
not_support_theme: "This Theme doesn't support adding「Express checkout button」",
more_button: "더 많은 결제 옵션",
}
};
const enUS = {
ec: {
not_active_channel: "Please activate {channelName} on B Admin or select another provider in the「Express checkout button」 settings, otherwise it will not display.",
not_support_theme: "This Theme doesn't support adding「Express checkout button」",
more_button: "More payment options",
skeleton_layer_tips_title: "Express Checkout Button",
skeleton_layer_tips_content: "Please click the「Express checkout button」on the block list,then you could enable the payment option you want to display in settings.",
mock_tips: "Whether the Express checkout button is displayed also depends on the browser used by the buyer and the currency and amount of the product.",
not_find_form_tips: "Express Checkout Button could only be added to Product details block.",
}
};
const nlNL = {
ec: {
not_active_channel: "Please activate {channelName} on B Admin or select another provider in the「Express checkout button」 settings, otherwise it will not display.",
not_support_theme: "This Theme doesn't support adding「Express checkout button」",
more_button: "Meer betalingsmogelijkheden",
}
};
const plPL = {
ec: {
not_active_channel: "Please activate {channelName} on B Admin or select another provider in the「Express checkout button」 settings, otherwise it will not display.",
not_support_theme: "This Theme doesn't support adding「Express checkout button」",
more_button: "Więcej Opcji Płatności",
}
};
const ptPT = {
ec: {
not_active_channel: "Please activate {channelName} on B Admin or select another provider in the「Express checkout button」 settings, otherwise it will not display.",
not_support_theme: "This Theme doesn't support adding「Express checkout button」",
more_button: "Mais opções de pagamento",
}
};
const ruRU = {
ec: {
not_active_channel: "Please activate {channelName} on B Admin or select another provider in the「Express checkout button」 settings, otherwise it will not display.",
not_support_theme: "This Theme doesn't support adding「Express checkout button」",
more_button: "Другие варианты оплаты",
}
};
const thTH = {
ec: {
not_active_channel: "Please activate {channelName} on B Admin or select another provider in the「Express checkout button」 settings, otherwise it will not display.",
not_support_theme: "This Theme doesn't support adding「Express checkout button」",
more_button: "ตัวเลือกการชำระเงินเพิ่มเติม",
}
};
window.payment_plugin_message = {
getPluginI18nMessages,
"zh-CN": zhCN,
"zh-TW": zhTW,
"ar-SA": arSA,
"de-DE": deDE,
"es-ES": esES,
"fr-FR": frFR,
"id-ID": idID,
"it-IT": itIT,
"ja-JP": jaJP,
"ko-KR": koKR,
"en-US": enUS,
"nl-NL": nlNL,
"pl-PL": plPL,
"pt-PT": ptPT,
"ru-RU": ruRU,
"th-TH": thTH,
}
document.dispatchEvent(new CustomEvent('payment_plugin_message_reader'));
try {
const dom = document.getElementById('pm-payment-express-button-1539149753700-7');
dom.i18n = window?.payment_plugin_message?.getPluginI18nMessages;
if (dom.i18n) {
document.dispatchEvent(new CustomEvent('payment_ec_core_ready', {
detail: {
i18n: true
}
}))
} else {
document.addEventListener('payment_plugin_message_reader', () => {
dom.i18n = window?.payment_plugin_message?.getPluginI18nMessages;
document.dispatchEvent(new CustomEvent('payment_ec_core_ready', {
detail: {
i18n: true
}
}))
}, {once: true});
}
} catch (e) {
}
// 通用工具方法
try {
const dom = document.getElementById('pm-payment-express-button-1539149753700-7')
const ROOT_URL = (C_SETTINGS && C_SETTINGS.routes && C_SETTINGS.routes.root) || '';
const eventListeners = {};
const commonUtils = function () {
return {
getProduct() {
const productJson = document.querySelector('#product-json');
if (productJson?.textContent) {
return JSON.parse(productJson.textContent);
}
if (window.jQuery) {
const $product = window.jQuery?.(document)?.data('djproduct');
const productData = JSON.parse(JSON.stringify($product || {}));
return productData || {};
}
return {};
},
isChrome() {
return navigator?.userAgent?.indexOf('Chrome') > -1 || navigator?.userAgent?.indexOf('CriOS') > -1;
},
isSafari() {
let userAgentString = navigator.userAgent;
let chromeAgent = userAgentString.indexOf('Chrome') > -1 || navigator?.userAgent?.indexOf('CriOS') > -1;
let safariAgent = userAgentString.indexOf('Safari') > -1;
if (chromeAgent && safariAgent) {
safariAgent = false;
}
return safariAgent;
},
isPreview() {
return !!window?.C_EDITING_SETTINGS?.oseid;
},
multiply(a, b) {
const precision = 2; // 保留两位小数
return Number((a * b).toFixed(precision));
},
loadScript(fnReady, id, src, datasets, onError, attributeConfig = {}) {
const sdkDomId = id + '-sdk';
const loadedEventName = `${id}-loaded`;
if (fnReady()) {
return Promise.resolve({id: true});
}
const existingScript = document.getElementById(sdkDomId);
if (existingScript) {
return new Promise((resolve) => {
const handleLoaded = () => {
if (fnReady()) {
resolve({id: true});
} else {
resolve({id: false});
onError && onError();
}
};
if (existingScript.dataset.loaded === 'true') {
handleLoaded();
} else {
window.addEventListener(loadedEventName, handleLoaded, {once: true});
}
});
}
return new Promise((resolve) => {
const s = document.createElement('script');
s.id = sdkDomId;
s.src = src;
s.defer = true;
if (datasets) {
Object.keys(datasets).map((item) => {
s.dataset[item] = datasets[item];
});
}
s.onload = function () {
s.dataset.loaded = 'true';
window.dispatchEvent(new CustomEvent(loadedEventName));
resolve({id: true});
};
s.onerror = function () {
resolve({id: false});
onError && onError();
};
Object.keys(attributeConfig).forEach((key) => {
s.setAttribute(key, attributeConfig[key]);
});
document.head.appendChild(s);
});
},
track(eventName, data) {
window.sa && window?.sa?.track('pm_' + eventName, JSON.parse(JSON.stringify(data)));
},
getExtUrl(name) {
const url = document.cookie.match(new RegExp('\\b' + name.replace(/_/g, '-') + '-(v[s0-9]+)'));
if (url && url[1]) {
return `${name}.${url[1]}.js`;
} else {
return window?.exts?.[name];
}
},
req: {
post: async (url, data = {}) => {
try {
const response = await fetch(req.ROOT_URL + url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
...data,
body: JSON.stringify(data.body),
});
return await response.json()
} catch (error) {
throw new Error('post request error' + error);
}
},
get: async (url, data = {}) => {
try {
const response = await fetch(ROOT_URL + url);
return await response.json()
} catch (error) {
throw new Error('get request error' + error);
}
}
},
debounce(fn, wait) {
let timeout = null;
return function () {
if (timeout !== null) {
clearTimeout(timeout);
}
timeout = setTimeout(function () {
fn.apply(this, arguments);
}, wait);
}
},
delayCallback(cb) {
window.requestIdleCallback ? requestIdleCallback(cb, {timeout: 50}) : setTimeout(cb, 50);
},
loadFilly(tag, cb, trackEcPerfFn) {
if (!tag) {
return
}
const script = document.createElement('script');
script.type = 'text/javaScript';
script.src = `//static.staticdj.com/${tag}`;
script.onload = function() {
if (trackEcPerfFn && !performance.getEntriesByName('ec_filly_load_end').length) {
performance.mark('ec_filly_load_end');
const fillyStart = performance.getEntriesByName('ec_filly_load_start')[0];
const channels = window?.C_SETTINGS?.payment_settings?.dynamic_config?.express_checkout_config?.express_channels || [];
trackEcPerfFn({
eventName: 'ec_perf_filly_load_end',
extraData: {
filly_load_time: fillyStart ? performance.now() - fillyStart.startTime : null,
payment_channel: channels.join(',')
},
eventType: 'card_expose'
});
}
cb && cb();
};
document.getElementsByTagName('head')[0].appendChild(script);
},
ecEvent: {
on: (eventName, listener, useCapture) => {
eventListeners[eventName] = listener;
window.addEventListener(eventName, listener, useCapture);
},
emit: (eventName, data) => window.dispatchEvent(new CustomEvent(eventName, {detail: data})),
},
trackEcPerf({ eventName, extraData, eventType }) {
const skeletonStart = performance.getEntriesByName('ec_skeleton_start')[0];
const sinceSkeletonStart = skeletonStart ? performance.now() - skeletonStart.startTime : null;
window.sa &&
window.sa.track('function_expose', {
function_name: 'payment_platform',
plugin_name: 'payment_platform',
business_type: 'product_payment',
module: 'payment',
module_type: 'payment_platform',
event_type: eventType || 'expose',
event_developer: 'cartoon',
event_info: JSON.stringify(
Object.assign(
{
process_name: eventName,
process_type: 'perf',
since_skeleton: sinceSkeletonStart
},
extraData || {}
)
)
});
}
}
}
dom.commonUtilsFn = commonUtils;
document.dispatchEvent(new CustomEvent('payment_ec_core_ready', {
detail: {
commonUtils: true
}
}))
} catch (e) {
}
// 核心数据
try {
const dom = document.getElementById('pm-payment-express-button-1539149753700-7');
const coreData = function () {
const {getProduct} = dom.commonUtils;
let productDetail = getProduct();
let productPrice = productDetail?.selected?.price
|| 4.26;
const shopCurrencyCode = "USD";
const expressCheckoutList = {
sdkErrorList: [],
paymentChannelList: [],
disabledChannelList: [],
showChannelList: [],
blockChannelList: [],
extraChannelList: [],
};
const channelType = {
googlepay: ['shoplazzagoogle'],
applepay: ['shoplazzaapple'],
credit: ['paypal']
};
const ecGlobalVarEnums = {
paypal: 'pluginPaypalEC'
};
const providerEnums = {
SHOPLAZZA: 'shoplazza',
STRIPE: 'stripe',
PAYPAL: 'paypal'
};
const channelEnums = {
SHOPLAZZA_GOOGLE: 'shoplazzagoogle',
SHOPLAZZA_APPLE: 'shoplazzaapple',
STRIPE_GOOGLE: 'stripegoogle',
STRIPE_APPLE: 'stripeapple',
PAYPAL: 'paypal'
};
const channelThemeConfig = {
[channelEnums.PAYPAL]: {
default: {
url: 'oss/operation/f557c83808e1cd456411170286a1ea95.svg',
classList: ['paypal-card'],
},
},
[channelEnums.SHOPLAZZA_GOOGLE]: {
light: {
url: 'oss/operation/778afb93da43adf75bdc80b078e5d4fd.svg',
classList: ['googlepay-light'],
},
dark: {
url: 'oss/operation/e53180c224f0b0af44b44663775aa930.svg',
classList: ['googlepay-dark'],
},
},
[channelEnums.SHOPLAZZA_APPLE]: {
light: {
url: 'oss/operation/dadceb884044e0a9bbfe26c15192f542.svg',
classList: ['applepay-light'],
},
dark: {
url: 'oss/operation/6597f66eac8b0681ebfb75941e8f6f52.svg',
classList: ['applepay-dark'],
},
},
};
function getContainerDomId() {
const domIDSuffix = '-express-button-container';
const prefix = 'pm-';
const formatId = (channel) => `${prefix}${channel}${domIDSuffix}-1539149753700-7`;
return {
[providerEnums.PAYPAL]: formatId(providerEnums.PAYPAL),
[channelEnums.SHOPLAZZA_GOOGLE]: formatId(channelEnums.SHOPLAZZA_GOOGLE),
[channelEnums.SHOPLAZZA_APPLE]: formatId(channelEnums.SHOPLAZZA_APPLE),
};
}
return {
ecGlobalVarEnums,
providerEnums,
channelEnums,
productPrice,
shopCurrencyCode,
getChannelThemeConfig(ecName) {
const themeType = window.PaymentEC?.settings?.express_theme_configs?.[ecName]?.theme_type?.toLowerCase() ||
'default';
return channelThemeConfig[ecName][themeType] || channelThemeConfig[ecName]['dark'];
},
getProductPrice() {
return productDetail?.selected?.price;
},
getProductDetail() {
return productDetail;
},
setProductDetail(data) {
productDetail = data;
},
isRequiresShipping() {
return productDetail?.product?.requires_shipping
},
getOpenChannelType() {
const {paymentChannelList, blockChannelList} = expressCheckoutList
const openList = paymentChannelList.filter(item => blockChannelList.includes(item)) || [];
return {
hasApplepay: openList.filter(item => channelType.applepay.includes(item))?.length > 0,
hasGooglepay: openList.filter(item => channelType.googlepay.includes(item))?.length > 0,
hasCredit: openList.filter(item => channelType.credit.includes(item))?.length > 0
}
},
containerDomId: getContainerDomId(),
channel2ProviderEnums: {
[channelEnums.PAYPAL]: channelEnums.PAYPAL,
[channelEnums.SHOPLAZZA_GOOGLE]: channelEnums.SHOPLAZZA_GOOGLE,
[channelEnums.SHOPLAZZA_APPLE]: channelEnums.SHOPLAZZA_APPLE,
[channelEnums.STRIPE_GOOGLE]: providerEnums.STRIPE,
[channelEnums.STRIPE_APPLE]: providerEnums.STRIPE,
},
getExpressCheckoutList() {
return expressCheckoutList;
},
setShowChannel(showChannelList = []) {
expressCheckoutList.showChannelList = showChannelList;
return expressCheckoutList;
},
setBlockChannel(blockChannelList = []) {
expressCheckoutList.blockChannelList = blockChannelList;
return expressCheckoutList;
},
setPaymentChannelList(paymentChannelList = []) {
expressCheckoutList.paymentChannelList = paymentChannelList;
return expressCheckoutList;
},
setSdkErrorList(paymentChannelList = []) {
expressCheckoutList.sdkErrorList = paymentChannelList;
return expressCheckoutList;
},
setExtraChannelList(extraChannelList = []) {
expressCheckoutList.extraChannelList = extraChannelList;
return expressCheckoutList;
},
setDisabledChannelList(disabledChannelList = []) {
expressCheckoutList.disabledChannelList = disabledChannelList;
return expressCheckoutList;
}
}
}
dom.coreDataFn = coreData;
document.dispatchEvent(new CustomEvent('payment_ec_core_ready', {
detail: {
coreData: true
}
}))
} catch (e) {
console.log(e);
}
// 通用业务数据处理方法
try {
const dom = document.getElementById('pm-payment-express-button-1539149753700-7')
const businessUtils = function () {
const {track, isChrome, isSafari, req, isPreview, multiply, loadScript, trackEcPerf} = dom.commonUtils;
const {getProductPrice, containerDomId, ecGlobalVarEnums, channelType, getOpenChannelType} = dom.coreData;
const {
channelEnums,
shopCurrencyCode,
isRequiresShipping,
getProductDetail,
setShowChannel,
setBlockChannel,
setSdkErrorList,
setExtraChannelList,
setDisabledChannelList,
setPaymentChannelList,
getExpressCheckoutList
} = dom.coreData;
const _businessUtils = {
getECConfig: async () => {
if (window.PaymentEC?.settings) {
return window.PaymentEC?.settings;
}
// 优先从 C_SETTINGS 获取 express_checkout_config
let ecConfig = window?.C_SETTINGS?.payment_settings?.dynamic_config?.express_checkout_config;
// 如果 C_SETTINGS 中没有,使用接口请求作为兜底
if (!ecConfig) {
const result = await req.get('/api/payment/settings');
ecConfig = result?.settings?.express_checkout_config || {};
}
const {blockChannelList} = getExpressCheckoutList();
const filteredExpressChannels = blockChannelList.filter(ecName =>
ecConfig?.express_channels?.includes(ecName)) || [];
setPaymentChannelList(filteredExpressChannels);
window.PaymentEC.settings = {
...ecConfig,
express_channels: filteredExpressChannels,
currencyCode: shopCurrencyCode
};
return window.PaymentEC.settings;
},
getAttributeConfig(channelInfo) {
const {ecGlobalVar, ecName} = channelInfo;
const config = {
paypal: {
'data-namespace': ecGlobalVar
}
};
return config[ecName] || {};
},
getThemeFormData() {
let themeFormData = {};
const formDOM = dom.closest("form");
if (formDOM) {
themeFormData = {
note: '',
product_id: '',
variant_id: '',
quantity: 1,
properties: {},
};
const formData = new FormData(formDOM);
const formDataKey = formData.keys();
for (const key of formDataKey) {
const value = formData.get(key);
const propertiesKey = key.match(/^properties(?:\.(\w+)$|\[(\w+)\]$)/);
if (!propertiesKey) {
themeFormData[key] = value;
continue;
}
const objKey = propertiesKey[1] || propertiesKey[2];
themeFormData['properties'] = {...themeFormData['properties'], [objKey]: value};
}
}
return themeFormData;
},
getProductFormData() {
const themeFormData = _businessUtils.getThemeFormData()
return [{
...themeFormData,
note: themeFormData?.note || "",
product_id: themeFormData?.product_id || "",
variant_id: themeFormData?.variant_id || "",
quantity: themeFormData?.quantity || 1,
// 与主题确认,只以一个为准,防止form不存在的数据仍被传递
properties: themeFormData?.properties || {},
}]
},
getOrderFetchParams(data) {
if (!data) {
return {};
}
return {
line_items: data.map((item) => ({
...item,
note: item?.note || "",
quantity: item?.quantity || 1,
product_id: item?.product_id,
variant_id: item?.variant_id,
properties: item?.properties,
})),
refer_info: {
source: 'buy_now',
},
customer_note: '',
};
},
isAllowTheme() {
const allowThemeList = ['Nova 2023', 'Dropshiping', 'Geek', 'Hero', 'Eva'];
const currentTheme = window?.C_SETTINGS?.theme?.merchant_theme_name;
return allowThemeList.includes(currentTheme);
},
getSubscriptionIdInit() {
let defaultID;
const selectSubscriptionEnum = {
CLOSE: 1,
ACTIVE: 2,
}
const productDetail = getProductDetail();
const sellingPlan = "";
if (!sellingPlan || typeof sellingPlan !== "object") {
return null;
}
let sellingItems;
if (sellingPlan?.spu?.[productDetail?.product?.id]) {
sellingItems = sellingPlan.spu[productDetail?.product?.id]
}
if (sellingPlan?.sku?.[productDetail?.selected?.id]) {
sellingItems = sellingPlan.sku[productDetail?.product?.id]
}
if (sellingItems?.cycles === selectSubscriptionEnum.ACTIVE && sellingItems?.selected_selling_plan_option_id) {
defaultID = sellingItems?.selected_selling_plan_option_id
}
return defaultID ?? null
},
getSubscriptionId() {
const formData = _businessUtils.getThemeFormData();
const defaultID = _businessUtils.getSubscriptionIdInit();
console.log(`[paymentEC]订阅信息:form-${formData?.properties?._selling_plan_option_id},默认-${defaultID}`);
if (formData?.properties) {
return formData?.properties?._selling_plan_option_id
}
return defaultID ?? null;
},
isSubscription() {
return !!_businessUtils.getSubscriptionId();
},
isAllowSubscriptionPay(channel) {
if (!_businessUtils.isSubscription()) {
return true;
}
return [channelEnums.PAYPAL].includes(channel);
},
checkApplePayAvailability(channel) {
let available = false;
let reason = 'no_session';
const isApplePay = channelType?.applepay?.includes?.(channel);
if(!isApplePay){
return true;
}
if (window.ApplePaySession) {
if (window.ApplePaySession.canMakePayments()) {
available = true;
reason = 'available';
} else {
reason = 'cannot_make_payments';
}
}
trackEcPerf({
eventName: 'ec_applepay_availability_check',
extraData: {
available: available,
reason: reason,
layer: 'pike_init',
payment_channel: channel
},
eventType: 'expose'
});
return available;
},
blockChannelHandler() {
const block_googlePay = false &&
"shoplazzagoogle";
const block_applePay = false &&
"shoplazzaapple";
const block_credit = false &&
"paypal";
const blockChannel = {
googlepay: block_googlePay,
applepay: block_applePay,
credit: block_credit
};
if (!isPreview()) {
const expressChannels = window?.C_SETTINGS?.payment_settings?.dynamic_config?.express_checkout_config?.express_channels || [];
const hasStripe = expressChannels.includes('shoplazzagoogle') || expressChannels.includes('shoplazzaapple');
const {hasApplepay} = getOpenChannelType();
if (hasStripe) {
loadScript(() => window.Stripe, 'Stripe', 'https://js.stripe.com/v3/');
}
if (hasApplepay) {
loadScript(() => {
const hasAppURL = document.getElementById('applepay-sdk');
return !!hasAppURL;
}, 'applepay', 'https://applepay.cdn-apple.com/jsapi/v1/apple-pay-sdk.js');
}
}
const sortList = ['credit', 'googlepay', 'applepay'];
const methodSort = Object.keys(blockChannel).filter(key => blockChannel[key] && key).sort((a, b) => {
const indexA = sortList.indexOf(a);
const indexB = sortList.indexOf(b);
return indexA - indexB;
}).map(key => blockChannel[key]);
const result = setBlockChannel(methodSort);
track('setBlockChannel', result);
return result;
},
showECButtonHandler() {
const {
paymentChannelList,
sdkErrorList,
disabledChannelList,
extraChannelList,
} = getExpressCheckoutList();
const showChannelList = paymentChannelList.filter((ecName) =>
!sdkErrorList.includes(ecName) && !disabledChannelList.includes(ecName) &&
!extraChannelList.includes(ecName)) || [];
const result = setShowChannel(showChannelList);
track('showECButton', result);
return result;
},
filterECButtonHandler({type}, cb) {
const {
paymentChannelList,
sdkErrorList,
disabledChannelList,
extraChannelList,
} = getExpressCheckoutList();
const showChannelList = paymentChannelList.filter((ecName) =>
!sdkErrorList.includes(ecName) && !disabledChannelList.includes(ecName) &&
!extraChannelList.includes(ecName)) || [];
const result = setShowChannel(showChannelList.filter((ecName) => ecName !== type) || []);
cb && cb();
track('filterECButton', result);
return result;
},
loadSDKErrorHandler(type) {
const {sdkErrorList} = getExpressCheckoutList();
const result = setSdkErrorList([...sdkErrorList, type]);
track('loadSDKError', result);
return result;
},
extraFilterShowHandler(channel) {
const {extraChannelList} = getExpressCheckoutList();
const result = setExtraChannelList(extraChannelList.filter(ecName => ecName !== channel));
track('extraFilterEvent_show', result);
return result;
},
extraFilterHideHandler(channel) {
const {extraChannelList} = getExpressCheckoutList();
const result = setExtraChannelList([...extraChannelList, channel]);
track('extraFilterEvent_hide', result);
return result;
},
disabledChannelListHandler(checkoutData = {}, cb) {
const {paymentChannelList} = getExpressCheckoutList();
const productDetail = getProductDetail();
const disabledChannelList = paymentChannelList.filter(ecName => {
let mustDisable = false;
if(!_businessUtils.checkApplePayAvailability(ecName)){
mustDisable = true;
}
if (!isRequiresShipping() && ecName !== channelEnums.PAYPAL) {
mustDisable = true;
}
if (!_businessUtils.isAllowSubscriptionPay(ecName)) {
mustDisable = true;
}
if (!productDetail?.selected?.available) {
mustDisable = true;
}
const {payment_due} = checkoutData?.prices;
const paymentDueNum = Number(payment_due || 0) * 100;
const showFlag = paymentDueNum > 0;
return mustDisable || !showFlag;
})
const result = setDisabledChannelList(disabledChannelList)
result?.disabledChannelList?.forEach(ecName => {
cb && cb(ecName);
})
track('disabledChannelListEvent', result);
},
async getCheckoutData() {
const formData = _businessUtils.getProductFormData();
const totalPrice = multiply(getProductPrice(), formData?.[0]?.quantity || 0);
return {
prices: {payment_due: totalPrice, subtotal_price: totalPrice},
orderParams: _businessUtils.getOrderFetchParams(_businessUtils.getProductFormData()),
containerDOMIdEnums: containerDomId,
ecGlobalVarEnums
}
},
}
return _businessUtils
}
dom.businessUtilsFn = businessUtils;
document.dispatchEvent(new CustomEvent('payment_ec_core_ready', {
detail: {
businessUtils: true
}
}))
} catch (e) {
}
// 通用渲染方法
try {
const dom = document.getElementById('pm-payment-express-button-1539149753700-7');
const containerDOM = 'pm-payment-express-button-container-1539149753700-7';
const commonRenderUtils = function () {
return {
addChildrenDOM(id, allowShow, options = {}) {
if (!id) {
return;
}
const paymentEl = document.getElementById(containerDOM);
const childrenEL = document.getElementById(id);
if (paymentEl && childrenEL) {
childrenEL.style.display = allowShow ? 'block' : 'none';
return;
}
if (paymentEl && !childrenEL) {
const dom = document.createElement('div');
dom.id = id;
dom.style.display = allowShow ? 'block' : 'none';
if (options?.style) {
Object.keys(options?.style).forEach(key => {
dom.style[key] = options.style[key];
})
}
if (Array.isArray(options?.classList)) {
dom.classList.add(...options.classList)
}
paymentEl.appendChild(dom);
}
},
removeChildrenDOM(id) {
if (!id) {
return;
}
const paymentEl = document.getElementById(containerDOM);
const childrenEL = document.getElementById(id);
if (paymentEl && childrenEL) {
// childrenEL.remove();
childrenEL.style.display = 'none';
}
},
mockAddChildrenDOM(id, allowShow, options = {}) {
if (!id) {
return;
}
const paymentEl = document.getElementById(containerDOM);
const childrenEL = document.getElementById(id);
if (paymentEl && childrenEL) {
childrenEL.style.display = allowShow ? 'flex' : 'none';
return;
}
if (paymentEl && !childrenEL) {
const dom = document.createElement('div');
dom.id = id;
dom.style.display = allowShow ? 'flex' : 'none';
if (options?.style) {
Object.keys(options?.style).forEach(key => {
dom.style[key] = options.style[key];
})
}
if (Array.isArray(options?.classList)) {
dom.classList.add(...options.classList)
}
dom.classList.add('mock-img');
const img = document.createElement('img');
img.src = `//static.staticdj.com/${options?.url}`;
dom.appendChild(img);
paymentEl.appendChild(dom);
}
},
resetRenderDOM() {
const resetStyleList = [
"pm-payment-express-error-tips-1539149753700-7",
"pm-payment-express-more-button-1539149753700-7",
"pm-payment-express-mock-tips-1539149753700-7",
"pm-payment-express-skeletonLayer-1539149753700-7",
]
const resetHtmlList = [
"pm-payment-express-skeletonLayer-title-content-1539149753700-7",
"pm-payment-express-skeletonLayer-content-1539149753700-7",
"pm-payment-express-mock-tips-1539149753700-7",
"pm-payment-express-error-tips-1539149753700-7",
"pm-payment-express-button-container-1539149753700-7",
"pm-payment-express-more-button-1539149753700-7",
]
resetStyleList.forEach(domID => {
const content = document.getElementById(domID);
if (content) {
content.style.display = 'none';
}
})
resetHtmlList.forEach(domID => {
const content = document.getElementById(domID);
if (content) {
content.innerHTML = '';
}
})
}
}
}
dom.commonRenderUtilsFn = commonRenderUtils;
document.dispatchEvent(new CustomEvent('payment_ec_core_ready', {
detail: {
commonRenderUtils: true
}
}))
} catch (e) {
}
// 错误提示渲染
try {
const dom = document.getElementById('pm-payment-express-button-1539149753700-7');
const renderTipsUtils = function () {
const {i18n} = dom;
const {isPreview} = dom.commonUtils;
const {channelEnums} = dom.coreData;
return {
showChannelNotOpenTips(channelList) {
const tipsDom = document.getElementById('pm-payment-express-error-tips-1539149753700-7');
if (!isPreview()) {
return;
}
if (!tipsDom) {
return;
}
tipsDom.style.display = channelList.length > 0 ? 'block' : 'none';
const channelName = {
[channelEnums.SHOPLAZZA_GOOGLE]: "ShoplazzaPayments - GooglePay",
[channelEnums.SHOPLAZZA_APPLE]: "ShoplazzaPayments - ApplePay",
[channelEnums.PAYPAL]: "PayPal",
}
channelList.forEach(ecName => {
const id = `pm-payment-express-error-tips-1539149753700-7-${ecName}`;
const hasDom = document.getElementById(id)
if (!hasDom) {
const dom = document.createElement('div');
dom.id = id;
dom.innerHTML = i18n('ec.not_active_channel', {channelName: channelName[ecName]});
tipsDom.appendChild(dom);
}
})
},
disabledThemTips() {
const tipsDom = document.getElementById('pm-payment-express-error-tips-1539149753700-7');
if (!isPreview()) {
return;
}
if (!tipsDom) {
return;
}
tipsDom.style.display = 'block';
const id = 'pm-payment-express-error-tips-1539149753700-7-theme';
const hasDom = document.getElementById(id);
if (!hasDom) {
const dom = document.createElement('div');
dom.id = id;
dom.innerHTML = i18n('ec.not_support_theme');
tipsDom.appendChild(dom);
}
},
notFindFormTips() {
const tipsDom = document.getElementById('pm-payment-express-error-tips-1539149753700-7');
if (!isPreview()) {
return;
}
if (!tipsDom) {
return;
}
tipsDom.style.display = 'block';
const id = 'pm-payment-express-error-tips-1539149753700-7-theme';
const hasDom = document.getElementById(id);
if (!hasDom) {
const dom = document.createElement('div');
dom.id = id;
dom.innerHTML = i18n('ec.not_find_form_tips');
tipsDom.appendChild(dom);
}
},
showSkeletonLayerTips() {
const skeletonLayerDOMId = 'pm-payment-express-skeletonLayer-1539149753700-7';
const skeletonLayerDOM = document.getElementById(skeletonLayerDOMId);
const titleDOM = document.getElementById('pm-payment-express-skeletonLayer-title-content-1539149753700-7');
const contentDOM = document.getElementById('pm-payment-express-skeletonLayer-content-1539149753700-7');
if (!skeletonLayerDOM || !titleDOM || !contentDOM) {
return;
}
skeletonLayerDOM.style.display = 'block';
titleDOM.innerHTML = i18n('ec.skeleton_layer_tips_title');
contentDOM.innerHTML = i18n('ec.skeleton_layer_tips_content');
},
showMockTips() {
const tipsDOM = document.getElementById('pm-payment-express-mock-tips-1539149753700-7');
if (!tipsDOM) {
return;
}
tipsDOM.style.display = 'block';
tipsDOM.innerHTML = i18n('ec.mock_tips');
}
}
}
dom.renderTipsUtilsFn = renderTipsUtils;
document.dispatchEvent(new CustomEvent('payment_ec_core_ready', {
detail: {
renderTipsUtils: true
}
}))
} catch (e) {
}
// 更多信息渲染
try {
const dom = document.getElementById('pm-payment-express-button-1539149753700-7');
const moreDOM = document.getElementById('pm-payment-express-more-button-1539149753700-7');
const moreButtonConfig = {
firstClick: true,
maxSize: isNaN(1) ? 1 : 1
};
const renderMoreUtils = function () {
const {i18n} = dom;
const {getExpressCheckoutList, channelEnums} = dom.coreData;
function moreButtonEvent(cb) {
if (!moreDOM) {
return;
}
moreDOM.style.display = 'none';
moreButtonConfig.firstClick = false;
cb && cb();
}
return {
getMoreButtonConfig() {
return moreButtonConfig
},
showMoreButton(cb) {
if (!moreDOM) {
return;
}
let {showChannelList} = getExpressCheckoutList();
const showLength = showChannelList.length;
const {firstClick, maxSize} = moreButtonConfig;
moreDOM.style.display = (firstClick && showLength > 0 && showLength > maxSize) ? 'block' : 'none';
moreDOM.innerHTML = i18n('ec.more_button');
moreDOM.onclick = () => moreButtonEvent(cb);
},
}
}
dom.renderMoreUtilsFn = renderMoreUtils;
document.dispatchEvent(new CustomEvent('payment_ec_core_ready', {
detail: {
renderMoreUtils: true
}
}))
} catch (e) {
}
try {
const dom = document.getElementById('pm-payment-express-button-1539149753700-7');
function start() {
const {
getExtUrl,
loadFilly,
delayCallback,
ecEvent,
track,
loadScript,
debounce,
trackEcPerf
} = dom.commonUtils;
const {
blockChannelHandler,
getAttributeConfig,
showECButtonHandler,
filterECButtonHandler,
loadSDKErrorHandler,
extraFilterShowHandler,
extraFilterHideHandler,
disabledChannelListHandler,
getECConfig,
isAllowTheme,
getCheckoutData,
getThemeFormData
} = dom.businessUtils;
const {addChildrenDOM, removeChildrenDOM} = dom.commonRenderUtils;
const {getMoreButtonConfig, showMoreButton} = dom.renderMoreUtils;
const {
ecGlobalVarEnums,
providerEnums,
channelEnums,
getExpressCheckoutList,
getProductPrice,
getProductDetail,
setProductDetail,
containerDomId,
channel2ProviderEnums,
getChannelThemeConfig
} = dom.coreData;
const expressChannels = window?.C_SETTINGS?.payment_settings?.dynamic_config?.express_checkout_config?.express_channels || [];
if (!performance.getEntriesByName('ec_skeleton_start').length) {
performance.mark('ec_skeleton_start');
trackEcPerf({
eventName: 'ec_perf_skeleton_start',
extraData: {
page_start: performance.now(),
payment_channel: expressChannels.join(',')
},
eventType: 'card_expose'
});
}
function getFilly() {
if (!performance.getEntriesByName('ec_filly_load_start').length) {
performance.mark('ec_filly_load_start');
trackEcPerf({
eventName: 'ec_perf_filly_load_start',
extraData: {
payment_channel: expressChannels.join(',')
},
eventType: 'card_expose'
});
}
const fillyTag = getExtUrl('filly');
if (fillyTag) {
loadFilly(fillyTag, init, trackEcPerf);
}
}
function extraFilterEvent(e) {
const {channel, domId, allowShow} = e?.detail || {};
if (channel && domId) {
if (allowShow) {
extraFilterShowHandler(channel);
const container = document.getElementById(domId);
if (container) {
container.style.border = 'none';
container.style.backgroundColor = 'transparent';
}
} else {
extraFilterHideHandler(channel);
filterECButtonHandler({type: channel},
() => removeChildrenDOM(domId)
);
}
renderEC();
}
}
const renderEC = () => {
showECButtonHandler();
const expressCheckoutList = getExpressCheckoutList();
var {showChannelList} = expressCheckoutList;
const {firstClick, maxSize} = getMoreButtonConfig();
if (showChannelList.length === 0) {
showMoreButton(renderEC);
}
showChannelList.forEach((ecName, index) => {
const disableShow = firstClick && index >= maxSize;
const containerId = containerDomId[channel2ProviderEnums[ecName]];
addChildrenDOM(containerId, !disableShow, getChannelThemeConfig(ecName));
showMoreButton(renderEC);
});
}
const loadErrorEvent = (type) => {
const domID = containerDomId[type];
if (!domID) {
return;
}
loadSDKErrorHandler(type);
filterECButtonHandler({type},
() => removeChildrenDOM(domID)
);
showMoreButton(renderEC);
};
async function loadEC() {
const themeFormData = getThemeFormData?.() || {};
if (!themeFormData?.product_id || !themeFormData?.variant_id) {
return;
}
const ecConfig = await getECConfig();
const expressCheckoutList = getExpressCheckoutList();
track('loadEC', expressCheckoutList);
if (ecConfig) {
const checkoutData = await getCheckoutData();
disabledChannelListHandler(checkoutData, (ecName) => {
filterECButtonHandler({type: ecName},
() => removeChildrenDOM(containerDomId[channel2ProviderEnums[ecName]])
);
});
renderEC();
window.PaymentEC.handleEcPluginsLoad =
({
channelInfos = [],
loadedCbFn = () => {
}
}) => {
const expressCheckoutLoadList = [];
const { showChannelList } = getExpressCheckoutList();
const hasStripeChannels = channelInfos.some(info => info.ecGlobalVar === 'Stripe');
const stripeExpressChannels = (window?.C_SETTINGS?.payment_settings?.dynamic_config?.express_checkout_config?.express_channels || [])
.filter(ch => ['shoplazzagoogle'].includes(ch));
if (hasStripeChannels && !performance.getEntriesByName('ec_stripe_load_start').length) {
performance.mark('ec_stripe_load_start');
trackEcPerf({
eventName: 'ec_perf_stripe_load_start',
extraData: {
payment_channel: stripeExpressChannels.join(',')
},
eventType: 'card_expose'
});
}
channelInfos.map((channelInfo) => {
const {ecGlobalVar, ecName = '', sdkPath = '', datasets, fnReady, sdkReadyCb, sdkName} = channelInfo;
let hasContainer = document.getElementById(containerDomId[ecName]);
if (!hasContainer) {
return;
}
const scriptName = sdkName || ecName === 'paypal' ? ecGlobalVar : ecName;
performance.mark('ec_third_party_sdk_load_start');
trackEcPerf({
eventName: 'ec_third_party_sdk_load_start',
extraData: {
payment_channel: ecName,
sdk_path: sdkPath
},
eventType: 'expose'
});
const attributeConfig = getAttributeConfig(channelInfo) || {};
const loadPromise = loadScript(fnReady ? fnReady : () => window[ecGlobalVar], scriptName, sdkPath, datasets, () => {
loadErrorEvent(ecName);
}, attributeConfig).then(() => {
if (ecGlobalVar === 'Stripe' && !performance.getEntriesByName('ec_stripe_load_end').length) {
performance.mark('ec_stripe_load_end');
const stripeStart = performance.getEntriesByName('ec_stripe_load_start')[0];
trackEcPerf({
eventName: 'ec_perf_stripe_load_end',
extraData: {
stripe_load_time: stripeStart ? performance.now() - stripeStart.startTime : null,
payment_channel: stripeExpressChannels.join(',')
},
eventType: 'card_expose'
});
}
performance.mark('ec_third_party_sdk_load_end');
const sdkStartLoadTime = performance.getEntriesByName('ec_third_party_sdk_load_start')[0];
trackEcPerf({
eventName: 'ec_third_party_sdk_load_end',
extraData: {
sdk_load_time: sdkStartLoadTime ? performance.now() - sdkStartLoadTime.startTime : null,
payment_channel: ecName,
sdk_path: sdkPath
},
eventType: 'expose'
});
sdkReadyCb?.({
...checkoutData,
loadSpent: 0,
paymentExpressChannels: showChannelList
});
});
expressCheckoutLoadList.push(loadPromise);
});
const startLoadSDKTime = Date.now();
Promise.all(expressCheckoutLoadList).then(() => {
const finishLoadSDKTime = Date.now();
const loadSpent = finishLoadSDKTime - startLoadSDKTime;
loadedCbFn({...checkoutData, loadSpent, paymentExpressChannels: showChannelList});
});
};
// 通知外部数据变更
ecEvent.emit('tc_payment_ec_data_change', {
ecGlobalVarEnums,
containerDOMIdEnums: containerDomId
});
}
}
const loadECDebounce = debounce(loadEC, 300)
async function refreshEC(data = {}, sources) {
if (!sources) {
console.warn('[paymentEC]hide: sources is null');
return;
}
if (data?.detail?.selected?.price) {
setProductDetail(data?.detail)
}
loadECDebounce();
}
function init() {
ecEvent.on('shoplazza_express_channels_change', extraFilterEvent, false);
ecEvent.on('shoplazza_express_channels_change_ready', extraFilterEvent, false);
if (typeof window.PaymentEC === 'object') {
window.PaymentEC.getCheckoutData = getCheckoutData;
}
document.addEventListener('dj.variantChange', (data) => refreshEC(data, 'variantChange'));
document.addEventListener('payment_ec_refresh', (data) => refreshEC(data, data?.detail?.sources));
refreshEC({}, 'init');
}
if (isAllowTheme()) {
blockChannelHandler();
// 提前加载 filly:如果 DOM 已经准备好,立即加载;否则在 DOMContentLoaded 时加载
// 使用 DOMContentLoaded 替代 load 事件,提前加载时机(比 window.load 更早)
if (document.readyState === 'complete' || document.readyState === 'interactive') {
getFilly();
} else if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', getFilly, {once: true});
} else {
// 兜底:如果 readyState 未知,立即加载
getFilly();
}
}
}
dom.startFn = start;
document.dispatchEvent(new CustomEvent('payment_ec_core_ready', {
detail: {
start: true
}
}))
} catch (e) {
console.log(e);
}
// 预览模式
try {
const dom = document.getElementById('pm-payment-express-button-1539149753700-7');
function start() {
const {track} = dom.commonUtils;
const {showMoreButton, getMoreButtonConfig} = dom.renderMoreUtils;
const {
showECButtonHandler,
getECConfig,
blockChannelHandler,
isAllowTheme,
getThemeFormData
} = dom.businessUtils;
const {
disabledThemTips,
showChannelNotOpenTips,
showSkeletonLayerTips,
showMockTips,
notFindFormTips
} = dom.renderTipsUtils;
const {mockAddChildrenDOM, resetRenderDOM} = dom.commonRenderUtils;
const {
channelEnums,
getChannelThemeConfig,
getExpressCheckoutList,
getOpenChannelType
} = dom.coreData;
const mockDomId = {
[channelEnums.PAYPAL]: channelEnums.PAYPAL,
[channelEnums.SHOPLAZZA_GOOGLE]: channelEnums.SHOPLAZZA_GOOGLE,
[channelEnums.SHOPLAZZA_APPLE]: channelEnums.SHOPLAZZA_APPLE,
[channelEnums.STRIPE_GOOGLE]: channelEnums.STRIPE_GOOGLE,
[channelEnums.STRIPE_APPLE]: channelEnums.STRIPE_APPLE,
}
const renderNotOpenTips = () => {
const {blockChannelList, paymentChannelList} = getExpressCheckoutList();
const notOpenChannel = blockChannelList.filter(ecName => !paymentChannelList.includes(ecName));
showChannelNotOpenTips(notOpenChannel);
}
const renderMockTips = () => {
const {hasApplepay, hasGooglepay} = getOpenChannelType();
if (hasApplepay || hasGooglepay) {
showMockTips();
}
}
const renderEC = () => {
showECButtonHandler();
const {showChannelList} = getExpressCheckoutList();
const {firstClick, maxSize} = getMoreButtonConfig();
if (showChannelList.length === 0) {
showMoreButton(renderEC);
}
showChannelList.forEach((ecName, index) => {
const disableShow = firstClick && index >= maxSize;
mockAddChildrenDOM(mockDomId[ecName], !disableShow, getChannelThemeConfig(ecName));
showMoreButton(renderEC);
});
}
async function loadEC() {
const date = new Date().getTime();
dom.loadEC_timestamp = date
const ecConfig = await getECConfig();
if (date !== dom.loadEC_timestamp) {
return;
}
const expressCheckoutList = getExpressCheckoutList();
track('preview-loadEC', expressCheckoutList);
resetRenderDOM();
// 初始化时没有事件推送
if (ecConfig) {
renderNotOpenTips();
renderEC();
renderMockTips();
}
}
const init = () => {
blockChannelHandler();
const {blockChannelList} = getExpressCheckoutList();
if (!isAllowTheme()) {
disabledThemTips()
return;
}
const themeFormData = getThemeFormData?.() || {};
if (!themeFormData?.product_id || !themeFormData?.variant_id) {
notFindFormTips();
return;
}
if (blockChannelList.length > 0) {
loadEC();
} else {
showSkeletonLayerTips()
}
}
init();
}
dom.mockStartFn = start;
document.dispatchEvent(new CustomEvent('payment_ec_core_ready', {
detail: {
start: true
}
}))
} catch (e) {
}
try {
const dom = document.getElementById('pm-payment-express-button-1539149753700-7');
window.PaymentEC = {}
const delayCallback = (cb) => {
window.requestIdleCallback ? requestIdleCallback(cb, {timeout: 50}) : setTimeout(cb, 50);
}
const checkReady = function (data) {
const {
i18n,
commonUtilsFn,
coreDataFn,
businessUtilsFn,
commonRenderUtilsFn,
renderTipsUtilsFn,
renderMoreUtilsFn,
startFn,
mockStartFn
} = dom
let readyData = {
commonUtils: !!(commonUtilsFn) || false,
coreData: !!(coreDataFn) || false,
businessUtils: !!(businessUtilsFn) || false,
commonRenderUtils: !!(commonRenderUtilsFn) || false,
renderTipsUtils: !!(renderTipsUtilsFn) || false,
renderMoreUtils: !!(renderMoreUtilsFn) || false,
start: !!(startFn) || false,
mockStart: !!(mockStartFn) || false,
i18n: !!(i18n) || false
}
if (data?.detail) {
Object.keys(data.detail).forEach(key => {
readyData[key] = data.detail[key]
})
}
let isReady = true;
Object.keys(readyData).forEach(key => {
if (!readyData[key]) {
isReady = false
}
})
return isReady
}
const readyFn = () => {
if (!checkReady()) {
return;
}
document.removeEventListener('payment_ec_core_ready', readyFn);
dom.commonUtils = dom.commonUtilsFn();
dom.coreData = dom.coreDataFn();
dom.businessUtils = dom.businessUtilsFn();
dom.commonRenderUtils = dom.commonRenderUtilsFn();
dom.renderTipsUtils = dom.renderTipsUtilsFn();
dom.renderMoreUtils = dom.renderMoreUtilsFn();
const productData = dom?.commonUtils?.getProduct?.() || {};
if (JSON.stringify(productData) === '{}') {
return;
}
if (dom?.commonUtils?.isPreview()) {
dom.mockStartFn()
} else {
dom.startFn();
}
}
const init = () => {
if (checkReady()) {
readyFn();
} else {
document.addEventListener('payment_ec_core_ready', readyFn)
}
}
// 使用 DOMContentLoaded 替代 window.load,提前初始化时机(与 filly 加载逻辑一致)
if (document.readyState === 'complete' || document.readyState === 'interactive') {
delayCallback(init);
} else if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => delayCallback(init), {once: true});
} else {
// 兜底:如果 readyState 未知,立即执行
delayCallback(init);
}
} catch (e) {
}
${function(){
const variantData = data.variant || {"id":"65be1a1c-8c5c-4923-a40a-06a732bed6f6","product_id":"61c60c5f-ce35-4f58-ac28-d1df39e862bc","title":"BLUE-4-6","weight_unit":"kg","inventory_quantity":-34,"sku":"","barcode":"","position":27,"option1":"BLUE","option2":"4-6","option3":"","note":"","image":{"src":"\/\/img.staticdj.com\/969b9c099723523bed71a4ac69a18be1.webp","path":"969b9c099723523bed71a4ac69a18be1.webp","width":800,"height":800,"alt":"","aspect_ratio":1},"wholesale_price":[{"price":4.26,"min_quantity":1}],"weight":"0","compare_at_price":"36.99","price":"4.26","retail_price":"36.99","available":true,"url":"\/products\/cnryt?variant=65be1a1c-8c5c-4923-a40a-06a732bed6f6","available_quantity":999999999,"options":[{"name":"color \uff08Want different sizes, leave color-size comment in cart\uff09","value":"BLUE"},{"name":"Top size\uff08Guaranteed return\/exchange if size not fit\uff09","value":"4-6"}],"off_ratio":88,"flashsale_info":[],"sales":52};
return `
Sku : ${variantData && variantData.sku}
Weight : ${variantData && variantData.weight}${variantData && variantData.weight_unit}
Barcode : ${variantData && variantData.barcode}
`
}()}
${function() {
const minDays = parseInt('1');
const maxDays = parseInt('3');
const customText = "Estimated Delivery\uff1a{min_date} - {max_date}";
const minDate = new Date(Date.now() + (minDays * 86400000));
const maxDate = new Date(Date.now() + (maxDays * 86400000));
const formatDate = (minDate.getFullYear() == maxDate.getFullYear() && minDate.getFullYear() == new Date().getFullYear())
? new Intl.DateTimeFormat('en', { month: 'short', day: '2-digit' })
: new Intl.DateTimeFormat('en', { month: 'short', day: '2-digit', year: 'numeric' });
const tipText = customText.replace(/\{min_date\}/g, '' + formatDate.format(minDate) + ' ')
.replace(/\{max_date\}/g, '' + formatDate.format(maxDate) + ' ');
return `
${tipText}
`;
}()}
${function() {
const postageFreeAmount = 25.45;
const custom_text = "Buy {amount} more to enjoy FREE Shipping";
const totalPrice = +data.total_price;
const diffPrice = postageFreeAmount - totalPrice;
const percentDiff = (diffPrice > 0 ? (totalPrice / postageFreeAmount * 100) : 100) + '%';
let tipText = "Your order is free delivery";
if (diffPrice > 0) {
tipText = custom_text.replace('{amount}', `
`);
}
return `
`;
}()}
${function() {
const minInventory = parseInt('10');
const maxInventory = parseInt('50');
const randomInventory = Math.round(Math.random() * (maxInventory - minInventory)) + minInventory;
const customText = "Only {stock} item(s) left in stock!".replace(/\{stock\}/g, '' + randomInventory + ' ');
const barWidth = (randomInventory / maxInventory) * 100 + '%';
return `
`;
}()}
people are viewing this right now
Free worldwide shipping over 29.99$
Body Measurements Size Chart: US UK EU Bust Waist Hips CM inch CM inch CM inch XS 4 8 36 85 33.5 63 24.8 93 36.6 S 6 - 8 10 - 12 38 - 40 90 35.4 68 26.8 98 38.6 M 10 14 42 95 37.4 73 28.7 103 40.6 L 12 16 44 100 39.4 78 30.7 108 42.5 XL 14 18 46 105 41.3 83 32.7 113 44.5 2XL 16 20 48 110 43.3 88 34.6 118 46.5 3XL 18 22 50 115 45.3 93 36.6 123 48.4 *Depending on your body type and dressing habits, the above sizesare for reference only.
Tops size:
Tops Size Length Bust Shoulder Sleeve Length CM inch CM inch CM inch CM inch XS 57 22.4 98 38.6 39 15.4 58 22.8 S 58 22.8 103 40.6 40 15.7 59 23.2 M 59 23.2 108 42.5 41 16.1 60 23.6 L 60 23.6 113 44.5 42 16.5 61 24.0 XL 61 24.0 118 46.5 43 16.9 62 24.4 2XL 62 24.4 123 48.4 44 17.3 63 24.8 3XL 63 24.8 128 50.4 45 17.7 64 25.2
Pants size: Pants Size Length Inseam Waist Hips CM inch CM inch CM inch CM inch XS 101 39.8 70 27.6 66 26.0 108 42.5 S 102 40.2 70.5 27.8 71 28.0 113 44.5 M 103 40.6 71 28.0 76 29.9 118 46.5 L 104 40.9 71.5 28.1 81 31.9 123 48.4 XL 105 41.3 72 28.3 86 33.9 128 50.4 2XL 106 41.7 72.5 28.5 91 35.8 133 52.4 3XL 107 42.1 73 28.7 96 37.8 138 54.3 Dress Size: Size Length Bust Sleeve Length CM inch CM inch CM inch XS 88 34.6 88 34.6 22 8.7 S 89 35.0 92 36.2 25 9.8 M 90 35.4 97 38.2 26 10.2 L 91 35.8 102 40.2 27 10.6 XL 92 36.2 107 42.1 28 11.0 2XL 93 36.6 112 44.1 29 11.4 3XL 94 37.0 117 46.1 30 11.8 *This data was obtained from manually measuring the product, it may be off by 1-2 CM.
About this product
Details
✨ Women’s 2 Piece Casual Jogging Set – Pullover & Pants✨
Key Features
Soft & Stretchy Fabric : Made from polyester, rayon, and spandex for comfort and ease of movement.
2-Piece Outfit : Includes pullover top with pockets + wide-leg pants with elastic drawstring waist.
Relaxed Fit : Loose, coordinated style ideal for daily wear or light exercise.
Versatile Styling : Pair with sneakers or sandals for travel, jogging, yoga, or casual outings.
Easy Care : Hand wash recommended; follow size chart for the best fit.
Season
Autumn,Spring,Winter
Sleeve Type
Cuff Sleeve
Sleeve Length
Long Sleeve
Size Type
Normal Type
Hem Length
Ankle Length
Back Style
Backless
Material
51%Polyester,43%Rayon,6%Spandex
Bottom Style
Pants
Clothing Type
Blouse/Shirt,Trousers/Leggings
Washing Instructions
Hand Wash Only
Fit
Loose-Fitting
Neckline
Crew Neck
Occasion
Casual
OUR GUARANTEE
📦 Insured Worldwide Shipping: Each order includes real-time tracking details and insurance coverage in the unlikely event that a package gets lost or stolen in transit.
💰 Money-Back Guarantee: If your items arrive damaged or become defective within 30 days of normal usage, we will gladly issue a replacement or refund.
✉️ 24/7 Customer Support: We have a team of live reps ready to help and answer any questions you have within a 24-hour time frame, 7 days a week.
🔒 Safe & Secure Checkouts: We use state-of-the-art SSL Secure encryption to keep your personal and financial information 100% protected.
100% CUSTOMER SATISFACTION GUARANTEED
Our Facebook rating is 4.5/5! Pages below 2 can't advertise. We focus on quality and service—shop with confidence!
OUR FACTORY ADDRESS
❤️Brand Philosophy:We have independent factories and professional clothing designers! Specializing in designing fashionable and innovative clothing designs, so you can enjoy high quality fabrics and comfort!
/** @private {string} */
class SpzCustomAnchorScroll extends SPZ.BaseElement {
static deferredMount() {
return false;
}
constructor(element) {
super(element);
/** @private {Element} */
this.scrollableContainer_ = null;
}
isLayoutSupported(layout) {
return layout == SPZCore.Layout.LOGIC;
}
buildCallback() {
this.viewport_ = this.getViewport();
this.initActions_();
}
setTarget(containerId, targetId) {
this.containerId = '#' + containerId;
this.targetId = '#' + targetId;
}
scrollToTarget() {
const container = document.querySelector(this.containerId);
const target = container.querySelector(this.targetId);
const {scrollTop} = container;
const eleOffsetTop = this.getOffsetTop_(target, container);
this.viewport_
.interpolateScrollIntoView_(
container,
scrollTop,
scrollTop + eleOffsetTop
);
}
initActions_() {
this.registerAction(
'scrollToTarget',
(invocation) => this.scrollToTarget(invocation?.caller)
);
this.registerAction(
'setTarget',
(invocation) => this.setTarget(invocation?.args?.containerId, invocation?.args?.targetId)
);
}
/**
* @param {Element} element
* @param {Element} container
* @return {number}
* @private
*/
getOffsetTop_(element, container) {
if (!element./*OK*/ getClientRects().length) {
return 0;
}
const rect = element./*OK*/ getBoundingClientRect();
if (rect.width || rect.height) {
return rect.top - container./*OK*/ getBoundingClientRect().top;
}
return rect.top;
}
}
SPZ.defineElement('spz-custom-anchor-scroll', SpzCustomAnchorScroll);
const STRENGTHEN_TRUST_URL = "/api/strengthen_trust/settings";
class SpzCustomStrengthenTrust extends SPZ.BaseElement {
constructor(element) {
super(element);
this.renderElement_ = null;
}
isLayoutSupported(layout) {
return layout == SPZCore.Layout.CONTAINER;
}
buildCallback() {
this.xhr_ = SPZServices.xhrFor(this.win);
const renderId = this.element.getAttribute('render-id');
SPZCore.Dom.waitForChild(
document.body,
() => !!document.getElementById(renderId),
() => {
this.renderElement_ = SPZCore.Dom.scopedQuerySelector(
document.body,
`#${renderId}`
);
if (this.renderElement_) {
this.render_();
}
this.registerAction('track', (invocation) => {
this.track_(invocation.args);
});
}
);
}
render_() {
this.fetchData_().then((data) => {
if (!data) {
return;
}
SPZ.whenApiDefined(this.renderElement_).then((apis) => {
apis?.render(data);
document.querySelector('#strengthen-trust-render-1539149753700').addEventListener('click',(event)=>{
if(event.target.nodeName == 'A'){
this.track_({type: 'trust_content_click'});
}
})
});
});
}
track_(data = {}) {
const track = window.sa && window.sa.track;
if (!track) {
return;
}
track('trust_enhancement_event', data);
}
parseJSON_(string) {
let result = {};
try {
result = JSON.parse(string);
} catch (e) {}
return result;
}
fetchData_() {
return this.xhr_
.fetchJson(STRENGTHEN_TRUST_URL)
.then((responseData) => {
if (!responseData || !responseData.data) {
return null;
}
const data = responseData.data;
const moduleSettings = (data.module_settings || []).reduce((result, moduleSetting) => {
return result.concat(Object.assign(moduleSetting, {
logos: (moduleSetting.logos || []).map((item) => {
return moduleSetting.logos_type == 'custom' ? this.parseJSON_(item) : item;
})
}));
}, []);
return Object.assign(data, {
module_settings: moduleSettings,
isEditor: window.self !== window.top,
});
});
}
}
SPZ.defineElement('spz-custom-strengthen-trust', SpzCustomStrengthenTrust);
${data.module_title}
To display this card to customers, you need to go to "Booster & Store Conversion" to turn on the trust enhancement feature.
${item.content.replaceAll("{store_name}","ZARA OUTLET")}
To display this card to customers, you need to go to "Booster & Store Conversion" to turn on the trust enhancement feature.