@redonvn/zalo-ads-sdk 0.2.20 → 0.2.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -2048,7 +2048,24 @@ var VALID_AD_TYPES = {
2048
2048
  COMMERCE: 28,
2049
2049
  // Server trả về 28 (không phải 23)
2050
2050
  ARTICLE: 3,
2051
- DISPLAY: 21
2051
+ DISPLAY: 21,
2052
+ // Masthead Mobile (Format 1) & Masthead Desktop (Format 2)
2053
+ DISPLAY_HALFPAGE: 26,
2054
+ // Halfpage Mobile (Format 13) & Halfpage PC (Format 14)
2055
+ DISPLAY_MEDIUM_RECTANGLE: 23
2056
+ // Medium Rectangle 300x250 (Format 6)
2057
+ };
2058
+ var ZALO_DISPLAY_AD_FORMAT = {
2059
+ MASTHEAD_MOBILE: 1,
2060
+ // Kích thước 640x360, Os: [0, 1]
2061
+ MASTHEAD_DESKTOP: 2,
2062
+ // Kích thước 970x250, Os: [3]
2063
+ MEDIUM_RECTANGLE: 6,
2064
+ // Kích thước 300x250, Os: [0, 1]
2065
+ HALFPAGE_MOBILE: 13,
2066
+ // Kích thước 300x600, Os: [0, 1]
2067
+ HALFPAGE_PC: 14
2068
+ // Kích thước 300x600, Os: [3]
2052
2069
  };
2053
2070
  var ALLOWED_CTAS_PER_AD_TYPE = {
2054
2071
  // Website (Type 5): 9 CTAs
@@ -2099,7 +2116,41 @@ var ALLOWED_CTAS_PER_AD_TYPE = {
2099
2116
  ZaloAdsActionType.XEM_THEM
2100
2117
  ],
2101
2118
  // OA (Type 2): Cố định Quan tâm OA (6)
2102
- [VALID_AD_TYPES.OA]: [ZaloAdsActionType.QUAN_TAM_OA]
2119
+ [VALID_AD_TYPES.OA]: [ZaloAdsActionType.QUAN_TAM_OA],
2120
+ // Display (Type 21) & Display Halfpage (Type 26): Hỗ trợ các hành động cơ bản
2121
+ [VALID_AD_TYPES.DISPLAY]: [
2122
+ ZaloAdsActionType.DAT_NGAY,
2123
+ ZaloAdsActionType.MUA_NGAY,
2124
+ ZaloAdsActionType.THU_NGAY,
2125
+ ZaloAdsActionType.TAI_NGAY,
2126
+ ZaloAdsActionType.XEM_THEM,
2127
+ ZaloAdsActionType.NGHE_NGAY,
2128
+ ZaloAdsActionType.CHOI_NGAY,
2129
+ ZaloAdsActionType.DANG_KY,
2130
+ ZaloAdsActionType.GOI_DIEN
2131
+ ],
2132
+ [VALID_AD_TYPES.DISPLAY_HALFPAGE]: [
2133
+ ZaloAdsActionType.DAT_NGAY,
2134
+ ZaloAdsActionType.MUA_NGAY,
2135
+ ZaloAdsActionType.THU_NGAY,
2136
+ ZaloAdsActionType.TAI_NGAY,
2137
+ ZaloAdsActionType.XEM_THEM,
2138
+ ZaloAdsActionType.NGHE_NGAY,
2139
+ ZaloAdsActionType.CHOI_NGAY,
2140
+ ZaloAdsActionType.DANG_KY,
2141
+ ZaloAdsActionType.GOI_DIEN
2142
+ ],
2143
+ [VALID_AD_TYPES.DISPLAY_MEDIUM_RECTANGLE]: [
2144
+ ZaloAdsActionType.DAT_NGAY,
2145
+ ZaloAdsActionType.MUA_NGAY,
2146
+ ZaloAdsActionType.THU_NGAY,
2147
+ ZaloAdsActionType.TAI_NGAY,
2148
+ ZaloAdsActionType.XEM_THEM,
2149
+ ZaloAdsActionType.NGHE_NGAY,
2150
+ ZaloAdsActionType.CHOI_NGAY,
2151
+ ZaloAdsActionType.DANG_KY,
2152
+ ZaloAdsActionType.GOI_DIEN
2153
+ ]
2103
2154
  };
2104
2155
  function validateCreateCampaignPayload(payload, options = {}) {
2105
2156
  const errors = [];
@@ -2195,11 +2246,15 @@ function validateUpdateAdPayload(payload, options = {}) {
2195
2246
  if (!adInfo) {
2196
2247
  errors.push("Settings.AdInfo l\xE0 b\u1EAFt bu\u1ED9c.");
2197
2248
  } else {
2249
+ const rawTypeNum = Number(payload.Type);
2250
+ const isDisplayAd = rawTypeNum === 21 || rawTypeNum === 23 || rawTypeNum === 26 || adInfo.AdFormat === 1 || adInfo.AdFormat === 2 || adInfo.AdFormat === 6 || adInfo.AdFormat === 13 || adInfo.AdFormat === 14;
2198
2251
  if (!adInfo.Caption || !adInfo.Caption.trim()) {
2199
- errors.push("Settings.AdInfo.Caption l\xE0 b\u1EAFt bu\u1ED9c.");
2252
+ if (!isDisplayAd) {
2253
+ errors.push("Settings.AdInfo.Caption l\xE0 b\u1EAFt bu\u1ED9c.");
2254
+ }
2200
2255
  } else {
2201
2256
  const captionLen = adInfo.Caption.trim().length;
2202
- if (captionLen < 35) {
2257
+ if (!isDisplayAd && captionLen < 35) {
2203
2258
  errors.push(`Settings.AdInfo.Caption qu\xE1 ng\u1EAFn (${captionLen}/35 k\xFD t\u1EF1 t\u1ED1i thi\u1EC3u).`);
2204
2259
  } else if (captionLen > 90) {
2205
2260
  errors.push(`Settings.AdInfo.Caption qu\xE1 d\xE0i (${captionLen}/90 k\xFD t\u1EF1 t\u1ED1i \u0111a).`);
@@ -2223,6 +2278,14 @@ function validateUpdateAdPayload(payload, options = {}) {
2223
2278
  );
2224
2279
  }
2225
2280
  }
2281
+ if (isDisplayAd) {
2282
+ const targetUrl = typeof adInfo.TargetUrl === "object" ? adInfo.TargetUrl?.Url : adInfo.TargetUrl;
2283
+ if (!targetUrl || typeof targetUrl !== "string" || !targetUrl.trim()) {
2284
+ errors.push("Settings.AdInfo.TargetUrl l\xE0 b\u1EAFt bu\u1ED9c \u0111\u1ED1i v\u1EDBi Qu\u1EA3ng c\xE1o Display.");
2285
+ } else if (!/^https?:\/\//i.test(targetUrl.trim())) {
2286
+ errors.push("Settings.AdInfo.TargetUrl ph\u1EA3i l\xE0 m\u1ED9t \u0111\u01B0\u1EDDng d\u1EABn h\u1EE3p l\u1EC7 (b\u1EAFt \u0111\u1EA7u b\u1EB1ng http:// ho\u1EB7c https://).");
2287
+ }
2288
+ }
2226
2289
  if (adInfo.Action !== void 0 && payload.Type) {
2227
2290
  const allowedCtas = ALLOWED_CTAS_PER_AD_TYPE[payload.Type];
2228
2291
  if (allowedCtas && !allowedCtas.includes(adInfo.Action)) {
@@ -2346,6 +2409,65 @@ function validateUpdateAdPayload(payload, options = {}) {
2346
2409
  "Ng\xE2n s\xE1ch t\u1ED1i thi\u1EC3u cho Qu\u1EA3ng c\xE1o Tin nh\u1EAFn l\xE0 600,000 VN\u0110 (Price.Limit >= 600000). N\u1EBFu \u0111\u1EB7t d\u01B0\u1EDBi 600k m\xE1y ch\u1EE7 s\u1EBD t\u1EEB ch\u1ED1i."
2347
2410
  );
2348
2411
  }
2412
+ } else if (Number(payload.Type) === 21 || Number(payload.Type) === 23 || Number(payload.Type) === 26 || payload.Type === VALID_AD_TYPES.DISPLAY || payload.Type === VALID_AD_TYPES.DISPLAY_HALFPAGE || payload.Type === VALID_AD_TYPES.DISPLAY_MEDIUM_RECTANGLE) {
2413
+ const adFormat = payload.Settings?.AdInfo?.AdFormat;
2414
+ const typeNum = Number(payload.Type);
2415
+ const isHalfpage = typeNum === 26 || adFormat === 13 || adFormat === 14 || (payload.AdName || "").toLowerCase().includes("halfpage");
2416
+ const isMediumRectangle = typeNum === 23 || adFormat === 6 || (payload.AdName || "").toLowerCase().includes("medium rectangle") || (payload.AdName || "").toLowerCase().includes("300x250");
2417
+ const adNameLower = (payload.AdName || "").toLowerCase();
2418
+ const isWelcome = adNameLower.includes("welcome");
2419
+ const isMasthead = (adNameLower.includes("masthead") || typeNum === 21 || adFormat === 1 || adFormat === 2) && !isWelcome && !isMediumRectangle;
2420
+ if (price.Type !== 1 && price.Type !== 2) {
2421
+ errors.push(
2422
+ "\u0110\u1ED1i v\u1EDBi Qu\u1EA3ng c\xE1o Display, Price.Type ch\u1EC9 ch\u1EA5p nh\u1EADn 1 (CPM - L\u01B0\u1EE3t hi\u1EC3n th\u1ECB) ho\u1EB7c 2 (CPC - L\u01B0\u1EE3t nh\u1EA5n)."
2423
+ );
2424
+ }
2425
+ if ((isHalfpage || isMasthead) && price.Type === 2) {
2426
+ errors.push(
2427
+ `Qu\u1EA3ng c\xE1o ${isHalfpage ? "Halfpage (Type 26 / AdFormat 13, 14)" : "Masthead (Type 21)"} ch\u1EC9 h\u1ED7 tr\u1EE3 t\xEDnh ti\u1EC1n theo l\u01B0\u1EE3t hi\u1EC3n th\u1ECB CPM (Price.Type = 1), kh\xF4ng h\u1ED7 tr\u1EE3 CPC.`
2428
+ );
2429
+ }
2430
+ if (price.Type === 1) {
2431
+ if (price.Bid !== void 0) {
2432
+ if (isWelcome && price.Bid < 3e4) {
2433
+ errors.push(
2434
+ `Gi\xE1 \u0111\u1EB7t m\u1ED7i 1,000 l\u01B0\u1EE3t hi\u1EC3n th\u1ECB (Price.Bid) cho Qu\u1EA3ng c\xE1o Welcome Mobile t\u1ED1i thi\u1EC3u l\xE0 30,000 VN\u0110. Hi\u1EC7n t\u1EA1i \u0111ang truy\u1EC1n: ${price.Bid}\u0111.`
2435
+ );
2436
+ } else if (isHalfpage && price.Bid < 16e3) {
2437
+ errors.push(
2438
+ `Gi\xE1 \u0111\u1EB7t m\u1ED7i 1,000 l\u01B0\u1EE3t hi\u1EC3n th\u1ECB (Price.Bid) cho Qu\u1EA3ng c\xE1o Halfpage t\u1ED1i thi\u1EC3u l\xE0 16,000 VN\u0110. Hi\u1EC7n t\u1EA1i \u0111ang truy\u1EC1n: ${price.Bid}\u0111.`
2439
+ );
2440
+ } else if (isMasthead && price.Bid < 13e3) {
2441
+ errors.push(
2442
+ `Gi\xE1 \u0111\u1EB7t m\u1ED7i 1,000 l\u01B0\u1EE3t hi\u1EC3n th\u1ECB (Price.Bid) cho Qu\u1EA3ng c\xE1o Masthead t\u1ED1i thi\u1EC3u l\xE0 13,000 VN\u0110. Hi\u1EC7n t\u1EA1i \u0111ang truy\u1EC1n: ${price.Bid}\u0111.`
2443
+ );
2444
+ } else if (isMediumRectangle && price.Bid < 8e3) {
2445
+ errors.push(
2446
+ `Gi\xE1 \u0111\u1EB7t m\u1ED7i 1,000 l\u01B0\u1EE3t hi\u1EC3n th\u1ECB (Price.Bid) cho Qu\u1EA3ng c\xE1o Medium Rectangle CPM t\u1ED1i thi\u1EC3u l\xE0 8,000 VN\u0110. Hi\u1EC7n t\u1EA1i \u0111ang truy\u1EC1n: ${price.Bid}\u0111.`
2447
+ );
2448
+ } else if (price.Bid < 8e3) {
2449
+ errors.push(
2450
+ `Gi\xE1 \u0111\u1EB7t m\u1ED7i 1,000 l\u01B0\u1EE3t hi\u1EC3n th\u1ECB (Price.Bid) cho Qu\u1EA3ng c\xE1o Display CPM t\u1ED1i thi\u1EC3u l\xE0 8,000 VN\u0110 (s\xE0n h\u1EC7 th\u1ED1ng). Hi\u1EC7n t\u1EA1i \u0111ang truy\u1EC1n: ${price.Bid}\u0111.`
2451
+ );
2452
+ }
2453
+ }
2454
+ if (price.Limit !== void 0 && price.Limit > 0 && price.Limit < 50) {
2455
+ errors.push(
2456
+ "S\u1ED1 l\u01B0\u1EE3ng l\u01B0\u1EE3t hi\u1EC3n th\u1ECB t\u1ED1i thi\u1EC3u (Price.Limit) cho Qu\u1EA3ng c\xE1o Display CPM l\xE0 50 (t\u01B0\u01A1ng \u1EE9ng 50,000 l\u01B0\u1EE3t hi\u1EC3n th\u1ECB)."
2457
+ );
2458
+ }
2459
+ } else if (price.Type === 2) {
2460
+ if (price.Bid !== void 0 && price.Bid < 1800) {
2461
+ errors.push(
2462
+ `Gi\xE1 \u0111\u1EB7t m\u1ED7i l\u01B0\u1EE3t nh\u1EA5n (Price.Bid) cho Qu\u1EA3ng c\xE1o Display CPC t\u1ED1i thi\u1EC3u l\xE0 1,800 VN\u0110. Hi\u1EC7n t\u1EA1i \u0111ang truy\u1EC1n: ${price.Bid}\u0111.`
2463
+ );
2464
+ }
2465
+ if (price.Limit !== void 0 && price.Limit > 0 && price.Limit < 50) {
2466
+ errors.push(
2467
+ "S\u1ED1 l\u01B0\u1EE3ng l\u01B0\u1EE3t nh\u1EA5n t\u1ED1i thi\u1EC3u (Price.Limit) cho Qu\u1EA3ng c\xE1o Display CPC l\xE0 50 l\u01B0\u1EE3t nh\u1EA5n."
2468
+ );
2469
+ }
2470
+ }
2349
2471
  }
2350
2472
  if (payload.Settings && payload.Settings.DeliveryType !== void 0) {
2351
2473
  const deliveryType = payload.Settings.DeliveryType;
@@ -2362,6 +2484,21 @@ function validateUpdateAdPayload(payload, options = {}) {
2362
2484
  }
2363
2485
  const targeting = payload.Settings?.Targeting;
2364
2486
  if (targeting) {
2487
+ if (targeting.Gender !== void 0 && targeting.Gender !== null) {
2488
+ if (![0, 1, 2].includes(Number(targeting.Gender))) {
2489
+ errors.push(`M\xE3 gi\u1EDBi t\xEDnh 'Gender' (${targeting.Gender}) kh\xF4ng h\u1EE3p l\u1EC7. H\u1EE3p l\u1EC7: 0 (T\u1EA5t c\u1EA3), 1 (Nam), 2 (N\u1EEF).`);
2490
+ }
2491
+ }
2492
+ if (targeting.CustomAudienceId !== void 0 && targeting.CustomAudienceId !== null) {
2493
+ if (typeof targeting.CustomAudienceId !== "number" || targeting.CustomAudienceId < 0) {
2494
+ errors.push("CustomAudienceId ph\u1EA3i l\xE0 s\u1ED1 nguy\xEAn kh\xF4ng \xE2m (>= 0).");
2495
+ }
2496
+ }
2497
+ if (targeting.ExcludedAudienceId !== void 0 && targeting.ExcludedAudienceId !== null) {
2498
+ if (typeof targeting.ExcludedAudienceId !== "number" || targeting.ExcludedAudienceId < 0) {
2499
+ errors.push("ExcludedAudienceId ph\u1EA3i l\xE0 s\u1ED1 nguy\xEAn kh\xF4ng \xE2m (>= 0).");
2500
+ }
2501
+ }
2365
2502
  const newAges = targeting.NewAgeRanges;
2366
2503
  const oldAges = targeting.AgeRanges;
2367
2504
  if (newAges && newAges.length > 0) {
@@ -2484,6 +2621,14 @@ function validateUpdateAdPayload(payload, options = {}) {
2484
2621
  if (!payload.ZaloFeedId && !payload.articleId) {
2485
2622
  errors.push("ZaloFeedId ho\u1EB7c articleId l\xE0 b\u1EAFt bu\u1ED9c \u0111\u1ED1i v\u1EDBi Qu\u1EA3ng c\xE1o B\xE0i Vi\u1EBFt (Ad Type 3).");
2486
2623
  }
2624
+ } else if (Number(payload.Type) === 21 || Number(payload.Type) === 23 || Number(payload.Type) === 26 || payload.Type === VALID_AD_TYPES.DISPLAY || payload.Type === VALID_AD_TYPES.DISPLAY_HALFPAGE || payload.Type === VALID_AD_TYPES.DISPLAY_MEDIUM_RECTANGLE) {
2625
+ const img = payload.Settings?.Image;
2626
+ const hasImage = img?.LandscapeCoverUrl || img?.BaseLandscapeCoverUrl || img?.PortraitCoverUrl || img?.SquareCoverUrl;
2627
+ if (!hasImage) {
2628
+ errors.push(
2629
+ "Qu\u1EA3ng c\xE1o Display b\u1EAFt bu\u1ED9c ph\u1EA3i c\xF3 \xEDt nh\u1EA5t m\u1ED9t banner h\xECnh \u1EA3nh (Settings.Image.LandscapeCoverUrl ho\u1EB7c BaseLandscapeCoverUrl)."
2630
+ );
2631
+ }
2487
2632
  }
2488
2633
  return {
2489
2634
  isValid: errors.length === 0,
@@ -2725,7 +2870,19 @@ function validateUpdateWebsiteAd(payload, options = {}) {
2725
2870
  return validateUpdateAdPayload({ ...payload, Type: VALID_AD_TYPES.WEBSITE }, options);
2726
2871
  }
2727
2872
  function validateUpdateDisplayAd(payload, options = {}) {
2728
- return validateUpdateAdPayload({ ...payload, Type: VALID_AD_TYPES.DISPLAY }, options);
2873
+ const adFormat = payload.Settings?.AdInfo?.AdFormat;
2874
+ const rawType = Number(payload.Type);
2875
+ const isHalfpage = rawType === 26 || adFormat === 13 || adFormat === 14;
2876
+ const isMediumRectangle = rawType === 23 || adFormat === 6;
2877
+ let effectiveType;
2878
+ if (isHalfpage) {
2879
+ effectiveType = VALID_AD_TYPES.DISPLAY_HALFPAGE;
2880
+ } else if (isMediumRectangle) {
2881
+ effectiveType = VALID_AD_TYPES.DISPLAY_MEDIUM_RECTANGLE;
2882
+ } else {
2883
+ effectiveType = payload.Type ?? VALID_AD_TYPES.DISPLAY;
2884
+ }
2885
+ return validateUpdateAdPayload({ ...payload, Type: effectiveType }, options);
2729
2886
  }
2730
2887
  function validateUpdateVideoAd(payload, options = {}) {
2731
2888
  return validateUpdateAdPayload({ ...payload, Type: VALID_AD_TYPES.VIDEO }, options);
@@ -8871,6 +9028,7 @@ export {
8871
9028
  VALID_AD_TYPES,
8872
9029
  VALID_CAMPAIGN_TYPES,
8873
9030
  VIETNAM_PHONE_REGEX,
9031
+ ZALO_DISPLAY_AD_FORMAT,
8874
9032
  ZALO_NEW_REGION_PROVINCE_CODES,
8875
9033
  ZALO_PROFILE_ADS_PROVINCES,
8876
9034
  ZALO_PROFILE_ADS_SUB_CATEGORIES,