import { isNothing, isSome } from "@jet/environment/types/optional"; import * as serverData from "../../../foundation/json-parsing/server-data"; import { BuyParameters } from "../../../foundation/metrics/buy-parameters"; import { URL } from "../../../foundation/network/urls"; import * as productVariants from "../../product-page/product-page-variants"; import { MetricsReferralContext } from "../metrics-referral-context"; import * as metricsPosting from "../posting"; import * as misc from "./misc"; import { IAdSearchInformation } from "./models"; import * as metricsUtil from "./util"; //* ************************* //* Buy Metrics //* ************************* /** * Adds bag driven metrics fields. * @param objectGraph * @returns */ function addBagMetricsToBuyParams(objectGraph, baseBuyParams) { const buyParams = new BuyParameters(baseBuyParams); const metricsConfiguration = objectGraph.bag.metricsConfiguration; const language = serverData.asString(metricsConfiguration, "metricsBase.language"); buyParams.set("languageId", language); return buyParams.toString(); } export function addPageMetricsToBuyParams(objectGraph, baseBuyParams, adamId, pageInformation, targetType, kind, metricsPlatformDisplayStyle, productVariantData, inAppEventId, excludeCrossfireAttribution, extRefApp2, extRefUrl2) { const buyParams = new BuyParameters(baseBuyParams); addPageMetricsToBuyParamsObject(objectGraph, buyParams, adamId, pageInformation, targetType, kind, metricsPlatformDisplayStyle, productVariantData, inAppEventId, excludeCrossfireAttribution, extRefApp2, extRefUrl2); addExtraInfoToBuyParamsObject(objectGraph, buyParams, pageInformation); return buyParams.toString(); } export function addPageMetricsToBuyParamsObject(objectGraph, buyParams, adamId, pageInformation, targetType, kind, metricsPlatformDisplayStyle, productVariantData, inAppEventId, excludeCrossfireAttribution, extRefApp2, extRefUrl2) { var _a, _b, _c; const fields = misc.fieldsFromPageInformation(pageInformation); if (targetType) { buyParams.set("impressionType", targetType); // impressionType == targetType == locationType. } if (kind) { buyParams.set("kind", kind); } const pageId = serverData.asString(serverData.asJSONData(fields), "pageId"); buyParams.set("pageId", pageId); const pageType = serverData.asString(serverData.asJSONData(fields), "pageType"); buyParams.set("pageType", pageType); // Ad container id const iAdContainerId = serverData.asString(serverData.asJSONData(fields), "iAdContainerId"); if (isSome(iAdContainerId) && iAdContainerId.length > 0) { buyParams.set(adBuyParamKeys.containerId, iAdContainerId, null); } // we add search terms to the page if the page is for the item we are buying, or if // its for a streamlined contingent offer const pageIds = (_a = pageId === null || pageId === void 0 ? void 0 : pageId.split("_")) !== null && _a !== void 0 ? _a : []; const pageMatchesProduct = pageIds.includes(adamId); const isProductPage = pageType === "Software"; const isStreamlinedContingentOffer = ((_b = buyParams.get("contingentItemId", null)) === null || _b === void 0 ? void 0 : _b.length) > 0; if (!isProductPage || pageMatchesProduct || isStreamlinedContingentOffer) { // If there is a native search term, it will overwrite this // initial value below in `addNativeMetricsToBuyParams`. let searchTerm = serverData.asString(serverData.asJSONData(pageInformation), "searchTermContext.resultsTerm"); // A search term may be attached to the product URL as a means of associating buys with searches. if (serverData.isNullOrEmpty(searchTerm)) { searchTerm = metricsUtil.searchTermFromProductURL(pageInformation === null || pageInformation === void 0 ? void 0 : pageInformation.pageUrl); } if (!serverData.isNull(searchTerm)) { buyParams.set("searchTerm", searchTerm); } } if (isProductPage && isSome(pageInformation) && isSome(pageInformation.pageUrl)) { const pageUrl = new URL(pageInformation.pageUrl); if (((_c = pageUrl.query) === null || _c === void 0 ? void 0 : _c["context"]) === "browserChoice") { buyParams.set("prevPage", "BrowserChoice"); buyParams.set("browserChoiceScreenBuy", "1", null); } } productVariants.addProductPageVariantMetricsToBuyParams(buyParams, adamId, pageInformation === null || pageInformation === void 0 ? void 0 : pageInformation.productVariantData, productVariantData !== null && productVariantData !== void 0 ? productVariantData : undefined); if (!serverData.isNull(metricsPlatformDisplayStyle) && metricsPlatformDisplayStyle.length > 0) { buyParams.set("platformDisplayStyle", metricsPlatformDisplayStyle); } // App Event ID buyParams.set("inAppEventId", inAppEventId); // Referrer if (!excludeCrossfireAttribution) { if (serverData.isDefinedNonNull(MetricsReferralContext.shared.activeReferralData)) { buyParams.set("extRefApp2", MetricsReferralContext.shared.activeReferralData.extRefApp2, null); buyParams.set("extRefUrl2", MetricsReferralContext.shared.activeReferralData.extRefUrl2, null); if (isSome(MetricsReferralContext.shared.activeReferralData.kind)) { const extRefAppKindName = MetricsReferralContext.shared.activeReferralData.kind.name; if (extRefAppKindName === "clip" || extRefAppKindName === "appClip") { buyParams.set("hostApp", "com.apple.AppStore.clipOverlay"); } } } else { buyParams.set("extRefApp2", extRefApp2, null); buyParams.set("extRefUrl2", extRefUrl2, null); } } } function addExtraInfoToBuyParamsObject(objectGraph, buyParams, pageInformation) { var _a, _b; if (isNothing(pageInformation)) { return; } const extraInfo = []; const hasiAdData = isSome((_a = pageInformation.iAdInfo) === null || _a === void 0 ? void 0 : _a.clickFields["hasiAdData"]); if (hasiAdData) { const iAdExtraInfoKey = isIAdFromCurrentPage(objectGraph, pageInformation) ? "iAdSponsored" : "iAdOriginated"; extraInfo.push({ key: iAdExtraInfoKey, value: "true" }); } if (serverData.isDefinedNonNullNonEmpty(extraInfo)) { const extraInfoParamValue = extraInfo .map((extraInfoValue) => `${extraInfoValue.key}=${extraInfoValue.value}`) .join(";"); const encodedValue = (_b = objectGraph.cryptography) === null || _b === void 0 ? void 0 : _b.base64Encode(extraInfoParamValue); if (isSome(encodedValue)) { buyParams.set("extraInfo", encodedValue); } } } /** * Determines if an iAd is from the current page by comparing the iAd's placement type with the page type. * * This function validates whether an advertisement is correctly placed on the appropriate page type. * Different ad placement types are designed for specific page contexts, and this function ensures * that the placement matches the current page context. * * @param objectGraph - The AppStore object graph providing access to app services and utilities * @param pageInformation - Information about the current page, including its type and iAd information * @returns True if the iAd's placement type matches the current page type, false otherwise * or if pageInformation or iAdInfo is missing */ function isIAdFromCurrentPage(objectGraph, pageInformation) { const iAdInfo = pageInformation === null || pageInformation === void 0 ? void 0 : pageInformation.iAdInfo; if (isNothing(pageInformation) || isNothing(iAdInfo)) { return false; } const pageType = pageInformation.baseFields["pageType"]; const iAdPlacementType = IAdSearchInformation.placementTypeFromPlacementId(objectGraph, iAdInfo.placementId); switch (iAdPlacementType) { case "searchLanding": return pageType === "SearchLanding"; case "today": return pageType === "Today"; case "productPageYMAL": case "productPageYMALDuringDownload": return pageType === "Software" || pageType === "SoftwareBundle"; default: return pageType === "Search"; } } /** * Returns a copy of the specified buy parameters enriched with native metrics fields. * * @param baseBuyParams The buy parameters of an app which have previously been * decorated with page metrics. Passing buy parameters that were not decorated with page * metrics can result in incorrect search terms being reported. * @param adamId The identifier of the app whose buy parameters are being decorated. * @param excludeCrossfireAttribution Specifies whether crossfire attribution should be * excluded from the decorated buy params. * @param nativeMetrics Metrics fields provided by native code. * @param osMetrics Metrics fields describing the device's OS provided by native code. * @returns A copy of `baseBuyParams` enriched with native metrics fields. */ export function addNativeValuesToBuyParams(objectGraph, baseBuyParams, adamId, excludeCrossfireAttribution, isAppInstalled, nativeMetrics, osMetrics) { const baseBuyParamsObject = new BuyParameters(baseBuyParams); addNativeValuesToBuyParamsObject(objectGraph, baseBuyParamsObject, adamId, excludeCrossfireAttribution, isAppInstalled, nativeMetrics, osMetrics); return baseBuyParamsObject.toString(); } /** * Returns a copy of the specified buy parameters enriched with native metrics fields. * * @param baseBuyParams The buy parameters of an app which have previously been * decorated with page metrics. Passing buy parameters that were not decorated with page * metrics can result in incorrect search terms being reported. * @param adamId The identifier of the app whose buy parameters are being decorated. * @param excludeCrossfireAttribution Specifies whether crossfire attribution should be * excluded from the decorated buy params. * @param nativeMetrics Metrics fields provided by native code. * @param osMetrics Metrics fields describing the device's OS provided by native code. * @returns A copy of `baseBuyParams` enriched with native metrics fields. */ export function addNativeValuesToBuyParamsObject(objectGraph, buyParams, adamId, excludeCrossfireAttribution, isAppInstalled, nativeMetrics, osMetrics) { var _a, _b; const pageContext = serverData.asString(nativeMetrics, "pageContext"); buyParams.set("pageContext", pageContext); const paymentTopic = objectGraph.props.enabled("paymentTopicFromBag") ? objectGraph.bag.metricsPaymentTopic : undefined; buyParams.set("topic", paymentTopic !== null && paymentTopic !== void 0 ? paymentTopic : objectGraph.bag.metricsTopic); metricsPosting.buyDecorator.useNativeValues(nativeMetrics); const decoratorParams = metricsPosting.buyDecorator.params; for (const key of Object.keys(decoratorParams)) { if (key === "prevPage" && isSome(buyParams.get("prevPage"))) { // We may have added a `prevPage` param earlier, specifically don't override this value if we have. continue; } const value = serverData.asString(decoratorParams, key); buyParams.set(key, value); } if (!serverData.isNull(osMetrics)) { for (const key of Object.keys(osMetrics)) { const value = serverData.asString(osMetrics, key); buyParams.set(key, value); buyParams.set(key, value, null); } } if (!nativeMetrics) { // ^^ Is this actually needed? buyParams.set("searchTerm", null); buyParams.set("platformDisplayStyle", null); return; } const hostApp = serverData.asString(nativeMetrics, "hostApp"); if (isSome(hostApp) && hostApp.length > 0) { buyParams.set("hostApp", hostApp); } const isContingentOffer = ((_a = buyParams.get("contingentItemId", null)) === null || _a === void 0 ? void 0 : _a.length) > 0; const app = serverData.asString(nativeMetrics, "app"); if (isContingentOffer) { // Contingent Offer Streamline buy app install flag buyParams.set("app", objectGraph.host.clientIdentifier); } else if (isSome(app) && app.length > 0) { buyParams.set("app", app); } if (!excludeCrossfireAttribution && !MetricsReferralContext.shared.shouldUseJSReferralData) { const extRefUrl = serverData.asString(nativeMetrics, "extRefUrl2"); const extractedSiriRefApp = metricsUtil.extractSiriRefAppFromRefURL(extRefUrl); if (extRefUrl && extractedSiriRefApp) { nativeMetrics["refApp"] = extractedSiriRefApp; } // ^^ Is this actually needed? const usageContext = serverData.asString(nativeMetrics, "usageContext"); if (isSome(usageContext)) { switch (usageContext) { case "overlay": buyParams.set("hostApp", "com.apple.AppStore.overlay"); break; case "overlayClip": buyParams.set("hostApp", "com.apple.AppStore.clipOverlay"); break; default: break; } buyParams.set("extRefApp2", hostApp, null); } else { const extRefApp2 = serverData.asString(nativeMetrics, "extRefApp2"); buyParams.set("extRefApp2", extRefApp2, null); const extRefUrl2 = serverData.asString(nativeMetrics, "extRefUrl2"); buyParams.set("extRefUrl2", extRefUrl2, null); const extRefAppType = serverData.asString(nativeMetrics, "extRefAppType"); if (extRefAppType === "clip") { buyParams.set("hostApp", "com.apple.AppStore.clipOverlay"); } } } // we add search terms to the page if the page is for the item we are buying const pageId = buyParams.get("pageId"); const pageType = buyParams.get("pageType"); const pageIds = (_b = pageId === null || pageId === void 0 ? void 0 : pageId.split("_")) !== null && _b !== void 0 ? _b : []; const pageMatchesProduct = pageIds.includes(adamId); const isProductPage = pageType === "Software"; if (!isProductPage || pageMatchesProduct) { const searchTerm = metricsUtil.searchTermFromRefURL(serverData.asString(nativeMetrics, "refUrl")); if (serverData.isDefinedNonNull(searchTerm)) { buyParams.set("searchTerm", searchTerm); } } // Remove ownerDsid from buyParams buyParams.set("ownerDsid", null, null); } /** * * @param adamId The identifier of the app whose buy parameters are being decorated. * @param buyParams The buy parameters of an app from a Media API response. * @param pageInformation The purchase configuration page metrics configuration * from an offer action. * @param excludeCrossfireAttribution Specifies whether crossfire attribution should be * excluded from the decorated buy params. * @param targetType The target type that buy occured on * @param kind The Kind that buy occured on, if any. * @param metricsPlatformDisplayStyle The platform display style from an offer action. * @param nativeMetrics Metrics fields provided by native code. * @param osMetrics Metrics fields describing the device's OS provided by native code. * @returns A copy of `buyParams` decorated with all metrics. */ export function addMetricsToBuyParams(objectGraph, adamId, buyParams, pageInformation, excludeCrossfireAttribution, isAppInstalled, targetType, kind, metricsPlatformDisplayStyle, nativeMetrics, osMetrics, productVariantData, inAppEventId, extRefApp2, extRefUrl2) { // Future: Build BuyParameters once, instead of creating it per modification. const bagMetricsBuyParams = addBagMetricsToBuyParams(objectGraph, buyParams); const pageMetricsBuyParams = addPageMetricsToBuyParams(objectGraph, bagMetricsBuyParams, adamId, pageInformation, targetType, kind, metricsPlatformDisplayStyle, productVariantData, inAppEventId, excludeCrossfireAttribution, extRefApp2, extRefUrl2); const nativeMetricsBuyParams = addNativeValuesToBuyParams(objectGraph, pageMetricsBuyParams, adamId, excludeCrossfireAttribution, isAppInstalled, nativeMetrics, osMetrics); return nativeMetricsBuyParams; } /** * Keys used for ad-related buy params. */ export const adBuyParamKeys = { containerId: "mtContainerId", placementId: "mtIadPlacementId", templateType: "mtIadTemplateType", }; /** * Copy ad-related buy params to the override buy params. * Override buy params are used for updated and redownloads, and in those cases we lose iAd download attribution. * If the original buy params had ad fields, we should copy them over here. * @param originalBuyParamsString The original buy params attached to the offerAction. * @param overrideBuyParamsString The override buy params from the purchase. * @returns An updated buy params string with any ad-related fields copied over. */ export function copyAdBuyParamsToOverrideBuyParams(originalBuyParamsString, overrideBuyParamsString) { const originalBuyParams = new BuyParameters(originalBuyParamsString); const overrideBuyParams = new BuyParameters(overrideBuyParamsString); const originalPlacementId = originalBuyParams.get(adBuyParamKeys.placementId, null); if ((originalPlacementId === null || originalPlacementId === void 0 ? void 0 : originalPlacementId.length) > 0 && serverData.isNullOrEmpty(overrideBuyParams.get(adBuyParamKeys.placementId, null))) { overrideBuyParams.set(adBuyParamKeys.placementId, originalPlacementId, null); } const originalContainerId = originalBuyParams.get(adBuyParamKeys.containerId, null); if ((originalContainerId === null || originalContainerId === void 0 ? void 0 : originalContainerId.length) > 0 && serverData.isNullOrEmpty(overrideBuyParams.get(adBuyParamKeys.containerId, null))) { overrideBuyParams.set(adBuyParamKeys.containerId, originalContainerId, null); } const originalTemplateType = originalBuyParams.get(adBuyParamKeys.templateType, null); if ((originalTemplateType === null || originalTemplateType === void 0 ? void 0 : originalTemplateType.length) > 0 && serverData.isNullOrEmpty(overrideBuyParams.get(adBuyParamKeys.templateType, null))) { overrideBuyParams.set(adBuyParamKeys.templateType, originalTemplateType, null); } return overrideBuyParams.toString(); } //# sourceMappingURL=buy.js.map