@openpartner/sdk 0.1.0 → 0.3.0
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/CHANGELOG.md +79 -0
- package/README.md +140 -6
- package/dist/browser.cjs +260 -26
- package/dist/browser.cjs.map +1 -1
- package/dist/browser.d.cts +163 -18
- package/dist/browser.d.ts +163 -18
- package/dist/browser.js +260 -26
- package/dist/browser.js.map +1 -1
- package/dist/server.cjs +60 -2
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +112 -8
- package/dist/server.d.ts +112 -8
- package/dist/server.js +56 -1
- package/dist/server.js.map +1 -1
- package/dist/{shared-CLfBjPeb.d.cts → shared-dh1vlldf.d.cts} +19 -4
- package/dist/{shared-CLfBjPeb.d.ts → shared-dh1vlldf.d.ts} +19 -4
- package/dist-global/openpartner.js +1 -0
- package/package.json +38 -16
- package/LICENSE +0 -21
package/dist/browser.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/browser.ts","../src/shared.ts"],"sourcesContent":["/**\n * Browser SDK — runs inside a merchant's web app. Captures the `cref`\n * attribution token from landing-page URLs and the first-party cookie\n * the click router sets, stashes it in localStorage so it survives\n * Safari / ITP cookie expiry and SPA navigations, and stitches to the\n * authenticated user on signup or login.\n *\n * Typical integration:\n *\n * import { OpenPartner } from '@openpartner/sdk';\n *\n * const op = OpenPartner.init({ apiUrl: 'https://openpartner.example.com' });\n *\n * // After login/signup, once you know who the user is:\n * await op.identify(user.id);\n */\n\nimport { OpenPartnerError, requestJson, type IdentifyResponse } from './shared.js';\n\nexport { OpenPartnerError };\nexport type { IdentifyResponse };\n\nexport interface OpenPartnerConfig {\n /** Base URL of your OpenPartner API, e.g. \"https://openpartner.example.com\". */\n apiUrl: string;\n /** First-party cookie name set by the click router. Default `_cref`. */\n cookieName?: string;\n /** Query-string parameter that carries the cref on the landing URL. Default `cref`. */\n queryParam?: string;\n /** localStorage key the captured cref is stashed under. Default `openpartner:cref`. */\n storageKey?: string;\n /**\n * Called when identify() fails. Default: log to console. Pass a noop\n * to suppress entirely or hook into your observability stack.\n */\n onError?: (err: unknown) => void;\n}\n\nconst DEFAULTS = {\n cookieName: '_cref',\n queryParam: 'cref',\n storageKey: 'openpartner:cref',\n onError: (err: unknown) => {\n console.warn('[openpartner] identify failed', err);\n },\n};\n\nexport class OpenPartner {\n private config: Required<OpenPartnerConfig>;\n\n private constructor(config: OpenPartnerConfig) {\n this.config = { ...DEFAULTS, ...config } as Required<OpenPartnerConfig>;\n this.captureCref();\n }\n\n static init(config: OpenPartnerConfig): OpenPartner {\n return new OpenPartner(config);\n }\n\n /** The cref we're holding onto, if any. Useful for debugging. */\n getCref(): string | null {\n if (typeof localStorage === 'undefined') return null;\n return localStorage.getItem(this.config.storageKey);\n }\n\n /**\n * Captured referral token for this visitor. Pass to Stripe Checkout\n * as `client_reference_id` to enable webhook-based attribution\n * without an explicit identify() call:\n *\n * stripe.checkout.sessions.create({\n * client_reference_id: op.getReferral() ?? undefined,\n * // ...\n * });\n *\n * Returns null if the visitor didn't arrive via a partner link.\n */\n getReferral(): string | null {\n return this.getCref();\n }\n\n /** Explicitly discard the captured cref. */\n clear(): void {\n if (typeof localStorage === 'undefined') return;\n localStorage.removeItem(this.config.storageKey);\n }\n\n /**\n * Capture the cref from query string or cookie and stash in\n * localStorage. Runs on init so the value survives SPA navigations,\n * ITP cookie expiry, and subsequent page loads.\n */\n private captureCref(): void {\n if (typeof window === 'undefined' || typeof localStorage === 'undefined') return;\n\n const url = new URL(window.location.href);\n const queryCref = url.searchParams.get(this.config.queryParam);\n\n if (queryCref) {\n localStorage.setItem(this.config.storageKey, queryCref);\n return;\n }\n\n const cookieCref = this.readCookie(this.config.cookieName);\n if (cookieCref && !localStorage.getItem(this.config.storageKey)) {\n localStorage.setItem(this.config.storageKey, cookieCref);\n }\n }\n\n private readCookie(name: string): string | null {\n if (typeof document === 'undefined') return null;\n const match = document.cookie.match(new RegExp(`(^|; )${name}=([^;]+)`));\n return match ? decodeURIComponent(match[2]!) : null;\n }\n\n /**\n * Stitch the current captured click to an authenticated user. Call\n * this on login, signup, or anywhere the user becomes known.\n *\n * No-op if no cref was captured. Errors are reported through\n * `config.onError` by default — attribution is best-effort from the\n * browser — but you can await this and try/catch if you want to\n * surface failures explicitly.\n */\n async identify(userId: string): Promise<IdentifyResponse | null> {\n const cref = this.getCref();\n if (!cref) return null;\n\n try {\n return await requestJson<IdentifyResponse>(`${this.config.apiUrl}/attribution/identify`, {\n method: 'POST',\n body: { cref, userId, ts: Date.now() },\n keepalive: true,\n });\n } catch (err) {\n this.config.onError(err);\n return null;\n }\n }\n}\n","/**\n * Shared types + error class for both the browser and server entry\n * points.\n *\n * Keeping these in their own module means browser imports won't drag in\n * `fetch` polyfills or server-only code, and server imports won't touch\n * anything that references `window` or `document`.\n */\n\nexport class OpenPartnerError extends Error {\n constructor(\n message: string,\n public readonly status: number,\n public readonly code?: string,\n public readonly detail?: unknown,\n ) {\n super(message);\n this.name = 'OpenPartnerError';\n }\n}\n\nexport interface IdentifyResponse {\n ok: boolean;\n identityId: string | null;\n firstStitch: boolean;\n backfilledAttributions?: number;\n}\n\nexport interface EventAttribution {\n status: 'attributed' | 'no_identity' | 'no_click' | 'outside_window' | 'already_attributed';\n model?: 'last_click' | 'first_click' | 'linear' | 'position';\n touches?: Array<{\n clickId: string;\n partnerId: string;\n weight: number;\n attributionId: string;\n commissionId?: string;\n }>;\n}\n\nexport interface EventResponse {\n ok: boolean;\n eventId: string;\n attribution: EventAttribution;\n}\n\n/**\n * Small fetch helper used by both entries. Normalizes non-2xx responses\n * into OpenPartnerError so callers can try/catch with a stable shape\n * instead of poking at Response objects themselves.\n */\nexport type JsonRequestInit = Omit<RequestInit, 'body'> & { body?: unknown };\n\nexport async function requestJson<T>(\n input: string,\n init: JsonRequestInit = {},\n fetchImpl: typeof fetch = globalThis.fetch,\n): Promise<T> {\n const headers = new Headers(init.headers);\n if (init.body !== undefined && !headers.has('content-type')) {\n headers.set('content-type', 'application/json');\n }\n const res = await fetchImpl(input, {\n ...init,\n headers,\n body:\n init.body === undefined\n ? undefined\n : typeof init.body === 'string'\n ? init.body\n : JSON.stringify(init.body),\n });\n\n const text = await res.text();\n let body: unknown = undefined;\n if (text) {\n try {\n body = JSON.parse(text);\n } catch {\n body = text;\n }\n }\n\n if (!res.ok) {\n const errBody = (body ?? {}) as { error?: string; detail?: unknown };\n throw new OpenPartnerError(\n errBody.error ?? `${res.status} ${res.statusText}`,\n res.status,\n errBody.error,\n errBody.detail ?? body,\n );\n }\n\n return body as T;\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/browser.ts","../src/shared.ts"],"sourcesContent":["/**\n * Browser SDK — runs inside a merchant's web app. Captures the `cref`\n * attribution token from landing-page URLs and the first-party cookie\n * the click router sets, stashes it in localStorage so it survives SPA\n * navigations and later visits, and stitches to the authenticated user on\n * signup or login.\n *\n * Typical integration:\n *\n * import { OpenPartner } from '@openpartner/sdk';\n *\n * const op = OpenPartner.init({ apiUrl: 'https://openpartner.example.com' });\n *\n * // After login/signup, once you know who the user is:\n * await op.identify(user.id);\n *\n * With a captured referral it also hands the click to Stripe on its own:\n * Payment Links (buy.stripe.com) get `client_reference_id`, and Stripe\n * Pricing Table / Buy Button embeds get `client-reference-id`, including\n * ones added to the page later. A brand whose Stripe account is connected\n * to OpenPartner then needs no code for those checkouts. `stripe: false`\n * turns it off.\n *\n * How long a referral lasts. Safari deletes storage that a page's own\n * scripts wrote (localStorage, and cookies set through document.cookie)\n * after 7 days without a visit, so a visitor who clicks, leaves for a\n * fortnight and comes back to buy without signing up first arrives with\n * nothing. A cookie the brand's own server sets on the landing response\n * isn't capped that way: `referralCookieHeader` in `@openpartner/sdk/server`\n * writes one, and this SDK reads it back (`op_ref`), on every subdomain it\n * covers. Once identify() has run, the stitch lives server-side and no\n * browser storage matters.\n *\n * Consent. `consent: 'pending'` holds the referral in memory until\n * setConsent('granted'): nothing is read from or written to storage or\n * cookies, no Stripe link is tagged and nothing is sent until then.\n *\n * Landing report. When a click first arrives from the URL the SDK tells\n * OpenPartner (POST /attribution/landing), so Admin → Tracking health can\n * count the clicks that never reached a page running it. `reportLanding:\n * false` turns it off.\n *\n * No bundler? The same SDK ships as a script tag that starts itself\n * (auto.ts), served at /op.js by an OpenPartner portal.\n */\n\nimport { OpenPartnerError, requestJson, type IdentifyResponse } from './shared.js';\n\nexport { OpenPartnerError };\nexport type { IdentifyResponse };\n\nexport interface StripeLinkingConfig {\n /** Extra Payment Link hosts — a custom domain set up for Payment Links\n * in Stripe (e.g. \"pay.yourbrand.com\"). buy.stripe.com is always on. */\n linkHosts?: string[];\n}\n\n/** Whether this visitor agreed to referral tracking. */\nexport type ConsentState = 'granted' | 'pending' | 'denied';\n\nexport interface OpenPartnerConfig {\n /** Base URL of your OpenPartner API, e.g. \"https://openpartner.example.com\". */\n apiUrl: string;\n /** First-party cookie name set by the click router. Default `_cref`. */\n cookieName?: string;\n /** Query-string parameter that carries the cref on the landing URL. Default `cref`. */\n queryParam?: string;\n /** localStorage key the captured cref is stashed under. Default `openpartner:cref`. */\n storageKey?: string;\n /**\n * Hand the captured referral to Stripe Payment Links and Pricing Table /\n * Buy Button embeds on the page. Default on; `false` turns it off.\n */\n stripe?: boolean | StripeLinkingConfig;\n /**\n * Also keep the referral in a first-party cookie, so your server can read\n * it straight off the request when it creates a Stripe Checkout Session\n * (`client_reference_id: req.cookies.op_ref`). Off by default; the\n * script-tag build turns it on. `domain` shares it across subdomains\n * (\".yourbrand.com\") when visitors land on one and check out on another.\n *\n * The SDK reads this cookie back whether or not it writes it (`op_ref`\n * unless `name` says otherwise): that's how a page on another subdomain,\n * or a visit after Safari cleared localStorage, finds the referral, and\n * how one your server set (`referralCookieHeader`) reaches identify().\n */\n referralCookie?: false | { name?: string; domain?: string; days?: number };\n /**\n * Whether the visitor has agreed to referral tracking. Default\n * `'granted'`: the SDK stores the referral as soon as it loads. Behind a\n * consent banner, start at `'pending'` and call `setConsent()` from its\n * callback. Until then the referral from the URL is held in memory only,\n * so a visitor who agrees on the landing page is still credited.\n * `'denied'` uses nothing.\n */\n consent?: ConsentState;\n /**\n * Tell OpenPartner when a partner click arrives on your site, so Admin →\n * Tracking health can show clicks lost between the partner link and your\n * page. Sends the click id once per click, nothing about the visitor.\n * Default on; `false` turns it off.\n */\n reportLanding?: boolean;\n /**\n * Called when a call to OpenPartner fails: identify(), or a landing\n * report OpenPartner answered with `click_not_found` (which usually means\n * `apiUrl` names another program; other landing failures are dropped).\n * Default: log to console. Pass a noop to suppress entirely or\n * hook into your observability stack.\n */\n onError?: (err: unknown) => void;\n}\n\nconst DEFAULTS = {\n cookieName: '_cref',\n queryParam: 'cref',\n storageKey: 'openpartner:cref',\n stripe: true,\n referralCookie: false,\n consent: 'granted',\n reportLanding: true,\n onError: (err: unknown) => {\n console.warn('[openpartner] request failed', err);\n },\n};\n\n/** Stripe accepts a client_reference_id of letters, digits, \"-\" and \"_\",\n * up to 200, and silently drops anything else. A click id always fits; a\n * value that doesn't came from somewhere else and isn't sent. */\nconst STRIPE_REFERENCE_RE = /^[A-Za-z0-9_-]{1,200}$/;\nconst STRIPE_EMBED_SELECTOR = 'stripe-pricing-table, stripe-buy-button';\nconst REFERRAL_COOKIE_DEFAULT = 'op_ref';\n\nexport class OpenPartner {\n private config: Required<OpenPartnerConfig>;\n private observer: MutationObserver | null = null;\n private consent: ConsentState;\n /** The URL's referral while consent is pending, held in memory only. */\n private held: string | null = null;\n /** This page's referral, kept here too: where localStorage throws it is\n * the only copy until the cookie is written. */\n private current: string | null = null;\n /** The Stripe links and embeds this SDK tagged, and with what, so a\n * withdrawn consent can take the tags back off. */\n private tagged: Array<{ el: Element; value: string }> = [];\n\n private constructor(config: OpenPartnerConfig) {\n this.config = { ...DEFAULTS, ...config } as Required<OpenPartnerConfig>;\n this.consent = this.config.consent;\n if (this.consent === 'granted') this.start();\n else if (this.consent === 'pending') this.held = this.queryCref();\n // A returning visitor who has since said no: drop what an earlier\n // visit stored.\n else this.clear();\n }\n\n static init(config: OpenPartnerConfig): OpenPartner {\n return new OpenPartner(config);\n }\n\n /** The cref we're holding onto, if any. Useful for debugging. Null until\n * consent is granted. */\n getCref(): string | null {\n if (this.consent !== 'granted') return null;\n return this.storageGet() ?? this.current ?? this.readCookie(this.referralCookieName());\n }\n\n /**\n * Captured referral token for this visitor. Pass to Stripe Checkout\n * as `client_reference_id` to enable webhook-based attribution\n * without an explicit identify() call:\n *\n * stripe.checkout.sessions.create({\n * client_reference_id: op.getReferral() ?? undefined,\n * // ...\n * });\n *\n * Returns null if the visitor didn't arrive via a partner link, or\n * hasn't agreed to tracking yet.\n */\n getReferral(): string | null {\n return this.getCref();\n }\n\n /** Explicitly discard the captured cref, the referral cookie, and the\n * references this SDK added to Stripe links and embeds. */\n clear(): void {\n this.held = null;\n this.current = null;\n this.untagStripe();\n this.storageRemove();\n if (typeof document === 'undefined') return;\n const name = this.referralCookieName();\n if (!this.readCookie(name)) return;\n const cfg = this.config.referralCookie;\n document.cookie = `${name}=; Max-Age=0; Path=/`;\n if (cfg && cfg.domain) document.cookie = `${name}=; Max-Age=0; Path=/; Domain=${cfg.domain}`;\n }\n\n /**\n * Record the visitor's answer to your consent banner. `'granted'` stores\n * the referral (the one held since the page loaded, or one already\n * stored), starts tagging Stripe links and sends the landing report.\n * `'denied'` stops all of it, takes the references it added off Stripe\n * links and embeds, and deletes what was stored. Calling it again is\n * safe.\n */\n setConsent(state: 'granted' | 'denied'): void {\n if (state === 'granted') {\n if (this.consent === 'granted') return;\n this.consent = state;\n this.start();\n return;\n }\n this.consent = state;\n this.stopLinkingStripe();\n this.clear();\n }\n\n /**\n * Add the referral to every Stripe Payment Link and Pricing Table / Buy\n * Button embed under `root` that doesn't carry a reference already.\n * Runs on its own at init and for anything added later; call it yourself\n * only if you build Stripe elements inside a shadow root. Returns how\n * many it changed.\n */\n linkStripe(root: ParentNode = document): number {\n const referral = this.getReferral();\n if (!referral || !STRIPE_REFERENCE_RE.test(referral) || typeof document === 'undefined') return 0;\n const hosts = new Set(['buy.stripe.com', ...this.stripeLinkHosts()]);\n let changed = 0;\n\n const anchors: HTMLAnchorElement[] = [];\n if (root instanceof HTMLAnchorElement) anchors.push(root);\n anchors.push(...Array.from(root.querySelectorAll<HTMLAnchorElement>('a[href]')));\n for (const a of anchors) {\n let url: URL;\n try {\n url = new URL(a.href);\n } catch {\n continue;\n }\n // One the brand set itself (or an earlier pass) stays as it is.\n if (!hosts.has(url.hostname.toLowerCase()) || url.searchParams.has('client_reference_id')) continue;\n url.searchParams.set('client_reference_id', referral);\n a.href = url.toString();\n this.remember(a, referral);\n changed += 1;\n }\n\n const embeds: Element[] = [];\n if (root instanceof Element && root.matches(STRIPE_EMBED_SELECTOR)) embeds.push(root);\n embeds.push(...Array.from(root.querySelectorAll(STRIPE_EMBED_SELECTOR)));\n for (const el of embeds) {\n if (el.hasAttribute('client-reference-id')) continue;\n el.setAttribute('client-reference-id', referral);\n this.remember(el, referral);\n changed += 1;\n }\n return changed;\n }\n\n private remember(el: Element, value: string): void {\n // Elements an SPA has thrown away go, so the list can't grow forever.\n this.tagged = this.tagged.filter((t) => t.el.isConnected);\n this.tagged.push({ el, value });\n }\n\n /** Take back the references this SDK added. One the brand set, or one\n * changed since, stays. */\n private untagStripe(): void {\n for (const { el, value } of this.tagged) {\n if (el instanceof HTMLAnchorElement) {\n let url: URL;\n try {\n url = new URL(el.href);\n } catch {\n continue;\n }\n if (url.searchParams.get('client_reference_id') !== value) continue;\n url.searchParams.delete('client_reference_id');\n el.href = url.toString();\n } else if (el.getAttribute('client-reference-id') === value) {\n el.removeAttribute('client-reference-id');\n }\n }\n this.tagged = [];\n }\n\n /** Stop watching the page for new Stripe links and embeds. */\n stopLinkingStripe(): void {\n this.observer?.disconnect();\n this.observer = null;\n }\n\n private stripeLinkHosts(): string[] {\n const cfg = this.config.stripe;\n return typeof cfg === 'object' && cfg.linkHosts ? cfg.linkHosts.map((h) => h.trim().toLowerCase()).filter(Boolean) : [];\n }\n\n /** Everything the SDK does once it may: store, mirror, link, report. */\n private start(): void {\n const landed = this.captureCref(this.held);\n this.held = null;\n this.writeReferralCookie();\n if (this.config.stripe !== false) this.watchStripe();\n if (landed && this.config.reportLanding !== false) void this.reportLanding(landed);\n }\n\n /**\n * Link what's on the page now, then anything added or re-pointed later\n * (SPAs, Stripe embeds mounted on demand). Only with a referral: a visitor\n * who didn't come from a partner link costs nothing.\n *\n * Pricing Table and Buy Button embeds read client-reference-id when\n * Stripe's script renders them, so the script tag belongs in <head>,\n * ahead of the embed: the observer then sets the attribute as the\n * element is parsed, before Stripe upgrades it.\n */\n private watchStripe(): void {\n if (typeof document === 'undefined' || this.observer || !this.getReferral()) return;\n this.linkStripe(document);\n if (typeof MutationObserver === 'undefined') return;\n this.observer = new MutationObserver((records) => {\n for (const record of records) {\n if (record.type === 'attributes') {\n // An href re-pointed at a Payment Link.\n if (record.target instanceof HTMLAnchorElement) this.linkStripe(record.target);\n continue;\n }\n for (const node of Array.from(record.addedNodes)) {\n if (node instanceof Element) this.linkStripe(node);\n }\n }\n });\n // Our own edits only ever add a missing reference, so the second pass\n // they trigger finds nothing to change.\n this.observer.observe(document.documentElement, {\n childList: true,\n subtree: true,\n attributes: true,\n attributeFilter: ['href'],\n });\n }\n\n /** The cref on the landing URL, if any. */\n private queryCref(): string | null {\n if (typeof window === 'undefined') return null;\n return new URL(window.location.href).searchParams.get(this.config.queryParam);\n }\n\n /**\n * Capture the cref from the URL (or the one held while consent was\n * pending), else the router's cookie, else the referral cookie, and\n * stash it in localStorage so it survives SPA navigations and later\n * page loads. Returns the cref when it's a click arriving now, one this\n * browser didn't have yet, for the landing report.\n */\n private captureCref(held: string | null): string | null {\n if (typeof window === 'undefined') return null;\n\n const incoming = held ?? this.queryCref();\n if (incoming) {\n const isNew = this.storageGet() !== incoming;\n this.current = incoming;\n this.storageSet(incoming);\n return isNew ? incoming : null;\n }\n\n if (this.storageGet()) return null;\n // A page that isn't the landing page: the router's own cookie (a\n // self-host whose router shares the brand's domain), then the referral\n // cookie a sibling subdomain or the brand's server wrote.\n const restored = this.readCookie(this.config.cookieName) ?? this.readCookie(this.referralCookieName());\n if (restored) {\n this.current = restored;\n this.storageSet(restored);\n }\n return null;\n }\n\n private referralCookieName(): string {\n const cfg = this.config.referralCookie;\n return (cfg && cfg.name) || REFERRAL_COOKIE_DEFAULT;\n }\n\n /**\n * Mirror the referral into the brand's own first-party cookie (when\n * configured). Left alone when it already holds this referral: in Safari\n * rewriting it from script would trade a server-set cookie's full\n * lifetime for the 7-day cap. A new click overwrites it.\n */\n private writeReferralCookie(): void {\n const cfg = this.config.referralCookie;\n if (!cfg || typeof document === 'undefined') return;\n const referral = this.getReferral();\n if (!referral || !STRIPE_REFERENCE_RE.test(referral)) return;\n const name = this.referralCookieName();\n if (this.readCookie(name) === referral) return;\n const maxAge = Math.round((cfg.days ?? 90) * 86400);\n const secure = typeof location !== 'undefined' && location.protocol === 'https:' ? '; Secure' : '';\n const domain = cfg.domain ? `; Domain=${cfg.domain}` : '';\n document.cookie = `${name}=${encodeURIComponent(referral)}; Max-Age=${maxAge}; Path=/; SameSite=Lax${domain}${secure}`;\n }\n\n private readCookie(name: string): string | null {\n if (typeof document === 'undefined') return null;\n const match = document.cookie.match(new RegExp(`(^|; )${name}=([^;]+)`));\n if (!match) return null;\n try {\n return decodeURIComponent(match[2]!);\n } catch {\n return null;\n }\n }\n\n // localStorage throws in some sandboxed iframes and with site data\n // blocked; the referral then lives in the cookie alone.\n private storageGet(): string | null {\n try {\n return typeof localStorage === 'undefined' ? null : localStorage.getItem(this.config.storageKey);\n } catch {\n return null;\n }\n }\n\n private storageSet(value: string): void {\n try {\n if (typeof localStorage !== 'undefined') localStorage.setItem(this.config.storageKey, value);\n } catch {\n // Storage unavailable; see storageGet.\n }\n }\n\n private storageRemove(): void {\n try {\n if (typeof localStorage !== 'undefined') localStorage.removeItem(this.config.storageKey);\n } catch {\n // Storage unavailable; see storageGet.\n }\n }\n\n /** Tell OpenPartner the click arrived. Best-effort, once per click. */\n private async reportLanding(cref: string): Promise<void> {\n if (!STRIPE_REFERENCE_RE.test(cref) || typeof fetch === 'undefined') return;\n try {\n await requestJson(`${this.config.apiUrl}/attribution/landing`, {\n method: 'POST',\n body: { cref },\n keepalive: true,\n });\n } catch (err) {\n // Only a click this program doesn't have is worth reporting: it\n // usually means apiUrl names another program. Anything else (offline,\n // rate limited, an API from before the landing report) is dropped:\n // the report is a diagnostic, never part of attribution.\n if (err instanceof OpenPartnerError && err.code === 'click_not_found') this.config.onError(err);\n }\n }\n\n /**\n * Stitch the current captured click to an authenticated user. Call\n * this on login, signup, or anywhere the user becomes known.\n *\n * No-op if no cref was captured, or consent isn't granted. Errors are\n * reported through `config.onError` by default — attribution is\n * best-effort from the browser — but you can await this and try/catch\n * if you want to surface failures explicitly.\n */\n async identify(userId: string): Promise<IdentifyResponse | null> {\n const cref = this.getCref();\n if (!cref) return null;\n\n try {\n return await requestJson<IdentifyResponse>(`${this.config.apiUrl}/attribution/identify`, {\n method: 'POST',\n body: { cref, userId, ts: Date.now() },\n keepalive: true,\n });\n } catch (err) {\n this.config.onError(err);\n return null;\n }\n }\n}\n","/**\n * Shared types + error class for both the browser and server entry\n * points.\n *\n * Keeping these in their own module means browser imports won't drag in\n * `fetch` polyfills or server-only code, and server imports won't touch\n * anything that references `window` or `document`.\n */\n\nexport class OpenPartnerError extends Error {\n constructor(\n message: string,\n public readonly status: number,\n public readonly code?: string,\n public readonly detail?: unknown,\n ) {\n super(message);\n this.name = 'OpenPartnerError';\n }\n}\n\nexport interface IdentifyResponse {\n ok: boolean;\n identityId: string | null;\n firstStitch: boolean;\n backfilledAttributions?: number;\n}\n\nexport interface EventAttribution {\n /**\n * 'not_attributed' only appears on a replay (`replayed: true`) of an event\n * that didn't attribute the first time; which miss it was isn't stored.\n * 'corrective': refund / chargeback / dispute_created / invoice_payment_failed —\n * recorded, never attributed.\n */\n status:\n | 'attributed'\n | 'no_identity'\n | 'no_click'\n | 'outside_window'\n | 'already_attributed'\n | 'not_attributed'\n | 'corrective';\n model?: 'last_click' | 'first_click' | 'linear' | 'position';\n touches?: Array<{\n clickId: string;\n partnerId: string;\n weight: number;\n attributionId: string;\n commissionId?: string;\n }>;\n}\n\nexport interface EventResponse {\n ok: boolean;\n eventId: string;\n attribution: EventAttribution;\n /** True when an earlier call with the same externalEventId already\n * recorded this event; eventId and attribution are that call's. */\n replayed?: boolean;\n}\n\nexport interface ConversionResponse extends EventResponse {\n /** The user the conversion was recorded under: the userId you passed, or\n * the click's own `click:<clickId>` user. */\n userId: string;\n}\n\n/**\n * Small fetch helper used by both entries. Normalizes non-2xx responses\n * into OpenPartnerError so callers can try/catch with a stable shape\n * instead of poking at Response objects themselves.\n */\nexport type JsonRequestInit = Omit<RequestInit, 'body'> & { body?: unknown };\n\nexport async function requestJson<T>(\n input: string,\n init: JsonRequestInit = {},\n fetchImpl: typeof fetch = globalThis.fetch,\n): Promise<T> {\n const headers = new Headers(init.headers);\n if (init.body !== undefined && !headers.has('content-type')) {\n headers.set('content-type', 'application/json');\n }\n const res = await fetchImpl(input, {\n ...init,\n headers,\n body:\n init.body === undefined\n ? undefined\n : typeof init.body === 'string'\n ? init.body\n : JSON.stringify(init.body),\n });\n\n const text = await res.text();\n let body: unknown = undefined;\n if (text) {\n try {\n body = JSON.parse(text);\n } catch {\n body = text;\n }\n }\n\n if (!res.ok) {\n const errBody = (body ?? {}) as { error?: string; detail?: unknown };\n throw new OpenPartnerError(\n errBody.error ?? `${res.status} ${res.statusText}`,\n res.status,\n errBody.error,\n errBody.detail ?? body,\n );\n }\n\n return body as T;\n}\n"],"mappings":"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","names":[]}
|
package/dist/browser.d.cts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { I as IdentifyResponse } from './shared-
|
|
2
|
-
export { O as OpenPartnerError } from './shared-
|
|
1
|
+
import { I as IdentifyResponse } from './shared-dh1vlldf.cjs';
|
|
2
|
+
export { O as OpenPartnerError } from './shared-dh1vlldf.cjs';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Browser SDK — runs inside a merchant's web app. Captures the `cref`
|
|
6
6
|
* attribution token from landing-page URLs and the first-party cookie
|
|
7
|
-
* the click router sets, stashes it in localStorage so it survives
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* the click router sets, stashes it in localStorage so it survives SPA
|
|
8
|
+
* navigations and later visits, and stitches to the authenticated user on
|
|
9
|
+
* signup or login.
|
|
10
10
|
*
|
|
11
11
|
* Typical integration:
|
|
12
12
|
*
|
|
@@ -16,8 +16,44 @@ export { O as OpenPartnerError } from './shared-CLfBjPeb.cjs';
|
|
|
16
16
|
*
|
|
17
17
|
* // After login/signup, once you know who the user is:
|
|
18
18
|
* await op.identify(user.id);
|
|
19
|
+
*
|
|
20
|
+
* With a captured referral it also hands the click to Stripe on its own:
|
|
21
|
+
* Payment Links (buy.stripe.com) get `client_reference_id`, and Stripe
|
|
22
|
+
* Pricing Table / Buy Button embeds get `client-reference-id`, including
|
|
23
|
+
* ones added to the page later. A brand whose Stripe account is connected
|
|
24
|
+
* to OpenPartner then needs no code for those checkouts. `stripe: false`
|
|
25
|
+
* turns it off.
|
|
26
|
+
*
|
|
27
|
+
* How long a referral lasts. Safari deletes storage that a page's own
|
|
28
|
+
* scripts wrote (localStorage, and cookies set through document.cookie)
|
|
29
|
+
* after 7 days without a visit, so a visitor who clicks, leaves for a
|
|
30
|
+
* fortnight and comes back to buy without signing up first arrives with
|
|
31
|
+
* nothing. A cookie the brand's own server sets on the landing response
|
|
32
|
+
* isn't capped that way: `referralCookieHeader` in `@openpartner/sdk/server`
|
|
33
|
+
* writes one, and this SDK reads it back (`op_ref`), on every subdomain it
|
|
34
|
+
* covers. Once identify() has run, the stitch lives server-side and no
|
|
35
|
+
* browser storage matters.
|
|
36
|
+
*
|
|
37
|
+
* Consent. `consent: 'pending'` holds the referral in memory until
|
|
38
|
+
* setConsent('granted'): nothing is read from or written to storage or
|
|
39
|
+
* cookies, no Stripe link is tagged and nothing is sent until then.
|
|
40
|
+
*
|
|
41
|
+
* Landing report. When a click first arrives from the URL the SDK tells
|
|
42
|
+
* OpenPartner (POST /attribution/landing), so Admin → Tracking health can
|
|
43
|
+
* count the clicks that never reached a page running it. `reportLanding:
|
|
44
|
+
* false` turns it off.
|
|
45
|
+
*
|
|
46
|
+
* No bundler? The same SDK ships as a script tag that starts itself
|
|
47
|
+
* (auto.ts), served at /op.js by an OpenPartner portal.
|
|
19
48
|
*/
|
|
20
49
|
|
|
50
|
+
interface StripeLinkingConfig {
|
|
51
|
+
/** Extra Payment Link hosts — a custom domain set up for Payment Links
|
|
52
|
+
* in Stripe (e.g. "pay.yourbrand.com"). buy.stripe.com is always on. */
|
|
53
|
+
linkHosts?: string[];
|
|
54
|
+
}
|
|
55
|
+
/** Whether this visitor agreed to referral tracking. */
|
|
56
|
+
type ConsentState = 'granted' | 'pending' | 'denied';
|
|
21
57
|
interface OpenPartnerConfig {
|
|
22
58
|
/** Base URL of your OpenPartner API, e.g. "https://openpartner.example.com". */
|
|
23
59
|
apiUrl: string;
|
|
@@ -28,16 +64,68 @@ interface OpenPartnerConfig {
|
|
|
28
64
|
/** localStorage key the captured cref is stashed under. Default `openpartner:cref`. */
|
|
29
65
|
storageKey?: string;
|
|
30
66
|
/**
|
|
31
|
-
*
|
|
32
|
-
*
|
|
67
|
+
* Hand the captured referral to Stripe Payment Links and Pricing Table /
|
|
68
|
+
* Buy Button embeds on the page. Default on; `false` turns it off.
|
|
69
|
+
*/
|
|
70
|
+
stripe?: boolean | StripeLinkingConfig;
|
|
71
|
+
/**
|
|
72
|
+
* Also keep the referral in a first-party cookie, so your server can read
|
|
73
|
+
* it straight off the request when it creates a Stripe Checkout Session
|
|
74
|
+
* (`client_reference_id: req.cookies.op_ref`). Off by default; the
|
|
75
|
+
* script-tag build turns it on. `domain` shares it across subdomains
|
|
76
|
+
* (".yourbrand.com") when visitors land on one and check out on another.
|
|
77
|
+
*
|
|
78
|
+
* The SDK reads this cookie back whether or not it writes it (`op_ref`
|
|
79
|
+
* unless `name` says otherwise): that's how a page on another subdomain,
|
|
80
|
+
* or a visit after Safari cleared localStorage, finds the referral, and
|
|
81
|
+
* how one your server set (`referralCookieHeader`) reaches identify().
|
|
82
|
+
*/
|
|
83
|
+
referralCookie?: false | {
|
|
84
|
+
name?: string;
|
|
85
|
+
domain?: string;
|
|
86
|
+
days?: number;
|
|
87
|
+
};
|
|
88
|
+
/**
|
|
89
|
+
* Whether the visitor has agreed to referral tracking. Default
|
|
90
|
+
* `'granted'`: the SDK stores the referral as soon as it loads. Behind a
|
|
91
|
+
* consent banner, start at `'pending'` and call `setConsent()` from its
|
|
92
|
+
* callback. Until then the referral from the URL is held in memory only,
|
|
93
|
+
* so a visitor who agrees on the landing page is still credited.
|
|
94
|
+
* `'denied'` uses nothing.
|
|
95
|
+
*/
|
|
96
|
+
consent?: ConsentState;
|
|
97
|
+
/**
|
|
98
|
+
* Tell OpenPartner when a partner click arrives on your site, so Admin →
|
|
99
|
+
* Tracking health can show clicks lost between the partner link and your
|
|
100
|
+
* page. Sends the click id once per click, nothing about the visitor.
|
|
101
|
+
* Default on; `false` turns it off.
|
|
102
|
+
*/
|
|
103
|
+
reportLanding?: boolean;
|
|
104
|
+
/**
|
|
105
|
+
* Called when a call to OpenPartner fails: identify(), or a landing
|
|
106
|
+
* report OpenPartner answered with `click_not_found` (which usually means
|
|
107
|
+
* `apiUrl` names another program; other landing failures are dropped).
|
|
108
|
+
* Default: log to console. Pass a noop to suppress entirely or
|
|
109
|
+
* hook into your observability stack.
|
|
33
110
|
*/
|
|
34
111
|
onError?: (err: unknown) => void;
|
|
35
112
|
}
|
|
36
113
|
declare class OpenPartner {
|
|
37
114
|
private config;
|
|
115
|
+
private observer;
|
|
116
|
+
private consent;
|
|
117
|
+
/** The URL's referral while consent is pending, held in memory only. */
|
|
118
|
+
private held;
|
|
119
|
+
/** This page's referral, kept here too: where localStorage throws it is
|
|
120
|
+
* the only copy until the cookie is written. */
|
|
121
|
+
private current;
|
|
122
|
+
/** The Stripe links and embeds this SDK tagged, and with what, so a
|
|
123
|
+
* withdrawn consent can take the tags back off. */
|
|
124
|
+
private tagged;
|
|
38
125
|
private constructor();
|
|
39
126
|
static init(config: OpenPartnerConfig): OpenPartner;
|
|
40
|
-
/** The cref we're holding onto, if any. Useful for debugging.
|
|
127
|
+
/** The cref we're holding onto, if any. Useful for debugging. Null until
|
|
128
|
+
* consent is granted. */
|
|
41
129
|
getCref(): string | null;
|
|
42
130
|
/**
|
|
43
131
|
* Captured referral token for this visitor. Pass to Stripe Checkout
|
|
@@ -49,28 +137,85 @@ declare class OpenPartner {
|
|
|
49
137
|
* // ...
|
|
50
138
|
* });
|
|
51
139
|
*
|
|
52
|
-
* Returns null if the visitor didn't arrive via a partner link
|
|
140
|
+
* Returns null if the visitor didn't arrive via a partner link, or
|
|
141
|
+
* hasn't agreed to tracking yet.
|
|
53
142
|
*/
|
|
54
143
|
getReferral(): string | null;
|
|
55
|
-
/** Explicitly discard the captured cref
|
|
144
|
+
/** Explicitly discard the captured cref, the referral cookie, and the
|
|
145
|
+
* references this SDK added to Stripe links and embeds. */
|
|
56
146
|
clear(): void;
|
|
57
147
|
/**
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
148
|
+
* Record the visitor's answer to your consent banner. `'granted'` stores
|
|
149
|
+
* the referral (the one held since the page loaded, or one already
|
|
150
|
+
* stored), starts tagging Stripe links and sends the landing report.
|
|
151
|
+
* `'denied'` stops all of it, takes the references it added off Stripe
|
|
152
|
+
* links and embeds, and deletes what was stored. Calling it again is
|
|
153
|
+
* safe.
|
|
154
|
+
*/
|
|
155
|
+
setConsent(state: 'granted' | 'denied'): void;
|
|
156
|
+
/**
|
|
157
|
+
* Add the referral to every Stripe Payment Link and Pricing Table / Buy
|
|
158
|
+
* Button embed under `root` that doesn't carry a reference already.
|
|
159
|
+
* Runs on its own at init and for anything added later; call it yourself
|
|
160
|
+
* only if you build Stripe elements inside a shadow root. Returns how
|
|
161
|
+
* many it changed.
|
|
162
|
+
*/
|
|
163
|
+
linkStripe(root?: ParentNode): number;
|
|
164
|
+
private remember;
|
|
165
|
+
/** Take back the references this SDK added. One the brand set, or one
|
|
166
|
+
* changed since, stays. */
|
|
167
|
+
private untagStripe;
|
|
168
|
+
/** Stop watching the page for new Stripe links and embeds. */
|
|
169
|
+
stopLinkingStripe(): void;
|
|
170
|
+
private stripeLinkHosts;
|
|
171
|
+
/** Everything the SDK does once it may: store, mirror, link, report. */
|
|
172
|
+
private start;
|
|
173
|
+
/**
|
|
174
|
+
* Link what's on the page now, then anything added or re-pointed later
|
|
175
|
+
* (SPAs, Stripe embeds mounted on demand). Only with a referral: a visitor
|
|
176
|
+
* who didn't come from a partner link costs nothing.
|
|
177
|
+
*
|
|
178
|
+
* Pricing Table and Buy Button embeds read client-reference-id when
|
|
179
|
+
* Stripe's script renders them, so the script tag belongs in <head>,
|
|
180
|
+
* ahead of the embed: the observer then sets the attribute as the
|
|
181
|
+
* element is parsed, before Stripe upgrades it.
|
|
182
|
+
*/
|
|
183
|
+
private watchStripe;
|
|
184
|
+
/** The cref on the landing URL, if any. */
|
|
185
|
+
private queryCref;
|
|
186
|
+
/**
|
|
187
|
+
* Capture the cref from the URL (or the one held while consent was
|
|
188
|
+
* pending), else the router's cookie, else the referral cookie, and
|
|
189
|
+
* stash it in localStorage so it survives SPA navigations and later
|
|
190
|
+
* page loads. Returns the cref when it's a click arriving now, one this
|
|
191
|
+
* browser didn't have yet, for the landing report.
|
|
61
192
|
*/
|
|
62
193
|
private captureCref;
|
|
194
|
+
private referralCookieName;
|
|
195
|
+
/**
|
|
196
|
+
* Mirror the referral into the brand's own first-party cookie (when
|
|
197
|
+
* configured). Left alone when it already holds this referral: in Safari
|
|
198
|
+
* rewriting it from script would trade a server-set cookie's full
|
|
199
|
+
* lifetime for the 7-day cap. A new click overwrites it.
|
|
200
|
+
*/
|
|
201
|
+
private writeReferralCookie;
|
|
63
202
|
private readCookie;
|
|
203
|
+
private storageGet;
|
|
204
|
+
private storageSet;
|
|
205
|
+
private storageRemove;
|
|
206
|
+
/** Tell OpenPartner the click arrived. Best-effort, once per click. */
|
|
207
|
+
private reportLanding;
|
|
64
208
|
/**
|
|
65
209
|
* Stitch the current captured click to an authenticated user. Call
|
|
66
210
|
* this on login, signup, or anywhere the user becomes known.
|
|
67
211
|
*
|
|
68
|
-
* No-op if no cref was captured. Errors are
|
|
69
|
-
* `config.onError` by default — attribution is
|
|
70
|
-
* browser — but you can await this and try/catch
|
|
71
|
-
* surface failures explicitly.
|
|
212
|
+
* No-op if no cref was captured, or consent isn't granted. Errors are
|
|
213
|
+
* reported through `config.onError` by default — attribution is
|
|
214
|
+
* best-effort from the browser — but you can await this and try/catch
|
|
215
|
+
* if you want to surface failures explicitly.
|
|
72
216
|
*/
|
|
73
217
|
identify(userId: string): Promise<IdentifyResponse | null>;
|
|
74
218
|
}
|
|
75
219
|
|
|
76
|
-
export { IdentifyResponse, OpenPartner
|
|
220
|
+
export { IdentifyResponse, OpenPartner };
|
|
221
|
+
export type { ConsentState, OpenPartnerConfig, StripeLinkingConfig };
|
package/dist/browser.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { I as IdentifyResponse } from './shared-
|
|
2
|
-
export { O as OpenPartnerError } from './shared-
|
|
1
|
+
import { I as IdentifyResponse } from './shared-dh1vlldf.js';
|
|
2
|
+
export { O as OpenPartnerError } from './shared-dh1vlldf.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Browser SDK — runs inside a merchant's web app. Captures the `cref`
|
|
6
6
|
* attribution token from landing-page URLs and the first-party cookie
|
|
7
|
-
* the click router sets, stashes it in localStorage so it survives
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* the click router sets, stashes it in localStorage so it survives SPA
|
|
8
|
+
* navigations and later visits, and stitches to the authenticated user on
|
|
9
|
+
* signup or login.
|
|
10
10
|
*
|
|
11
11
|
* Typical integration:
|
|
12
12
|
*
|
|
@@ -16,8 +16,44 @@ export { O as OpenPartnerError } from './shared-CLfBjPeb.js';
|
|
|
16
16
|
*
|
|
17
17
|
* // After login/signup, once you know who the user is:
|
|
18
18
|
* await op.identify(user.id);
|
|
19
|
+
*
|
|
20
|
+
* With a captured referral it also hands the click to Stripe on its own:
|
|
21
|
+
* Payment Links (buy.stripe.com) get `client_reference_id`, and Stripe
|
|
22
|
+
* Pricing Table / Buy Button embeds get `client-reference-id`, including
|
|
23
|
+
* ones added to the page later. A brand whose Stripe account is connected
|
|
24
|
+
* to OpenPartner then needs no code for those checkouts. `stripe: false`
|
|
25
|
+
* turns it off.
|
|
26
|
+
*
|
|
27
|
+
* How long a referral lasts. Safari deletes storage that a page's own
|
|
28
|
+
* scripts wrote (localStorage, and cookies set through document.cookie)
|
|
29
|
+
* after 7 days without a visit, so a visitor who clicks, leaves for a
|
|
30
|
+
* fortnight and comes back to buy without signing up first arrives with
|
|
31
|
+
* nothing. A cookie the brand's own server sets on the landing response
|
|
32
|
+
* isn't capped that way: `referralCookieHeader` in `@openpartner/sdk/server`
|
|
33
|
+
* writes one, and this SDK reads it back (`op_ref`), on every subdomain it
|
|
34
|
+
* covers. Once identify() has run, the stitch lives server-side and no
|
|
35
|
+
* browser storage matters.
|
|
36
|
+
*
|
|
37
|
+
* Consent. `consent: 'pending'` holds the referral in memory until
|
|
38
|
+
* setConsent('granted'): nothing is read from or written to storage or
|
|
39
|
+
* cookies, no Stripe link is tagged and nothing is sent until then.
|
|
40
|
+
*
|
|
41
|
+
* Landing report. When a click first arrives from the URL the SDK tells
|
|
42
|
+
* OpenPartner (POST /attribution/landing), so Admin → Tracking health can
|
|
43
|
+
* count the clicks that never reached a page running it. `reportLanding:
|
|
44
|
+
* false` turns it off.
|
|
45
|
+
*
|
|
46
|
+
* No bundler? The same SDK ships as a script tag that starts itself
|
|
47
|
+
* (auto.ts), served at /op.js by an OpenPartner portal.
|
|
19
48
|
*/
|
|
20
49
|
|
|
50
|
+
interface StripeLinkingConfig {
|
|
51
|
+
/** Extra Payment Link hosts — a custom domain set up for Payment Links
|
|
52
|
+
* in Stripe (e.g. "pay.yourbrand.com"). buy.stripe.com is always on. */
|
|
53
|
+
linkHosts?: string[];
|
|
54
|
+
}
|
|
55
|
+
/** Whether this visitor agreed to referral tracking. */
|
|
56
|
+
type ConsentState = 'granted' | 'pending' | 'denied';
|
|
21
57
|
interface OpenPartnerConfig {
|
|
22
58
|
/** Base URL of your OpenPartner API, e.g. "https://openpartner.example.com". */
|
|
23
59
|
apiUrl: string;
|
|
@@ -28,16 +64,68 @@ interface OpenPartnerConfig {
|
|
|
28
64
|
/** localStorage key the captured cref is stashed under. Default `openpartner:cref`. */
|
|
29
65
|
storageKey?: string;
|
|
30
66
|
/**
|
|
31
|
-
*
|
|
32
|
-
*
|
|
67
|
+
* Hand the captured referral to Stripe Payment Links and Pricing Table /
|
|
68
|
+
* Buy Button embeds on the page. Default on; `false` turns it off.
|
|
69
|
+
*/
|
|
70
|
+
stripe?: boolean | StripeLinkingConfig;
|
|
71
|
+
/**
|
|
72
|
+
* Also keep the referral in a first-party cookie, so your server can read
|
|
73
|
+
* it straight off the request when it creates a Stripe Checkout Session
|
|
74
|
+
* (`client_reference_id: req.cookies.op_ref`). Off by default; the
|
|
75
|
+
* script-tag build turns it on. `domain` shares it across subdomains
|
|
76
|
+
* (".yourbrand.com") when visitors land on one and check out on another.
|
|
77
|
+
*
|
|
78
|
+
* The SDK reads this cookie back whether or not it writes it (`op_ref`
|
|
79
|
+
* unless `name` says otherwise): that's how a page on another subdomain,
|
|
80
|
+
* or a visit after Safari cleared localStorage, finds the referral, and
|
|
81
|
+
* how one your server set (`referralCookieHeader`) reaches identify().
|
|
82
|
+
*/
|
|
83
|
+
referralCookie?: false | {
|
|
84
|
+
name?: string;
|
|
85
|
+
domain?: string;
|
|
86
|
+
days?: number;
|
|
87
|
+
};
|
|
88
|
+
/**
|
|
89
|
+
* Whether the visitor has agreed to referral tracking. Default
|
|
90
|
+
* `'granted'`: the SDK stores the referral as soon as it loads. Behind a
|
|
91
|
+
* consent banner, start at `'pending'` and call `setConsent()` from its
|
|
92
|
+
* callback. Until then the referral from the URL is held in memory only,
|
|
93
|
+
* so a visitor who agrees on the landing page is still credited.
|
|
94
|
+
* `'denied'` uses nothing.
|
|
95
|
+
*/
|
|
96
|
+
consent?: ConsentState;
|
|
97
|
+
/**
|
|
98
|
+
* Tell OpenPartner when a partner click arrives on your site, so Admin →
|
|
99
|
+
* Tracking health can show clicks lost between the partner link and your
|
|
100
|
+
* page. Sends the click id once per click, nothing about the visitor.
|
|
101
|
+
* Default on; `false` turns it off.
|
|
102
|
+
*/
|
|
103
|
+
reportLanding?: boolean;
|
|
104
|
+
/**
|
|
105
|
+
* Called when a call to OpenPartner fails: identify(), or a landing
|
|
106
|
+
* report OpenPartner answered with `click_not_found` (which usually means
|
|
107
|
+
* `apiUrl` names another program; other landing failures are dropped).
|
|
108
|
+
* Default: log to console. Pass a noop to suppress entirely or
|
|
109
|
+
* hook into your observability stack.
|
|
33
110
|
*/
|
|
34
111
|
onError?: (err: unknown) => void;
|
|
35
112
|
}
|
|
36
113
|
declare class OpenPartner {
|
|
37
114
|
private config;
|
|
115
|
+
private observer;
|
|
116
|
+
private consent;
|
|
117
|
+
/** The URL's referral while consent is pending, held in memory only. */
|
|
118
|
+
private held;
|
|
119
|
+
/** This page's referral, kept here too: where localStorage throws it is
|
|
120
|
+
* the only copy until the cookie is written. */
|
|
121
|
+
private current;
|
|
122
|
+
/** The Stripe links and embeds this SDK tagged, and with what, so a
|
|
123
|
+
* withdrawn consent can take the tags back off. */
|
|
124
|
+
private tagged;
|
|
38
125
|
private constructor();
|
|
39
126
|
static init(config: OpenPartnerConfig): OpenPartner;
|
|
40
|
-
/** The cref we're holding onto, if any. Useful for debugging.
|
|
127
|
+
/** The cref we're holding onto, if any. Useful for debugging. Null until
|
|
128
|
+
* consent is granted. */
|
|
41
129
|
getCref(): string | null;
|
|
42
130
|
/**
|
|
43
131
|
* Captured referral token for this visitor. Pass to Stripe Checkout
|
|
@@ -49,28 +137,85 @@ declare class OpenPartner {
|
|
|
49
137
|
* // ...
|
|
50
138
|
* });
|
|
51
139
|
*
|
|
52
|
-
* Returns null if the visitor didn't arrive via a partner link
|
|
140
|
+
* Returns null if the visitor didn't arrive via a partner link, or
|
|
141
|
+
* hasn't agreed to tracking yet.
|
|
53
142
|
*/
|
|
54
143
|
getReferral(): string | null;
|
|
55
|
-
/** Explicitly discard the captured cref
|
|
144
|
+
/** Explicitly discard the captured cref, the referral cookie, and the
|
|
145
|
+
* references this SDK added to Stripe links and embeds. */
|
|
56
146
|
clear(): void;
|
|
57
147
|
/**
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
148
|
+
* Record the visitor's answer to your consent banner. `'granted'` stores
|
|
149
|
+
* the referral (the one held since the page loaded, or one already
|
|
150
|
+
* stored), starts tagging Stripe links and sends the landing report.
|
|
151
|
+
* `'denied'` stops all of it, takes the references it added off Stripe
|
|
152
|
+
* links and embeds, and deletes what was stored. Calling it again is
|
|
153
|
+
* safe.
|
|
154
|
+
*/
|
|
155
|
+
setConsent(state: 'granted' | 'denied'): void;
|
|
156
|
+
/**
|
|
157
|
+
* Add the referral to every Stripe Payment Link and Pricing Table / Buy
|
|
158
|
+
* Button embed under `root` that doesn't carry a reference already.
|
|
159
|
+
* Runs on its own at init and for anything added later; call it yourself
|
|
160
|
+
* only if you build Stripe elements inside a shadow root. Returns how
|
|
161
|
+
* many it changed.
|
|
162
|
+
*/
|
|
163
|
+
linkStripe(root?: ParentNode): number;
|
|
164
|
+
private remember;
|
|
165
|
+
/** Take back the references this SDK added. One the brand set, or one
|
|
166
|
+
* changed since, stays. */
|
|
167
|
+
private untagStripe;
|
|
168
|
+
/** Stop watching the page for new Stripe links and embeds. */
|
|
169
|
+
stopLinkingStripe(): void;
|
|
170
|
+
private stripeLinkHosts;
|
|
171
|
+
/** Everything the SDK does once it may: store, mirror, link, report. */
|
|
172
|
+
private start;
|
|
173
|
+
/**
|
|
174
|
+
* Link what's on the page now, then anything added or re-pointed later
|
|
175
|
+
* (SPAs, Stripe embeds mounted on demand). Only with a referral: a visitor
|
|
176
|
+
* who didn't come from a partner link costs nothing.
|
|
177
|
+
*
|
|
178
|
+
* Pricing Table and Buy Button embeds read client-reference-id when
|
|
179
|
+
* Stripe's script renders them, so the script tag belongs in <head>,
|
|
180
|
+
* ahead of the embed: the observer then sets the attribute as the
|
|
181
|
+
* element is parsed, before Stripe upgrades it.
|
|
182
|
+
*/
|
|
183
|
+
private watchStripe;
|
|
184
|
+
/** The cref on the landing URL, if any. */
|
|
185
|
+
private queryCref;
|
|
186
|
+
/**
|
|
187
|
+
* Capture the cref from the URL (or the one held while consent was
|
|
188
|
+
* pending), else the router's cookie, else the referral cookie, and
|
|
189
|
+
* stash it in localStorage so it survives SPA navigations and later
|
|
190
|
+
* page loads. Returns the cref when it's a click arriving now, one this
|
|
191
|
+
* browser didn't have yet, for the landing report.
|
|
61
192
|
*/
|
|
62
193
|
private captureCref;
|
|
194
|
+
private referralCookieName;
|
|
195
|
+
/**
|
|
196
|
+
* Mirror the referral into the brand's own first-party cookie (when
|
|
197
|
+
* configured). Left alone when it already holds this referral: in Safari
|
|
198
|
+
* rewriting it from script would trade a server-set cookie's full
|
|
199
|
+
* lifetime for the 7-day cap. A new click overwrites it.
|
|
200
|
+
*/
|
|
201
|
+
private writeReferralCookie;
|
|
63
202
|
private readCookie;
|
|
203
|
+
private storageGet;
|
|
204
|
+
private storageSet;
|
|
205
|
+
private storageRemove;
|
|
206
|
+
/** Tell OpenPartner the click arrived. Best-effort, once per click. */
|
|
207
|
+
private reportLanding;
|
|
64
208
|
/**
|
|
65
209
|
* Stitch the current captured click to an authenticated user. Call
|
|
66
210
|
* this on login, signup, or anywhere the user becomes known.
|
|
67
211
|
*
|
|
68
|
-
* No-op if no cref was captured. Errors are
|
|
69
|
-
* `config.onError` by default — attribution is
|
|
70
|
-
* browser — but you can await this and try/catch
|
|
71
|
-
* surface failures explicitly.
|
|
212
|
+
* No-op if no cref was captured, or consent isn't granted. Errors are
|
|
213
|
+
* reported through `config.onError` by default — attribution is
|
|
214
|
+
* best-effort from the browser — but you can await this and try/catch
|
|
215
|
+
* if you want to surface failures explicitly.
|
|
72
216
|
*/
|
|
73
217
|
identify(userId: string): Promise<IdentifyResponse | null>;
|
|
74
218
|
}
|
|
75
219
|
|
|
76
|
-
export { IdentifyResponse, OpenPartner
|
|
220
|
+
export { IdentifyResponse, OpenPartner };
|
|
221
|
+
export type { ConsentState, OpenPartnerConfig, StripeLinkingConfig };
|