@monetize.software/sdk 3.0.0-alpha.12 → 3.0.0-alpha.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/core.d.ts CHANGED
@@ -743,6 +743,17 @@ export declare class BillingClient {
743
743
  errorUrl?: string;
744
744
  shopUrl?: string;
745
745
  trialDays?: number;
746
+ /** Активный offer для этой цены — резолвится host'ом через
747
+ * `paywall.getOfferForPrice(priceId)?.offer.id` или
748
+ * `findApplicableOffer(client.getCachedOffers(), priceId)?.id`. Без
749
+ * явной передачи бэк сделает auto-resolve по email — но только для
750
+ * end_date-офферов. duration_minutes-офферы тикают в clientStorage и
751
+ * сервер их не видит: для них offerId ОБЯЗАН прийти от клиента, иначе
752
+ * скидка не применится на чекауте, хотя UI её показывал.
753
+ *
754
+ * Передавать offer-id всегда безопасно — бэк сам проверит applicable
755
+ * ли offer к этому юзеру (страна/email/режим) и игнорирует если нет. */
756
+ offerId?: string;
746
757
  /**
747
758
  * Stage 1 защиты от дубликатов покупок. Идемпотентный ключ запроса
748
759
  * (UUID). Повторный вызов с тем же ключом вернёт тот же checkout-URL
package/dist/core.js CHANGED
@@ -20,24 +20,24 @@ class E {
20
20
  n.set("Accept", "application/json"), n.set("X-SDK-Version", m), n.set("X-Paywall-Id", this.opts.paywallId), this.opts.capabilities?.length && n.set("X-SDK-Capabilities", this.opts.capabilities.join(","));
21
21
  const o = await this.opts.getAuthToken?.();
22
22
  o && n.set("Authorization", `Bearer ${o}`);
23
- const u = typeof FormData < "u" && e.body instanceof FormData;
24
- e.body && !n.has("Content-Type") && !u && n.set("Content-Type", "application/json");
25
- let d;
23
+ const d = typeof FormData < "u" && e.body instanceof FormData;
24
+ e.body && !n.has("Content-Type") && !d && n.set("Content-Type", "application/json");
25
+ let f;
26
26
  try {
27
- d = await a(s, {
27
+ f = await a(s, {
28
28
  ...e,
29
29
  headers: n,
30
30
  credentials: "omit"
31
31
  });
32
- } catch (c) {
33
- throw (c && typeof c == "object" && "name" in c ? c.name : void 0) === "AbortError" ? new r("aborted", "Request aborted", { cause: c }) : new r("network_error", "Network request failed", { cause: c });
32
+ } catch (h) {
33
+ throw (h && typeof h == "object" && "name" in h ? h.name : void 0) === "AbortError" ? new r("aborted", "Request aborted", { cause: h }) : new r("network_error", "Network request failed", { cause: h });
34
34
  }
35
- const y = (d.headers.get("content-type") ?? "").includes("application/json") ? await d.json().catch(() => null) : null;
36
- if (!d.ok) {
37
- const c = y && typeof y == "object" && "code" in y && String(y.code) || `http_${d.status}`, g = y && typeof y == "object" && "message" in y && String(y.message) || d.statusText || "Request failed";
38
- throw new r(c, g, { status: d.status, cause: y });
35
+ const l = (f.headers.get("content-type") ?? "").includes("application/json") ? await f.json().catch(() => null) : null;
36
+ if (!f.ok) {
37
+ const h = l && typeof l == "object" && "code" in l && String(l.code) || `http_${f.status}`, g = l && typeof l == "object" && "message" in l && String(l.message) || f.statusText || "Request failed";
38
+ throw new r(h, g, { status: f.status, cause: l });
39
39
  }
40
- return y;
40
+ return l;
41
41
  }
42
42
  }
43
43
  class D {
@@ -63,37 +63,37 @@ class D {
63
63
  a.set("X-SDK-Version", m), a.set("X-Paywall-Id", this.paywallId), this.capabilities?.length && a.set("X-SDK-Capabilities", this.capabilities.join(","));
64
64
  const n = await this.auth?.getAccessToken();
65
65
  n ? a.set("Authorization", `Bearer ${n}`) : this.userId && a.set("X-User-ID", this.userId);
66
- const o = typeof FormData < "u" && t.body instanceof FormData, u = typeof Blob < "u" && t.body instanceof Blob, d = typeof ReadableStream < "u" && t.body instanceof ReadableStream, f = typeof t.body == "string";
67
- let h;
68
- t.body === void 0 || t.body === null ? h = void 0 : o || u || d || f ? h = t.body : (h = JSON.stringify(t.body), a.has("Content-Type") || a.set("Content-Type", "application/json"));
69
- const y = this.customFetch ?? fetch;
66
+ const o = typeof FormData < "u" && t.body instanceof FormData, d = typeof Blob < "u" && t.body instanceof Blob, f = typeof ReadableStream < "u" && t.body instanceof ReadableStream, p = typeof t.body == "string";
70
67
  let c;
68
+ t.body === void 0 || t.body === null ? c = void 0 : o || d || f || p ? c = t.body : (c = JSON.stringify(t.body), a.has("Content-Type") || a.set("Content-Type", "application/json"));
69
+ const l = this.customFetch ?? fetch;
70
+ let h;
71
71
  try {
72
- c = await y(s.toString(), {
72
+ h = await l(s.toString(), {
73
73
  method: t.method ?? "POST",
74
74
  headers: a,
75
- body: h,
75
+ body: c,
76
76
  signal: t.signal,
77
77
  credentials: "omit"
78
78
  });
79
- } catch (p) {
80
- const P = p instanceof Error ? p.message : String(p);
81
- throw new r("network_error", `Network request failed: ${P}`, { cause: p });
79
+ } catch (y) {
80
+ const P = y instanceof Error ? y.message : String(y);
81
+ throw new r("network_error", `Network request failed: ${P}`, { cause: y });
82
82
  }
83
- if (c.status === 402) {
84
- const p = await q(c);
85
- throw this.onQuotaExceeded?.(p), p;
83
+ if (h.status === 402) {
84
+ const y = await q(h);
85
+ throw this.onQuotaExceeded?.(y), y;
86
86
  }
87
- if (!c.ok) {
88
- const p = await $(c.clone());
87
+ if (!h.ok) {
88
+ const y = await $(h.clone());
89
89
  throw new r(
90
- p ?? `http_${c.status}`,
91
- c.statusText || "Gateway request failed",
92
- { status: c.status }
90
+ y ?? `http_${h.status}`,
91
+ h.statusText || "Gateway request failed",
92
+ { status: h.status }
93
93
  );
94
94
  }
95
- const g = c.headers.get("X-Query-Type") ?? void 0;
96
- return this.onChargeSuccess?.(g), c;
95
+ const g = h.headers.get("X-Query-Type") ?? void 0;
96
+ return this.onChargeSuccess?.(g), h;
97
97
  }
98
98
  }
99
99
  async function q(i) {
@@ -198,7 +198,7 @@ const M = {
198
198
  function L(i) {
199
199
  return i || (F() ? M : typeof window < "u" && "localStorage" in window ? x : J);
200
200
  }
201
- const l = {
201
+ const u = {
202
202
  visitorId: "pw-visitor-id",
203
203
  lastLoginMethod: (i) => `pw-${i}-last-login-method`,
204
204
  lastLoginEmail: (i) => `pw-${i}-last-login-email`,
@@ -242,13 +242,13 @@ function C() {
242
242
  }
243
243
  async function b(i) {
244
244
  try {
245
- const e = await i.getItem(l.visitorId);
245
+ const e = await i.getItem(u.visitorId);
246
246
  if (e && typeof e == "string" && e.length >= 16) return e;
247
247
  } catch {
248
248
  }
249
249
  const t = C();
250
250
  try {
251
- await i.setItem(l.visitorId, t);
251
+ await i.setItem(u.visitorId, t);
252
252
  } catch {
253
253
  }
254
254
  return t;
@@ -444,7 +444,7 @@ class yt {
444
444
  async hydrateBootstrapFromStorage() {
445
445
  if (!this.cachedBootstrap)
446
446
  try {
447
- const t = await this.storage.getItem(l.bootstrap(this.paywallId));
447
+ const t = await this.storage.getItem(u.bootstrap(this.paywallId));
448
448
  if (!t) return;
449
449
  const e = JSON.parse(t);
450
450
  if (!e?.bootstrap || Date.now() - e.at > _ || this.cachedBootstrap) return;
@@ -463,7 +463,7 @@ class yt {
463
463
  try {
464
464
  const { user: e, ...s } = t;
465
465
  await this.storage.setItem(
466
- l.bootstrap(this.paywallId),
466
+ u.bootstrap(this.paywallId),
467
467
  JSON.stringify({ at: Date.now(), bootstrap: s })
468
468
  );
469
469
  } catch {
@@ -474,7 +474,7 @@ class yt {
474
474
  // no-op, всё работает как раньше через сеть.
475
475
  subscribeBootstrapStorage() {
476
476
  typeof this.storage.watch == "function" && (this.bootstrapStorageUnwatch = this.storage.watch(
477
- l.bootstrap(this.paywallId),
477
+ u.bootstrap(this.paywallId),
478
478
  (t) => {
479
479
  if (t)
480
480
  try {
@@ -620,7 +620,7 @@ class yt {
620
620
  }
621
621
  }
622
622
  storageKey() {
623
- return l.userState(this.paywallId, B(this.identity));
623
+ return u.userState(this.paywallId, B(this.identity));
624
624
  }
625
625
  async hydrateUserFromStorage() {
626
626
  if (!this.cachedUser)
@@ -775,7 +775,7 @@ class yt {
775
775
  }
776
776
  }
777
777
  balancesStorageKey() {
778
- return l.balances(this.paywallId, B(this.identity));
778
+ return u.balances(this.paywallId, B(this.identity));
779
779
  }
780
780
  async hydrateBalancesFromStorage() {
781
781
  if (!this.cachedBalances)
@@ -832,20 +832,24 @@ class yt {
832
832
  "Idempotency-Key": t.idempotencyKey ?? C()
833
833
  };
834
834
  this.apiKey && (n["X-Api-Key"] = this.apiKey);
835
- const o = this.cachedBootstrap?.settings, u = t.successUrl ?? o?.success_redirect_url ?? void 0, d = t.shopUrl ?? o?.checkout_shop_url ?? void 0, f = this.api.request(`/api/v1/paywall/${this.paywallId}/start-checkout`, {
835
+ const o = this.cachedBootstrap?.settings, d = t.successUrl ?? o?.success_redirect_url ?? void 0, f = t.shopUrl ?? o?.checkout_shop_url ?? void 0, c = this.cachedBootstrap?.prices.find(
836
+ (h) => h.id === t.priceId
837
+ )?.local?.currency ?? void 0, l = this.api.request(`/api/v1/paywall/${this.paywallId}/start-checkout`, {
836
838
  method: "POST",
837
839
  headers: n,
838
840
  signal: t.signal,
839
841
  body: JSON.stringify({
840
842
  email: this.identity.email,
841
843
  priceId: Number(t.priceId),
842
- successUrl: u,
844
+ offerId: t.offerId,
845
+ successUrl: d,
843
846
  errorUrl: t.errorUrl,
844
- shopUrl: d,
847
+ shopUrl: f,
845
848
  productName: o?.checkout_product_name ?? void 0,
846
849
  trial_days: t.trialDays,
847
850
  ignoreActivePurchase: t.ignoreActivePurchase ? !0 : void 0,
848
- userMeta: this.identity.userId ? { userId: this.identity.userId } : void 0
851
+ userMeta: this.identity.userId ? { userId: this.identity.userId } : void 0,
852
+ localCurrency: c
849
853
  })
850
854
  }).then((h) => ({ url: h.checkoutUrl, acquiring: h.acquiring })).catch((h) => {
851
855
  throw h instanceof r && h.status === 409 && h.cause && typeof h.cause == "object" && h.cause.hasActivePurchase === !0 ? new r(
@@ -854,10 +858,10 @@ class yt {
854
858
  { status: 409, cause: h.cause }
855
859
  ) : h;
856
860
  });
857
- return this.inflightCheckouts.set(e, f), f.finally(() => {
858
- this.inflightCheckouts.get(e) === f && this.inflightCheckouts.delete(e);
861
+ return this.inflightCheckouts.set(e, l), l.finally(() => {
862
+ this.inflightCheckouts.get(e) === l && this.inflightCheckouts.delete(e);
859
863
  }).catch(() => {
860
- }), f;
864
+ }), l;
861
865
  }
862
866
  /**
863
867
  * URL Stripe/Paddle/Chargebee customer portal — место, где залогиненный
@@ -1453,8 +1457,8 @@ class pt {
1453
1457
  id: a.user.id,
1454
1458
  email: a.user.email,
1455
1459
  is_anonymous: a.user.is_anonymous ?? !1
1456
- }, u = { ...n, user: o };
1457
- return this.setSession(u, { event: "USER_UPDATED" }), await this.clearAnonRefreshToken(), { kind: "updated", session: u };
1460
+ }, d = { ...n, user: o };
1461
+ return this.setSession(d, { event: "USER_UPDATED" }), await this.clearAnonRefreshToken(), { kind: "updated", session: d };
1458
1462
  }
1459
1463
  /**
1460
1464
  * OAuth signin через popup с PKCE. Жизненный цикл:
@@ -1513,7 +1517,7 @@ class pt {
1513
1517
  await this.hydrated, this.gcOAuthFlows();
1514
1518
  const e = Z(), s = await tt(e), a = et(), n = {}, o = await this.getAccessToken().catch(() => null);
1515
1519
  o && (n.Authorization = `Bearer ${o}`);
1516
- const { authorize_url: u } = await this.api.request(
1520
+ const { authorize_url: d } = await this.api.request(
1517
1521
  `/api/v1/paywall/${this.paywallId}/auth/oauth/init`,
1518
1522
  {
1519
1523
  method: "POST",
@@ -1531,7 +1535,7 @@ class pt {
1531
1535
  userMeta: t.userMeta,
1532
1536
  provider: t.provider,
1533
1537
  startedAt: Date.now()
1534
- }), this.recordLastLoginMethod(t.provider), { authorize_url: u, state: a };
1538
+ }), this.recordLastLoginMethod(t.provider), { authorize_url: d, state: a };
1535
1539
  }
1536
1540
  /**
1537
1541
  * Шаг 2 OAuth split-API: обменивает code (полученный из popup) на session,
@@ -1718,7 +1722,7 @@ class pt {
1718
1722
  }
1719
1723
  }
1720
1724
  storageKey() {
1721
- return l.authSession(this.paywallId);
1725
+ return u.authSession(this.paywallId);
1722
1726
  }
1723
1727
  async hydrate() {
1724
1728
  try {
@@ -1772,7 +1776,7 @@ class pt {
1772
1776
  }
1773
1777
  async readAnonRefreshToken() {
1774
1778
  try {
1775
- const t = await this.storage.getItem(l.anonRefreshToken(this.paywallId));
1779
+ const t = await this.storage.getItem(u.anonRefreshToken(this.paywallId));
1776
1780
  return typeof t == "string" && t.length > 0 ? t : null;
1777
1781
  } catch {
1778
1782
  return null;
@@ -1781,7 +1785,7 @@ class pt {
1781
1785
  async writeAnonRefreshToken(t) {
1782
1786
  try {
1783
1787
  await this.storage.setItem(
1784
- l.anonRefreshToken(this.paywallId),
1788
+ u.anonRefreshToken(this.paywallId),
1785
1789
  t
1786
1790
  );
1787
1791
  } catch {
@@ -1790,7 +1794,7 @@ class pt {
1790
1794
  async clearAnonRefreshToken() {
1791
1795
  try {
1792
1796
  await this.storage.removeItem(
1793
- l.anonRefreshToken(this.paywallId)
1797
+ u.anonRefreshToken(this.paywallId)
1794
1798
  );
1795
1799
  } catch {
1796
1800
  }
@@ -1803,8 +1807,8 @@ class pt {
1803
1807
  async getLastLogin() {
1804
1808
  try {
1805
1809
  const [t, e] = await Promise.all([
1806
- this.storage.getItem(l.lastLoginMethod(this.paywallId)),
1807
- this.storage.getItem(l.lastLoginEmail(this.paywallId))
1810
+ this.storage.getItem(u.lastLoginMethod(this.paywallId)),
1811
+ this.storage.getItem(u.lastLoginEmail(this.paywallId))
1808
1812
  ]);
1809
1813
  return !t || !ot(t) ? null : { method: t, email: typeof e == "string" && e ? e : null };
1810
1814
  } catch {
@@ -1818,11 +1822,11 @@ class pt {
1818
1822
  this.recordLastLoginMethod(t), e && this.recordLastLoginEmail(e);
1819
1823
  }
1820
1824
  recordLastLoginMethod(t) {
1821
- this.storage.setItem(l.lastLoginMethod(this.paywallId), t).catch(() => {
1825
+ this.storage.setItem(u.lastLoginMethod(this.paywallId), t).catch(() => {
1822
1826
  });
1823
1827
  }
1824
1828
  recordLastLoginEmail(t) {
1825
- this.storage.setItem(l.lastLoginEmail(this.paywallId), t).catch(() => {
1829
+ this.storage.setItem(u.lastLoginEmail(this.paywallId), t).catch(() => {
1826
1830
  });
1827
1831
  }
1828
1832
  /**
@@ -1834,7 +1838,7 @@ class pt {
1834
1838
  */
1835
1839
  async readVisitorId() {
1836
1840
  try {
1837
- const t = await this.storage.getItem(l.visitorId);
1841
+ const t = await this.storage.getItem(u.visitorId);
1838
1842
  return typeof t == "string" && t.length >= 16 ? t : void 0;
1839
1843
  } catch {
1840
1844
  return;
@@ -1846,19 +1850,19 @@ function rt(i, t) {
1846
1850
  return new Promise((e, s) => {
1847
1851
  let a = !1;
1848
1852
  const n = () => {
1849
- a = !0, window.removeEventListener("message", o), clearInterval(u), clearTimeout(d);
1850
- }, o = (f) => {
1853
+ a = !0, window.removeEventListener("message", o), clearInterval(d), clearTimeout(f);
1854
+ }, o = (p) => {
1851
1855
  if (a) return;
1852
- const h = f.data;
1853
- if (!(!h || h.type !== "pw-oauth") && h.messageId === t) {
1854
- if (h.status === "success" && h.code) {
1856
+ const c = p.data;
1857
+ if (!(!c || c.type !== "pw-oauth") && c.messageId === t) {
1858
+ if (c.status === "success" && c.code) {
1855
1859
  n();
1856
1860
  try {
1857
1861
  i.close();
1858
1862
  } catch {
1859
1863
  }
1860
- e(h.code);
1861
- } else if (h.status === "error") {
1864
+ e(c.code);
1865
+ } else if (c.status === "error") {
1862
1866
  n();
1863
1867
  try {
1864
1868
  i.close();
@@ -1867,21 +1871,21 @@ function rt(i, t) {
1867
1871
  s(
1868
1872
  new r(
1869
1873
  "oauth_failed",
1870
- h.description || h.error || "OAuth provider returned error"
1874
+ c.description || c.error || "OAuth provider returned error"
1871
1875
  )
1872
1876
  );
1873
1877
  }
1874
1878
  }
1875
- }, u = setInterval(() => {
1879
+ }, d = setInterval(() => {
1876
1880
  if (a) return;
1877
- let f;
1881
+ let p;
1878
1882
  try {
1879
- f = i.closed;
1883
+ p = i.closed;
1880
1884
  } catch {
1881
1885
  return;
1882
1886
  }
1883
- f && (n(), s(new r("oauth_cancelled", "auth popup was closed")));
1884
- }, nt), d = setTimeout(() => {
1887
+ p && (n(), s(new r("oauth_cancelled", "auth popup was closed")));
1888
+ }, nt), f = setTimeout(() => {
1885
1889
  if (!a) {
1886
1890
  n();
1887
1891
  try {
@@ -2050,7 +2054,7 @@ export {
2050
2054
  r as PaywallError,
2051
2055
  R as QuotaExceededError,
2052
2056
  m as SDK_VERSION,
2053
- l as STORAGE_KEYS,
2057
+ u as STORAGE_KEYS,
2054
2058
  L as createStorage,
2055
2059
  b as ensureVisitorId,
2056
2060
  wt as findApplicableOffer,