1// Use the localized config from PHP 2const config = { 3 stripeKey: coeditorConnectorConfig.stripeKey, 4 checkoutUrl: coeditorConnectorConfig.checkoutUrl, 5 baseUrl: coeditorConnectorConfig.baseUrl, 6}; 7 8function configureCoeditorPiano() { 9 const piano = window.coeditorConnectorConfig; 10 if (!piano || !piano.pianoAppId) { 11 return; 12 } 13 14 tp.push(["setAid", piano.pianoAppId]); 15 tp.push(["setUseTinypassAccounts", false]); 16 tp.push(["setUsePianoIdUserProvider", true]); 17 tp.push(["setRequestUserAuthForLinkedTerm", false]); 18 19 if (piano.endpoint) { 20 tp.push(["setEndpoint", piano.endpoint]); 21 } else if (piano.sandbox) { 22 tp.push(["setEndpoint", "https://sandbox.tinypass.com/api/v3"]); 23 } else { 24 tp.push(["setEndpoint", "https://buy.tinypass.com/api/v3"]); 25 } 26 27 if (piano.pianoIdUrl) { 28 tp.push(["setPianoIdUrl", piano.pianoIdUrl]); 29 } 30 if (piano.composerHost) { 31 tp.push(["setComposerHost", piano.composerHost]); 32 } 33 if (piano.staticDomain) { 34 tp.push(["setStaticDomain", piano.staticDomain]); 35 } 36 37 tp.push([ 38 "addHandler", 39 "loginSuccess", 40 function () { 41 setTimeout(function () { 42 location.reload(); 43 }, 2000); 44 }, 45 ]); 46} 47 48// ------------------------------ 49// Long tracking params helper 50// ------------------------------ 51// Stripe drops query params where the VALUE is >= 50 chars on redirect. 52// We persist those params and re-apply them on the thank-you page. 53const COEDITOR_TRACKING_PARAMS_KEY = "coeditor_tracking_params_v1"; 54const COEDITOR_REFER_URL_KEY = "refer_url"; 55const COEDITOR_TRACKING_MIN_LEN = 50; 56const COEDITOR_SUBS_ATTRIBUTION_COOKIE = "subs_attribution"; 57const COEDITOR_DEFAULT_SUBS_ATTRIBUTION_KEYS = [ 58 "unherd_subs_attribution", 59 "spec_subs_attribution", 60 "subs_attribution", 61]; 62const COEDITOR_SUBS_ATTRIBUTION_COOKIE_MAX = 3500; 63 64let COEDITOR_TRACKING_PARAMS = {}; 65 66function isStripeDroppedParamValue(value) { 67 if (!value) return false; 68 const s = String(value); 69 const decodedLen = s.length; 70 const encodedLen = encodeURIComponent(s).length; 71 return Math.max(decodedLen, encodedLen) >= COEDITOR_TRACKING_MIN_LEN; 72} 73 74function getTrackingParamsFromUrl(href = window.location.href) { 75 try { 76 const url = new URL(href); 77 const out = {}; 78 for (const [k, v] of url.searchParams.entries()) { 79 if (isStripeDroppedParamValue(v)) out[k] = v; 80 } 81 return out; 82 } catch { 83 return {}; 84 } 85} 86 87function getStoredReferUrl() { 88 try { 89 const stored = localStorage.getItem(COEDITOR_REFER_URL_KEY); 90 return stored && stored.trim() ? stored.trim() : null; 91 } catch { 92 return null; 93 } 94} 95 96function getCheckoutReferUrl() { 97 const storedRefer = getStoredReferUrl(); 98 if (storedRefer) { 99 return storedRefer; 100 } 101 102 const queryRefer = decodeReferUrlParam( 103 new URLSearchParams(window.location.search).get("refer_url"), 104 ); 105 return queryRefer || ""; 106} 107 108function decodeReferUrlParam(raw) { 109 if (!raw) return ""; 110 try { 111 return decodeURIComponent(raw); 112 } catch { 113 return raw; 114 } 115} 116 117let pendingReferUrlFromSession = null; 118let referRedirectStarted = false; 119 120function getPendingReferUrl() { 121 return getStoredReferUrl() || pendingReferUrlFromSession || ""; 122} 123 124function isCrossSellOfferVisible() { 125 const wrapper = document.getElementById("cross_sell_wrapper"); 126 return !!(wrapper && wrapper.querySelector(".do_cross_sell")); 127} 128 129function redirectToPendingReferUrl() { 130 const target = getPendingReferUrl(); 131 if (!target) { 132 return false; 133 } 134 135 try { 136 localStorage.removeItem(COEDITOR_REFER_URL_KEY); 137 } catch {} 138 139 referRedirectStarted = true; 140 window.location.replace(target); 141 return true; 142} 143 144function maybeRedirectAfterCheckout(session) { 145 if (session && session.redirect_url) { 146 pendingReferUrlFromSession = session.redirect_url; 147 } 148 149 if (!getPendingReferUrl()) { 150 return false; 151 } 152 153 if (isCrossSellOfferVisible()) { 154 return false; 155 } 156 157 return redirectToPendingReferUrl(); 158} 159 160window.coeditorReferRedirect = { 161 maybeRedirectAfterCheckout, 162 redirectToPendingReferUrl, 163 isCrossSellOfferVisible, 164}; 165 166// IMPORTANT: on /checkout/ your long param often only exists inside refer_url (encoded). 167function getTrackingParamsFromReferUrlParam() { 168 try { 169 const storedRefer = getStoredReferUrl(); 170 if (storedRefer) { 171 return getTrackingParamsFromUrl(storedRefer); 172 } 173 174 const url = new URL(window.location.href); 175 const referRaw = url.searchParams.get("refer_url"); 176 if (!referRaw) return {}; 177 178 return getTrackingParamsFromUrl(decodeReferUrlParam(referRaw)); 179 } catch { 180 return {}; 181 } 182} 183 184function getConfiguredSubsAttributionKeys() { 185 const configured = window.coeditorConnectorConfig?.subsAttributionKeys; 186 if (Array.isArray(configured) && configured.length) { 187 return configured.filter((key) => typeof key === "string" && key); 188 }
189 return COEDITOR_DEFAULT_SUBS_ATTRIBUTION_KEYS.slice(); 190} 191 192function getSubsAttributionStorageKeys() { 193 const keys = getConfiguredSubsAttributionKeys(); 194 try { 195 for (let i = 0; i < localStorage.length; i++) { 196 const key = localStorage.key(i); 197 if (key && /subs_attribution$/i.test(key) && !keys.includes(key)) { 198 keys.push(key); 199 } 200 } 201 } catch {} 202 return keys; 203} 204 205function getSubsAttribution() { 206 const merged = {}; 207 for (const key of getSubsAttributionStorageKeys()) { 208 try { 209 const raw = localStorage.getItem(key); 210 if (!raw) continue; 211 const parsed = JSON.parse(raw); 212 if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) { 213 Object.assign(merged, parsed); 214 } 215 } catch {} 216 } 217 return Object.keys(merged).length ? merged : null; 218} 219 220function persistSubsAttributionCookie(attribution) { 221 if (!attribution) return; 222 223 try { 224 const encoded = encodeURIComponent(JSON.stringify(attribution)); 225 if (encoded.length > COEDITOR_SUBS_ATTRIBUTION_COOKIE_MAX) { 226 return; 227 } 228 document.cookie = 229 COEDITOR_SUBS_ATTRIBUTION_COOKIE + 230 "=" + 231 encoded + 232 "; path=/; SameSite=Lax"; 233 } catch {} 234} 235 236function getTrackingParamsFromStorage() { 237 const out = {}; 238 try { 239 const raw = localStorage.getItem(COEDITOR_TRACKING_PARAMS_KEY); 240 if (raw) Object.assign(out, JSON.parse(raw)); 241 } catch {} 242 return out; 243} 244 245function storeTrackingParams(params) { 246 if (!params || typeof params !== "object") return; 247 if (Object.keys(params).length === 0) return; 248 249 try { 250 const existing = getTrackingParamsFromStorage(); 251 const merged = { ...existing, ...params }; 252 localStorage.setItem(COEDITOR_TRACKING_PARAMS_KEY, JSON.stringify(merged)); 253 } catch {} 254} 255 256function clearStoredTrackingParams() { 257 try { 258 localStorage.removeItem(COEDITOR_TRACKING_PARAMS_KEY); 259 } catch {} 260} 261 262// Capture once on page load: 263// - from current URL 264// - AND from decoded refer_url (checkout page usually has it here) 265function captureTrackingParamsOnce() { 266 const fromUrl = getTrackingParamsFromUrl(); 267 const fromRefer = getTrackingParamsFromReferUrlParam(); 268 const merged = { ...fromRefer, ...fromUrl }; // current URL wins 269 270 if (Object.keys(merged).length) { 271 COEDITOR_TRACKING_PARAMS = { ...COEDITOR_TRACKING_PARAMS, ...merged }; 272 storeTrackingParams(merged); 273 return COEDITOR_TRACKING_PARAMS; 274 } 275 276 COEDITOR_TRACKING_PARAMS = getTrackingParamsFromStorage(); 277 return COEDITOR_TRACKING_PARAMS; 278} 279 280// Read-only 281function getTrackingParams() { 282 const fromUrl = getTrackingParamsFromUrl(); 283 const fromStore = getTrackingParamsFromStorage(); 284 return { ...fromStore, ...COEDITOR_TRACKING_PARAMS, ...fromUrl }; 285} 286 287function ensureTrackingParamsInUrl(params) { 288 if (!params || typeof params !== "object") return; 289 290 const url = new URL(window.location.href); 291 let changed = false; 292 293 for (const [k, v] of Object.entries(params)) { 294 if (!v) continue; 295 296 const current = url.searchParams.get(k); 297 298 // Add if missing, or restore if Stripe truncated/blanked it 299 if (!current || current.length < String(v).length) { 300 url.searchParams.set(k, v); 301 changed = true; 302 } 303 } 304 305 if (changed) window.history.replaceState({}, "", url.toString()); 306} 307 308// Keep your existing nonce behaviour, but also restore tracking params on thank-you URL 309function ensureNonceAndTrackingParamsInUrl() { 310 const url = new URL(window.location.href); 311 const csid = url.searchParams.get("checkout_session_id"); 312 if (!csid) return; 313 314 let changed = false; 315 316 if (url.searchParams.get("__nonce") !== "true") { 317 url.searchParams.set("__nonce", "true"); 318 changed = true; 319 } 320 321 const tracking = getTrackingParams(); 322 for (const [k, v] of Object.entries(tracking)) { 323 if (!v) continue; 324 if (url.searchParams.get(k) !== v) { 325 url.searchParams.set(k, v); 326 changed = true; 327 } 328 } 329 330 if (changed) window.history.replaceState({}, "", url.toString()); 331} 332 333 334async function handleCheckoutSession() { 335 const urlParams = new URLSearchParams(window.location.search); 336 const csid = urlParams.get("checkout_session_id"); 337 if (!csid) return; 338 339 try { 340 const trackingParams = getTrackingParams(); 341 const fastStatus = Boolean(getPendingReferUrl() && !isCrossSellOfferVisible()); 342 343 const cleanBaseUrl = stripQueryParams(config.baseUrl); 344 const response = await fetch(`${cleanBaseUrl}/wp-json/coeditor-connector/v1/stripe-status`, { 345 headers: { 346 Accept: "application/json", 347 "Content-Type": "application/json", 348 }, 349 method: "POST", 350 body: JSON.stringify({ 351 session_id: csid,
352 stripeAccountId: urlParams.get("sid"), 353 tracking_params: trackingParams, 354 fast_status: fastStatus, 355 subs_attribution: getSubsAttribution(), 356 }), 357 }); 358 359 const session = await response.json(); 360 361 if (session.status === "open") { 362 const fallbackUrl = config.checkoutUrl || config.baseUrl + "/offer/"; 363 window.location.replace(fallbackUrl); 364 return; // stop further processing 365 } 366 367 if (session.status === "complete") { 368 const expires = new Date(Date.now() + 3600 * 1000); 369 const cookieDomain = window.location.hostname; 370 371 if (session.token?.access_token) { 372 document.cookie = 373 "__utp=" + 374 session.token.access_token + 375 "; expires=" + 376 expires.toUTCString() + 377 "; path=/"; 378 } 379 380 if (session.piano_user_id) { 381 document.cookie = 382 "tpuser=" + 383 session.piano_user_id + 384 ";domain=." + 385 cookieDomain + 386 ";path=/"; 387 } 388 389 document.body.classList.add("coeditor-logged-in"); 390 391 if (session.customer_email) { 392 localStorage.setItem( 393 "coeditor_customer_email", 394 session.customer_email, 395 ); 396 } 397 398 try { 399 localStorage.setItem( 400 "coeditor_stripe_checkout", 401 JSON.stringify({ 402 session_id: session.session_id || csid, 403 subscription_id: session.subscription_id || null, 404 currency: session.currency || null, 405 amount_total: session.amount_total ?? null, 406 payment_status: session.payment_status || null, 407 customer_email: session.customer_email || null, 408 customer_name: session.customer_name || null, 409 saved_at: Date.now(), 410 product_name: session.product_name || null, 411 product_description: session.product_description || null, 412 price_id: session.price_id || null, 413 product_id: session.product_id || null, 414 unit_amount: session.unit_amount || null, 415 quantity: session.quantity || 1, 416 billing_interval: session.billing_interval || null, 417 billing_interval_count: 418 session.billing_interval_count || null, 419 billing_cycle: session.billing_cycle || null, 420 }), 421 ); 422 } catch (error) { 423 console.warn("Could not persist Stripe checkout info:", error); 424 } 425 426 const prefix = "offerFields_"; 427 for (let i = localStorage.length - 1; i >= 0; i--) { 428 const key = localStorage.key(i); 429 430 if (key && key.startsWith(prefix)) { 431 localStorage.removeItem(key); 432 } 433 } 434 435 loginUser(); 436 437 const tracking = getTrackingParams(); 438 const urlNow = new URL(window.location.href); 439 const allPresent = Object.entries(tracking).every( 440 ([key, value]) => urlNow.searchParams.get(key) === value, 441 ); 442 443 if (allPresent) { 444 clearStoredTrackingParams(); 445 COEDITOR_TRACKING_PARAMS = {}; 446 } 447 448 const redirectDelayStartedAt = performance.now(); 449 450 console.log("[Checkout] Waiting before redirect"); 451 452 await waitBeforeCheckoutRedirect(); 453 454 console.log( 455 `[Checkout] Redirecting after ${Math.round( 456 performance.now() - redirectDelayStartedAt, 457 )} ms`, 458 ); 459 460 if (maybeRedirectAfterCheckout(session)) { 461 return; 462 } 463 } 464 465 } catch (error) { 466 console.error("Error fetching session status:", error); 467 } 468 } 469 470 const CHECKOUT_REDIRECT_DELAY_MS = 1500; 471 472 function waitBeforeCheckoutRedirect() { 473 return new Promise((resolve) => { 474 window.setTimeout(resolve, CHECKOUT_REDIRECT_DELAY_MS); 475 }); 476 } 477 478// This is for elements not yet available when the script runs 479function watchForElements(selector, callback) { 480 const observer = new MutationObserver((mutations, obs) => { 481 const elements = document.querySelectorAll(selector); 482 if (elements.length > 0) { 483 callback(elements); 484 obs.disconnect(); 485 } 486 }); 487 observer.observe(document.body, { childList: true, subtree: true }); 488} 489 490function stripQueryParams(urlString) { 491 const url = new URL(urlString); 492 return url.origin + url.pathname; 493} 494 495function loginUser() { 496 var user = tp.pianoId.getUser(); 497 var name = user.email.slice(0, 2); 498 if (user.firstName && user.lastName) { 499 name = user.firstName.slice(0, 1) + user.lastName.slice(0, 1); 500 } else if (user.firstName) { 501 name = user.firstName.slice(0, 2); 502 } else if (user.lastName) { 503 name = user.lastName.slice(0, 2); 504 } 505 506 watchForElements(".header-nav__button--account", (elements) => { 507 elements[0].style.display = "block"; 508 }); 509 510 watchForElements(".header-nav__button--login", (elements) => { 511 elements[0].style.display = "none"; 512 }); 513 514// if (document.querySelectorAll("#auth-link-1").length > 0) { 515// const button1 = document.getElementById("auth-link-1"); 516// const button2 = document.getElementById("auth-link-2"); 517 518// button1.innerHTML = "My Account";
519// button1.href = "/my-account"; 520// button2.innerHTML = "Logout"; 521 522// button2.addEventListener("click", function () { 523// button2.innerText = "Please wait"; 524 525// // Legacy logout flow, 526// // tp.user.logout(function () { 527// // setTimeout(function () { 528// // tp.pianoId.logout(); 529// // window.location.href = window.location.origin; 530// // }, 2000); 531// // }); 532 533// tp.pianoId.logout(); 534// window.location.href = window.location.origin; 535// }); 536// } 537 538 if (document.querySelectorAll("#briefingAccount").length > 0) { 539 var accountContainer = document.getElementById("briefingAccount"); 540 if (tp.pianoId.isUserValid()) { 541 accountContainer.innerHTML = '<button id="logoutBtn">Logout</button>'; 542 document.getElementById("logoutBtn").addEventListener("click", function () { 543 tp.pianoId.logout(); 544 window.location.reload(); 545 }); 546 } else { 547 accountContainer.innerHTML = '<button id="loginBtn">Login</button>'; 548 document.getElementById("loginBtn").addEventListener("click", function () { 549 tp.pianoId.show({ screen: "login" }); 550 }); 551 } 552 } 553 554 if (document.querySelectorAll(".single-post").length > 0) { 555 document.querySelector(".single-post").classList.add("logged-in"); 556 } 557 558 if (document.querySelectorAll(".hamburger").length > 0) { 559 document.querySelector(".hamburger").innerHTML = 560 '<div class="user-name">' + name + "</div>"; 561 } 562 563 [].forEach.call(document.querySelectorAll(".not-logged"), function (el) { 564 el.style.display = "none"; 565 }); 566 567 [].forEach.call(document.querySelectorAll(".logged"), function (el) { 568 el.style.display = "block"; 569 }); 570 571 if (document.querySelectorAll(".piano-login-link").length > 0) { 572 document.querySelectorAll(".piano-login-link").forEach(function (el) { 573 el.classList.add("loggedin"); 574 el.innerHTML = '<a href="/my-subscription">My Account</a>'; 575 }); 576 } 577} 578 579document.addEventListener("DOMContentLoaded", async function () { 580 // NEW: capture long params from URL and/or refer_url early 581 captureTrackingParamsOnce(); 582 583 // NEW: on thank-you pages, restore nonce + tracking params to the URL immediately 584 ensureNonceAndTrackingParamsInUrl(); 585 586 window.addEventListener("message", (event) => { 587 if (event.data?.type === "checkoutData") { 588 localStorage.setItem("offerFields_" + event.data.fieldId, event.data.value); 589 } 590 }); 591 592 // Initialize the tp object 593 tp = window["tp"] || []; 594 configureCoeditorPiano(); 595 596 function onLinkedTermSelected(sessionId, linkedTerm) { 597 const subsAttribution = getSubsAttribution(); 598 persistSubsAttributionCookie(subsAttribution); 599 600 // Keep the Spectator cookie name so older Connector PHP still receives it. 601 try { 602 const specSubsAttribution = localStorage.getItem("spec_subs_attribution"); 603 if (specSubsAttribution) { 604 document.cookie = 605 "spec_subs_attribution=" + 606 encodeURIComponent(specSubsAttribution) + 607 "; path=/"; 608 } 609 } catch {} 610 611 var data; 612 if (linkedTerm.linkedTermData !== undefined) { 613 data = linkedTerm.linkedTermData; 614 var payload = data; 615 var externalTermId = data.externalTermId; 616 var externalProductId = data.externalProductIds; 617 } else if (linkedTerm.linkTermSignedData !== undefined) { 618 data = linkedTerm.linkTermSignedData; 619 var payload = JSON.parse(data.payload); 620 var externalTermId = payload.externalTermId; 621 var externalProductId = payload.externalProductIds; 622 } else { 623 console.log("No link term data found"); 624 } 625 626 const url = new URL(config.checkoutUrl.replace(/\/+$/, "")); 627 628 console.log(payload.customData); 629 630 url.searchParams.set("session_id", sessionId); 631 url.searchParams.set("term_id", externalTermId); 632 url.searchParams.set("coupon", payload.customData.coupon ?? ""); 633 url.searchParams.set("offer_id", payload.customData.offer_id ?? ""); 634 url.searchParams.set("price_id", externalTermId); 635 url.searchParams.set("product_id", externalProductId); 636 url.searchParams.set("coupon_id", payload.customData.coupon_id ?? ""); 637 url.searchParams.set("description", payload.customData.product_description ?? ""); 638 const storedRefer = getStoredReferUrl(); 639 if (storedRefer) { 640 url.searchParams.set("refer_url", encodeURIComponent(storedRefer)); 641 } else { 642 url.searchParams.set("refer_url", encodeURIComponent(window.location.href)); 643 } 644 url.searchParams.set("allow_shipping", payload.customData.allow_shipping ?? "false"); 645 url.searchParams.set("free_gift", payload.customData.free_gift ?? ""); 646 url.searchParams.set("transaction_type", payload.customData.transaction_type ?? ""); 647 url.searchParams.set("request_phone_number", payload.customData.request_phone_number ?? ""); 648 url.searchParams.set("p_mode", payload.customData.p_mode ?? ""); 649 650 651 // NEW (minimal): capture before leaving this page 652 captureTrackingParamsOnce(); 653 654 window.location.href = url.toString(); 655 } 656 657 tp.push(["setLinkedTermSelected", onLinkedTermSelected]); 658 659 tp.push([ 660 "init", 661 function () { 662 tp.pianoId.init({ confirmation: "none" }); 663 664 if (tp.pianoId.isUserValid()) { 665 document.body.classList.add("coeditor-logged-in"); 666 } 667 668 tp.experience.init(); 669 }, 670 ]); 671 672 await handleCheckoutSession(); 673
674 const urlParams = new URLSearchParams(window.location.search); 675 const nonce = urlParams.get("__nonce"); 676 const checkoutSessionId = urlParams.get("checkout_session_id"); 677 678 if (checkoutSessionId !== null && (nonce === null || nonce === "false")) { 679 urlParams.set("__nonce", "true"); 680 const newUrl = `${window.location.pathname}?${urlParams.toString()}`; 681 window.history.replaceState({}, "", newUrl); 682 } 683 684 if (checkoutSessionId !== null && !referRedirectStarted) { 685 loginUser(); 686 } 687 688 initialize(); 689 690 async function initialize() { 691 const urlParams = new URLSearchParams(window.location.search); 692 693 if (urlParams.get("session_id") === null) { 694 console.log("No session_id provided"); 695 return; 696 } else { 697 let stripePublicKey = null; 698 let stripeAccountId = null; 699 700 const getCookie = (name) => { 701 const value = `; ${document.cookie}`; 702 const parts = value.split(`; ${name}=`); 703 if (parts.length === 2) return parts.pop().split(";").shift(); 704 }; 705 706 const decodeJWT = (token) => { 707 try { 708 const [header, payload, signature] = token.split("."); 709 const decodedPayload = JSON.parse( 710 atob(payload.replace(/-/g, "+").replace(/_/g, "/")), 711 ); 712 return decodedPayload; 713 } catch (error) { 714 console.error("Failed to decode JWT:", error); 715 return null; 716 } 717 }; 718 719 const fetchClientSecret = async () => { 720 const utpCookie = getCookie("__utp"); 721 const offer = localStorage.getItem("templateId"); 722 723 const product = { 724 price_id: urlParams.get("price_id"), 725 term_id: urlParams.get("term_id"), 726 product_id: urlParams.get("product_id"), 727 session_id: urlParams.get("session_id"), 728 allow_billing_address: urlParams.get("allow_shipping"), 729 coupon: urlParams.get("coupon"), 730 coupon_id: urlParams.get("coupon_id"), 731 description: urlParams.get("description"), 732 refer_url: getCheckoutReferUrl(), 733 refer_url_from_storage: Boolean(getStoredReferUrl()), 734 free_gift: urlParams.get("free_gift"), 735 transaction_type: urlParams.get("transaction_type"), 736 request_phone_number: urlParams.get("request_phone_number"), 737 p_mode: urlParams.get("p_mode"), 738 offer_fields: {}, 739 }; 740 741 console.log(product); 742 const prefix = "offerFields_"; 743 for (let i = 0; i < localStorage.length; i++) { 744 const key = localStorage.key(i); 745 if (key.startsWith(prefix)) { 746 const fieldId = key.replace(prefix, ""); 747 product.offer_fields[fieldId] = localStorage.getItem(key); 748 } 749 } 750 751 // Optional: include tracking params on create-session too 752 product.tracking_params = getTrackingParams(); 753 754 const subsAttribution = getSubsAttribution(); 755 if (subsAttribution) { 756 product.subs_attribution = subsAttribution; 757 persistSubsAttributionCookie(subsAttribution); 758 } 759 760 const fbcCookie = getCookie("_fbc"); 761 const fpcCookie = getCookie("_fpc"); 762 if (fbcCookie || fpcCookie) { 763 product.tracking_cookies = {}; 764 if (fbcCookie) product.tracking_cookies._fbc = fbcCookie; 765 if (fpcCookie) product.tracking_cookies._fpc = fpcCookie; 766 } 767 768 if (utpCookie) { 769 const decodedPayload = decodeJWT(utpCookie); 770 if (decodedPayload?.email) { 771 product.customer_email = decodedPayload.email; 772 } 773 } 774 775 const cleanBaseUrl = stripQueryParams(config.baseUrl); 776 777 const response = await fetch( 778 `${cleanBaseUrl}/wp-json/coeditor-connector/v1/stripe-create-session`, 779 { 780 method: "POST", 781 headers: { "Content-Type": "application/json" }, 782 body: JSON.stringify(product), 783 }, 784 ); 785 786 const result = await response.json(); 787 788 stripePublicKey = result.public_key; 789 stripeAccountId = result.account_id; 790 791 return [ 792 { 793 clientSecret: result.clientSecret, 794 publicKey: result.public_key, 795 accountId: result.account_id, 796 }, 797 ]; 798 }; 799 800 const [{ clientSecret, publicKey, accountId }] = 801 await fetchClientSecret(); 802 803 const stripe = accountId 804 ? Stripe(stripePublicKey || config.stripeKey, { stripeAccount: accountId }) 805 : Stripe(stripePublicKey || config.stripeKey); 806
807 const checkout = await stripe.initEmbeddedCheckout({ 808 clientSecret, 809 }); 810 811 checkout.mount("#checkout"); 812 } 813 } 814}); 815 816(function ($) { 817 "use strict"; 818 819 $(document).ready(function () { 820 $(document).on("click", ".do_cross_sell", function (e) { 821 e.preventDefault(); 822 823 let data; 824 825 const urlParams = new URLSearchParams(window.location.search); 826 827 const email = this.getAttribute("data-email"); 828 if (!email) return alert("Missing email."); 829 830 const productId = this.getAttribute("data-product"); 831 const publication = this.getAttribute("data-publication"); 832 833 jQuery.ajax({ 834 type: "POST", 835 url: coeditorConnectorConfig.ajax_url, 836 data: { 837 action: "cross_sell_user", 838 session: urlParams.get("checkout_session_id"), 839 stripe_account: urlParams.get("sid"), 840 productId: productId, 841 publication: publication, 842 nonce: coeditorConnectorConfig.nonce, 843 }, 844 success: function (response) { 845 document.getElementById("cross_sell_wrapper").innerHTML = ""; 846 data = typeof response === "string" ? JSON.parse(response) : response; 847 if (data.success) { 848 document.getElementById("cross_sell_wrapper").innerHTML = 849 data.rendered_template; 850 if (window.coeditorReferRedirect) { 851 window.coeditorReferRedirect.redirectToPendingReferUrl(); 852 } 853 } 854 }, 855 error: function (err) { 856 console.error("AJAX error:", err); 857 }, 858 }); 859 }); 860 }); 861})(jQuery);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.