Merchant API

Add a Photo

Allow a user to create a check-in.

Get Photo Details

Remove a check-in, if the acting user is the owner of the check-in.


${value.text.secondary}
`; } async function selectItem({ target }) { if (target.tagName === "BUTTON") { const link = target.dataset.object; const addressDetail = await fetchAddressDetails(link); const { location = {} } = addressDetail; const { address = "", country = "", postcode = "", locality = "", region = "", } = location; addressInput.value = address; address2Input.value = ""; countryInput.value = country; postcodeInput.value = postcode; cityInput.value = locality; regionInput.value = region; // generate new session token after a complete search sessionToken = generateRandomSessionToken(); address2Input && address2Input.focus(); dropDownField.style.display = "none"; } } async function fetchAddressDetails(link) { try { const results = await fetch(`https://api.foursquare.com${link}`, { method: "get", headers: new Headers({ Accept: "application/json", Authorization: fsqAPIToken, }), }); const data = await results.json(); return data; } catch (err) { logError(err); } } function highlightedNameElement(textObject) { if (!textObject) return ""; const { primary, highlight } = textObject; if (highlight && highlight.length) { let beginning = 0; let hightligtedWords = ""; for (let i = 0; i < highlight.length; i++) { const { start, length } = highlight[i]; hightligtedWords += primary.substr(beginning, start - beginning); hightligtedWords += "" + primary.substr(start, length) + ""; beginning = start + length; } hightligtedWords += primary.substr(beginning); return hightligtedWords; } return primary; } function debounce(func, timeout = 300) { let timer; return (...args) => { clearTimeout(timer); timer = setTimeout(() => { func.apply(this, args); }, timeout); }; } } localAddressAutoFillJs(); } if (state && state.params && state.params.slug === "local-search-map") { function loadLocalMapSearchJs() { mapboxgl.accessToken = "pk.eyJ1IjoiZm91cnNxdWFyZSIsImEiOiJjbDNqNXdrN20wN3JtM2JvMWFqZGxoaGljIn0.uSxJ2t7E96TrBFsn3cXT_g"; const fsqAPIToken = "fsq3bgqdcpLAJFkodk8gisc2F+NenA7gK/zI97A9nKQAXIw="; let userLat = 40.7128; let userLng = -74.006; let sessionToken = generateRandomSessionToken(); const inputField = document.getElementById("explorer-search"); const dropDownField = document.getElementById("explorer-dropdown"); const ulField = document.getElementById("explorer-suggestions"); const errorField = document.getElementById("explorer-error"); const notFoundField = document.getElementById("explorer-not-found"); const onChangeAutoComplete = debounce(changeAutoComplete); inputField.addEventListener("input", onChangeAutoComplete); ulField.addEventListener("click", selectItem); function success(pos) { const { latitude, longitude } = pos.coords; userLat = latitude; userLng = longitude; flyToLocation(userLat, userLng); } function logError(err) { console.warn(`ERROR(${err.code}): ${err.message}`); } navigator.geolocation.getCurrentPosition(success, logError, { enableHighAccuracy: true, timeout: 5000, maximumAge: 0, }); const map = new mapboxgl.Map({ container: "map", style: "mapbox://styles/mapbox/light-v10", center: [userLng, userLat], zoom: 12, }); map.addControl(new mapboxgl.GeolocateControl()); map.addControl(new mapboxgl.NavigationControl()); let currentMarker; /* Generate a random string with 32 characters. Session Token is a user-generated token to identify a session for billing purposes. Learn more about session tokens. https://docs.foursquare.com/reference/session-tokens */ function generateRandomSessionToken(length = 32) { let result = ""; const characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; for (let i = 0; i < length; i++) { result += characters[Math.floor(Math.random() * characters.length)]; } return result; } let isFetching = false; async function changeAutoComplete({ target }) { const { value: inputSearch = "" } = target; ulField.innerHTML = ""; notFoundField.style.display = "none"; errorField.style.display = "none"; if (inputSearch.length && !isFetching) { try { isFetching = true; const results = await autoComplete(inputSearch); if (results && results.length) { results.forEach((value) => { addItem(value); }); } else { notFoundField.innerHTML = `Foursquare can't find ${inputSearch}. Make sure your search is spelled correctly. Don't see the place you're looking for?.`; notFoundField.style.display = "block"; } } catch (err) { errorField.style.display = "block"; logError(err); } finally { isFetching = false; dropDownField.style.display = "block"; } } else { dropDownField.style.display = "none"; } } async function autoComplete(query) { const { lng, lat } = map.getCenter(); userLat = lat; userLng = lng; try { const searchParams = new URLSearchParams({ query, types: "place", ll: `${userLat},${userLng}`, radius: 50000, session_token: sessionToken, }).toString(); const searchResults = await fetch( `https://api.foursquare.com/v3/autocomplete?${searchParams}`, { method: "get", headers: new Headers({ Accept: "application/json", Authorization: fsqAPIToken, }), } ); const data = await searchResults.json(); return data.results; } catch (error) { throw error; } } function addItem(value) { const placeDetail = value[value.type]; if (!placeDetail || !placeDetail.geocodes || !placeDetail.geocodes.main) return; const { latitude, longitude } = placeDetail.geocodes.main; const fsqId = placeDetail.fsq_id; const dataObject = JSON.stringify({ latitude, longitude, fsqId }); ulField.innerHTML += `
  • ${highlightedNameElement(value.text)}
    ${value.text.secondary}
  • `; } async function selectItem({ target }) { if (target.tagName === "LI") { const valueObject = JSON.parse(target.dataset.object); const { latitude, longitude, fsqId } = valueObject; const placeDetail = await fetchPlacesDetails(fsqId); addMarkerAndPopup(latitude, longitude, placeDetail); flyToLocation(latitude, longitude); // generate new session token after a complete search sessionToken = generateRandomSessionToken(); const name = target.dataset.name; inputField.value = target.children[0].textContent; dropDownField.style.display = "none"; } } async function fetchPlacesDetails(fsqId) { try { const searchParams = new URLSearchParams({ fields: "fsq_id,name,geocodes,location,photos,rating", session_token: sessionToken, }).toString(); const results = await fetch( `https://api.foursquare.com/v3/places/${fsqId}?${searchParams}`, { method: "get", headers: new Headers({ Accept: "application/json", Authorization: fsqAPIToken, }), } ); const data = await results.json(); return data; } catch (err) { logError(err); } } function createPopup(placeDetail) { const { location = {}, name = "", photos = [], rating } = placeDetail; let photoUrl = "https://files.readme.io/c163d6e-placeholder.svg"; if (photos.length && photos[0]) { photoUrl = `${photos[0].prefix}56${photos[0].suffix}`; } const popupHTML = `
    photo of ${name}
    ${name}
    ${location.address}
    ${ rating ? `
    ${rating}
    ` : `
    ` }
    `; const markerHeight = 35; const markerRadius = 14; const linearOffset = 8; const verticalOffset = 8; const popupOffsets = { top: [0, verticalOffset], "top-left": [0, verticalOffset], "top-right": [0, verticalOffset], bottom: [0, -(markerHeight + verticalOffset)], "bottom-left": [ 0, (markerHeight + verticalOffset - markerRadius + linearOffset) * -1, ], "bottom-right": [ 0, (markerHeight + verticalOffset - markerRadius + linearOffset) * -1, ], left: [ markerRadius + linearOffset, (markerHeight - markerRadius) * -1, ], right: [ -(markerRadius + linearOffset), (markerHeight - markerRadius) * -1, ], }; return new mapboxgl.Popup({ offset: popupOffsets, closeButton: false, }).setHTML(popupHTML); } function addMarkerAndPopup(lat, lng, placeDetail) { if (currentMarker) currentMarker.remove(); currentMarker = new mapboxgl.Marker({ color: "#3333FF", }) .setLngLat([lng, lat]) .setPopup(createPopup(placeDetail)) .addTo(map); currentMarker.togglePopup(); } function flyToLocation(lat, lng) { map.flyTo({ center: [lng, lat], }); } function highlightedNameElement(textObject) { if (!textObject) return ""; const { primary, highlight } = textObject; if (highlight && highlight.length) { let beginning = 0; let hightligtedWords = ""; for (let i = 0; i < highlight.length; i++) { const { start, length } = highlight[i]; hightligtedWords += primary.substr(beginning, start - beginning); hightligtedWords += "" + primary.substr(start, length) + ""; beginning = start + length; } hightligtedWords += primary.substr(beginning); return hightligtedWords; } return primary; } function debounce(func, timeout = 300) { let timer; return (...args) => { clearTimeout(timer); timer = setTimeout(() => { func.apply(this, args); }, timeout); }; } } loadLocalMapSearchJs(); } }); $(window).on("pageLoad", function (e, state) { const openSearchPopup = () => { const headerSearch = document.getElementById("hub-search-results"); if (headerSearch) { headerSearch.classList.add("hub-search-results-active"); document .getElementsByClassName("Input Input_md SearchBox-InputUQZAW9QXMe-c")[0] ?.focus(); } }; const triggerSearchButton = document.getElementsByClassName( "landing-page-trigger-search-button" )[0]; if (triggerSearchButton) { triggerSearchButton.addEventListener("click", openSearchPopup); } }); // START: Add Segment Tracking to Try It button // TODO: Verify this runs on route changes, so button gets instrumented on every page $(window).on("load", function (e, state) { var tryItBtn = document.querySelector(".rm-TryIt"); // in case the button has not yet renedered if (tryItBtn === undefined) return; tryItBtn.addEventListener("click", () => { console.log("Try It button Clicked (project)"); analytics.track("Try It button Clicked"); }); }); // END: Add Segment Tracking to Try It button // CUSTOM LAUNCH TOP NAV CODE - README document.addEventListener('DOMContentLoaded', () => { setTimeout(() => { document.querySelectorAll('.Header-leftADQdGVqx1wqU a[href]').forEach(a => { a.addEventListener('click', e => { e.preventDefault(); window.location.href = a.href; }); }); }, 100); }); // CUSTOM LAUNCH SIDEBAR CODE (FINAL project: inject Places first, then Users under Welcome) // Runs after a short delay to avoid flicker or unwanted auto-expansion. document.addEventListener("DOMContentLoaded", function () { // Delay entire injection by 1 second setTimeout(() => { (async function() { // Normalize path (strip trailing slash) const path = window.location.pathname.replace(/\/$/, ''); // Only act on the FINAL project paths if (!path.startsWith('/developer/reference/')) { return; } // Pages to pull from: Places API overview and Users API overview // **Places first**, then Users const toInject = [ { otherPageUrl: '/fsq-developers-places/reference/places-api-overview', targetSectionText: 'Places API' }, { otherPageUrl: '/fsq-developers-users/reference/users-api-overview', targetSectionText: 'Users API' } ]; // If versioned (e.g. /v2023-05-01/ in URL), insert version segment into otherPageUrl const versionMatch = window.location.pathname.match(/\/(v\d{4}-\d{2}-\d{2})\//); function applyVersion(url) { if (versionMatch) { const version = versionMatch[1]; return url.replace('/reference/', `/${version}/reference/`); } return url; } // Fetch & cache a single section's HTML from another page async function fetchSectionHtml(otherPageUrl, targetSectionText) { const versionedUrl = applyVersion(otherPageUrl); const cacheKey = `crossSection:${versionedUrl}:${targetSectionText}`; const cached = sessionStorage.getItem(cacheKey); if (cached) { return cached; } try { const resp = await fetch(versionedUrl, { credentials: 'same-origin' }); if (!resp.ok) { console.warn('[CrossInject] Fetch failed', resp.status, versionedUrl); return null; } const htmlText = await resp.text(); // Parse to DOM const parser = new DOMParser(); const doc = parser.parseFromString(htmlText, 'text/html'); // Find the sidebar section const sidebarSelector = '.rm-Sidebar'; const otherSidebar = doc.querySelector(sidebarSelector); if (!otherSidebar) { console.warn('[CrossInject] Sidebar not found for', versionedUrl); return null; } // Locate the
    whose

    includes targetSectionText let foundSection = null; otherSidebar.querySelectorAll('section').forEach(sec => { if (foundSection) return; const h2 = sec.querySelector('h2'); if (h2 && h2.textContent.trim().includes(targetSectionText)) { foundSection = sec; } }); if (!foundSection) { console.warn('[CrossInject] Section not found:', targetSectionText, 'in', versionedUrl); return null; } const sectionHtml = foundSection.outerHTML; try { sessionStorage.setItem(cacheKey, sectionHtml); } catch (_) {} return sectionHtml; } catch (err) { console.error('[CrossInject] Error fetching/parsing:', err); return null; } } // Wait until the current page's sidebar is present function whenSidebarReady(fn) { const interval = setInterval(() => { const sidebarNav = document.querySelector('.rm-Sidebar'); if (sidebarNav) { clearInterval(interval); fn(sidebarNav); } }, 100); // Stop polling after 5s setTimeout(() => clearInterval(interval), 5000); } // Collapse helper for a section element function collapseSection(sec) { const h2 = sec.querySelector('h2'); const ul = sec.querySelector('ul'); if (h2) { h2.classList.remove('section-expanded'); h2.classList.add('section-collapsed'); h2.setAttribute('aria-expanded', 'false'); } if (ul) { ul.classList.add('section-collapsed'); ul.classList.remove('section-list-expanded'); } } // Expand helper function expandSection(sec) { const h2 = sec.querySelector('h2'); const ul = sec.querySelector('ul'); if (h2) { h2.classList.remove('section-collapsed'); h2.classList.add('section-expanded'); h2.setAttribute('aria-expanded', 'true'); } if (ul) { ul.classList.remove('section-collapsed'); ul.classList.add('section-list-expanded'); } } // Fetch all needed sections in parallel const results = await Promise.all( toInject.map(item => fetchSectionHtml(item.otherPageUrl, item.targetSectionText) .then(html => ({ ...item, sectionHtml: html })) ) ); const validSections = results.filter(r => r.sectionHtml); if (validSections.length === 0) { return; } // Once our sidebar is ready, inject under "Welcome" whenSidebarReady(sidebarNav => { const contentDiv = sidebarNav.querySelector('.hub-sidebar-content'); if (!contentDiv) { console.warn('[CrossInject] .hub-sidebar-content not found; abort insertion.'); return; } // Find "Welcome" section const welcomeSection = Array.from(contentDiv.querySelectorAll('section')).find(sec => { const h2 = sec.querySelector('h2'); return h2 && h2.textContent.trim() === 'Welcome'; }); let insertAfter = welcomeSection; validSections.forEach(({ targetSectionText, sectionHtml }) => { const wrapper = document.createElement('div'); wrapper.innerHTML = sectionHtml; const originalSectionNode = wrapper.firstElementChild; if (!originalSectionNode) { console.warn('[CrossInject] No section node for', targetSectionText); return; } const sectionClone = originalSectionNode.cloneNode(true); // Adapt heading & toggle as needed: const injectedH2 = sectionClone.querySelector('h2'); const injectedUL = sectionClone.querySelector('ul'); if (injectedH2 && injectedUL) { // Copy classes from an existing template

    if present const templateH2 = sidebarNav.querySelector('.hub-sidebar-content section h2'); if (templateH2) { injectedH2.className = templateH2.className; } else { injectedH2.classList.add('Sidebar-headingTRQyOa2pk0gh', 'rm-Sidebar-heading'); } injectedH2.setAttribute('tabindex', '0'); // Remove existing chevrons, clone from template or fallback Array.from(injectedH2.querySelectorAll('.icon-chevron')).forEach(el => el.remove()); if (templateH2) { templateH2.querySelectorAll('.icon-chevron').forEach(iconSpan => { injectedH2.appendChild(iconSpan.cloneNode(true)); }); } else { const fallbackSpan = document.createElement('span'); fallbackSpan.className = 'icon-chevron'; injectedH2.appendChild(fallbackSpan); } // Initially collapsed collapseSection(sectionClone); // Toggle handler const toggleFn = () => { const isCollapsed = injectedUL.classList.contains('section-collapsed'); if (isCollapsed) { expandSection(sectionClone); } else { collapseSection(sectionClone); } }; injectedH2.style.cursor = 'pointer'; injectedH2.addEventListener('click', toggleFn); injectedH2.addEventListener('keydown', e => { if (e.key === 'Enter' || e.key === ' ' || e.key === 'Spacebar') { e.preventDefault(); toggleFn(); } }); } // Nested toggles sectionClone.querySelectorAll('a.Sidebar-link_parent').forEach(aParent => { let btn = aParent.querySelector('button'); if (!btn && aParent.nextElementSibling && aParent.nextElementSibling.tagName === 'BUTTON') { btn = aParent.nextElementSibling; } let subUl = null; if (btn) { const maybeUl = btn.parentElement.nextElementSibling; if (maybeUl && maybeUl.classList.contains('subpages')) subUl = maybeUl; } if (!subUl) { const parentLi = aParent.closest('li'); if (parentLi) { const maybe = parentLi.querySelector('ul.subpages'); if (maybe) subUl = maybe; } } if (btn && subUl) { // collapsed by default btn.setAttribute('aria-expanded', 'false'); subUl.classList.add('section-collapsed'); subUl.classList.remove('section-list-expanded'); btn.style.cursor = 'pointer'; btn.addEventListener('click', e => { e.preventDefault(); const expanded = btn.getAttribute('aria-expanded') === 'true'; if (expanded) { btn.setAttribute('aria-expanded', 'false'); subUl.classList.add('section-collapsed'); subUl.classList.remove('section-list-expanded'); } else { btn.setAttribute('aria-expanded', 'true'); subUl.classList.remove('section-collapsed'); subUl.classList.add('section-list-expanded'); } }); } }); // Insert right after Welcome (Places first, then Users) if (insertAfter && insertAfter.parentElement === contentDiv) { insertAfter.insertAdjacentElement('afterend', sectionClone); insertAfter = sectionClone; } else { contentDiv.appendChild(sectionClone); } console.log(`[CrossInject] Injected "${targetSectionText}" after Welcome.`); }); // After injecting both, auto-expand whichever matches current path function normalizeHref(href) { try { const url = new URL(href, window.location.origin); return url.pathname.replace(/\/$/, ''); } catch { return href.replace(/\/$/, ''); } } const normPath = window.location.pathname.replace(/\/$/, ''); // Clear any previous active marks: contentDiv.querySelectorAll('a[aria-current]').forEach(a => { a.removeAttribute('aria-current'); a.classList.remove('active'); }); // Find matches among injected links: const allLinks = Array.from(contentDiv.querySelectorAll('a')); allLinks.forEach(a => { const href = a.getAttribute('href'); if (!href) return; if (normalizeHref(href) === normPath) { // mark active a.setAttribute('aria-current', 'page'); a.classList.add('active'); // expand its parent section let sec = a.closest('section'); if (sec) expandSection(sec); // expand ancestors let anc = a.parentElement; while (anc && anc !== contentDiv) { if (anc.tagName === 'UL' && anc.classList.contains('subpages')) { anc.classList.remove('section-collapsed'); anc.classList.add('section-list-expanded'); const parentLi = anc.closest('li'); if (parentLi) { const btn = parentLi.querySelector('button[aria-expanded]'); if (btn) btn.setAttribute('aria-expanded', 'true'); } } if (anc.tagName === 'SECTION') { expandSection(anc); } anc = anc.parentElement; } } }); // If none matched exactly, all remain collapsed. }); })(); }, 600); });