@monetize.software/sdk-extension 3.0.0-alpha.1 → 3.0.0-alpha.10
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-BXHR4SOG.js → chrome-port-CB1NEZOC.js} +367 -216
- package/dist/chunks/chrome-port-CB1NEZOC.js.map +1 -0
- package/dist/chunks/chrome-port-DptrBLez.js +2 -0
- package/dist/chunks/chrome-port-DptrBLez.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 +29 -0
- 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 +2106 -1040
- 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
|
}
|
|
@@ -1277,10 +1383,10 @@ class bt {
|
|
|
1277
1383
|
* - Без identity возвращает empty-state (сервер тоже так делает).
|
|
1278
1384
|
*/
|
|
1279
1385
|
async getUser({ force: t = !1, signal: e } = {}) {
|
|
1280
|
-
return !t && this.cachedUser && Date.now() - this.cachedUserAt <
|
|
1386
|
+
return !t && this.cachedUser && Date.now() - this.cachedUserAt < nt ? this.cachedUser : this.inflightUser ? this.inflightUser : (this.inflightUser = (async () => {
|
|
1281
1387
|
try {
|
|
1282
1388
|
if (!this.identity?.email)
|
|
1283
|
-
return this.applyUser(
|
|
1389
|
+
return this.applyUser(S), S;
|
|
1284
1390
|
const s = await this.api.request(
|
|
1285
1391
|
`/api/v1/paywall/${this.paywallId}/user-state`,
|
|
1286
1392
|
{ headers: { "X-User-Email": this.identity.email }, signal: e }
|
|
@@ -1333,7 +1439,7 @@ class bt {
|
|
|
1333
1439
|
return this.cachedUser;
|
|
1334
1440
|
}
|
|
1335
1441
|
applyUser(t) {
|
|
1336
|
-
const e = !
|
|
1442
|
+
const e = !ct(this.cachedUser, t);
|
|
1337
1443
|
if (this.cachedUser = t, this.cachedUserAt = Date.now(), e) {
|
|
1338
1444
|
this.persistUser(t);
|
|
1339
1445
|
for (const s of this.userListeners)
|
|
@@ -1345,7 +1451,7 @@ class bt {
|
|
|
1345
1451
|
}
|
|
1346
1452
|
}
|
|
1347
1453
|
storageKey() {
|
|
1348
|
-
return
|
|
1454
|
+
return u.userState(this.paywallId, A(this.identity));
|
|
1349
1455
|
}
|
|
1350
1456
|
async hydrateUserFromStorage() {
|
|
1351
1457
|
if (!this.cachedUser)
|
|
@@ -1353,7 +1459,7 @@ class bt {
|
|
|
1353
1459
|
const t = await this.storage.getItem(this.storageKey());
|
|
1354
1460
|
if (!t) return;
|
|
1355
1461
|
const e = JSON.parse(t);
|
|
1356
|
-
if (!e?.user || Date.now() - e.at >
|
|
1462
|
+
if (!e?.user || Date.now() - e.at > rt || this.cachedUser) return;
|
|
1357
1463
|
this.applyUser(e.user);
|
|
1358
1464
|
} catch {
|
|
1359
1465
|
}
|
|
@@ -1382,7 +1488,7 @@ class bt {
|
|
|
1382
1488
|
*/
|
|
1383
1489
|
async getBalances({ force: t = !1, signal: e } = {}) {
|
|
1384
1490
|
const s = Date.now(), a = this.cachedBalances ? s - this.cachedBalancesAt : 1 / 0;
|
|
1385
|
-
return !t && this.cachedBalances && (a <
|
|
1491
|
+
return !t && this.cachedBalances && (a < ht || a < lt) ? this.cachedBalances : !t && this.cachedBalances && a < T ? (this.fetchBalances({ signal: e }).catch(() => {
|
|
1386
1492
|
}), this.cachedBalances) : this.inflightBalances ? this.inflightBalances : this.fetchBalances({ signal: e });
|
|
1387
1493
|
}
|
|
1388
1494
|
// Network primitive — единая точка для force/stale-revalidate/cold-start.
|
|
@@ -1473,7 +1579,7 @@ class bt {
|
|
|
1473
1579
|
*/
|
|
1474
1580
|
createApiGatewayClient(t = {}) {
|
|
1475
1581
|
const e = t.onChargeSuccess, s = t.onQuotaExceeded;
|
|
1476
|
-
return new
|
|
1582
|
+
return new st({
|
|
1477
1583
|
paywallId: this.paywallId,
|
|
1478
1584
|
apiOrigin: this.apiOrigin,
|
|
1479
1585
|
auth: this.auth,
|
|
@@ -1490,7 +1596,7 @@ class bt {
|
|
|
1490
1596
|
});
|
|
1491
1597
|
}
|
|
1492
1598
|
applyBalances(t, { persist: e = !0 } = {}) {
|
|
1493
|
-
const s = !
|
|
1599
|
+
const s = !ut(this.cachedBalances, t);
|
|
1494
1600
|
if (this.cachedBalances = t, this.cachedBalancesAt = Date.now(), e && this.persistBalances(t), s)
|
|
1495
1601
|
for (const a of this.balanceListeners)
|
|
1496
1602
|
try {
|
|
@@ -1500,7 +1606,7 @@ class bt {
|
|
|
1500
1606
|
}
|
|
1501
1607
|
}
|
|
1502
1608
|
balancesStorageKey() {
|
|
1503
|
-
return
|
|
1609
|
+
return u.balances(this.paywallId, A(this.identity));
|
|
1504
1610
|
}
|
|
1505
1611
|
async hydrateBalancesFromStorage() {
|
|
1506
1612
|
if (!this.cachedBalances)
|
|
@@ -1557,16 +1663,16 @@ class bt {
|
|
|
1557
1663
|
"Idempotency-Key": t.idempotencyKey ?? R()
|
|
1558
1664
|
};
|
|
1559
1665
|
this.apiKey && (n["X-Api-Key"] = this.apiKey);
|
|
1560
|
-
const r = this.cachedBootstrap?.settings,
|
|
1666
|
+
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
1667
|
method: "POST",
|
|
1562
1668
|
headers: n,
|
|
1563
1669
|
signal: t.signal,
|
|
1564
1670
|
body: JSON.stringify({
|
|
1565
1671
|
email: this.identity.email,
|
|
1566
1672
|
priceId: Number(t.priceId),
|
|
1567
|
-
successUrl:
|
|
1673
|
+
successUrl: h,
|
|
1568
1674
|
errorUrl: t.errorUrl,
|
|
1569
|
-
shopUrl:
|
|
1675
|
+
shopUrl: d,
|
|
1570
1676
|
productName: r?.checkout_product_name ?? void 0,
|
|
1571
1677
|
trial_days: t.trialDays,
|
|
1572
1678
|
ignoreActivePurchase: t.ignoreActivePurchase ? !0 : void 0,
|
|
@@ -1579,10 +1685,10 @@ class bt {
|
|
|
1579
1685
|
{ status: 409, cause: c.cause }
|
|
1580
1686
|
) : c;
|
|
1581
1687
|
});
|
|
1582
|
-
return this.inflightCheckouts.set(e,
|
|
1583
|
-
this.inflightCheckouts.get(e) ===
|
|
1688
|
+
return this.inflightCheckouts.set(e, f), f.finally(() => {
|
|
1689
|
+
this.inflightCheckouts.get(e) === f && this.inflightCheckouts.delete(e);
|
|
1584
1690
|
}).catch(() => {
|
|
1585
|
-
}),
|
|
1691
|
+
}), f;
|
|
1586
1692
|
}
|
|
1587
1693
|
/**
|
|
1588
1694
|
* URL Stripe/Paddle/Chargebee customer portal — место, где залогиненный
|
|
@@ -1629,43 +1735,65 @@ class bt {
|
|
|
1629
1735
|
* `/api/v1/paywall/[id]/user` без unstable_cache, потому что list для UI
|
|
1630
1736
|
* должен быть свежим после cancel-а.
|
|
1631
1737
|
*
|
|
1632
|
-
* Auth
|
|
1633
|
-
*
|
|
1738
|
+
* Auth (два пути):
|
|
1739
|
+
* - Bearer (через AuthClient) — user.id резолвится из сессии, identity
|
|
1740
|
+
* в query игнорируется.
|
|
1741
|
+
* - `apiKey` + `identity.email`/`identity.userId` — server-SDK путь для
|
|
1742
|
+
* интеграций со своей авторизацией. Бэк проверяет, что identity линкована
|
|
1743
|
+
* к этому пейволу (защита от cross-paywall lookup).
|
|
1744
|
+
* Без auth и без apiKey+identity — `identity_required`.
|
|
1634
1745
|
*/
|
|
1635
1746
|
async listPurchases(t = {}) {
|
|
1636
|
-
|
|
1747
|
+
const e = !!(this.identity?.email || this.identity?.userId);
|
|
1748
|
+
if (!this.auth && !(this.apiKey && e))
|
|
1637
1749
|
throw new o(
|
|
1638
|
-
"
|
|
1639
|
-
"listPurchases requires AuthClient (Bearer
|
|
1750
|
+
"identity_required",
|
|
1751
|
+
"listPurchases requires AuthClient (Bearer) or apiKey + identity.email/userId"
|
|
1640
1752
|
);
|
|
1641
|
-
|
|
1753
|
+
const s = {};
|
|
1754
|
+
this.apiKey && (s["X-Api-Key"] = this.apiKey);
|
|
1755
|
+
const a = new URLSearchParams();
|
|
1756
|
+
this.apiKey && this.identity?.email && a.set("email", this.identity.email), this.apiKey && this.identity?.userId && a.set("user_id", this.identity.userId);
|
|
1757
|
+
const n = a.toString(), r = n ? `/api/v1/paywall/${this.paywallId}/user?${n}` : `/api/v1/paywall/${this.paywallId}/user`;
|
|
1758
|
+
return (await this.api.request(r, {
|
|
1642
1759
|
method: "GET",
|
|
1760
|
+
headers: s,
|
|
1643
1761
|
signal: t.signal
|
|
1644
1762
|
})).purchases ?? [];
|
|
1645
1763
|
}
|
|
1646
1764
|
/**
|
|
1647
|
-
* Отменить подписку. Бэк
|
|
1648
|
-
*
|
|
1649
|
-
*
|
|
1765
|
+
* Отменить подписку. Бэк проверит, что subscription принадлежит юзеру
|
|
1766
|
+
* (Bearer-путь — из сессии; apiKey-путь — из identity), и сделает cancel у
|
|
1767
|
+
* acquiring'а (Stripe/Paddle/Chargebee/Overpay). По умолчанию cancel в
|
|
1768
|
+
* конце текущего периода — юзер сохраняет access до renewal date'ы.
|
|
1650
1769
|
*
|
|
1651
|
-
* `reason` обязательна (валидация на бэке).
|
|
1652
|
-
* причин в host-UI, как в legacy customer portal'е.
|
|
1770
|
+
* `reason` обязательна (валидация на бэке).
|
|
1653
1771
|
*
|
|
1654
|
-
* Auth
|
|
1772
|
+
* Auth (два пути):
|
|
1773
|
+
* - Bearer (через AuthClient) — стандартный путь для UI customer-portal'a.
|
|
1774
|
+
* - `apiKey` + `identity.email`/`identity.userId` — для self-service UI на
|
|
1775
|
+
* бэке клиента со своей авторизацией. Бэк дополнительно фильтрует
|
|
1776
|
+
* subscription по paywall_id, чтобы owner пейвола A не отменил подписку
|
|
1777
|
+
* пейвола B.
|
|
1655
1778
|
*/
|
|
1656
1779
|
async cancelSubscription(t) {
|
|
1657
|
-
|
|
1780
|
+
const e = !!(this.identity?.email || this.identity?.userId);
|
|
1781
|
+
if (!this.auth && !(this.apiKey && e))
|
|
1658
1782
|
throw new o(
|
|
1659
|
-
"
|
|
1660
|
-
"cancelSubscription requires AuthClient (Bearer
|
|
1783
|
+
"identity_required",
|
|
1784
|
+
"cancelSubscription requires AuthClient (Bearer) or apiKey + identity.email/userId"
|
|
1661
1785
|
);
|
|
1662
|
-
|
|
1786
|
+
const s = {};
|
|
1787
|
+
this.apiKey && (s["X-Api-Key"] = this.apiKey);
|
|
1788
|
+
const a = {
|
|
1789
|
+
subscriptionId: t.subscriptionId,
|
|
1790
|
+
paywallId: this.paywallId,
|
|
1791
|
+
cancellationReason: t.reason
|
|
1792
|
+
};
|
|
1793
|
+
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
1794
|
method: "POST",
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
paywallId: this.paywallId,
|
|
1667
|
-
cancellationReason: t.reason
|
|
1668
|
-
}),
|
|
1795
|
+
headers: s,
|
|
1796
|
+
body: JSON.stringify(a),
|
|
1669
1797
|
signal: t.signal
|
|
1670
1798
|
});
|
|
1671
1799
|
}
|
|
@@ -1698,19 +1826,42 @@ class bt {
|
|
|
1698
1826
|
});
|
|
1699
1827
|
}
|
|
1700
1828
|
}
|
|
1701
|
-
function
|
|
1829
|
+
function O(i) {
|
|
1702
1830
|
return { email: i.email, userId: i.id };
|
|
1703
1831
|
}
|
|
1704
1832
|
function dt(i, t) {
|
|
1705
1833
|
return i === t ? !0 : !i || !t ? !1 : i.email === t.email && i.userId === t.userId && i.anonymousId === t.anonymousId;
|
|
1706
1834
|
}
|
|
1835
|
+
function U(i) {
|
|
1836
|
+
if (!i) return null;
|
|
1837
|
+
const t = i.trim();
|
|
1838
|
+
if (!t) return null;
|
|
1839
|
+
try {
|
|
1840
|
+
return new URL(t.includes("://") ? t : `https://${t}`).origin;
|
|
1841
|
+
} catch {
|
|
1842
|
+
return null;
|
|
1843
|
+
}
|
|
1844
|
+
}
|
|
1707
1845
|
function ft(i, t) {
|
|
1846
|
+
const e = U(i);
|
|
1847
|
+
if (!(!e || U(t) === e))
|
|
1848
|
+
throw new o(
|
|
1849
|
+
"invalid_config",
|
|
1850
|
+
`apiOrigin mismatch: SDK initialized with "${t}" but paywall is configured with custom_domain "${i}". Use the custom_domain from the platform paywall settings.`
|
|
1851
|
+
);
|
|
1852
|
+
}
|
|
1853
|
+
function E(i, t) {
|
|
1708
1854
|
return {
|
|
1709
1855
|
type: "modal",
|
|
1710
1856
|
blocks: [
|
|
1857
|
+
// offer_banner НЕ в default layout — PaywallRoot рендерит его как
|
|
1858
|
+
// top-tab над dialog'ом (rounded-top, negative margin), за пределами
|
|
1859
|
+
// scrollable area. Блок остаётся в registry для opt-in inline-вариантa.
|
|
1711
1860
|
{ type: "heading", text: i.name || "Upgrade", level: 1 },
|
|
1712
1861
|
{ type: "price_grid", priceIds: t.map((e) => e.id) },
|
|
1713
|
-
{ type: "cta_button",
|
|
1862
|
+
{ type: "cta_button", action: "checkout" },
|
|
1863
|
+
{ type: "guarantee_badge" },
|
|
1864
|
+
{ type: "current_session" }
|
|
1714
1865
|
]
|
|
1715
1866
|
};
|
|
1716
1867
|
}
|
|
@@ -1729,7 +1880,7 @@ function M(i) {
|
|
|
1729
1880
|
if (a && Object.prototype.hasOwnProperty.call(t, a)) return a;
|
|
1730
1881
|
return null;
|
|
1731
1882
|
}
|
|
1732
|
-
function
|
|
1883
|
+
function w(i) {
|
|
1733
1884
|
const t = M(i);
|
|
1734
1885
|
if (!t) return;
|
|
1735
1886
|
const e = i.locales?.[t];
|
|
@@ -1740,7 +1891,7 @@ function b(i) {
|
|
|
1740
1891
|
return "label" in a && (n.label = a.label ?? null), "description" in a && (n.description = a.description ?? null), n;
|
|
1741
1892
|
})));
|
|
1742
1893
|
}
|
|
1743
|
-
const yt = 1500, pt = 20,
|
|
1894
|
+
const yt = 1500, pt = 20, L = 200;
|
|
1744
1895
|
class St {
|
|
1745
1896
|
constructor(t) {
|
|
1746
1897
|
this.buffer = [], this.flushTimer = null, this.destroyed = !1, this.unloadHandler = null, this.visibilityHandler = null, this.opts = t, this.isEnabled() && this.attachUnloadHandlers();
|
|
@@ -1756,7 +1907,7 @@ class St {
|
|
|
1756
1907
|
this.flush();
|
|
1757
1908
|
return;
|
|
1758
1909
|
}
|
|
1759
|
-
this.buffer.length >
|
|
1910
|
+
this.buffer.length > L && (this.buffer = this.buffer.slice(-L)), this.scheduleFlush();
|
|
1760
1911
|
}
|
|
1761
1912
|
scheduleFlush() {
|
|
1762
1913
|
if (this.flushTimer || this.destroyed) return;
|
|
@@ -1804,7 +1955,7 @@ class St {
|
|
|
1804
1955
|
// body-level дубликаты для beacon-flow, читаются сервером как fallback.
|
|
1805
1956
|
visitor_id: e,
|
|
1806
1957
|
user_id: s,
|
|
1807
|
-
sdk_version:
|
|
1958
|
+
sdk_version: m,
|
|
1808
1959
|
paywall_id: this.opts.paywallId,
|
|
1809
1960
|
capabilities: this.opts.capabilities?.join(",") ?? ""
|
|
1810
1961
|
}), n = this.opts.sendBeacon ?? (typeof navigator < "u" && typeof navigator.sendBeacon == "function" ? navigator.sendBeacon.bind(navigator) : null);
|
|
@@ -1821,7 +1972,7 @@ class St {
|
|
|
1821
1972
|
buildHeaders(t, e) {
|
|
1822
1973
|
const s = {
|
|
1823
1974
|
"Content-Type": "application/json",
|
|
1824
|
-
"X-SDK-Version":
|
|
1975
|
+
"X-SDK-Version": m,
|
|
1825
1976
|
"X-Paywall-Id": this.opts.paywallId,
|
|
1826
1977
|
"X-Visitor-Id": t
|
|
1827
1978
|
};
|
|
@@ -1840,13 +1991,13 @@ class St {
|
|
|
1840
1991
|
}
|
|
1841
1992
|
}
|
|
1842
1993
|
const C = 3600 * 1e3;
|
|
1843
|
-
function
|
|
1994
|
+
function v(i) {
|
|
1844
1995
|
return `paywall-${i}-trial-time-first-open`;
|
|
1845
1996
|
}
|
|
1846
|
-
function
|
|
1997
|
+
function b(i) {
|
|
1847
1998
|
return `paywall-${i}-skip-times`;
|
|
1848
1999
|
}
|
|
1849
|
-
class
|
|
2000
|
+
class q {
|
|
1850
2001
|
constructor(t, e, s) {
|
|
1851
2002
|
this.storage = t, this.paywallId = e, this.config = s;
|
|
1852
2003
|
}
|
|
@@ -1857,10 +2008,10 @@ class F {
|
|
|
1857
2008
|
return this.config.mode === "time" ? this.recordTime() : this.recordOpens();
|
|
1858
2009
|
}
|
|
1859
2010
|
async reset() {
|
|
1860
|
-
await this.storage.removeItem(this.config.mode === "time" ?
|
|
2011
|
+
await this.storage.removeItem(this.config.mode === "time" ? v(this.paywallId) : b(this.paywallId));
|
|
1861
2012
|
}
|
|
1862
2013
|
async checkTime() {
|
|
1863
|
-
const t = this.config.payload * C, e = await this.storage.getItem(
|
|
2014
|
+
const t = this.config.payload * C, e = await this.storage.getItem(v(this.paywallId)), s = e ? Number(e) : null;
|
|
1864
2015
|
if (!s || !Number.isFinite(s))
|
|
1865
2016
|
return {
|
|
1866
2017
|
mode: "time",
|
|
@@ -1881,7 +2032,7 @@ class F {
|
|
|
1881
2032
|
};
|
|
1882
2033
|
}
|
|
1883
2034
|
async checkOpens() {
|
|
1884
|
-
const t = this.config.payload, e = await this.storage.getItem(
|
|
2035
|
+
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
2036
|
return {
|
|
1886
2037
|
mode: "opens",
|
|
1887
2038
|
blocked: n,
|
|
@@ -1890,7 +2041,7 @@ class F {
|
|
|
1890
2041
|
};
|
|
1891
2042
|
}
|
|
1892
2043
|
async recordTime() {
|
|
1893
|
-
const t = this.config.payload * C, e =
|
|
2044
|
+
const t = this.config.payload * C, e = v(this.paywallId), s = await this.storage.getItem(e);
|
|
1894
2045
|
let a = s ? Number(s) : null;
|
|
1895
2046
|
(!a || !Number.isFinite(a)) && (a = Date.now(), await this.storage.setItem(e, String(a)));
|
|
1896
2047
|
const n = a + t, r = Math.max(0, n - Date.now());
|
|
@@ -1904,23 +2055,23 @@ class F {
|
|
|
1904
2055
|
};
|
|
1905
2056
|
}
|
|
1906
2057
|
async recordOpens() {
|
|
1907
|
-
const t = this.config.payload, e =
|
|
2058
|
+
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
2059
|
await this.storage.setItem(e, String(r));
|
|
1909
|
-
const
|
|
2060
|
+
const h = Math.max(0, t - r);
|
|
1910
2061
|
return {
|
|
1911
2062
|
mode: "opens",
|
|
1912
2063
|
blocked: r < t,
|
|
1913
|
-
remainingActions:
|
|
2064
|
+
remainingActions: h,
|
|
1914
2065
|
totalActions: t
|
|
1915
2066
|
};
|
|
1916
2067
|
}
|
|
1917
2068
|
}
|
|
1918
|
-
let
|
|
2069
|
+
let N = !1;
|
|
1919
2070
|
class gt {
|
|
1920
2071
|
constructor(t, e, s) {
|
|
1921
|
-
|
|
2072
|
+
N || (N = !0, console.warn(
|
|
1922
2073
|
'[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
|
|
2074
|
+
)), this.fallback = new q(t, e, s);
|
|
1924
2075
|
}
|
|
1925
2076
|
check() {
|
|
1926
2077
|
return this.fallback.check();
|
|
@@ -1932,10 +2083,10 @@ class gt {
|
|
|
1932
2083
|
return this.fallback.reset();
|
|
1933
2084
|
}
|
|
1934
2085
|
}
|
|
1935
|
-
function
|
|
1936
|
-
return e.storage === "server" ? new gt(i, t, e) : new
|
|
2086
|
+
function vt(i, t, e) {
|
|
2087
|
+
return e.storage === "server" ? new gt(i, t, e) : new q(i, t, e);
|
|
1937
2088
|
}
|
|
1938
|
-
const
|
|
2089
|
+
const bt = 1;
|
|
1939
2090
|
function _t(i) {
|
|
1940
2091
|
return i instanceof o ? {
|
|
1941
2092
|
name: "PaywallError",
|
|
@@ -1965,7 +2116,7 @@ function Bt(i) {
|
|
|
1965
2116
|
function wt(i) {
|
|
1966
2117
|
let t = !1;
|
|
1967
2118
|
const e = /* @__PURE__ */ new Set(), s = /* @__PURE__ */ new Set(), a = (r) => {
|
|
1968
|
-
for (const
|
|
2119
|
+
for (const h of e) h(r);
|
|
1969
2120
|
}, n = () => {
|
|
1970
2121
|
if (!t) {
|
|
1971
2122
|
t = !0;
|
|
@@ -1978,8 +2129,8 @@ function wt(i) {
|
|
|
1978
2129
|
if (!t)
|
|
1979
2130
|
try {
|
|
1980
2131
|
i.postMessage(r);
|
|
1981
|
-
} catch (
|
|
1982
|
-
throw n(),
|
|
2132
|
+
} catch (h) {
|
|
2133
|
+
throw n(), h;
|
|
1983
2134
|
}
|
|
1984
2135
|
},
|
|
1985
2136
|
onMessage(r) {
|
|
@@ -2000,15 +2151,15 @@ function kt(i) {
|
|
|
2000
2151
|
}
|
|
2001
2152
|
export {
|
|
2002
2153
|
mt as A,
|
|
2003
|
-
|
|
2154
|
+
It as B,
|
|
2004
2155
|
St as E,
|
|
2005
2156
|
o as P,
|
|
2006
|
-
|
|
2157
|
+
bt as a,
|
|
2007
2158
|
kt as b,
|
|
2008
|
-
|
|
2159
|
+
vt as c,
|
|
2009
2160
|
wt as p,
|
|
2010
2161
|
Bt as r,
|
|
2011
2162
|
_t as s,
|
|
2012
|
-
|
|
2163
|
+
Z as w
|
|
2013
2164
|
};
|
|
2014
|
-
//# sourceMappingURL=chrome-port-
|
|
2165
|
+
//# sourceMappingURL=chrome-port-CB1NEZOC.js.map
|