@monetize.software/sdk-extension 3.0.0-alpha.1 → 3.0.0-alpha.11
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/LICENSE +21 -0
- package/README.md +21 -20
- package/dist/chunks/ar-7cgIM-Vl.js +2 -0
- package/dist/chunks/ar-7cgIM-Vl.js.map +1 -0
- package/dist/chunks/ar-B2Wg_IrC.js +126 -0
- package/dist/chunks/ar-B2Wg_IrC.js.map +1 -0
- package/dist/chunks/chrome-port-BXK5myXz.js +2 -0
- package/dist/chunks/chrome-port-BXK5myXz.js.map +1 -0
- package/dist/chunks/{chrome-port-BXHR4SOG.js → chrome-port-KYVwww_u.js} +376 -216
- package/dist/chunks/chrome-port-KYVwww_u.js.map +1 -0
- package/dist/chunks/cs-BNo9Dx0Q.js +122 -0
- package/dist/chunks/cs-BNo9Dx0Q.js.map +1 -0
- package/dist/chunks/cs-S05PC5AC.js +2 -0
- package/dist/chunks/cs-S05PC5AC.js.map +1 -0
- package/dist/chunks/da-Bi4zBG14.js +2 -0
- package/dist/chunks/da-Bi4zBG14.js.map +1 -0
- package/dist/chunks/da-Do9Lq6En.js +122 -0
- package/dist/chunks/da-Do9Lq6En.js.map +1 -0
- package/dist/chunks/de-C8pDZNvx.js +141 -0
- package/dist/chunks/de-C8pDZNvx.js.map +1 -0
- package/dist/chunks/de-nCDB6D2W.js +2 -0
- package/dist/chunks/de-nCDB6D2W.js.map +1 -0
- package/dist/chunks/el-BrKaa978.js +2 -0
- package/dist/chunks/el-BrKaa978.js.map +1 -0
- package/dist/chunks/el-DzMNX-_P.js +126 -0
- package/dist/chunks/el-DzMNX-_P.js.map +1 -0
- package/dist/chunks/es-B-Wtyzrl.js +2 -0
- package/dist/chunks/es-B-Wtyzrl.js.map +1 -0
- package/dist/chunks/es-YrKt-q4w.js +141 -0
- package/dist/chunks/es-YrKt-q4w.js.map +1 -0
- package/dist/chunks/fi-Bh44pwZ4.js +122 -0
- package/dist/chunks/fi-Bh44pwZ4.js.map +1 -0
- package/dist/chunks/fi-D1SGXjnO.js +2 -0
- package/dist/chunks/fi-D1SGXjnO.js.map +1 -0
- package/dist/chunks/fr-Bc0pw4ws.js +141 -0
- package/dist/chunks/fr-Bc0pw4ws.js.map +1 -0
- package/dist/chunks/fr-BhYf-iKk.js +2 -0
- package/dist/chunks/fr-BhYf-iKk.js.map +1 -0
- package/dist/chunks/he-BXAaFv6Y.js +2 -0
- package/dist/chunks/he-BXAaFv6Y.js.map +1 -0
- package/dist/chunks/he-Bfm-bhe3.js +126 -0
- package/dist/chunks/he-Bfm-bhe3.js.map +1 -0
- package/dist/chunks/hi-D-O-B9Dn.js +126 -0
- package/dist/chunks/hi-D-O-B9Dn.js.map +1 -0
- package/dist/chunks/hi-xblDO0O7.js +2 -0
- package/dist/chunks/hi-xblDO0O7.js.map +1 -0
- package/dist/chunks/hu-CmIuAbLL.js +122 -0
- package/dist/chunks/hu-CmIuAbLL.js.map +1 -0
- package/dist/chunks/hu-Wa46p0y4.js +2 -0
- package/dist/chunks/hu-Wa46p0y4.js.map +1 -0
- package/dist/chunks/id-CQEo5X94.js +2 -0
- package/dist/chunks/id-CQEo5X94.js.map +1 -0
- package/dist/chunks/id-DN7IES-A.js +122 -0
- package/dist/chunks/id-DN7IES-A.js.map +1 -0
- package/dist/chunks/it-8AYCm0xz.js +2 -0
- package/dist/chunks/it-8AYCm0xz.js.map +1 -0
- package/dist/chunks/it-Cz5Nmqx5.js +141 -0
- package/dist/chunks/it-Cz5Nmqx5.js.map +1 -0
- package/dist/chunks/ja-BH9BlBh2.js +145 -0
- package/dist/chunks/ja-BH9BlBh2.js.map +1 -0
- package/dist/chunks/ja-q-COVayn.js +2 -0
- package/dist/chunks/ja-q-COVayn.js.map +1 -0
- package/dist/chunks/ko-B6HRCscZ.js +2 -0
- package/dist/chunks/ko-B6HRCscZ.js.map +1 -0
- package/dist/chunks/ko-CYV9QuYs.js +145 -0
- package/dist/chunks/ko-CYV9QuYs.js.map +1 -0
- package/dist/chunks/nl-BvkB900D.js +141 -0
- package/dist/chunks/nl-BvkB900D.js.map +1 -0
- package/dist/chunks/nl-CAd6_xlm.js +2 -0
- package/dist/chunks/nl-CAd6_xlm.js.map +1 -0
- package/dist/chunks/no-3s9_ormb.js +122 -0
- package/dist/chunks/no-3s9_ormb.js.map +1 -0
- package/dist/chunks/no-CAmz6bz6.js +2 -0
- package/dist/chunks/no-CAmz6bz6.js.map +1 -0
- package/dist/chunks/pl-C9WTGQtb.js +122 -0
- package/dist/chunks/pl-C9WTGQtb.js.map +1 -0
- package/dist/chunks/pl-DqUSTCaF.js +2 -0
- package/dist/chunks/pl-DqUSTCaF.js.map +1 -0
- package/dist/chunks/port-name-CF4WQQ3-.js +2 -0
- package/dist/chunks/port-name-CF4WQQ3-.js.map +1 -0
- package/dist/chunks/port-name-ervLBWAQ.js +6 -0
- package/dist/chunks/port-name-ervLBWAQ.js.map +1 -0
- package/dist/chunks/pt-8ARZnH0_.js +2 -0
- package/dist/chunks/pt-8ARZnH0_.js.map +1 -0
- package/dist/chunks/pt-uFVUv_Op.js +141 -0
- package/dist/chunks/pt-uFVUv_Op.js.map +1 -0
- package/dist/chunks/ro-BrqQ8Au-.js +122 -0
- package/dist/chunks/ro-BrqQ8Au-.js.map +1 -0
- package/dist/chunks/ro-D-NMbp2F.js +2 -0
- package/dist/chunks/ro-D-NMbp2F.js.map +1 -0
- package/dist/chunks/ru-8gbHPh0g.js +2 -0
- package/dist/chunks/ru-8gbHPh0g.js.map +1 -0
- package/dist/chunks/ru-DK594dA8.js +144 -0
- package/dist/chunks/ru-DK594dA8.js.map +1 -0
- package/dist/chunks/sv-CHNH8-mq.js +122 -0
- package/dist/chunks/sv-CHNH8-mq.js.map +1 -0
- package/dist/chunks/sv-D8a8hmx9.js +2 -0
- package/dist/chunks/sv-D8a8hmx9.js.map +1 -0
- package/dist/chunks/th-DfjUK0Y7.js +2 -0
- package/dist/chunks/th-DfjUK0Y7.js.map +1 -0
- package/dist/chunks/th-l24Pm5q-.js +126 -0
- package/dist/chunks/th-l24Pm5q-.js.map +1 -0
- package/dist/chunks/tr-ADpigSY5.js +122 -0
- package/dist/chunks/tr-ADpigSY5.js.map +1 -0
- package/dist/chunks/tr-BdBpz4tL.js +2 -0
- package/dist/chunks/tr-BdBpz4tL.js.map +1 -0
- package/dist/chunks/uk-CGqo4jek.js +144 -0
- package/dist/chunks/uk-CGqo4jek.js.map +1 -0
- package/dist/chunks/uk-Cx1zv1ao.js +2 -0
- package/dist/chunks/uk-Cx1zv1ao.js.map +1 -0
- package/dist/chunks/vi-Dk9bTu6f.js +122 -0
- package/dist/chunks/vi-Dk9bTu6f.js.map +1 -0
- package/dist/chunks/vi-oe2dW21I.js +2 -0
- package/dist/chunks/vi-oe2dW21I.js.map +1 -0
- package/dist/chunks/zh-CwczPMPp.js +2 -0
- package/dist/chunks/zh-CwczPMPp.js.map +1 -0
- package/dist/chunks/zh-LDkEV2D9.js +145 -0
- package/dist/chunks/zh-LDkEV2D9.js.map +1 -0
- package/dist/content/RemoteAuthClient.d.ts +8 -4
- package/dist/content/RemoteAuthClient.d.ts.map +1 -1
- package/dist/content/RemoteAuthClient.test-d.d.ts +2 -0
- package/dist/content/RemoteAuthClient.test-d.d.ts.map +1 -0
- package/dist/content/RemoteBillingClient.d.ts +34 -1
- package/dist/content/RemoteBillingClient.d.ts.map +1 -1
- package/dist/content/RemoteBillingClient.test-d.d.ts +2 -0
- package/dist/content/RemoteBillingClient.test-d.d.ts.map +1 -0
- package/dist/content.cjs +3 -3
- package/dist/content.cjs.map +1 -1
- package/dist/content.js +2356 -1221
- package/dist/content.js.map +1 -1
- package/dist/offscreen/server.d.ts.map +1 -1
- package/dist/offscreen.cjs +1 -1
- package/dist/offscreen.cjs.map +1 -1
- package/dist/offscreen.js +18 -15
- package/dist/offscreen.js.map +1 -1
- package/dist/shared/messages.d.ts +27 -4
- package/dist/shared/messages.d.ts.map +1 -1
- package/dist/shared/port-name.d.ts +1 -0
- package/dist/shared/port-name.d.ts.map +1 -1
- package/dist/shared/protocol.d.ts +1 -1
- package/dist/shared/protocol.d.ts.map +1 -1
- package/dist/sw.cjs +1 -1
- package/dist/sw.cjs.map +1 -1
- package/dist/sw.js +14 -14
- package/dist/sw.js.map +1 -1
- package/package.json +39 -21
- package/dist/chunks/chrome-port-BXHR4SOG.js.map +0 -1
- package/dist/chunks/chrome-port-EtYqHf3p.js +0 -2
- package/dist/chunks/chrome-port-EtYqHf3p.js.map +0 -1
- package/dist/chunks/port-name-BPfQKtdb.js +0 -5
- package/dist/chunks/port-name-BPfQKtdb.js.map +0 -1
- package/dist/chunks/port-name-qwB109u9.js +0 -2
- package/dist/chunks/port-name-qwB109u9.js.map +0 -1
|
@@ -3,47 +3,47 @@ class o extends Error {
|
|
|
3
3
|
super(e), this.name = "PaywallError", this.code = t, this.status = s.status, this.cause = s.cause;
|
|
4
4
|
}
|
|
5
5
|
}
|
|
6
|
-
class
|
|
6
|
+
class F extends o {
|
|
7
7
|
constructor(t) {
|
|
8
8
|
super("not_enough_queries", t.message ?? "Not enough queries", {
|
|
9
9
|
status: 402
|
|
10
10
|
}), this.name = "QuotaExceededError", this.balances = t.balances, this.queryType = t.queryType, this.currentBalance = t.currentBalance;
|
|
11
11
|
}
|
|
12
12
|
}
|
|
13
|
-
const
|
|
14
|
-
class
|
|
13
|
+
const m = "3.0.0-alpha.0";
|
|
14
|
+
class K {
|
|
15
15
|
constructor(t) {
|
|
16
16
|
this.opts = t;
|
|
17
17
|
}
|
|
18
18
|
async request(t, e = {}) {
|
|
19
19
|
const s = new URL(t, this.opts.apiOrigin).toString(), a = this.opts.fetch ?? fetch, n = new Headers(e.headers);
|
|
20
|
-
n.set("Accept", "application/json"), n.set("X-SDK-Version",
|
|
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 r = await this.opts.getAuthToken?.();
|
|
22
22
|
r && n.set("Authorization", `Bearer ${r}`);
|
|
23
|
-
const
|
|
24
|
-
e.body && !n.has("Content-Type") && !
|
|
25
|
-
let
|
|
23
|
+
const h = typeof FormData < "u" && e.body instanceof FormData;
|
|
24
|
+
e.body && !n.has("Content-Type") && !h && n.set("Content-Type", "application/json");
|
|
25
|
+
let d;
|
|
26
26
|
try {
|
|
27
|
-
|
|
27
|
+
d = await a(s, {
|
|
28
28
|
...e,
|
|
29
29
|
headers: n,
|
|
30
30
|
credentials: "omit"
|
|
31
31
|
});
|
|
32
|
-
} catch (
|
|
33
|
-
throw (
|
|
32
|
+
} catch (l) {
|
|
33
|
+
throw (l && typeof l == "object" && "name" in l ? l.name : void 0) === "AbortError" ? new o("aborted", "Request aborted", { cause: l }) : new o("network_error", "Network request failed", { cause: l });
|
|
34
34
|
}
|
|
35
|
-
const
|
|
36
|
-
if (!
|
|
37
|
-
const
|
|
38
|
-
throw new o(
|
|
35
|
+
const y = (d.headers.get("content-type") ?? "").includes("application/json") ? await d.json().catch(() => null) : null;
|
|
36
|
+
if (!d.ok) {
|
|
37
|
+
const l = 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 o(l, g, { status: d.status, cause: y });
|
|
39
39
|
}
|
|
40
|
-
return
|
|
40
|
+
return y;
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
|
-
function
|
|
43
|
+
function x() {
|
|
44
44
|
return typeof chrome < "u" && !!chrome?.storage?.local && !!chrome?.runtime?.id;
|
|
45
45
|
}
|
|
46
|
-
const
|
|
46
|
+
const H = {
|
|
47
47
|
getItem(i) {
|
|
48
48
|
return new Promise((t) => {
|
|
49
49
|
chrome.storage.local.get([i], (e) => {
|
|
@@ -73,7 +73,7 @@ const D = {
|
|
|
73
73
|
};
|
|
74
74
|
return e.addListener(s), () => e.removeListener(s);
|
|
75
75
|
}
|
|
76
|
-
},
|
|
76
|
+
}, J = {
|
|
77
77
|
async getItem(i) {
|
|
78
78
|
try {
|
|
79
79
|
return window.localStorage.getItem(i);
|
|
@@ -101,21 +101,21 @@ const D = {
|
|
|
101
101
|
};
|
|
102
102
|
return window.addEventListener("storage", e), () => window.removeEventListener("storage", e);
|
|
103
103
|
}
|
|
104
|
-
},
|
|
104
|
+
}, I = /* @__PURE__ */ new Map(), V = {
|
|
105
105
|
async getItem(i) {
|
|
106
|
-
return
|
|
106
|
+
return I.get(i) ?? null;
|
|
107
107
|
},
|
|
108
108
|
async setItem(i, t) {
|
|
109
|
-
|
|
109
|
+
I.set(i, t);
|
|
110
110
|
},
|
|
111
111
|
async removeItem(i) {
|
|
112
|
-
|
|
112
|
+
I.delete(i);
|
|
113
113
|
}
|
|
114
114
|
};
|
|
115
115
|
function P(i) {
|
|
116
|
-
return i || (
|
|
116
|
+
return i || (x() ? H : typeof window < "u" && "localStorage" in window ? J : V);
|
|
117
117
|
}
|
|
118
|
-
const
|
|
118
|
+
const u = {
|
|
119
119
|
visitorId: "pw-visitor-id",
|
|
120
120
|
lastLoginMethod: (i) => `pw-${i}-last-login-method`,
|
|
121
121
|
lastLoginEmail: (i) => `pw-${i}-last-login-email`,
|
|
@@ -157,20 +157,20 @@ function R() {
|
|
|
157
157
|
const e = Array.from(t, (s) => s.toString(16).padStart(2, "0")).join("");
|
|
158
158
|
return `${e.slice(0, 8)}-${e.slice(8, 12)}-${e.slice(12, 16)}-${e.slice(16, 20)}-${e.slice(20)}`;
|
|
159
159
|
}
|
|
160
|
-
async function
|
|
160
|
+
async function B(i) {
|
|
161
161
|
try {
|
|
162
|
-
const e = await i.getItem(
|
|
162
|
+
const e = await i.getItem(u.visitorId);
|
|
163
163
|
if (e && typeof e == "string" && e.length >= 16) return e;
|
|
164
164
|
} catch {
|
|
165
165
|
}
|
|
166
166
|
const t = R();
|
|
167
167
|
try {
|
|
168
|
-
await i.setItem(
|
|
168
|
+
await i.setItem(u.visitorId, t);
|
|
169
169
|
} catch {
|
|
170
170
|
}
|
|
171
171
|
return t;
|
|
172
172
|
}
|
|
173
|
-
function
|
|
173
|
+
function D(i) {
|
|
174
174
|
const t = new Uint8Array(i), e = typeof globalThis < "u" ? globalThis.crypto : void 0;
|
|
175
175
|
if (e && typeof e.getRandomValues == "function")
|
|
176
176
|
e.getRandomValues(t);
|
|
@@ -178,30 +178,35 @@ function q(i) {
|
|
|
178
178
|
for (let s = 0; s < i; s++) t[s] = Math.floor(Math.random() * 256);
|
|
179
179
|
return t;
|
|
180
180
|
}
|
|
181
|
-
function
|
|
181
|
+
function _(i) {
|
|
182
182
|
let t = "";
|
|
183
183
|
for (let e = 0; e < i.length; e++) t += String.fromCharCode(i[e]);
|
|
184
184
|
return btoa(t).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
185
185
|
}
|
|
186
|
-
function
|
|
187
|
-
return
|
|
186
|
+
function j() {
|
|
187
|
+
return _(D(64));
|
|
188
188
|
}
|
|
189
|
-
async function
|
|
189
|
+
async function X(i) {
|
|
190
190
|
const t = new TextEncoder().encode(i), e = globalThis.crypto;
|
|
191
191
|
if (!e?.subtle?.digest)
|
|
192
192
|
throw new Error("crypto.subtle is required for PKCE");
|
|
193
193
|
const s = await e.subtle.digest("SHA-256", t);
|
|
194
|
-
return
|
|
194
|
+
return _(new Uint8Array(s));
|
|
195
195
|
}
|
|
196
|
-
function
|
|
197
|
-
return
|
|
196
|
+
function G() {
|
|
197
|
+
return _(D(16));
|
|
198
198
|
}
|
|
199
|
-
const
|
|
199
|
+
const z = 6e4, Q = 600 * 1e3;
|
|
200
200
|
class mt {
|
|
201
201
|
constructor(t) {
|
|
202
202
|
if (this.session = null, this.inflightRefresh = null, this.inflightAnonSignin = null, this.listeners = /* @__PURE__ */ new Set(), this.storageUnwatch = null, this.destroyed = !1, this.oauthFlows = /* @__PURE__ */ new Map(), !t.paywallId)
|
|
203
203
|
throw new o("invalid_config", "paywallId is required");
|
|
204
|
-
|
|
204
|
+
if (!t.apiOrigin)
|
|
205
|
+
throw new o(
|
|
206
|
+
"invalid_config",
|
|
207
|
+
"apiOrigin is required. Pass the paywall custom_domain configured in the platform."
|
|
208
|
+
);
|
|
209
|
+
this.paywallId = t.paywallId, this.apiOrigin = t.apiOrigin, this.storage = P(t.storage), this.api = new K({
|
|
205
210
|
apiOrigin: this.apiOrigin,
|
|
206
211
|
paywallId: t.paywallId,
|
|
207
212
|
fetch: t.fetch
|
|
@@ -227,14 +232,15 @@ class mt {
|
|
|
227
232
|
async applyExternalSession(t) {
|
|
228
233
|
if (!this.destroyed && (await this.hydrated, !this.destroyed)) {
|
|
229
234
|
if (t == null) {
|
|
230
|
-
this.session && this.setSession(null, { skipPersist: !0 });
|
|
235
|
+
this.session && this.setSession(null, { skipPersist: !0, event: "SIGNED_OUT" });
|
|
231
236
|
return;
|
|
232
237
|
}
|
|
233
238
|
try {
|
|
234
239
|
const e = JSON.parse(t);
|
|
235
240
|
if (!e || typeof e.access_token != "string" || typeof e.refresh_token != "string" || typeof e.expires_at != "number" || !e.user)
|
|
236
241
|
return;
|
|
237
|
-
this.
|
|
242
|
+
const s = !this.session || this.session.user.id !== e.user.id ? "SIGNED_IN" : "TOKEN_REFRESHED";
|
|
243
|
+
this.setSession(e, { skipPersist: !0, event: s });
|
|
238
244
|
} catch {
|
|
239
245
|
}
|
|
240
246
|
}
|
|
@@ -291,7 +297,7 @@ class mt {
|
|
|
291
297
|
})
|
|
292
298
|
}
|
|
293
299
|
), n = this.toSession(a, a.user);
|
|
294
|
-
return this.setSession(n), n;
|
|
300
|
+
return this.setSession(n, { event: "SIGNED_IN" }), this.recordLastLogin("email", t.email), n;
|
|
295
301
|
}
|
|
296
302
|
/**
|
|
297
303
|
* Signup. Если в Supabase включён email confirm — сервер возвращает
|
|
@@ -317,9 +323,9 @@ class mt {
|
|
|
317
323
|
}
|
|
318
324
|
);
|
|
319
325
|
if (a.status === "confirmation_required")
|
|
320
|
-
return { kind: "confirmation_required", user: a.user };
|
|
326
|
+
return this.recordLastLogin("email", t.email), { kind: "confirmation_required", user: a.user };
|
|
321
327
|
const n = this.toSession(a, a.user);
|
|
322
|
-
return this.setSession(n), { kind: "signed_in", session: n };
|
|
328
|
+
return this.setSession(n, { event: "SIGNED_IN" }), this.recordLastLogin("email", t.email), { kind: "signed_in", session: n };
|
|
323
329
|
}
|
|
324
330
|
/**
|
|
325
331
|
* Повторная отправка confirmation-email после signUp с включённым
|
|
@@ -382,8 +388,8 @@ class mt {
|
|
|
382
388
|
user_meta: t.userMeta
|
|
383
389
|
})
|
|
384
390
|
}
|
|
385
|
-
), a = this.toSession(s, s.user);
|
|
386
|
-
return this.setSession(a), a;
|
|
391
|
+
), a = this.toSession(s, s.user), n = t.type === "recovery" ? "PASSWORD_RECOVERY" : "SIGNED_IN";
|
|
392
|
+
return this.setSession(a, { event: n }), a;
|
|
387
393
|
}
|
|
388
394
|
/**
|
|
389
395
|
* Запрос recovery email. Бэк всегда ok, чтобы не палить enumeration.
|
|
@@ -442,10 +448,8 @@ class mt {
|
|
|
442
448
|
* когда сервер начнёт возвращать challenge_required в риск-сценариях,
|
|
443
449
|
* SDK сможет передать proof-of-something обратно без breaking change.
|
|
444
450
|
*
|
|
445
|
-
* `
|
|
446
|
-
* нового anon-юзера). Используется в switch-account flow.
|
|
447
|
-
* остаётся `forceCaptcha`, хотя капчи там больше нет — менять имя ломает
|
|
448
|
-
* host-сигнатуру; смысл «принудительно новая anon-сессия» сохранён.
|
|
451
|
+
* `forceNewAnon: true` пропускает шаги 1-2 и сразу делает /signin (создаёт
|
|
452
|
+
* нового anon-юзера). Используется в switch-account flow.
|
|
449
453
|
*
|
|
450
454
|
* Параллельные вызовы дедуплицируются через `inflightAnonSignin` — два
|
|
451
455
|
* click'а на «Войти как гость» не создадут двух anon-юзеров (два /signup =
|
|
@@ -454,9 +458,9 @@ class mt {
|
|
|
454
458
|
async signInAnonymously(t = {}) {
|
|
455
459
|
if (this.inflightAnonSignin) return this.inflightAnonSignin;
|
|
456
460
|
this.inflightAnonSignin = (async () => {
|
|
457
|
-
if (await this.hydrated, !t.
|
|
461
|
+
if (await this.hydrated, !t.forceNewAnon && this.session?.user.is_anonymous === !0)
|
|
458
462
|
return this.session;
|
|
459
|
-
if (!t.
|
|
463
|
+
if (!t.forceNewAnon) {
|
|
460
464
|
const r = await this.resumeAnonymous();
|
|
461
465
|
if (r) return r;
|
|
462
466
|
}
|
|
@@ -475,7 +479,7 @@ class mt {
|
|
|
475
479
|
email: s.user.email ?? null,
|
|
476
480
|
is_anonymous: !0
|
|
477
481
|
}, n = this.toSession(s, a);
|
|
478
|
-
return this.setSession(n), await this.writeAnonRefreshToken(n.refresh_token), n;
|
|
482
|
+
return this.setSession(n, { event: "SIGNED_IN" }), await this.writeAnonRefreshToken(n.refresh_token), n;
|
|
479
483
|
})();
|
|
480
484
|
try {
|
|
481
485
|
return await this.inflightAnonSignin;
|
|
@@ -497,7 +501,7 @@ class mt {
|
|
|
497
501
|
`/api/v1/paywall/${this.paywallId}/auth/refresh`,
|
|
498
502
|
{ method: "POST", body: JSON.stringify({ refresh_token: t }) }
|
|
499
503
|
), s = this.session?.user.is_anonymous === !0 ? this.session.user : { id: "", email: null, is_anonymous: !0 }, a = this.toSession(e, s);
|
|
500
|
-
return this.setSession(a), await this.writeAnonRefreshToken(a.refresh_token), a;
|
|
504
|
+
return this.setSession(a, { event: "SIGNED_IN" }), await this.writeAnonRefreshToken(a.refresh_token), a;
|
|
501
505
|
} catch (e) {
|
|
502
506
|
if (e instanceof o && e.status === 401)
|
|
503
507
|
return await this.clearAnonRefreshToken(), null;
|
|
@@ -559,8 +563,8 @@ class mt {
|
|
|
559
563
|
id: a.user.id,
|
|
560
564
|
email: a.user.email,
|
|
561
565
|
is_anonymous: a.user.is_anonymous ?? !1
|
|
562
|
-
},
|
|
563
|
-
return this.setSession(
|
|
566
|
+
}, h = { ...n, user: r };
|
|
567
|
+
return this.setSession(h, { event: "USER_UPDATED" }), await this.clearAnonRefreshToken(), { kind: "updated", session: h };
|
|
564
568
|
}
|
|
565
569
|
/**
|
|
566
570
|
* OAuth signin через popup с PKCE. Жизненный цикл:
|
|
@@ -596,7 +600,7 @@ class mt {
|
|
|
596
600
|
"browser blocked auth popup — call from a user gesture"
|
|
597
601
|
);
|
|
598
602
|
t.onPopupOpened?.();
|
|
599
|
-
const n = await
|
|
603
|
+
const n = await Z(a, s);
|
|
600
604
|
if (this.destroyed)
|
|
601
605
|
throw this.oauthFlows.delete(s), new o("aborted", "AuthClient destroyed mid-flow");
|
|
602
606
|
return this.completeOAuthFlow({ state: s, code: n });
|
|
@@ -617,9 +621,9 @@ class mt {
|
|
|
617
621
|
*/
|
|
618
622
|
async startOAuthFlow(t) {
|
|
619
623
|
await this.hydrated, this.gcOAuthFlows();
|
|
620
|
-
const e =
|
|
624
|
+
const e = j(), s = await X(e), a = G(), n = {}, r = await this.getAccessToken().catch(() => null);
|
|
621
625
|
r && (n.Authorization = `Bearer ${r}`);
|
|
622
|
-
const { authorize_url:
|
|
626
|
+
const { authorize_url: h } = await this.api.request(
|
|
623
627
|
`/api/v1/paywall/${this.paywallId}/auth/oauth/init`,
|
|
624
628
|
{
|
|
625
629
|
method: "POST",
|
|
@@ -635,8 +639,9 @@ class mt {
|
|
|
635
639
|
return this.oauthFlows.set(a, {
|
|
636
640
|
verifier: e,
|
|
637
641
|
userMeta: t.userMeta,
|
|
642
|
+
provider: t.provider,
|
|
638
643
|
startedAt: Date.now()
|
|
639
|
-
}), { authorize_url:
|
|
644
|
+
}), this.recordLastLoginMethod(t.provider), { authorize_url: h, state: a };
|
|
640
645
|
}
|
|
641
646
|
/**
|
|
642
647
|
* Шаг 2 OAuth split-API: обменивает code (полученный из popup) на session,
|
|
@@ -671,10 +676,10 @@ class mt {
|
|
|
671
676
|
if (this.destroyed)
|
|
672
677
|
throw new o("aborted", "AuthClient destroyed mid-flow");
|
|
673
678
|
const n = this.toSession(a, a.user);
|
|
674
|
-
return this.setSession(n), n;
|
|
679
|
+
return this.setSession(n, { event: "SIGNED_IN" }), n.user.email && this.recordLastLoginEmail(n.user.email), n;
|
|
675
680
|
}
|
|
676
681
|
gcOAuthFlows() {
|
|
677
|
-
const t = Date.now() -
|
|
682
|
+
const t = Date.now() - Q;
|
|
678
683
|
for (const [e, s] of this.oauthFlows)
|
|
679
684
|
s.startedAt < t && this.oauthFlows.delete(e);
|
|
680
685
|
}
|
|
@@ -700,10 +705,10 @@ class mt {
|
|
|
700
705
|
body: JSON.stringify({ refresh_token: t })
|
|
701
706
|
}
|
|
702
707
|
), a = this.toSession(s, e);
|
|
703
|
-
return this.setSession(a), e.is_anonymous === !0 && await this.writeAnonRefreshToken(a.refresh_token), a;
|
|
708
|
+
return this.setSession(a, { event: "TOKEN_REFRESHED" }), e.is_anonymous === !0 && await this.writeAnonRefreshToken(a.refresh_token), a;
|
|
704
709
|
} catch (s) {
|
|
705
710
|
if (s instanceof o && s.status === 401)
|
|
706
|
-
return e.is_anonymous === !0 && await this.clearAnonRefreshToken(), this.setSession(null), null;
|
|
711
|
+
return e.is_anonymous === !0 && await this.clearAnonRefreshToken(), this.setSession(null, { event: "SIGNED_OUT" }), null;
|
|
707
712
|
throw s;
|
|
708
713
|
} finally {
|
|
709
714
|
this.inflightRefresh = null;
|
|
@@ -736,7 +741,7 @@ class mt {
|
|
|
736
741
|
method: "POST",
|
|
737
742
|
headers: { Authorization: `Bearer ${t}` }
|
|
738
743
|
}
|
|
739
|
-
), this.setSession(null);
|
|
744
|
+
), this.setSession(null, { event: "SIGNED_OUT" });
|
|
740
745
|
}
|
|
741
746
|
/**
|
|
742
747
|
* Signout: чистит локальную session СРАЗУ (UX — мгновенный logout без
|
|
@@ -756,7 +761,7 @@ class mt {
|
|
|
756
761
|
async signOut(t = {}) {
|
|
757
762
|
await this.hydrated;
|
|
758
763
|
const e = this.session?.access_token, s = this.session?.user.is_anonymous === !0;
|
|
759
|
-
if (this.setSession(null), t.forgetAnonymous && await this.clearAnonRefreshToken(), !!e && !(s && !t.forgetAnonymous))
|
|
764
|
+
if (this.setSession(null, { event: "SIGNED_OUT" }), t.forgetAnonymous && await this.clearAnonRefreshToken(), !!e && !(s && !t.forgetAnonymous))
|
|
760
765
|
try {
|
|
761
766
|
await this.api.request(
|
|
762
767
|
`/api/v1/paywall/${this.paywallId}/auth/signout`,
|
|
@@ -770,17 +775,30 @@ class mt {
|
|
|
770
775
|
}
|
|
771
776
|
/**
|
|
772
777
|
* Подписка на изменения session: signin/signup/refresh/signOut/expired-401.
|
|
773
|
-
*
|
|
774
|
-
*
|
|
778
|
+
*
|
|
779
|
+
* Гарантированный контракт: ПЕРВЫЙ callback каждому subscriber'у — всегда
|
|
780
|
+
* `event = 'INITIAL_SESSION'`, дёргается асинхронно после resolve hydrate'а
|
|
781
|
+
* (даже если session=null — listener получает explicit «нет сессии», а не
|
|
782
|
+
* молчание). Все последующие callback'и — реальные переходы с конкретным
|
|
783
|
+
* event'ом (SIGNED_IN / SIGNED_OUT / TOKEN_REFRESHED / USER_UPDATED /
|
|
784
|
+
* PASSWORD_RECOVERY).
|
|
785
|
+
*
|
|
786
|
+
* Это позволяет listener'у безопасно делать «only on real signin» побочные
|
|
787
|
+
* эффекты (force refetch balances и т.п.) через `event === 'SIGNED_IN'`,
|
|
788
|
+
* не путая их с восстановлением из storage.
|
|
789
|
+
*
|
|
790
|
+
* Возвращает unsubscribe.
|
|
775
791
|
*/
|
|
776
792
|
onAuthChange(t) {
|
|
777
|
-
|
|
793
|
+
return this.listeners.add(t), this.hydrated.then(() => {
|
|
794
|
+
if (this.destroyed || !this.listeners.has(t)) return;
|
|
778
795
|
const e = this.session;
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
})
|
|
782
|
-
|
|
783
|
-
|
|
796
|
+
try {
|
|
797
|
+
t("INITIAL_SESSION", e);
|
|
798
|
+
} catch (s) {
|
|
799
|
+
console.warn("[paywall] onAuthChange INITIAL_SESSION threw", s);
|
|
800
|
+
}
|
|
801
|
+
}), () => {
|
|
784
802
|
this.listeners.delete(t);
|
|
785
803
|
};
|
|
786
804
|
}
|
|
@@ -796,21 +814,21 @@ class mt {
|
|
|
796
814
|
user: e
|
|
797
815
|
};
|
|
798
816
|
}
|
|
799
|
-
setSession(t, e
|
|
817
|
+
setSession(t, e) {
|
|
800
818
|
if (this.destroyed) return;
|
|
801
819
|
const s = this.session;
|
|
802
|
-
this.session = t, e.skipPersist || this.persist(),
|
|
820
|
+
this.session = t, e.skipPersist || this.persist(), et(s, t) || this.emit(e.event);
|
|
803
821
|
}
|
|
804
|
-
emit() {
|
|
805
|
-
for (const
|
|
822
|
+
emit(t) {
|
|
823
|
+
for (const e of this.listeners)
|
|
806
824
|
try {
|
|
807
|
-
t
|
|
808
|
-
} catch (
|
|
809
|
-
console.warn("[paywall] onAuthChange listener threw",
|
|
825
|
+
e(t, this.session);
|
|
826
|
+
} catch (s) {
|
|
827
|
+
console.warn("[paywall] onAuthChange listener threw", s);
|
|
810
828
|
}
|
|
811
829
|
}
|
|
812
830
|
storageKey() {
|
|
813
|
-
return
|
|
831
|
+
return u.authSession(this.paywallId);
|
|
814
832
|
}
|
|
815
833
|
async hydrate() {
|
|
816
834
|
try {
|
|
@@ -819,7 +837,7 @@ class mt {
|
|
|
819
837
|
const e = JSON.parse(t);
|
|
820
838
|
if (!e || typeof e.access_token != "string" || typeof e.refresh_token != "string" || typeof e.expires_at != "number" || !e.user)
|
|
821
839
|
return;
|
|
822
|
-
this.session = e
|
|
840
|
+
this.session = e;
|
|
823
841
|
} catch {
|
|
824
842
|
}
|
|
825
843
|
}
|
|
@@ -833,7 +851,7 @@ class mt {
|
|
|
833
851
|
const e = JSON.parse(t);
|
|
834
852
|
if (!e || typeof e.access_token != "string" || typeof e.refresh_token != "string" || typeof e.expires_at != "number" || !e.user)
|
|
835
853
|
return;
|
|
836
|
-
this.setSession(e, { skipPersist: !0 });
|
|
854
|
+
this.setSession(e, { skipPersist: !0, event: "SIGNED_IN" });
|
|
837
855
|
} catch {
|
|
838
856
|
}
|
|
839
857
|
}
|
|
@@ -864,7 +882,7 @@ class mt {
|
|
|
864
882
|
}
|
|
865
883
|
async readAnonRefreshToken() {
|
|
866
884
|
try {
|
|
867
|
-
const t = await this.storage.getItem(
|
|
885
|
+
const t = await this.storage.getItem(u.anonRefreshToken(this.paywallId));
|
|
868
886
|
return typeof t == "string" && t.length > 0 ? t : null;
|
|
869
887
|
} catch {
|
|
870
888
|
return null;
|
|
@@ -873,7 +891,7 @@ class mt {
|
|
|
873
891
|
async writeAnonRefreshToken(t) {
|
|
874
892
|
try {
|
|
875
893
|
await this.storage.setItem(
|
|
876
|
-
|
|
894
|
+
u.anonRefreshToken(this.paywallId),
|
|
877
895
|
t
|
|
878
896
|
);
|
|
879
897
|
} catch {
|
|
@@ -882,11 +900,41 @@ class mt {
|
|
|
882
900
|
async clearAnonRefreshToken() {
|
|
883
901
|
try {
|
|
884
902
|
await this.storage.removeItem(
|
|
885
|
-
|
|
903
|
+
u.anonRefreshToken(this.paywallId)
|
|
886
904
|
);
|
|
887
905
|
} catch {
|
|
888
906
|
}
|
|
889
907
|
}
|
|
908
|
+
/**
|
|
909
|
+
* Last-used auth method + email — для UI бейджа "Last used" и pre-fill'а
|
|
910
|
+
* email-инпута. Storage paywall-scoped, поэтому переключение между
|
|
911
|
+
* пейволами на одном host'е не пересекает данные. Чтение всегда возвращает
|
|
912
|
+
* объект — отсутствующие поля = null. */
|
|
913
|
+
async getLastLogin() {
|
|
914
|
+
try {
|
|
915
|
+
const [t, e] = await Promise.all([
|
|
916
|
+
this.storage.getItem(u.lastLoginMethod(this.paywallId)),
|
|
917
|
+
this.storage.getItem(u.lastLoginEmail(this.paywallId))
|
|
918
|
+
]);
|
|
919
|
+
return !t || !tt(t) ? null : { method: t, email: typeof e == "string" && e ? e : null };
|
|
920
|
+
} catch {
|
|
921
|
+
return null;
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
/** Запись method и email атомарно (для email/password flows — оба известны
|
|
925
|
+
* на момент signin/signup'а). OAuth-flows используют раздельные
|
|
926
|
+
* recordLastLoginMethod (до popup) и recordLastLoginEmail (после exchange). */
|
|
927
|
+
recordLastLogin(t, e) {
|
|
928
|
+
this.recordLastLoginMethod(t), e && this.recordLastLoginEmail(e);
|
|
929
|
+
}
|
|
930
|
+
recordLastLoginMethod(t) {
|
|
931
|
+
this.storage.setItem(u.lastLoginMethod(this.paywallId), t).catch(() => {
|
|
932
|
+
});
|
|
933
|
+
}
|
|
934
|
+
recordLastLoginEmail(t) {
|
|
935
|
+
this.storage.setItem(u.lastLoginEmail(this.paywallId), t).catch(() => {
|
|
936
|
+
});
|
|
937
|
+
}
|
|
890
938
|
/**
|
|
891
939
|
* Читает stable visitor_id из storage если он там уже есть. НЕ генерит:
|
|
892
940
|
* AuthClient может быть инстанцирован раньше BillingClient, а синтетический
|
|
@@ -896,22 +944,22 @@ class mt {
|
|
|
896
944
|
*/
|
|
897
945
|
async readVisitorId() {
|
|
898
946
|
try {
|
|
899
|
-
const t = await this.storage.getItem(
|
|
947
|
+
const t = await this.storage.getItem(u.visitorId);
|
|
900
948
|
return typeof t == "string" && t.length >= 16 ? t : void 0;
|
|
901
949
|
} catch {
|
|
902
950
|
return;
|
|
903
951
|
}
|
|
904
952
|
}
|
|
905
953
|
}
|
|
906
|
-
const
|
|
907
|
-
function
|
|
954
|
+
const W = 5 * 6e4, Y = 500;
|
|
955
|
+
function Z(i, t) {
|
|
908
956
|
return new Promise((e, s) => {
|
|
909
957
|
let a = !1;
|
|
910
958
|
const n = () => {
|
|
911
|
-
a = !0, window.removeEventListener("message", r), clearInterval(
|
|
912
|
-
}, r = (
|
|
959
|
+
a = !0, window.removeEventListener("message", r), clearInterval(h), clearTimeout(d);
|
|
960
|
+
}, r = (f) => {
|
|
913
961
|
if (a) return;
|
|
914
|
-
const c =
|
|
962
|
+
const c = f.data;
|
|
915
963
|
if (!(!c || c.type !== "pw-oauth") && c.messageId === t) {
|
|
916
964
|
if (c.status === "success" && c.code) {
|
|
917
965
|
n();
|
|
@@ -934,16 +982,16 @@ function Y(i, t) {
|
|
|
934
982
|
);
|
|
935
983
|
}
|
|
936
984
|
}
|
|
937
|
-
},
|
|
985
|
+
}, h = setInterval(() => {
|
|
938
986
|
if (a) return;
|
|
939
|
-
let
|
|
987
|
+
let f;
|
|
940
988
|
try {
|
|
941
|
-
|
|
989
|
+
f = i.closed;
|
|
942
990
|
} catch {
|
|
943
991
|
return;
|
|
944
992
|
}
|
|
945
|
-
|
|
946
|
-
},
|
|
993
|
+
f && (n(), s(new o("oauth_cancelled", "auth popup was closed")));
|
|
994
|
+
}, Y), d = setTimeout(() => {
|
|
947
995
|
if (!a) {
|
|
948
996
|
n();
|
|
949
997
|
try {
|
|
@@ -952,19 +1000,26 @@ function Y(i, t) {
|
|
|
952
1000
|
}
|
|
953
1001
|
s(new o("oauth_timeout", "OAuth flow timed out"));
|
|
954
1002
|
}
|
|
955
|
-
},
|
|
1003
|
+
}, W);
|
|
956
1004
|
window.addEventListener("message", r);
|
|
957
1005
|
});
|
|
958
1006
|
}
|
|
959
|
-
function
|
|
1007
|
+
function tt(i) {
|
|
1008
|
+
return i === "google" || i === "apple" || i === "github" || i === "facebook" || i === "email";
|
|
1009
|
+
}
|
|
1010
|
+
function et(i, t) {
|
|
960
1011
|
return i === t ? !0 : !i || !t ? !1 : i.access_token === t.access_token && i.refresh_token === t.refresh_token && i.expires_at === t.expires_at && i.user.id === t.user.id && i.user.email === t.user.email;
|
|
961
1012
|
}
|
|
962
|
-
|
|
963
|
-
class et {
|
|
1013
|
+
class st {
|
|
964
1014
|
constructor(t) {
|
|
965
1015
|
if (!t.paywallId)
|
|
966
1016
|
throw new o("invalid_config", "paywallId is required");
|
|
967
|
-
|
|
1017
|
+
if (!t.apiOrigin)
|
|
1018
|
+
throw new o(
|
|
1019
|
+
"invalid_config",
|
|
1020
|
+
"apiOrigin is required. Pass the paywall custom_domain configured in the platform."
|
|
1021
|
+
);
|
|
1022
|
+
this.paywallId = t.paywallId, this.apiOrigin = t.apiOrigin, this.auth = t.auth, this.userId = t.userId, this.capabilities = t.capabilities, this.customFetch = t.fetch, this.onChargeSuccess = t.onChargeSuccess, this.onQuotaExceeded = t.onQuotaExceeded, t.userId && !t.auth && typeof window < "u" && typeof window.document < "u" && console.warn(
|
|
968
1023
|
"[paywall] WARNING: ApiGatewayClient.userId set without auth in browser. Client can spoof userId. Use AuthClient + Bearer for trusted user.id."
|
|
969
1024
|
);
|
|
970
1025
|
}
|
|
@@ -975,16 +1030,16 @@ class et {
|
|
|
975
1030
|
);
|
|
976
1031
|
s.searchParams.set("paywall_id", this.paywallId);
|
|
977
1032
|
const a = new Headers(t.headers);
|
|
978
|
-
a.set("X-SDK-Version",
|
|
1033
|
+
a.set("X-SDK-Version", m), a.set("X-Paywall-Id", this.paywallId), this.capabilities?.length && a.set("X-SDK-Capabilities", this.capabilities.join(","));
|
|
979
1034
|
const n = await this.auth?.getAccessToken();
|
|
980
1035
|
n ? a.set("Authorization", `Bearer ${n}`) : this.userId && a.set("X-User-ID", this.userId);
|
|
981
|
-
const r = typeof FormData < "u" && t.body instanceof FormData,
|
|
1036
|
+
const r = typeof FormData < "u" && t.body instanceof FormData, h = typeof Blob < "u" && t.body instanceof Blob, d = typeof ReadableStream < "u" && t.body instanceof ReadableStream, f = typeof t.body == "string";
|
|
982
1037
|
let c;
|
|
983
|
-
t.body === void 0 || t.body === null ? c = void 0 : r ||
|
|
984
|
-
const
|
|
985
|
-
let
|
|
1038
|
+
t.body === void 0 || t.body === null ? c = void 0 : r || h || d || f ? c = t.body : (c = JSON.stringify(t.body), a.has("Content-Type") || a.set("Content-Type", "application/json"));
|
|
1039
|
+
const y = this.customFetch ?? fetch;
|
|
1040
|
+
let l;
|
|
986
1041
|
try {
|
|
987
|
-
|
|
1042
|
+
l = await y(s.toString(), {
|
|
988
1043
|
method: t.method ?? "POST",
|
|
989
1044
|
headers: a,
|
|
990
1045
|
body: c,
|
|
@@ -992,26 +1047,26 @@ class et {
|
|
|
992
1047
|
credentials: "omit"
|
|
993
1048
|
});
|
|
994
1049
|
} catch (p) {
|
|
995
|
-
const
|
|
996
|
-
throw new o("network_error", `Network request failed: ${
|
|
1050
|
+
const $ = p instanceof Error ? p.message : String(p);
|
|
1051
|
+
throw new o("network_error", `Network request failed: ${$}`, { cause: p });
|
|
997
1052
|
}
|
|
998
|
-
if (
|
|
999
|
-
const p = await
|
|
1053
|
+
if (l.status === 402) {
|
|
1054
|
+
const p = await it(l);
|
|
1000
1055
|
throw this.onQuotaExceeded?.(p), p;
|
|
1001
1056
|
}
|
|
1002
|
-
if (!
|
|
1003
|
-
const p = await
|
|
1057
|
+
if (!l.ok) {
|
|
1058
|
+
const p = await at(l.clone());
|
|
1004
1059
|
throw new o(
|
|
1005
|
-
p ?? `http_${
|
|
1006
|
-
|
|
1007
|
-
{ status:
|
|
1060
|
+
p ?? `http_${l.status}`,
|
|
1061
|
+
l.statusText || "Gateway request failed",
|
|
1062
|
+
{ status: l.status }
|
|
1008
1063
|
);
|
|
1009
1064
|
}
|
|
1010
|
-
const g =
|
|
1011
|
-
return this.onChargeSuccess?.(g),
|
|
1065
|
+
const g = l.headers.get("X-Query-Type") ?? void 0;
|
|
1066
|
+
return this.onChargeSuccess?.(g), l;
|
|
1012
1067
|
}
|
|
1013
1068
|
}
|
|
1014
|
-
async function
|
|
1069
|
+
async function it(i) {
|
|
1015
1070
|
let t = {};
|
|
1016
1071
|
try {
|
|
1017
1072
|
t = await i.json();
|
|
@@ -1023,13 +1078,13 @@ async function st(i) {
|
|
|
1023
1078
|
const a = e[0];
|
|
1024
1079
|
Array.isArray(a) ? s = a : a && Array.isArray(a.balances) && (s = a.balances);
|
|
1025
1080
|
}
|
|
1026
|
-
return new
|
|
1081
|
+
return new F({
|
|
1027
1082
|
balances: s,
|
|
1028
1083
|
queryType: t.details?.queryType ?? "",
|
|
1029
1084
|
currentBalance: t.details?.currentBalance ?? null
|
|
1030
1085
|
});
|
|
1031
1086
|
}
|
|
1032
|
-
async function
|
|
1087
|
+
async function at(i) {
|
|
1033
1088
|
if (!(i.headers.get("content-type") ?? "").includes("application/json")) return null;
|
|
1034
1089
|
try {
|
|
1035
1090
|
const e = await i.json();
|
|
@@ -1038,35 +1093,40 @@ async function it(i) {
|
|
|
1038
1093
|
return null;
|
|
1039
1094
|
}
|
|
1040
1095
|
}
|
|
1041
|
-
const
|
|
1096
|
+
const nt = 5e3, rt = 30 * 6e4, k = 60 * 6e4, ot = 5 * 6e4, S = {
|
|
1042
1097
|
has_active_subscription: !1,
|
|
1043
1098
|
purchases: [],
|
|
1044
|
-
trial: null
|
|
1099
|
+
trial: null,
|
|
1100
|
+
had_previous_trial: !1
|
|
1045
1101
|
};
|
|
1046
1102
|
function A(i) {
|
|
1047
1103
|
return i && (i.email || i.userId || i.anonymousId) || "guest";
|
|
1048
1104
|
}
|
|
1049
|
-
function
|
|
1105
|
+
function ct(i, t) {
|
|
1050
1106
|
return i === t ? !0 : !i || !t ? !1 : JSON.stringify(i) === JSON.stringify(t);
|
|
1051
1107
|
}
|
|
1052
|
-
const
|
|
1053
|
-
function
|
|
1108
|
+
const ht = 5e3, T = 5 * 6e4, lt = 3e4;
|
|
1109
|
+
function ut(i, t) {
|
|
1054
1110
|
if (i === t) return !0;
|
|
1055
1111
|
if (!i || !t || i.length !== t.length) return !1;
|
|
1056
1112
|
for (let e = 0; e < i.length; e++)
|
|
1057
1113
|
if (i[e].type !== t[e].type || i[e].count !== t[e].count) return !1;
|
|
1058
1114
|
return !0;
|
|
1059
1115
|
}
|
|
1060
|
-
|
|
1061
|
-
class bt {
|
|
1116
|
+
class It {
|
|
1062
1117
|
constructor(t) {
|
|
1063
|
-
if (this.cachedBootstrap = null, this.cachedBootstrapAt = 0, this.inflightBootstrap = null, this.bootstrapListeners = /* @__PURE__ */ new Set(), this.bootstrapStorageUnwatch = null, this.authUnsubscribe = null, this.cachedUser = null, this.cachedUserAt = 0, this.inflightUser = null, this.userListeners = /* @__PURE__ */ new Set(), this.visitorIdPromise = null, this.visitorId = null, this.inflightCheckouts = /* @__PURE__ */ new Map(), this.cachedBalances = null, this.cachedBalancesAt = 0, this.balancesStorageUnwatch = null, this.inflightBalances = null, this.balanceListeners = /* @__PURE__ */ new Set(), !t.paywallId)
|
|
1118
|
+
if (this.cachedBootstrap = null, this.cachedBootstrapAt = 0, this.inflightBootstrap = null, this.bootstrapListeners = /* @__PURE__ */ new Set(), this.bootstrapStorageUnwatch = null, this.authUnsubscribe = null, this.cachedUser = null, this.cachedUserAt = 0, this.inflightUser = null, this.userListeners = /* @__PURE__ */ new Set(), this.visitorIdPromise = null, this.visitorId = null, this.inflightCheckouts = /* @__PURE__ */ new Map(), this.cachedBalances = null, this.cachedBalancesAt = 0, this.balancesStorageUnwatch = null, this.inflightBalances = null, this.balanceListeners = /* @__PURE__ */ new Set(), this.previewVersionCounter = 0, !t.paywallId)
|
|
1064
1119
|
throw new o("invalid_config", "paywallId is required");
|
|
1065
|
-
|
|
1120
|
+
if (!t.apiOrigin)
|
|
1121
|
+
throw new o(
|
|
1122
|
+
"invalid_config",
|
|
1123
|
+
'apiOrigin is required. Pass the paywall custom_domain configured in the platform (e.g. "https://pay.your-domain.com"). The legacy "appbox.space" fallback is not used in SDK 3.0.'
|
|
1124
|
+
);
|
|
1125
|
+
this.paywallId = t.paywallId, this.apiOrigin = t.apiOrigin, this.capabilities = t.capabilities, this.auth = t.auth, this.previewMode = t.preview === !0;
|
|
1066
1126
|
const e = t.auth?.getCachedUser();
|
|
1067
|
-
this.identity = t.identity ?? (e ?
|
|
1127
|
+
this.identity = t.identity ?? (e ? O(e) : void 0), this.apiKey = t.apiKey, this.fetchImpl = t.fetch, t.apiKey && typeof window < "u" && typeof window.document < "u" && console.error(
|
|
1068
1128
|
"[paywall] SECURITY: BillingClient.apiKey detected in browser context. This is a server-SDK key and exposes your account. Remove apiKey or move BillingClient to a trusted backend."
|
|
1069
|
-
), this.storage = P(t.storage), this.api = new
|
|
1129
|
+
), this.storage = P(t.storage), this.api = new K({
|
|
1070
1130
|
apiOrigin: this.apiOrigin,
|
|
1071
1131
|
paywallId: t.paywallId,
|
|
1072
1132
|
capabilities: t.capabilities,
|
|
@@ -1075,10 +1135,10 @@ class bt {
|
|
|
1075
1135
|
// делает lazy refresh, дедупит, на 401 возвращает null — тогда
|
|
1076
1136
|
// Authorization-хедер просто не выставится.
|
|
1077
1137
|
getAuthToken: t.auth ? () => t.auth.getAccessToken() : void 0
|
|
1078
|
-
}), t.auth && (this.authUnsubscribe = t.auth.onAuthChange((s) => {
|
|
1079
|
-
const
|
|
1080
|
-
dt(this.identity,
|
|
1081
|
-
})), this.hydrateUserFromStorage(), this.hydrateBootstrapFromStorage(), this.subscribeBootstrapStorage(), this.hydrateBalancesFromStorage(), this.subscribeBalancesStorage(), this.visitorIdPromise =
|
|
1138
|
+
}), t.auth && (this.authUnsubscribe = t.auth.onAuthChange((s, a) => {
|
|
1139
|
+
const n = a ? O(a.user) : void 0;
|
|
1140
|
+
dt(this.identity, n) || this.setIdentity(n);
|
|
1141
|
+
})), this.hydrateUserFromStorage(), this.hydrateBootstrapFromStorage(), this.subscribeBootstrapStorage(), this.hydrateBalancesFromStorage(), this.subscribeBalancesStorage(), this.visitorIdPromise = B(this.storage).then((s) => (this.visitorId = s, s));
|
|
1082
1142
|
}
|
|
1083
1143
|
/**
|
|
1084
1144
|
* Stable visitor_id (UUID v4). Первый вызов awaitит первичный резолв из
|
|
@@ -1086,15 +1146,15 @@ class bt {
|
|
|
1086
1146
|
* EventTracker'ом для атрибуции аналитики.
|
|
1087
1147
|
*/
|
|
1088
1148
|
async getVisitorId() {
|
|
1089
|
-
return this.visitorId ? this.visitorId : (this.visitorIdPromise || (this.visitorIdPromise =
|
|
1149
|
+
return this.visitorId ? this.visitorId : (this.visitorIdPromise || (this.visitorIdPromise = B(this.storage).then((t) => (this.visitorId = t, t))), this.visitorIdPromise);
|
|
1090
1150
|
}
|
|
1091
1151
|
/** Sync-доступ к visitor_id. null если ещё не зарезолвили (первые ms жизни). */
|
|
1092
1152
|
getCachedVisitorId() {
|
|
1093
1153
|
return this.visitorId;
|
|
1094
1154
|
}
|
|
1095
1155
|
setIdentity(t) {
|
|
1096
|
-
this.identity = t, this.cachedUser = null, this.cachedUserAt = 0, this.inflightUser = null, this.cachedBalances = null, this.cachedBalancesAt = 0, this.inflightBalances = null, this.balancesStorageUnwatch && (this.balancesStorageUnwatch(), this.balancesStorageUnwatch = null), this.hydrateBalancesFromStorage(), this.subscribeBalancesStorage(), this.hydrateUserFromStorage(), t
|
|
1097
|
-
});
|
|
1156
|
+
this.identity = t, this.cachedUser = null, this.cachedUserAt = 0, this.inflightUser = null, this.cachedBalances = null, this.cachedBalancesAt = 0, this.inflightBalances = null, this.balancesStorageUnwatch && (this.balancesStorageUnwatch(), this.balancesStorageUnwatch = null), this.hydrateBalancesFromStorage(), this.subscribeBalancesStorage(), this.hydrateUserFromStorage(), t ? this.getUser({ force: !0 }).catch(() => {
|
|
1157
|
+
}) : (this.applyUser(S), this.applyBalances([]));
|
|
1098
1158
|
}
|
|
1099
1159
|
/**
|
|
1100
1160
|
* Отписаться от auth-event'ов и сбросить listener'ы. Вызывать когда
|
|
@@ -1114,9 +1174,17 @@ class bt {
|
|
|
1114
1174
|
return this.storage;
|
|
1115
1175
|
}
|
|
1116
1176
|
async bootstrap(t = !1) {
|
|
1117
|
-
const e = typeof t == "boolean" ? { force: t } : t
|
|
1118
|
-
|
|
1119
|
-
|
|
1177
|
+
const e = typeof t == "boolean" ? { force: t } : t;
|
|
1178
|
+
if (this.previewMode) {
|
|
1179
|
+
if (this.cachedBootstrap) return this.cachedBootstrap;
|
|
1180
|
+
throw new o(
|
|
1181
|
+
"invalid_config",
|
|
1182
|
+
"BillingClient in preview mode but cachedBootstrap is not seeded. Call setBootstrap(bootstrap) before open()."
|
|
1183
|
+
);
|
|
1184
|
+
}
|
|
1185
|
+
const s = Date.now(), a = this.cachedBootstrap && this.cachedBootstrapAt > 0 && s - this.cachedBootstrapAt < k;
|
|
1186
|
+
return !e.force && a ? (s - this.cachedBootstrapAt > ot && this.revalidateBootstrap(e.signal).catch(() => {
|
|
1187
|
+
}), { ...this.cachedBootstrap, user: this.cachedUser ?? void 0 }) : this.inflightBootstrap ? this.inflightBootstrap : (this.inflightBootstrap = this.fetchBootstrap({
|
|
1120
1188
|
ifVersion: e.force ? void 0 : this.cachedBootstrap?.version,
|
|
1121
1189
|
signal: e.signal
|
|
1122
1190
|
}).finally(() => {
|
|
@@ -1134,6 +1202,44 @@ class bt {
|
|
|
1134
1202
|
this.bootstrapListeners.delete(t);
|
|
1135
1203
|
};
|
|
1136
1204
|
}
|
|
1205
|
+
/**
|
|
1206
|
+
* Заменить cachedBootstrap частичными или полными данными и эмитнуть всем
|
|
1207
|
+
* подписчикам. Используется host'ом в preview-mode (редактор админки) для
|
|
1208
|
+
* live-обновления открытой модалки без сетевого revalidate'а.
|
|
1209
|
+
*
|
|
1210
|
+
* Поведение:
|
|
1211
|
+
* - Без `cachedBootstrap` ожидаются как минимум `settings` + `prices` —
|
|
1212
|
+
* иначе PaywallRoot не сможет отрендерить тарифы и упадёт.
|
|
1213
|
+
* - С существующим кешем партиал мёрджится поверх: `settings` глубокий мёрдж
|
|
1214
|
+
* на 1 уровень (поля настроек), массивы `prices`/`offers` перезаписываются.
|
|
1215
|
+
* - Каждый вызов бампит `version` ("preview:<n>"), чтобы applyBootstrap'овая
|
|
1216
|
+
* проверка `versionChanged` всегда срабатывала и listener'ы дёргались.
|
|
1217
|
+
* - Persist в storage НЕ делаем — preview не должен утекать в другие вкладки.
|
|
1218
|
+
*
|
|
1219
|
+
* В non-preview режиме метод доступен, но это редкий путь (например, для
|
|
1220
|
+
* тестов host'а) — production-код должен полагаться на bootstrap() + revalidate.
|
|
1221
|
+
*/
|
|
1222
|
+
setBootstrap(t) {
|
|
1223
|
+
const e = this.cachedBootstrap ?? {
|
|
1224
|
+
settings: { id: this.paywallId, name: "" },
|
|
1225
|
+
prices: [],
|
|
1226
|
+
offers: []
|
|
1227
|
+
}, s = {
|
|
1228
|
+
...e,
|
|
1229
|
+
...t,
|
|
1230
|
+
settings: t.settings !== void 0 ? { ...e.settings, ...t.settings } : e.settings,
|
|
1231
|
+
prices: t.prices !== void 0 ? t.prices : e.prices,
|
|
1232
|
+
offers: t.offers !== void 0 ? t.offers : e.offers,
|
|
1233
|
+
version: `preview:${++this.previewVersionCounter}`
|
|
1234
|
+
};
|
|
1235
|
+
s.layout || (s.layout = E(s.settings, s.prices)), w(s), this.cachedBootstrap = s, this.cachedBootstrapAt = Date.now();
|
|
1236
|
+
for (const a of this.bootstrapListeners)
|
|
1237
|
+
try {
|
|
1238
|
+
a(s);
|
|
1239
|
+
} catch (n) {
|
|
1240
|
+
console.warn("[paywall] onBootstrapChange listener threw", n);
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1137
1243
|
// Network primitive — единая точка для force-запроса, revalidate'а и
|
|
1138
1244
|
// первого холодного bootstrap'а. `ifVersion` шлёт server-side short-circuit:
|
|
1139
1245
|
// если совпала — бэк отвечает `{unchanged: true, version, user}` и мы лишь
|
|
@@ -1148,7 +1254,7 @@ class bt {
|
|
|
1148
1254
|
if ("unchanged" in a && a.unchanged)
|
|
1149
1255
|
return this.cachedBootstrap ? (this.cachedBootstrapAt = Date.now(), a.user && this.applyUser(a.user), this.cachedBootstrap) : this.fetchBootstrap({ signal: t.signal });
|
|
1150
1256
|
const n = a;
|
|
1151
|
-
return n.layout || (n.layout =
|
|
1257
|
+
return ft(n.settings.custom_domain, this.apiOrigin), n.layout || (n.layout = E(n.settings, n.prices)), w(n), this.applyBootstrap(n, { persist: !0 }), n.user && this.applyUser(n.user), n;
|
|
1152
1258
|
}
|
|
1153
1259
|
// Фоновый revalidate из stale-while-revalidate ветки. Дедуплицируется через
|
|
1154
1260
|
// `inflightBootstrap`, чтобы параллельные revalidate'ы не пересекались.
|
|
@@ -1178,11 +1284,11 @@ class bt {
|
|
|
1178
1284
|
async hydrateBootstrapFromStorage() {
|
|
1179
1285
|
if (!this.cachedBootstrap)
|
|
1180
1286
|
try {
|
|
1181
|
-
const t = await this.storage.getItem(
|
|
1287
|
+
const t = await this.storage.getItem(u.bootstrap(this.paywallId));
|
|
1182
1288
|
if (!t) return;
|
|
1183
1289
|
const e = JSON.parse(t);
|
|
1184
|
-
if (!e?.bootstrap || Date.now() - e.at >
|
|
1185
|
-
|
|
1290
|
+
if (!e?.bootstrap || Date.now() - e.at > k || this.cachedBootstrap) return;
|
|
1291
|
+
w(e.bootstrap), this.cachedBootstrap = e.bootstrap, this.cachedBootstrapAt = e.at;
|
|
1186
1292
|
for (const s of this.bootstrapListeners)
|
|
1187
1293
|
try {
|
|
1188
1294
|
s(e.bootstrap);
|
|
@@ -1197,7 +1303,7 @@ class bt {
|
|
|
1197
1303
|
try {
|
|
1198
1304
|
const { user: e, ...s } = t;
|
|
1199
1305
|
await this.storage.setItem(
|
|
1200
|
-
|
|
1306
|
+
u.bootstrap(this.paywallId),
|
|
1201
1307
|
JSON.stringify({ at: Date.now(), bootstrap: s })
|
|
1202
1308
|
);
|
|
1203
1309
|
} catch {
|
|
@@ -1208,7 +1314,7 @@ class bt {
|
|
|
1208
1314
|
// no-op, всё работает как раньше через сеть.
|
|
1209
1315
|
subscribeBootstrapStorage() {
|
|
1210
1316
|
typeof this.storage.watch == "function" && (this.bootstrapStorageUnwatch = this.storage.watch(
|
|
1211
|
-
|
|
1317
|
+
u.bootstrap(this.paywallId),
|
|
1212
1318
|
(t) => {
|
|
1213
1319
|
if (t)
|
|
1214
1320
|
try {
|
|
@@ -1218,7 +1324,7 @@ class bt {
|
|
|
1218
1324
|
this.cachedBootstrapAt = e.at;
|
|
1219
1325
|
return;
|
|
1220
1326
|
}
|
|
1221
|
-
|
|
1327
|
+
w(e.bootstrap), this.applyBootstrap(e.bootstrap, { persist: !1 });
|
|
1222
1328
|
} catch {
|
|
1223
1329
|
}
|
|
1224
1330
|
}
|
|
@@ -1246,6 +1352,15 @@ class bt {
|
|
|
1246
1352
|
getCachedPrices() {
|
|
1247
1353
|
return this.cachedBootstrap?.prices ?? null;
|
|
1248
1354
|
}
|
|
1355
|
+
/** Sync-снимок офферов из последнего bootstrap'а. null = bootstrap ещё не
|
|
1356
|
+
* загружали, пустой массив = бэк отдал пейвол без офферов. Бэк уже
|
|
1357
|
+
* применил серверный таргетинг (target_countries / target_emails /
|
|
1358
|
+
* targeting_mode из offer_settings) — наружу выезжает только то, что
|
|
1359
|
+
* применимо к текущему юзеру. Клиентская сторона остаётся ответственной
|
|
1360
|
+
* за price_id-matching и countdown (см. core/offer.ts → resolveOffer). */
|
|
1361
|
+
getCachedOffers() {
|
|
1362
|
+
return this.cachedBootstrap?.offers ?? null;
|
|
1363
|
+
}
|
|
1249
1364
|
/**
|
|
1250
1365
|
* Снимок того, какой язык SDK сейчас считает «языком юзера». Полезно для
|
|
1251
1366
|
* синхронизации i18n хоста с тем, что фактически показывает пейвол — чтобы
|
|
@@ -1277,10 +1392,10 @@ class bt {
|
|
|
1277
1392
|
* - Без identity возвращает empty-state (сервер тоже так делает).
|
|
1278
1393
|
*/
|
|
1279
1394
|
async getUser({ force: t = !1, signal: e } = {}) {
|
|
1280
|
-
return !t && this.cachedUser && Date.now() - this.cachedUserAt <
|
|
1395
|
+
return !t && this.cachedUser && Date.now() - this.cachedUserAt < nt ? this.cachedUser : this.inflightUser ? this.inflightUser : (this.inflightUser = (async () => {
|
|
1281
1396
|
try {
|
|
1282
1397
|
if (!this.identity?.email)
|
|
1283
|
-
return this.applyUser(
|
|
1398
|
+
return this.applyUser(S), S;
|
|
1284
1399
|
const s = await this.api.request(
|
|
1285
1400
|
`/api/v1/paywall/${this.paywallId}/user-state`,
|
|
1286
1401
|
{ headers: { "X-User-Email": this.identity.email }, signal: e }
|
|
@@ -1333,7 +1448,7 @@ class bt {
|
|
|
1333
1448
|
return this.cachedUser;
|
|
1334
1449
|
}
|
|
1335
1450
|
applyUser(t) {
|
|
1336
|
-
const e = !
|
|
1451
|
+
const e = !ct(this.cachedUser, t);
|
|
1337
1452
|
if (this.cachedUser = t, this.cachedUserAt = Date.now(), e) {
|
|
1338
1453
|
this.persistUser(t);
|
|
1339
1454
|
for (const s of this.userListeners)
|
|
@@ -1345,7 +1460,7 @@ class bt {
|
|
|
1345
1460
|
}
|
|
1346
1461
|
}
|
|
1347
1462
|
storageKey() {
|
|
1348
|
-
return
|
|
1463
|
+
return u.userState(this.paywallId, A(this.identity));
|
|
1349
1464
|
}
|
|
1350
1465
|
async hydrateUserFromStorage() {
|
|
1351
1466
|
if (!this.cachedUser)
|
|
@@ -1353,7 +1468,7 @@ class bt {
|
|
|
1353
1468
|
const t = await this.storage.getItem(this.storageKey());
|
|
1354
1469
|
if (!t) return;
|
|
1355
1470
|
const e = JSON.parse(t);
|
|
1356
|
-
if (!e?.user || Date.now() - e.at >
|
|
1471
|
+
if (!e?.user || Date.now() - e.at > rt || this.cachedUser) return;
|
|
1357
1472
|
this.applyUser(e.user);
|
|
1358
1473
|
} catch {
|
|
1359
1474
|
}
|
|
@@ -1382,7 +1497,7 @@ class bt {
|
|
|
1382
1497
|
*/
|
|
1383
1498
|
async getBalances({ force: t = !1, signal: e } = {}) {
|
|
1384
1499
|
const s = Date.now(), a = this.cachedBalances ? s - this.cachedBalancesAt : 1 / 0;
|
|
1385
|
-
return !t && this.cachedBalances && (a <
|
|
1500
|
+
return !t && this.cachedBalances && (a < ht || a < lt) ? this.cachedBalances : !t && this.cachedBalances && a < T ? (this.fetchBalances({ signal: e }).catch(() => {
|
|
1386
1501
|
}), this.cachedBalances) : this.inflightBalances ? this.inflightBalances : this.fetchBalances({ signal: e });
|
|
1387
1502
|
}
|
|
1388
1503
|
// Network primitive — единая точка для force/stale-revalidate/cold-start.
|
|
@@ -1473,7 +1588,7 @@ class bt {
|
|
|
1473
1588
|
*/
|
|
1474
1589
|
createApiGatewayClient(t = {}) {
|
|
1475
1590
|
const e = t.onChargeSuccess, s = t.onQuotaExceeded;
|
|
1476
|
-
return new
|
|
1591
|
+
return new st({
|
|
1477
1592
|
paywallId: this.paywallId,
|
|
1478
1593
|
apiOrigin: this.apiOrigin,
|
|
1479
1594
|
auth: this.auth,
|
|
@@ -1490,7 +1605,7 @@ class bt {
|
|
|
1490
1605
|
});
|
|
1491
1606
|
}
|
|
1492
1607
|
applyBalances(t, { persist: e = !0 } = {}) {
|
|
1493
|
-
const s = !
|
|
1608
|
+
const s = !ut(this.cachedBalances, t);
|
|
1494
1609
|
if (this.cachedBalances = t, this.cachedBalancesAt = Date.now(), e && this.persistBalances(t), s)
|
|
1495
1610
|
for (const a of this.balanceListeners)
|
|
1496
1611
|
try {
|
|
@@ -1500,7 +1615,7 @@ class bt {
|
|
|
1500
1615
|
}
|
|
1501
1616
|
}
|
|
1502
1617
|
balancesStorageKey() {
|
|
1503
|
-
return
|
|
1618
|
+
return u.balances(this.paywallId, A(this.identity));
|
|
1504
1619
|
}
|
|
1505
1620
|
async hydrateBalancesFromStorage() {
|
|
1506
1621
|
if (!this.cachedBalances)
|
|
@@ -1557,16 +1672,16 @@ class bt {
|
|
|
1557
1672
|
"Idempotency-Key": t.idempotencyKey ?? R()
|
|
1558
1673
|
};
|
|
1559
1674
|
this.apiKey && (n["X-Api-Key"] = this.apiKey);
|
|
1560
|
-
const r = this.cachedBootstrap?.settings,
|
|
1675
|
+
const r = this.cachedBootstrap?.settings, h = t.successUrl ?? r?.success_redirect_url ?? void 0, d = t.shopUrl ?? r?.checkout_shop_url ?? void 0, f = this.api.request(`/api/v1/paywall/${this.paywallId}/start-checkout`, {
|
|
1561
1676
|
method: "POST",
|
|
1562
1677
|
headers: n,
|
|
1563
1678
|
signal: t.signal,
|
|
1564
1679
|
body: JSON.stringify({
|
|
1565
1680
|
email: this.identity.email,
|
|
1566
1681
|
priceId: Number(t.priceId),
|
|
1567
|
-
successUrl:
|
|
1682
|
+
successUrl: h,
|
|
1568
1683
|
errorUrl: t.errorUrl,
|
|
1569
|
-
shopUrl:
|
|
1684
|
+
shopUrl: d,
|
|
1570
1685
|
productName: r?.checkout_product_name ?? void 0,
|
|
1571
1686
|
trial_days: t.trialDays,
|
|
1572
1687
|
ignoreActivePurchase: t.ignoreActivePurchase ? !0 : void 0,
|
|
@@ -1579,10 +1694,10 @@ class bt {
|
|
|
1579
1694
|
{ status: 409, cause: c.cause }
|
|
1580
1695
|
) : c;
|
|
1581
1696
|
});
|
|
1582
|
-
return this.inflightCheckouts.set(e,
|
|
1583
|
-
this.inflightCheckouts.get(e) ===
|
|
1697
|
+
return this.inflightCheckouts.set(e, f), f.finally(() => {
|
|
1698
|
+
this.inflightCheckouts.get(e) === f && this.inflightCheckouts.delete(e);
|
|
1584
1699
|
}).catch(() => {
|
|
1585
|
-
}),
|
|
1700
|
+
}), f;
|
|
1586
1701
|
}
|
|
1587
1702
|
/**
|
|
1588
1703
|
* URL Stripe/Paddle/Chargebee customer portal — место, где залогиненный
|
|
@@ -1629,43 +1744,65 @@ class bt {
|
|
|
1629
1744
|
* `/api/v1/paywall/[id]/user` без unstable_cache, потому что list для UI
|
|
1630
1745
|
* должен быть свежим после cancel-а.
|
|
1631
1746
|
*
|
|
1632
|
-
* Auth
|
|
1633
|
-
*
|
|
1747
|
+
* Auth (два пути):
|
|
1748
|
+
* - Bearer (через AuthClient) — user.id резолвится из сессии, identity
|
|
1749
|
+
* в query игнорируется.
|
|
1750
|
+
* - `apiKey` + `identity.email`/`identity.userId` — server-SDK путь для
|
|
1751
|
+
* интеграций со своей авторизацией. Бэк проверяет, что identity линкована
|
|
1752
|
+
* к этому пейволу (защита от cross-paywall lookup).
|
|
1753
|
+
* Без auth и без apiKey+identity — `identity_required`.
|
|
1634
1754
|
*/
|
|
1635
1755
|
async listPurchases(t = {}) {
|
|
1636
|
-
|
|
1756
|
+
const e = !!(this.identity?.email || this.identity?.userId);
|
|
1757
|
+
if (!this.auth && !(this.apiKey && e))
|
|
1637
1758
|
throw new o(
|
|
1638
|
-
"
|
|
1639
|
-
"listPurchases requires AuthClient (Bearer
|
|
1759
|
+
"identity_required",
|
|
1760
|
+
"listPurchases requires AuthClient (Bearer) or apiKey + identity.email/userId"
|
|
1640
1761
|
);
|
|
1641
|
-
|
|
1762
|
+
const s = {};
|
|
1763
|
+
this.apiKey && (s["X-Api-Key"] = this.apiKey);
|
|
1764
|
+
const a = new URLSearchParams();
|
|
1765
|
+
this.apiKey && this.identity?.email && a.set("email", this.identity.email), this.apiKey && this.identity?.userId && a.set("user_id", this.identity.userId);
|
|
1766
|
+
const n = a.toString(), r = n ? `/api/v1/paywall/${this.paywallId}/user?${n}` : `/api/v1/paywall/${this.paywallId}/user`;
|
|
1767
|
+
return (await this.api.request(r, {
|
|
1642
1768
|
method: "GET",
|
|
1769
|
+
headers: s,
|
|
1643
1770
|
signal: t.signal
|
|
1644
1771
|
})).purchases ?? [];
|
|
1645
1772
|
}
|
|
1646
1773
|
/**
|
|
1647
|
-
* Отменить подписку. Бэк
|
|
1648
|
-
*
|
|
1649
|
-
*
|
|
1774
|
+
* Отменить подписку. Бэк проверит, что subscription принадлежит юзеру
|
|
1775
|
+
* (Bearer-путь — из сессии; apiKey-путь — из identity), и сделает cancel у
|
|
1776
|
+
* acquiring'а (Stripe/Paddle/Chargebee/Overpay). По умолчанию cancel в
|
|
1777
|
+
* конце текущего периода — юзер сохраняет access до renewal date'ы.
|
|
1650
1778
|
*
|
|
1651
|
-
* `reason` обязательна (валидация на бэке).
|
|
1652
|
-
* причин в host-UI, как в legacy customer portal'е.
|
|
1779
|
+
* `reason` обязательна (валидация на бэке).
|
|
1653
1780
|
*
|
|
1654
|
-
* Auth
|
|
1781
|
+
* Auth (два пути):
|
|
1782
|
+
* - Bearer (через AuthClient) — стандартный путь для UI customer-portal'a.
|
|
1783
|
+
* - `apiKey` + `identity.email`/`identity.userId` — для self-service UI на
|
|
1784
|
+
* бэке клиента со своей авторизацией. Бэк дополнительно фильтрует
|
|
1785
|
+
* subscription по paywall_id, чтобы owner пейвола A не отменил подписку
|
|
1786
|
+
* пейвола B.
|
|
1655
1787
|
*/
|
|
1656
1788
|
async cancelSubscription(t) {
|
|
1657
|
-
|
|
1789
|
+
const e = !!(this.identity?.email || this.identity?.userId);
|
|
1790
|
+
if (!this.auth && !(this.apiKey && e))
|
|
1658
1791
|
throw new o(
|
|
1659
|
-
"
|
|
1660
|
-
"cancelSubscription requires AuthClient (Bearer
|
|
1792
|
+
"identity_required",
|
|
1793
|
+
"cancelSubscription requires AuthClient (Bearer) or apiKey + identity.email/userId"
|
|
1661
1794
|
);
|
|
1662
|
-
|
|
1795
|
+
const s = {};
|
|
1796
|
+
this.apiKey && (s["X-Api-Key"] = this.apiKey);
|
|
1797
|
+
const a = {
|
|
1798
|
+
subscriptionId: t.subscriptionId,
|
|
1799
|
+
paywallId: this.paywallId,
|
|
1800
|
+
cancellationReason: t.reason
|
|
1801
|
+
};
|
|
1802
|
+
return this.apiKey && this.identity?.email && (a.email = this.identity.email), this.apiKey && this.identity?.userId && (a.userId = this.identity.userId), this.api.request("/api/paywall/cancel-subscription", {
|
|
1663
1803
|
method: "POST",
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
paywallId: this.paywallId,
|
|
1667
|
-
cancellationReason: t.reason
|
|
1668
|
-
}),
|
|
1804
|
+
headers: s,
|
|
1805
|
+
body: JSON.stringify(a),
|
|
1669
1806
|
signal: t.signal
|
|
1670
1807
|
});
|
|
1671
1808
|
}
|
|
@@ -1698,19 +1835,42 @@ class bt {
|
|
|
1698
1835
|
});
|
|
1699
1836
|
}
|
|
1700
1837
|
}
|
|
1701
|
-
function
|
|
1838
|
+
function O(i) {
|
|
1702
1839
|
return { email: i.email, userId: i.id };
|
|
1703
1840
|
}
|
|
1704
1841
|
function dt(i, t) {
|
|
1705
1842
|
return i === t ? !0 : !i || !t ? !1 : i.email === t.email && i.userId === t.userId && i.anonymousId === t.anonymousId;
|
|
1706
1843
|
}
|
|
1844
|
+
function U(i) {
|
|
1845
|
+
if (!i) return null;
|
|
1846
|
+
const t = i.trim();
|
|
1847
|
+
if (!t) return null;
|
|
1848
|
+
try {
|
|
1849
|
+
return new URL(t.includes("://") ? t : `https://${t}`).origin;
|
|
1850
|
+
} catch {
|
|
1851
|
+
return null;
|
|
1852
|
+
}
|
|
1853
|
+
}
|
|
1707
1854
|
function ft(i, t) {
|
|
1855
|
+
const e = U(i);
|
|
1856
|
+
if (!(!e || U(t) === e))
|
|
1857
|
+
throw new o(
|
|
1858
|
+
"invalid_config",
|
|
1859
|
+
`apiOrigin mismatch: SDK initialized with "${t}" but paywall is configured with custom_domain "${i}". Use the custom_domain from the platform paywall settings.`
|
|
1860
|
+
);
|
|
1861
|
+
}
|
|
1862
|
+
function E(i, t) {
|
|
1708
1863
|
return {
|
|
1709
1864
|
type: "modal",
|
|
1710
1865
|
blocks: [
|
|
1866
|
+
// offer_banner НЕ в default layout — PaywallRoot рендерит его как
|
|
1867
|
+
// top-tab над dialog'ом (rounded-top, negative margin), за пределами
|
|
1868
|
+
// scrollable area. Блок остаётся в registry для opt-in inline-вариантa.
|
|
1711
1869
|
{ type: "heading", text: i.name || "Upgrade", level: 1 },
|
|
1712
1870
|
{ type: "price_grid", priceIds: t.map((e) => e.id) },
|
|
1713
|
-
{ type: "cta_button",
|
|
1871
|
+
{ type: "cta_button", action: "checkout" },
|
|
1872
|
+
{ type: "guarantee_badge" },
|
|
1873
|
+
{ type: "current_session" }
|
|
1714
1874
|
]
|
|
1715
1875
|
};
|
|
1716
1876
|
}
|
|
@@ -1729,7 +1889,7 @@ function M(i) {
|
|
|
1729
1889
|
if (a && Object.prototype.hasOwnProperty.call(t, a)) return a;
|
|
1730
1890
|
return null;
|
|
1731
1891
|
}
|
|
1732
|
-
function
|
|
1892
|
+
function w(i) {
|
|
1733
1893
|
const t = M(i);
|
|
1734
1894
|
if (!t) return;
|
|
1735
1895
|
const e = i.locales?.[t];
|
|
@@ -1740,7 +1900,7 @@ function b(i) {
|
|
|
1740
1900
|
return "label" in a && (n.label = a.label ?? null), "description" in a && (n.description = a.description ?? null), n;
|
|
1741
1901
|
})));
|
|
1742
1902
|
}
|
|
1743
|
-
const yt = 1500, pt = 20,
|
|
1903
|
+
const yt = 1500, pt = 20, L = 200;
|
|
1744
1904
|
class St {
|
|
1745
1905
|
constructor(t) {
|
|
1746
1906
|
this.buffer = [], this.flushTimer = null, this.destroyed = !1, this.unloadHandler = null, this.visibilityHandler = null, this.opts = t, this.isEnabled() && this.attachUnloadHandlers();
|
|
@@ -1756,7 +1916,7 @@ class St {
|
|
|
1756
1916
|
this.flush();
|
|
1757
1917
|
return;
|
|
1758
1918
|
}
|
|
1759
|
-
this.buffer.length >
|
|
1919
|
+
this.buffer.length > L && (this.buffer = this.buffer.slice(-L)), this.scheduleFlush();
|
|
1760
1920
|
}
|
|
1761
1921
|
scheduleFlush() {
|
|
1762
1922
|
if (this.flushTimer || this.destroyed) return;
|
|
@@ -1804,7 +1964,7 @@ class St {
|
|
|
1804
1964
|
// body-level дубликаты для beacon-flow, читаются сервером как fallback.
|
|
1805
1965
|
visitor_id: e,
|
|
1806
1966
|
user_id: s,
|
|
1807
|
-
sdk_version:
|
|
1967
|
+
sdk_version: m,
|
|
1808
1968
|
paywall_id: this.opts.paywallId,
|
|
1809
1969
|
capabilities: this.opts.capabilities?.join(",") ?? ""
|
|
1810
1970
|
}), n = this.opts.sendBeacon ?? (typeof navigator < "u" && typeof navigator.sendBeacon == "function" ? navigator.sendBeacon.bind(navigator) : null);
|
|
@@ -1821,7 +1981,7 @@ class St {
|
|
|
1821
1981
|
buildHeaders(t, e) {
|
|
1822
1982
|
const s = {
|
|
1823
1983
|
"Content-Type": "application/json",
|
|
1824
|
-
"X-SDK-Version":
|
|
1984
|
+
"X-SDK-Version": m,
|
|
1825
1985
|
"X-Paywall-Id": this.opts.paywallId,
|
|
1826
1986
|
"X-Visitor-Id": t
|
|
1827
1987
|
};
|
|
@@ -1840,13 +2000,13 @@ class St {
|
|
|
1840
2000
|
}
|
|
1841
2001
|
}
|
|
1842
2002
|
const C = 3600 * 1e3;
|
|
1843
|
-
function
|
|
2003
|
+
function v(i) {
|
|
1844
2004
|
return `paywall-${i}-trial-time-first-open`;
|
|
1845
2005
|
}
|
|
1846
|
-
function
|
|
2006
|
+
function b(i) {
|
|
1847
2007
|
return `paywall-${i}-skip-times`;
|
|
1848
2008
|
}
|
|
1849
|
-
class
|
|
2009
|
+
class q {
|
|
1850
2010
|
constructor(t, e, s) {
|
|
1851
2011
|
this.storage = t, this.paywallId = e, this.config = s;
|
|
1852
2012
|
}
|
|
@@ -1857,10 +2017,10 @@ class F {
|
|
|
1857
2017
|
return this.config.mode === "time" ? this.recordTime() : this.recordOpens();
|
|
1858
2018
|
}
|
|
1859
2019
|
async reset() {
|
|
1860
|
-
await this.storage.removeItem(this.config.mode === "time" ?
|
|
2020
|
+
await this.storage.removeItem(this.config.mode === "time" ? v(this.paywallId) : b(this.paywallId));
|
|
1861
2021
|
}
|
|
1862
2022
|
async checkTime() {
|
|
1863
|
-
const t = this.config.payload * C, e = await this.storage.getItem(
|
|
2023
|
+
const t = this.config.payload * C, e = await this.storage.getItem(v(this.paywallId)), s = e ? Number(e) : null;
|
|
1864
2024
|
if (!s || !Number.isFinite(s))
|
|
1865
2025
|
return {
|
|
1866
2026
|
mode: "time",
|
|
@@ -1881,7 +2041,7 @@ class F {
|
|
|
1881
2041
|
};
|
|
1882
2042
|
}
|
|
1883
2043
|
async checkOpens() {
|
|
1884
|
-
const t = this.config.payload, e = await this.storage.getItem(
|
|
2044
|
+
const t = this.config.payload, e = await this.storage.getItem(b(this.paywallId)), s = e ? Number(e) : 0, a = Number.isFinite(s) ? s : 0, n = a < t, r = Math.max(0, t - a);
|
|
1885
2045
|
return {
|
|
1886
2046
|
mode: "opens",
|
|
1887
2047
|
blocked: n,
|
|
@@ -1890,7 +2050,7 @@ class F {
|
|
|
1890
2050
|
};
|
|
1891
2051
|
}
|
|
1892
2052
|
async recordTime() {
|
|
1893
|
-
const t = this.config.payload * C, e =
|
|
2053
|
+
const t = this.config.payload * C, e = v(this.paywallId), s = await this.storage.getItem(e);
|
|
1894
2054
|
let a = s ? Number(s) : null;
|
|
1895
2055
|
(!a || !Number.isFinite(a)) && (a = Date.now(), await this.storage.setItem(e, String(a)));
|
|
1896
2056
|
const n = a + t, r = Math.max(0, n - Date.now());
|
|
@@ -1904,23 +2064,23 @@ class F {
|
|
|
1904
2064
|
};
|
|
1905
2065
|
}
|
|
1906
2066
|
async recordOpens() {
|
|
1907
|
-
const t = this.config.payload, e =
|
|
2067
|
+
const t = this.config.payload, e = b(this.paywallId), s = await this.storage.getItem(e), a = s ? Number(s) : 0, n = Number.isFinite(a) ? a : 0, r = Math.min(t, n + 1);
|
|
1908
2068
|
await this.storage.setItem(e, String(r));
|
|
1909
|
-
const
|
|
2069
|
+
const h = Math.max(0, t - r);
|
|
1910
2070
|
return {
|
|
1911
2071
|
mode: "opens",
|
|
1912
2072
|
blocked: r < t,
|
|
1913
|
-
remainingActions:
|
|
2073
|
+
remainingActions: h,
|
|
1914
2074
|
totalActions: t
|
|
1915
2075
|
};
|
|
1916
2076
|
}
|
|
1917
2077
|
}
|
|
1918
|
-
let
|
|
2078
|
+
let N = !1;
|
|
1919
2079
|
class gt {
|
|
1920
2080
|
constructor(t, e, s) {
|
|
1921
|
-
|
|
2081
|
+
N || (N = !0, console.warn(
|
|
1922
2082
|
'[paywall] trial.storage="server" is not implemented yet — falling back to client storage. State lives in localStorage; users can reset trial by clearing site data.'
|
|
1923
|
-
)), this.fallback = new
|
|
2083
|
+
)), this.fallback = new q(t, e, s);
|
|
1924
2084
|
}
|
|
1925
2085
|
check() {
|
|
1926
2086
|
return this.fallback.check();
|
|
@@ -1932,10 +2092,10 @@ class gt {
|
|
|
1932
2092
|
return this.fallback.reset();
|
|
1933
2093
|
}
|
|
1934
2094
|
}
|
|
1935
|
-
function
|
|
1936
|
-
return e.storage === "server" ? new gt(i, t, e) : new
|
|
2095
|
+
function vt(i, t, e) {
|
|
2096
|
+
return e.storage === "server" ? new gt(i, t, e) : new q(i, t, e);
|
|
1937
2097
|
}
|
|
1938
|
-
const
|
|
2098
|
+
const bt = 1;
|
|
1939
2099
|
function _t(i) {
|
|
1940
2100
|
return i instanceof o ? {
|
|
1941
2101
|
name: "PaywallError",
|
|
@@ -1965,7 +2125,7 @@ function Bt(i) {
|
|
|
1965
2125
|
function wt(i) {
|
|
1966
2126
|
let t = !1;
|
|
1967
2127
|
const e = /* @__PURE__ */ new Set(), s = /* @__PURE__ */ new Set(), a = (r) => {
|
|
1968
|
-
for (const
|
|
2128
|
+
for (const h of e) h(r);
|
|
1969
2129
|
}, n = () => {
|
|
1970
2130
|
if (!t) {
|
|
1971
2131
|
t = !0;
|
|
@@ -1978,8 +2138,8 @@ function wt(i) {
|
|
|
1978
2138
|
if (!t)
|
|
1979
2139
|
try {
|
|
1980
2140
|
i.postMessage(r);
|
|
1981
|
-
} catch (
|
|
1982
|
-
throw n(),
|
|
2141
|
+
} catch (h) {
|
|
2142
|
+
throw n(), h;
|
|
1983
2143
|
}
|
|
1984
2144
|
},
|
|
1985
2145
|
onMessage(r) {
|
|
@@ -2000,15 +2160,15 @@ function kt(i) {
|
|
|
2000
2160
|
}
|
|
2001
2161
|
export {
|
|
2002
2162
|
mt as A,
|
|
2003
|
-
|
|
2163
|
+
It as B,
|
|
2004
2164
|
St as E,
|
|
2005
2165
|
o as P,
|
|
2006
|
-
|
|
2166
|
+
bt as a,
|
|
2007
2167
|
kt as b,
|
|
2008
|
-
|
|
2168
|
+
vt as c,
|
|
2009
2169
|
wt as p,
|
|
2010
2170
|
Bt as r,
|
|
2011
2171
|
_t as s,
|
|
2012
|
-
|
|
2172
|
+
Z as w
|
|
2013
2173
|
};
|
|
2014
|
-
//# sourceMappingURL=chrome-port-
|
|
2174
|
+
//# sourceMappingURL=chrome-port-KYVwww_u.js.map
|