@hyve-sdk/js 1.5.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +169 -658
- package/dist/index.d.mts +164 -137
- package/dist/index.d.ts +164 -137
- package/dist/index.js +483 -538
- package/dist/index.mjs +478 -530
- package/dist/react.d.mts +453 -0
- package/dist/react.d.ts +453 -0
- package/dist/react.js +2173 -0
- package/dist/react.mjs +2151 -0
- package/package.json +30 -15
package/dist/index.js
CHANGED
|
@@ -18,12 +18,13 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
18
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
19
|
|
|
20
20
|
// src/index.ts
|
|
21
|
-
var
|
|
22
|
-
__export(
|
|
21
|
+
var src_exports = {};
|
|
22
|
+
__export(src_exports, {
|
|
23
23
|
AdsService: () => AdsService,
|
|
24
24
|
BillingPlatform: () => BillingPlatform,
|
|
25
25
|
BillingService: () => BillingService,
|
|
26
26
|
CloudStorageAdapter: () => CloudStorageAdapter,
|
|
27
|
+
CrazyGamesService: () => CrazyGamesService,
|
|
27
28
|
HyveClient: () => HyveClient,
|
|
28
29
|
LocalStorageAdapter: () => LocalStorageAdapter,
|
|
29
30
|
Logger: () => Logger,
|
|
@@ -31,15 +32,11 @@ __export(index_exports, {
|
|
|
31
32
|
NativeMessageType: () => NativeMessageType,
|
|
32
33
|
PlaygamaService: () => PlaygamaService,
|
|
33
34
|
generateUUID: () => generateUUID,
|
|
34
|
-
handleVerifyMessage: () => handleVerifyMessage,
|
|
35
35
|
isDomainAllowed: () => isDomainAllowed,
|
|
36
36
|
logger: () => logger,
|
|
37
|
-
parseUrlParams: () => parseUrlParams
|
|
38
|
-
validateSignature: () => validateSignature,
|
|
39
|
-
verifyAuthentication: () => verifyAuthentication,
|
|
40
|
-
verifyHyveToken: () => verifyHyveToken
|
|
37
|
+
parseUrlParams: () => parseUrlParams
|
|
41
38
|
});
|
|
42
|
-
module.exports = __toCommonJS(
|
|
39
|
+
module.exports = __toCommonJS(src_exports);
|
|
43
40
|
|
|
44
41
|
// src/utils/index.ts
|
|
45
42
|
var import_uuid = require("uuid");
|
|
@@ -67,7 +64,7 @@ var Logger = class _Logger {
|
|
|
67
64
|
}
|
|
68
65
|
} else if (typeof window !== "undefined") {
|
|
69
66
|
try {
|
|
70
|
-
enabled = process
|
|
67
|
+
enabled = process?.env.NODE_ENV !== "production";
|
|
71
68
|
} catch (e) {
|
|
72
69
|
enabled = true;
|
|
73
70
|
}
|
|
@@ -167,136 +164,15 @@ var Logger = class _Logger {
|
|
|
167
164
|
var logger = new Logger();
|
|
168
165
|
|
|
169
166
|
// src/utils/auth.ts
|
|
170
|
-
var import_ethers = require("ethers");
|
|
171
167
|
function parseUrlParams(searchParams) {
|
|
172
168
|
const params = typeof searchParams === "string" ? new URLSearchParams(searchParams) : searchParams;
|
|
173
169
|
return {
|
|
174
|
-
signature: params.get("signature") || "",
|
|
175
|
-
message: params.get("message") || "",
|
|
176
170
|
gameStartTab: params.get("game_start_tab") || "",
|
|
177
|
-
hyveToken: params.get("hyve-token") || "",
|
|
178
171
|
platform: params.get("platform") || "",
|
|
179
172
|
hyveAccess: params.get("hyve-access") || "",
|
|
180
173
|
gameId: params.get("game-id") || ""
|
|
181
174
|
};
|
|
182
175
|
}
|
|
183
|
-
function validateSignature(signature, message) {
|
|
184
|
-
try {
|
|
185
|
-
const recoveredAddress = import_ethers.ethers.verifyMessage(message, signature);
|
|
186
|
-
return !!recoveredAddress;
|
|
187
|
-
} catch (error) {
|
|
188
|
-
logger.error("Signature validation error:", error);
|
|
189
|
-
return false;
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
function handleVerifyMessage(signature, message) {
|
|
193
|
-
try {
|
|
194
|
-
const metadata = JSON.parse(decodeURI(message));
|
|
195
|
-
if (!metadata.expiration || metadata.expiration < Date.now()) {
|
|
196
|
-
return false;
|
|
197
|
-
}
|
|
198
|
-
const userAddress = metadata.userId || metadata.address;
|
|
199
|
-
if (!userAddress) {
|
|
200
|
-
return false;
|
|
201
|
-
}
|
|
202
|
-
const byteMessage = new TextEncoder().encode(message);
|
|
203
|
-
const addressThatSignedMessage = import_ethers.ethers.verifyMessage(
|
|
204
|
-
byteMessage,
|
|
205
|
-
signature
|
|
206
|
-
);
|
|
207
|
-
if (!addressThatSignedMessage) {
|
|
208
|
-
return false;
|
|
209
|
-
}
|
|
210
|
-
if (addressThatSignedMessage.toLowerCase() !== userAddress.toLowerCase()) {
|
|
211
|
-
return false;
|
|
212
|
-
}
|
|
213
|
-
return userAddress;
|
|
214
|
-
} catch (error) {
|
|
215
|
-
logger.error("Error verifying message:", error);
|
|
216
|
-
return false;
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
function verifyHyveToken(hyveToken, maxAgeSec = 600) {
|
|
220
|
-
try {
|
|
221
|
-
const parts = hyveToken.split(".");
|
|
222
|
-
if (parts.length !== 4) {
|
|
223
|
-
logger.error(
|
|
224
|
-
"Invalid hyve-token format: expected 4 parts, got",
|
|
225
|
-
parts.length
|
|
226
|
-
);
|
|
227
|
-
return false;
|
|
228
|
-
}
|
|
229
|
-
const [signature, address, randomBase64, timestampStr] = parts;
|
|
230
|
-
if (!signature || !address || !randomBase64 || !timestampStr) {
|
|
231
|
-
logger.error("Missing hyve-token components");
|
|
232
|
-
return false;
|
|
233
|
-
}
|
|
234
|
-
const message = `${address}.${randomBase64}.${timestampStr}`;
|
|
235
|
-
const recoveredAddress = import_ethers.ethers.verifyMessage(message, signature);
|
|
236
|
-
if (recoveredAddress.toLowerCase() !== address.toLowerCase()) {
|
|
237
|
-
logger.error("Hyve-token signature verification failed");
|
|
238
|
-
return false;
|
|
239
|
-
}
|
|
240
|
-
const timestamp = parseInt(timestampStr, 10);
|
|
241
|
-
if (!Number.isFinite(timestamp)) {
|
|
242
|
-
logger.error("Invalid hyve-token timestamp");
|
|
243
|
-
return false;
|
|
244
|
-
}
|
|
245
|
-
const now = Math.floor(Date.now() / 1e3);
|
|
246
|
-
if (now - timestamp > maxAgeSec) {
|
|
247
|
-
logger.error(
|
|
248
|
-
`Hyve-token expired (age: ${now - timestamp}s, max: ${maxAgeSec}s)`
|
|
249
|
-
);
|
|
250
|
-
return false;
|
|
251
|
-
}
|
|
252
|
-
return address;
|
|
253
|
-
} catch (error) {
|
|
254
|
-
logger.error("Hyve-token verification error:", error);
|
|
255
|
-
return false;
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
function verifyAuthentication(params, maxAgeSec = 600) {
|
|
259
|
-
if (params.hyveToken) {
|
|
260
|
-
const modernAddress = verifyHyveToken(params.hyveToken, maxAgeSec);
|
|
261
|
-
if (modernAddress) {
|
|
262
|
-
return {
|
|
263
|
-
isValid: true,
|
|
264
|
-
address: modernAddress,
|
|
265
|
-
method: "modern"
|
|
266
|
-
};
|
|
267
|
-
} else {
|
|
268
|
-
return {
|
|
269
|
-
isValid: false,
|
|
270
|
-
address: null,
|
|
271
|
-
method: "modern",
|
|
272
|
-
error: "Modern token verification failed"
|
|
273
|
-
};
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
if (params.signature && params.message) {
|
|
277
|
-
const legacyAddress = handleVerifyMessage(params.signature, params.message);
|
|
278
|
-
if (legacyAddress) {
|
|
279
|
-
return {
|
|
280
|
-
isValid: true,
|
|
281
|
-
address: legacyAddress,
|
|
282
|
-
method: "legacy"
|
|
283
|
-
};
|
|
284
|
-
} else {
|
|
285
|
-
return {
|
|
286
|
-
isValid: false,
|
|
287
|
-
address: null,
|
|
288
|
-
method: "legacy",
|
|
289
|
-
error: "Legacy token verification failed"
|
|
290
|
-
};
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
return {
|
|
294
|
-
isValid: false,
|
|
295
|
-
address: null,
|
|
296
|
-
method: "none",
|
|
297
|
-
error: "No authentication tokens provided"
|
|
298
|
-
};
|
|
299
|
-
}
|
|
300
176
|
function isDomainAllowed(allowedDomains, hostname) {
|
|
301
177
|
logger.debug("Checking hostname:", hostname);
|
|
302
178
|
if (!allowedDomains) return true;
|
|
@@ -337,9 +213,7 @@ var NativeBridge = class {
|
|
|
337
213
|
* Checks if the app is running inside a React Native WebView
|
|
338
214
|
*/
|
|
339
215
|
static isNativeContext() {
|
|
340
|
-
|
|
341
|
-
console.log(`[NativeBridge] isNativeContext check: ${isNative}`);
|
|
342
|
-
return isNative;
|
|
216
|
+
return typeof window !== "undefined" && "ReactNativeWebView" in window;
|
|
343
217
|
}
|
|
344
218
|
/**
|
|
345
219
|
* Initializes the native bridge message listener
|
|
@@ -348,55 +222,37 @@ var NativeBridge = class {
|
|
|
348
222
|
static initialize() {
|
|
349
223
|
if (this.isInitialized) {
|
|
350
224
|
logger.debug("[NativeBridge] Already initialized");
|
|
351
|
-
console.log("[NativeBridge] \u26A0 Already initialized, skipping");
|
|
352
225
|
return;
|
|
353
226
|
}
|
|
354
227
|
if (typeof window === "undefined") {
|
|
355
228
|
logger.warn("[NativeBridge] Window not available, skipping initialization");
|
|
356
|
-
console.warn("[NativeBridge] \u26A0 Window not available, skipping initialization");
|
|
357
229
|
return;
|
|
358
230
|
}
|
|
359
|
-
const isInIframe = window !== window.parent;
|
|
360
|
-
console.log("[NativeBridge] ========== INITIALIZING ==========");
|
|
361
|
-
console.log(`[NativeBridge] Running in iframe: ${isInIframe}`);
|
|
362
|
-
console.log("[NativeBridge] Setting up message listeners on window and document");
|
|
363
231
|
const boundHandler = this.handleNativeMessage.bind(this);
|
|
364
232
|
window.addEventListener("message", boundHandler);
|
|
365
233
|
document.addEventListener("message", boundHandler);
|
|
366
|
-
if (isInIframe) {
|
|
367
|
-
console.log("[NativeBridge] Setting up parent window message listener for iframe context");
|
|
368
|
-
}
|
|
369
234
|
this.isInitialized = true;
|
|
370
|
-
console.log("[NativeBridge] \u2713 Event listeners attached to window and document");
|
|
371
|
-
console.log("[NativeBridge] \u2713 Initialized and ready to receive messages");
|
|
372
235
|
logger.info("[NativeBridge] Initialized and listening for native messages");
|
|
373
236
|
}
|
|
374
237
|
/**
|
|
375
238
|
* Handles incoming messages from React Native
|
|
376
239
|
*/
|
|
377
240
|
static handleNativeMessage(event) {
|
|
378
|
-
console.log("[NativeBridge] [HANDLER] handleNativeMessage called");
|
|
379
|
-
console.log("[NativeBridge] [HANDLER] Event source:", event.source === window ? "window" : "other");
|
|
380
|
-
console.log("[NativeBridge] [HANDLER] Event data:", event.data);
|
|
381
241
|
try {
|
|
382
242
|
let data;
|
|
383
243
|
if (typeof event.data === "string") {
|
|
384
244
|
try {
|
|
385
245
|
data = JSON.parse(event.data);
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
logger.debug("[NativeBridge] Failed to parse message, not JSON:", event.data);
|
|
246
|
+
} catch {
|
|
247
|
+
logger.debug("[NativeBridge] Ignoring non-JSON message");
|
|
389
248
|
return;
|
|
390
249
|
}
|
|
391
250
|
} else if (typeof event.data === "object" && event.data !== null) {
|
|
392
251
|
data = event.data;
|
|
393
|
-
logger.debug("[NativeBridge] Received message object:", data);
|
|
394
252
|
} else {
|
|
395
|
-
logger.debug("[NativeBridge] Received invalid message type:", typeof event.data);
|
|
396
253
|
return;
|
|
397
254
|
}
|
|
398
|
-
if (!data
|
|
399
|
-
logger.debug("[NativeBridge] Received message without type, ignoring");
|
|
255
|
+
if (!data?.type) {
|
|
400
256
|
return;
|
|
401
257
|
}
|
|
402
258
|
const nativeResponseTypes = [
|
|
@@ -408,22 +264,16 @@ var NativeBridge = class {
|
|
|
408
264
|
"PURCHASE_ERROR" /* PURCHASE_ERROR */
|
|
409
265
|
];
|
|
410
266
|
if (!nativeResponseTypes.includes(data.type)) {
|
|
411
|
-
logger.debug(`[NativeBridge] Ignoring non-native-response message: ${data.type}`);
|
|
412
267
|
return;
|
|
413
268
|
}
|
|
414
|
-
console.log("[NativeBridge] [HANDLER] Message type:", data.type);
|
|
415
269
|
const handler = this.handlers.get(data.type);
|
|
416
270
|
if (handler) {
|
|
417
|
-
|
|
418
|
-
logger.info(`[NativeBridge] Handling message: ${data.type}`, data.payload);
|
|
271
|
+
logger.debug(`[NativeBridge] Handling message: ${data.type}`);
|
|
419
272
|
handler(data.payload);
|
|
420
|
-
console.log(`[NativeBridge] [HANDLER] \u2713 Handler completed for: ${data.type}`);
|
|
421
273
|
} else {
|
|
422
|
-
console.warn(`[NativeBridge] [HANDLER] \u26A0 No handler registered for: ${data.type}`);
|
|
423
274
|
logger.warn(`[NativeBridge] No handler registered for: ${data.type}`);
|
|
424
275
|
}
|
|
425
276
|
} catch (error) {
|
|
426
|
-
console.error("[NativeBridge] [HANDLER] \u274C Error handling native message:", error);
|
|
427
277
|
logger.error("[NativeBridge] Error handling native message:", error);
|
|
428
278
|
}
|
|
429
279
|
}
|
|
@@ -433,27 +283,15 @@ var NativeBridge = class {
|
|
|
433
283
|
* @param payload Optional payload data
|
|
434
284
|
*/
|
|
435
285
|
static send(type, payload) {
|
|
436
|
-
console.log(`[NativeBridge] [SEND] Attempting to send message: ${type}`);
|
|
437
286
|
if (!this.isNativeContext()) {
|
|
438
|
-
|
|
439
|
-
logger.debug(
|
|
440
|
-
`[NativeBridge] Not in native context, skipping message: ${type}`
|
|
441
|
-
);
|
|
287
|
+
logger.debug(`[NativeBridge] Not in native context, skipping message: ${type}`);
|
|
442
288
|
return;
|
|
443
289
|
}
|
|
444
|
-
console.log(`[NativeBridge] [SEND] \u2713 In native context, sending message`);
|
|
445
290
|
try {
|
|
446
|
-
const message = {
|
|
447
|
-
type,
|
|
448
|
-
payload,
|
|
449
|
-
timestamp: Date.now()
|
|
450
|
-
};
|
|
451
|
-
console.log(`[NativeBridge] [SEND] Message object:`, message);
|
|
291
|
+
const message = { type, payload, timestamp: Date.now() };
|
|
452
292
|
window.ReactNativeWebView.postMessage(JSON.stringify(message));
|
|
453
|
-
console.log(`[NativeBridge] [SEND] \u2713 Message sent to native: ${type}`);
|
|
454
293
|
logger.debug(`[NativeBridge] Sent message to native: ${type}`, payload);
|
|
455
294
|
} catch (error) {
|
|
456
|
-
console.error(`[NativeBridge] [SEND] \u274C\u274C Error sending message to native:`, error);
|
|
457
295
|
logger.error(`[NativeBridge] Error sending message to native:`, error);
|
|
458
296
|
}
|
|
459
297
|
}
|
|
@@ -463,10 +301,7 @@ var NativeBridge = class {
|
|
|
463
301
|
* @param handler Function to call when message is received
|
|
464
302
|
*/
|
|
465
303
|
static on(type, handler) {
|
|
466
|
-
console.log(`[NativeBridge] [ON] Registering handler for: ${type}`);
|
|
467
304
|
this.handlers.set(type, handler);
|
|
468
|
-
console.log(`[NativeBridge] [ON] \u2713 Handler registered. Total handlers:`, this.handlers.size);
|
|
469
|
-
console.log(`[NativeBridge] [ON] All registered types:`, Array.from(this.handlers.keys()));
|
|
470
305
|
logger.debug(`[NativeBridge] Registered handler for: ${type}`);
|
|
471
306
|
}
|
|
472
307
|
/**
|
|
@@ -616,7 +451,6 @@ function generateUUID() {
|
|
|
616
451
|
// src/services/ads.ts
|
|
617
452
|
var AdsService = class {
|
|
618
453
|
config = {
|
|
619
|
-
enabled: false,
|
|
620
454
|
sound: "on",
|
|
621
455
|
debug: false,
|
|
622
456
|
onBeforeAd: () => {
|
|
@@ -626,87 +460,69 @@ var AdsService = class {
|
|
|
626
460
|
onRewardEarned: () => {
|
|
627
461
|
}
|
|
628
462
|
};
|
|
629
|
-
|
|
463
|
+
// Cached init promise — ensures adConfig() is only called once
|
|
464
|
+
initPromise = null;
|
|
630
465
|
ready = false;
|
|
631
466
|
/**
|
|
632
|
-
*
|
|
633
|
-
*
|
|
467
|
+
* Optionally configure the ads service.
|
|
468
|
+
* Not required — ads work without calling this.
|
|
634
469
|
*/
|
|
635
470
|
configure(config) {
|
|
636
471
|
this.config = {
|
|
637
472
|
...this.config,
|
|
638
473
|
...config,
|
|
639
|
-
onBeforeAd: config.onBeforeAd
|
|
640
|
-
onAfterAd: config.onAfterAd
|
|
641
|
-
onRewardEarned: config.onRewardEarned
|
|
474
|
+
onBeforeAd: config.onBeforeAd ?? this.config.onBeforeAd,
|
|
475
|
+
onAfterAd: config.onAfterAd ?? this.config.onAfterAd,
|
|
476
|
+
onRewardEarned: config.onRewardEarned ?? this.config.onRewardEarned
|
|
642
477
|
};
|
|
643
478
|
if (this.config.debug) {
|
|
644
|
-
|
|
645
|
-
enabled: this.config.enabled,
|
|
646
|
-
sound: this.config.sound
|
|
647
|
-
});
|
|
648
|
-
}
|
|
649
|
-
if (this.config.enabled && !this.initialized) {
|
|
650
|
-
this.initialize();
|
|
479
|
+
logger.debug("[AdsService] Configuration updated:", { sound: this.config.sound });
|
|
651
480
|
}
|
|
652
481
|
}
|
|
653
482
|
/**
|
|
654
|
-
* Initialize the
|
|
483
|
+
* Initialize the Google H5 Ads system.
|
|
484
|
+
* Returns a cached promise — safe to call multiple times.
|
|
655
485
|
*/
|
|
656
486
|
initialize() {
|
|
657
|
-
if (this.
|
|
658
|
-
|
|
659
|
-
if (
|
|
660
|
-
console.log("[AdsService] Ads disabled, skipping initialization");
|
|
661
|
-
}
|
|
662
|
-
return;
|
|
663
|
-
}
|
|
664
|
-
if (!window.adConfig || !window.adBreak) {
|
|
665
|
-
console.warn("[AdsService] Google Ads SDK not found. Ads will not be available.");
|
|
666
|
-
return;
|
|
667
|
-
}
|
|
668
|
-
if (this.config.debug) {
|
|
669
|
-
console.log("[AdsService] Initializing ads system...");
|
|
670
|
-
}
|
|
671
|
-
const googleConfig = {
|
|
672
|
-
sound: this.config.sound,
|
|
673
|
-
preloadAdBreaks: "on",
|
|
674
|
-
onReady: () => {
|
|
675
|
-
this.ready = true;
|
|
487
|
+
if (this.initPromise) return this.initPromise;
|
|
488
|
+
this.initPromise = new Promise((resolve) => {
|
|
489
|
+
if (!window.adConfig || !window.adBreak) {
|
|
676
490
|
if (this.config.debug) {
|
|
677
|
-
|
|
491
|
+
logger.debug("[AdsService] Google Ads SDK not found \u2014 ads unavailable");
|
|
678
492
|
}
|
|
493
|
+
resolve(false);
|
|
494
|
+
return;
|
|
679
495
|
}
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
496
|
+
if (this.config.debug) {
|
|
497
|
+
logger.debug("[AdsService] Initializing ads system...");
|
|
498
|
+
}
|
|
499
|
+
window.adConfig({
|
|
500
|
+
sound: this.config.sound,
|
|
501
|
+
preloadAdBreaks: "on",
|
|
502
|
+
onReady: () => {
|
|
503
|
+
this.ready = true;
|
|
504
|
+
if (this.config.debug) {
|
|
505
|
+
logger.debug("[AdsService] Ads ready");
|
|
506
|
+
}
|
|
507
|
+
resolve(true);
|
|
508
|
+
}
|
|
509
|
+
});
|
|
510
|
+
setTimeout(() => resolve(false), 5e3);
|
|
511
|
+
});
|
|
512
|
+
return this.initPromise;
|
|
683
513
|
}
|
|
684
514
|
/**
|
|
685
|
-
* Show an ad
|
|
686
|
-
* Returns immediately if ads are disabled
|
|
515
|
+
* Show an ad. Auto-initializes on the first call.
|
|
516
|
+
* Returns immediately with success: false if ads are disabled or unavailable.
|
|
687
517
|
*/
|
|
688
518
|
async show(type) {
|
|
689
519
|
const requestedAt = Date.now();
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
console.log("[AdsService] Ads disabled, skipping ad request");
|
|
693
|
-
}
|
|
520
|
+
const ready = await this.initialize();
|
|
521
|
+
if (!ready || !window.adBreak) {
|
|
694
522
|
return {
|
|
695
523
|
success: false,
|
|
696
524
|
type,
|
|
697
|
-
error: new Error("Ads
|
|
698
|
-
requestedAt,
|
|
699
|
-
completedAt: Date.now()
|
|
700
|
-
};
|
|
701
|
-
}
|
|
702
|
-
if (!this.initialized) {
|
|
703
|
-
this.initialize();
|
|
704
|
-
}
|
|
705
|
-
if (!this.ready || !window.adBreak) {
|
|
706
|
-
return {
|
|
707
|
-
success: false,
|
|
708
|
-
type,
|
|
709
|
-
error: new Error("Ads not ready"),
|
|
525
|
+
error: new Error("Ads not available"),
|
|
710
526
|
requestedAt,
|
|
711
527
|
completedAt: Date.now()
|
|
712
528
|
};
|
|
@@ -714,7 +530,7 @@ var AdsService = class {
|
|
|
714
530
|
return this.showAdBreak(type);
|
|
715
531
|
}
|
|
716
532
|
/**
|
|
717
|
-
* Show an ad break
|
|
533
|
+
* Show an ad break via the Google H5 API.
|
|
718
534
|
*/
|
|
719
535
|
async showAdBreak(type) {
|
|
720
536
|
const requestedAt = Date.now();
|
|
@@ -722,81 +538,48 @@ var AdsService = class {
|
|
|
722
538
|
const googleType = type === "rewarded" ? "reward" : type === "preroll" ? "start" : "next";
|
|
723
539
|
const adName = `${type}-ad-${Date.now()}`;
|
|
724
540
|
if (this.config.debug) {
|
|
725
|
-
|
|
541
|
+
logger.debug(`[AdsService] Showing ${type} ad`);
|
|
726
542
|
}
|
|
727
543
|
this.config.onBeforeAd(type);
|
|
728
544
|
const adBreakConfig = {
|
|
729
545
|
type: googleType,
|
|
730
546
|
name: adName,
|
|
731
|
-
beforeAd: () => {
|
|
732
|
-
if (this.config.debug) {
|
|
733
|
-
console.log("[AdsService] Ad started");
|
|
734
|
-
}
|
|
735
|
-
},
|
|
736
547
|
afterAd: () => {
|
|
737
|
-
if (this.config.debug) {
|
|
738
|
-
console.log("[AdsService] Ad finished");
|
|
739
|
-
}
|
|
740
548
|
this.config.onAfterAd(type);
|
|
741
549
|
},
|
|
742
550
|
adBreakDone: (info) => {
|
|
743
551
|
const completedAt = Date.now();
|
|
744
|
-
|
|
745
|
-
if (type === "rewarded") {
|
|
746
|
-
success = info?.breakStatus === "viewed";
|
|
747
|
-
} else {
|
|
748
|
-
success = info?.breakStatus !== "error";
|
|
749
|
-
}
|
|
552
|
+
const success = type === "rewarded" ? info?.breakStatus === "viewed" : info?.breakStatus !== "error";
|
|
750
553
|
const error = info?.breakStatus === "error" && info?.error ? new Error(info.error) : void 0;
|
|
751
554
|
if (this.config.debug) {
|
|
752
|
-
|
|
753
|
-
success,
|
|
754
|
-
status: info?.breakStatus
|
|
755
|
-
});
|
|
555
|
+
logger.debug("[AdsService] Ad break done:", { success, status: info?.breakStatus });
|
|
756
556
|
}
|
|
757
|
-
|
|
758
|
-
success,
|
|
759
|
-
type,
|
|
760
|
-
error,
|
|
761
|
-
requestedAt,
|
|
762
|
-
completedAt
|
|
763
|
-
};
|
|
764
|
-
resolve(result);
|
|
557
|
+
resolve({ success, type, error, requestedAt, completedAt });
|
|
765
558
|
}
|
|
766
559
|
};
|
|
767
560
|
if (type === "rewarded") {
|
|
768
|
-
adBreakConfig.beforeReward = (showAdFn) =>
|
|
769
|
-
|
|
770
|
-
console.log("[AdsService] beforeReward callback");
|
|
771
|
-
}
|
|
772
|
-
showAdFn();
|
|
773
|
-
};
|
|
774
|
-
adBreakConfig.adViewed = () => {
|
|
775
|
-
if (this.config.debug) {
|
|
776
|
-
console.log("[AdsService] Rewarded ad watched successfully");
|
|
777
|
-
}
|
|
778
|
-
this.config.onRewardEarned();
|
|
779
|
-
};
|
|
780
|
-
adBreakConfig.adDismissed = () => {
|
|
781
|
-
if (this.config.debug) {
|
|
782
|
-
console.log("[AdsService] Rewarded ad dismissed");
|
|
783
|
-
}
|
|
784
|
-
};
|
|
561
|
+
adBreakConfig.beforeReward = (showAdFn) => showAdFn();
|
|
562
|
+
adBreakConfig.adViewed = () => this.config.onRewardEarned();
|
|
785
563
|
}
|
|
786
564
|
window.adBreak(adBreakConfig);
|
|
787
565
|
});
|
|
788
566
|
}
|
|
789
567
|
/**
|
|
790
|
-
*
|
|
568
|
+
* Returns the configured ad lifecycle callbacks.
|
|
569
|
+
* Used by platform-specific providers (e.g. Playgama) to fire the same hooks.
|
|
791
570
|
*/
|
|
792
|
-
|
|
793
|
-
return
|
|
571
|
+
getCallbacks() {
|
|
572
|
+
return {
|
|
573
|
+
onBeforeAd: this.config.onBeforeAd,
|
|
574
|
+
onAfterAd: this.config.onAfterAd,
|
|
575
|
+
onRewardEarned: this.config.onRewardEarned
|
|
576
|
+
};
|
|
794
577
|
}
|
|
795
578
|
/**
|
|
796
|
-
* Check if ads are ready to show
|
|
579
|
+
* Check if ads have successfully initialized and are ready to show.
|
|
797
580
|
*/
|
|
798
581
|
isReady() {
|
|
799
|
-
return this.
|
|
582
|
+
return this.ready;
|
|
800
583
|
}
|
|
801
584
|
};
|
|
802
585
|
|
|
@@ -843,16 +626,17 @@ var PlaygamaService = class {
|
|
|
843
626
|
this.initialized = true;
|
|
844
627
|
return true;
|
|
845
628
|
} catch (error) {
|
|
846
|
-
|
|
629
|
+
logger.warn("[PlaygamaService] Failed to initialize:", error);
|
|
847
630
|
return false;
|
|
848
631
|
}
|
|
849
632
|
}
|
|
850
633
|
isInitialized() {
|
|
851
634
|
return this.initialized;
|
|
852
635
|
}
|
|
853
|
-
async showInterstitial() {
|
|
636
|
+
async showInterstitial(callbacks) {
|
|
854
637
|
const requestedAt = Date.now();
|
|
855
|
-
|
|
638
|
+
const bridge = window.bridge;
|
|
639
|
+
if (!this.initialized || !bridge) {
|
|
856
640
|
return {
|
|
857
641
|
success: false,
|
|
858
642
|
type: "interstitial",
|
|
@@ -861,13 +645,26 @@ var PlaygamaService = class {
|
|
|
861
645
|
completedAt: Date.now()
|
|
862
646
|
};
|
|
863
647
|
}
|
|
648
|
+
if (!bridge.advertisement.isInterstitialSupported) {
|
|
649
|
+
return {
|
|
650
|
+
success: false,
|
|
651
|
+
type: "interstitial",
|
|
652
|
+
error: new Error("Interstitial ads not supported on this platform"),
|
|
653
|
+
requestedAt,
|
|
654
|
+
completedAt: Date.now()
|
|
655
|
+
};
|
|
656
|
+
}
|
|
864
657
|
return new Promise((resolve) => {
|
|
865
658
|
const handler = (state) => {
|
|
866
|
-
if (state === "
|
|
867
|
-
|
|
659
|
+
if (state === "opened") {
|
|
660
|
+
callbacks?.onBeforeAd?.();
|
|
661
|
+
} else if (state === "closed") {
|
|
662
|
+
bridge.advertisement.off(bridge.EVENT_NAME.INTERSTITIAL_STATE_CHANGED, handler);
|
|
663
|
+
callbacks?.onAfterAd?.();
|
|
868
664
|
resolve({ success: true, type: "interstitial", requestedAt, completedAt: Date.now() });
|
|
869
665
|
} else if (state === "failed") {
|
|
870
|
-
|
|
666
|
+
bridge.advertisement.off(bridge.EVENT_NAME.INTERSTITIAL_STATE_CHANGED, handler);
|
|
667
|
+
callbacks?.onAfterAd?.();
|
|
871
668
|
resolve({
|
|
872
669
|
success: false,
|
|
873
670
|
type: "interstitial",
|
|
@@ -877,13 +674,14 @@ var PlaygamaService = class {
|
|
|
877
674
|
});
|
|
878
675
|
}
|
|
879
676
|
};
|
|
880
|
-
|
|
881
|
-
|
|
677
|
+
bridge.advertisement.on(bridge.EVENT_NAME.INTERSTITIAL_STATE_CHANGED, handler);
|
|
678
|
+
bridge.advertisement.showInterstitial();
|
|
882
679
|
});
|
|
883
680
|
}
|
|
884
|
-
async showRewarded() {
|
|
681
|
+
async showRewarded(callbacks) {
|
|
885
682
|
const requestedAt = Date.now();
|
|
886
|
-
|
|
683
|
+
const bridge = window.bridge;
|
|
684
|
+
if (!this.initialized || !bridge) {
|
|
887
685
|
return {
|
|
888
686
|
success: false,
|
|
889
687
|
type: "rewarded",
|
|
@@ -892,13 +690,26 @@ var PlaygamaService = class {
|
|
|
892
690
|
completedAt: Date.now()
|
|
893
691
|
};
|
|
894
692
|
}
|
|
693
|
+
if (!bridge.advertisement.isRewardedSupported) {
|
|
694
|
+
return {
|
|
695
|
+
success: false,
|
|
696
|
+
type: "rewarded",
|
|
697
|
+
error: new Error("Rewarded ads not supported on this platform"),
|
|
698
|
+
requestedAt,
|
|
699
|
+
completedAt: Date.now()
|
|
700
|
+
};
|
|
701
|
+
}
|
|
895
702
|
return new Promise((resolve) => {
|
|
896
703
|
let rewarded = false;
|
|
897
704
|
const handler = (state) => {
|
|
898
|
-
if (state === "
|
|
705
|
+
if (state === "opened") {
|
|
706
|
+
callbacks?.onBeforeAd?.();
|
|
707
|
+
} else if (state === "rewarded") {
|
|
899
708
|
rewarded = true;
|
|
709
|
+
callbacks?.onRewardEarned?.();
|
|
900
710
|
} else if (state === "closed" || state === "failed") {
|
|
901
|
-
|
|
711
|
+
bridge.advertisement.off(bridge.EVENT_NAME.REWARDED_STATE_CHANGED, handler);
|
|
712
|
+
callbacks?.onAfterAd?.();
|
|
902
713
|
resolve({
|
|
903
714
|
success: rewarded,
|
|
904
715
|
type: "rewarded",
|
|
@@ -908,8 +719,8 @@ var PlaygamaService = class {
|
|
|
908
719
|
});
|
|
909
720
|
}
|
|
910
721
|
};
|
|
911
|
-
|
|
912
|
-
|
|
722
|
+
bridge.advertisement.on(bridge.EVENT_NAME.REWARDED_STATE_CHANGED, handler);
|
|
723
|
+
bridge.advertisement.showRewarded();
|
|
913
724
|
});
|
|
914
725
|
}
|
|
915
726
|
loadScript() {
|
|
@@ -927,6 +738,176 @@ var PlaygamaService = class {
|
|
|
927
738
|
}
|
|
928
739
|
};
|
|
929
740
|
|
|
741
|
+
// src/services/crazygames.ts
|
|
742
|
+
var CRAZYGAMES_SDK_CDN = "https://sdk.crazygames.com/crazygames-sdk-v2.js";
|
|
743
|
+
var CrazyGamesService = class {
|
|
744
|
+
initialized = false;
|
|
745
|
+
/**
|
|
746
|
+
* Detects if the game is running on the CrazyGames platform.
|
|
747
|
+
* Games on CrazyGames run inside an iframe, so we check document.referrer
|
|
748
|
+
* and attempt to read the parent frame location.
|
|
749
|
+
*/
|
|
750
|
+
static isCrazyGamesDomain() {
|
|
751
|
+
try {
|
|
752
|
+
if (document.referrer.includes("crazygames.com")) {
|
|
753
|
+
return true;
|
|
754
|
+
}
|
|
755
|
+
if (window !== window.top) {
|
|
756
|
+
try {
|
|
757
|
+
if (window.parent.location.hostname.includes("crazygames.com")) {
|
|
758
|
+
return true;
|
|
759
|
+
}
|
|
760
|
+
} catch {
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
return false;
|
|
764
|
+
} catch {
|
|
765
|
+
return false;
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
/**
|
|
769
|
+
* Loads the CrazyGames SDK from CDN and confirms the environment is 'crazygames'.
|
|
770
|
+
* Safe to call multiple times — resolves immediately if already initialized.
|
|
771
|
+
*/
|
|
772
|
+
async initialize() {
|
|
773
|
+
if (this.initialized) return true;
|
|
774
|
+
try {
|
|
775
|
+
await this.loadScript();
|
|
776
|
+
const sdk = window.CrazyGames?.SDK;
|
|
777
|
+
if (!sdk) {
|
|
778
|
+
logger.warn("[CrazyGamesService] SDK not found after script load");
|
|
779
|
+
return false;
|
|
780
|
+
}
|
|
781
|
+
const env = await sdk.getEnvironment();
|
|
782
|
+
if (env !== "crazygames" && env !== "local") {
|
|
783
|
+
logger.warn("[CrazyGamesService] Unexpected environment:", env);
|
|
784
|
+
return false;
|
|
785
|
+
}
|
|
786
|
+
this.initialized = true;
|
|
787
|
+
return true;
|
|
788
|
+
} catch (error) {
|
|
789
|
+
logger.warn("[CrazyGamesService] Failed to initialize:", error);
|
|
790
|
+
return false;
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
isInitialized() {
|
|
794
|
+
return this.initialized;
|
|
795
|
+
}
|
|
796
|
+
/**
|
|
797
|
+
* Shows a midgame (interstitial) ad via the CrazyGames SDK.
|
|
798
|
+
*/
|
|
799
|
+
async showInterstitial(callbacks) {
|
|
800
|
+
const requestedAt = Date.now();
|
|
801
|
+
const sdk = window.CrazyGames?.SDK;
|
|
802
|
+
if (!this.initialized || !sdk) {
|
|
803
|
+
return {
|
|
804
|
+
success: false,
|
|
805
|
+
type: "interstitial",
|
|
806
|
+
error: new Error("CrazyGames SDK not initialized"),
|
|
807
|
+
requestedAt,
|
|
808
|
+
completedAt: Date.now()
|
|
809
|
+
};
|
|
810
|
+
}
|
|
811
|
+
return new Promise((resolve) => {
|
|
812
|
+
sdk.ad.requestAd("midgame", {
|
|
813
|
+
adStarted: () => {
|
|
814
|
+
callbacks?.onBeforeAd?.();
|
|
815
|
+
},
|
|
816
|
+
adFinished: () => {
|
|
817
|
+
callbacks?.onAfterAd?.();
|
|
818
|
+
resolve({ success: true, type: "interstitial", requestedAt, completedAt: Date.now() });
|
|
819
|
+
},
|
|
820
|
+
adError: (error) => {
|
|
821
|
+
callbacks?.onAfterAd?.();
|
|
822
|
+
resolve({
|
|
823
|
+
success: false,
|
|
824
|
+
type: "interstitial",
|
|
825
|
+
error: new Error(`CrazyGames interstitial ad error: ${error}`),
|
|
826
|
+
requestedAt,
|
|
827
|
+
completedAt: Date.now()
|
|
828
|
+
});
|
|
829
|
+
}
|
|
830
|
+
});
|
|
831
|
+
});
|
|
832
|
+
}
|
|
833
|
+
/**
|
|
834
|
+
* Shows a rewarded ad via the CrazyGames SDK.
|
|
835
|
+
* Resolves with success: true only when adFinished fires (ad was fully watched).
|
|
836
|
+
*/
|
|
837
|
+
async showRewarded(callbacks) {
|
|
838
|
+
const requestedAt = Date.now();
|
|
839
|
+
const sdk = window.CrazyGames?.SDK;
|
|
840
|
+
if (!this.initialized || !sdk) {
|
|
841
|
+
return {
|
|
842
|
+
success: false,
|
|
843
|
+
type: "rewarded",
|
|
844
|
+
error: new Error("CrazyGames SDK not initialized"),
|
|
845
|
+
requestedAt,
|
|
846
|
+
completedAt: Date.now()
|
|
847
|
+
};
|
|
848
|
+
}
|
|
849
|
+
return new Promise((resolve) => {
|
|
850
|
+
sdk.ad.requestAd("rewarded", {
|
|
851
|
+
adStarted: () => {
|
|
852
|
+
callbacks?.onBeforeAd?.();
|
|
853
|
+
},
|
|
854
|
+
adFinished: () => {
|
|
855
|
+
callbacks?.onRewardEarned?.();
|
|
856
|
+
callbacks?.onAfterAd?.();
|
|
857
|
+
resolve({ success: true, type: "rewarded", requestedAt, completedAt: Date.now() });
|
|
858
|
+
},
|
|
859
|
+
adError: (error) => {
|
|
860
|
+
callbacks?.onAfterAd?.();
|
|
861
|
+
resolve({
|
|
862
|
+
success: false,
|
|
863
|
+
type: "rewarded",
|
|
864
|
+
error: new Error(`CrazyGames rewarded ad error: ${error}`),
|
|
865
|
+
requestedAt,
|
|
866
|
+
completedAt: Date.now()
|
|
867
|
+
});
|
|
868
|
+
}
|
|
869
|
+
});
|
|
870
|
+
});
|
|
871
|
+
}
|
|
872
|
+
/**
|
|
873
|
+
* Notifies CrazyGames that gameplay has started.
|
|
874
|
+
* Call when the player actively begins or resumes playing.
|
|
875
|
+
*/
|
|
876
|
+
gameplayStart() {
|
|
877
|
+
if (!this.initialized) return;
|
|
878
|
+
window.CrazyGames?.SDK.game.gameplayStart();
|
|
879
|
+
}
|
|
880
|
+
/**
|
|
881
|
+
* Notifies CrazyGames that gameplay has stopped.
|
|
882
|
+
* Call during menu access, level completion, or pausing.
|
|
883
|
+
*/
|
|
884
|
+
gameplayStop() {
|
|
885
|
+
if (!this.initialized) return;
|
|
886
|
+
window.CrazyGames?.SDK.game.gameplayStop();
|
|
887
|
+
}
|
|
888
|
+
/**
|
|
889
|
+
* Triggers a celebration effect on the CrazyGames website.
|
|
890
|
+
* Use sparingly for significant achievements (boss defeat, personal record, etc.)
|
|
891
|
+
*/
|
|
892
|
+
happytime() {
|
|
893
|
+
if (!this.initialized) return;
|
|
894
|
+
window.CrazyGames?.SDK.game.happytime();
|
|
895
|
+
}
|
|
896
|
+
loadScript() {
|
|
897
|
+
return new Promise((resolve, reject) => {
|
|
898
|
+
if (window.CrazyGames?.SDK) {
|
|
899
|
+
resolve();
|
|
900
|
+
return;
|
|
901
|
+
}
|
|
902
|
+
const script = document.createElement("script");
|
|
903
|
+
script.src = CRAZYGAMES_SDK_CDN;
|
|
904
|
+
script.onload = () => resolve();
|
|
905
|
+
script.onerror = () => reject(new Error("Failed to load CrazyGames SDK script"));
|
|
906
|
+
document.head.appendChild(script);
|
|
907
|
+
});
|
|
908
|
+
}
|
|
909
|
+
};
|
|
910
|
+
|
|
930
911
|
// src/services/billing.ts
|
|
931
912
|
var BillingPlatform = /* @__PURE__ */ ((BillingPlatform3) => {
|
|
932
913
|
BillingPlatform3["WEB"] = "web";
|
|
@@ -937,41 +918,28 @@ var BillingPlatform = /* @__PURE__ */ ((BillingPlatform3) => {
|
|
|
937
918
|
var BillingService = class {
|
|
938
919
|
config;
|
|
939
920
|
platform = "unknown" /* UNKNOWN */;
|
|
940
|
-
|
|
921
|
+
initPromise = null;
|
|
941
922
|
nativeAvailable = false;
|
|
942
|
-
products = [];
|
|
943
923
|
// Stripe instance for web payments
|
|
944
924
|
stripe = null;
|
|
945
925
|
checkoutElement = null;
|
|
946
926
|
// Callbacks for purchase events
|
|
947
927
|
onPurchaseCompleteCallback;
|
|
948
928
|
onPurchaseErrorCallback;
|
|
949
|
-
// Callback for logging (so external UI can capture logs)
|
|
950
|
-
onLogCallback;
|
|
951
929
|
constructor(config) {
|
|
952
930
|
this.config = config;
|
|
953
931
|
this.detectPlatform();
|
|
954
932
|
}
|
|
955
933
|
/**
|
|
956
|
-
*
|
|
957
|
-
* Useful for displaying logs in a UI
|
|
958
|
-
*/
|
|
959
|
-
onLog(callback) {
|
|
960
|
-
this.onLogCallback = callback;
|
|
961
|
-
}
|
|
962
|
-
/**
|
|
963
|
-
* Internal logging method that calls both logger and custom callback
|
|
934
|
+
* Update billing configuration. Resets initialization so next call re-inits with new config.
|
|
964
935
|
*/
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
}
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
if (this.onLogCallback) {
|
|
973
|
-
this.onLogCallback(level, message, data);
|
|
974
|
-
}
|
|
936
|
+
configure(config) {
|
|
937
|
+
this.config = {
|
|
938
|
+
...this.config,
|
|
939
|
+
...config
|
|
940
|
+
};
|
|
941
|
+
this.initPromise = null;
|
|
942
|
+
logger.info("[BillingService] Configuration updated");
|
|
975
943
|
}
|
|
976
944
|
/**
|
|
977
945
|
* Detects if running on web or native platform
|
|
@@ -981,13 +949,13 @@ var BillingService = class {
|
|
|
981
949
|
const hasWindow = typeof window !== "undefined";
|
|
982
950
|
if (isNative) {
|
|
983
951
|
this.platform = "native" /* NATIVE */;
|
|
984
|
-
|
|
952
|
+
logger.info("[BillingService] Platform: NATIVE");
|
|
985
953
|
} else if (hasWindow) {
|
|
986
954
|
this.platform = "web" /* WEB */;
|
|
987
|
-
|
|
955
|
+
logger.info("[BillingService] Platform: WEB");
|
|
988
956
|
} else {
|
|
989
957
|
this.platform = "unknown" /* UNKNOWN */;
|
|
990
|
-
|
|
958
|
+
logger.warn("[BillingService] Platform: UNKNOWN");
|
|
991
959
|
}
|
|
992
960
|
}
|
|
993
961
|
/**
|
|
@@ -997,38 +965,40 @@ var BillingService = class {
|
|
|
997
965
|
return this.platform;
|
|
998
966
|
}
|
|
999
967
|
/**
|
|
1000
|
-
* Initialize the billing service
|
|
1001
|
-
*
|
|
968
|
+
* Initialize the billing service. Idempotent — returns cached promise on subsequent calls.
|
|
969
|
+
* Called automatically by getProducts() and purchase().
|
|
1002
970
|
*/
|
|
1003
|
-
|
|
1004
|
-
if (this.
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
971
|
+
initialize() {
|
|
972
|
+
if (this.initPromise) return this.initPromise;
|
|
973
|
+
logger.info(`[BillingService] Initializing for ${this.platform} platform...`);
|
|
974
|
+
this.initPromise = (async () => {
|
|
975
|
+
try {
|
|
976
|
+
if (this.platform === "native" /* NATIVE */) {
|
|
977
|
+
return await this.initializeNative();
|
|
978
|
+
} else if (this.platform === "web" /* WEB */) {
|
|
979
|
+
return await this.initializeWeb();
|
|
980
|
+
}
|
|
981
|
+
logger.error("[BillingService] Cannot initialize: unknown platform");
|
|
982
|
+
return false;
|
|
983
|
+
} catch (error) {
|
|
984
|
+
logger.error("[BillingService] Initialization failed:", error?.message);
|
|
985
|
+
return false;
|
|
1013
986
|
}
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
} catch (error) {
|
|
1017
|
-
this.log("error", "[BillingService] Initialization failed:", error?.message);
|
|
1018
|
-
return false;
|
|
1019
|
-
}
|
|
987
|
+
})();
|
|
988
|
+
return this.initPromise;
|
|
1020
989
|
}
|
|
1021
990
|
/**
|
|
1022
991
|
* Initialize native billing
|
|
1023
992
|
*/
|
|
1024
993
|
async initializeNative() {
|
|
1025
994
|
return new Promise((resolve) => {
|
|
995
|
+
let resolved = false;
|
|
1026
996
|
try {
|
|
1027
997
|
NativeBridge.initialize();
|
|
1028
998
|
NativeBridge.on(
|
|
1029
999
|
"PURCHASE_COMPLETE" /* PURCHASE_COMPLETE */,
|
|
1030
1000
|
(payload) => {
|
|
1031
|
-
|
|
1001
|
+
logger.info("[BillingService] Purchase complete:", payload.productId);
|
|
1032
1002
|
const result = {
|
|
1033
1003
|
success: true,
|
|
1034
1004
|
productId: payload.productId,
|
|
@@ -1043,7 +1013,7 @@ var BillingService = class {
|
|
|
1043
1013
|
NativeBridge.on(
|
|
1044
1014
|
"PURCHASE_ERROR" /* PURCHASE_ERROR */,
|
|
1045
1015
|
(payload) => {
|
|
1046
|
-
|
|
1016
|
+
logger.error("[BillingService] Purchase error:", payload.message);
|
|
1047
1017
|
const result = {
|
|
1048
1018
|
success: false,
|
|
1049
1019
|
productId: payload.productId || "unknown",
|
|
@@ -1061,8 +1031,8 @@ var BillingService = class {
|
|
|
1061
1031
|
"IAP_AVAILABILITY_RESULT" /* IAP_AVAILABILITY_RESULT */,
|
|
1062
1032
|
(payload) => {
|
|
1063
1033
|
this.nativeAvailable = payload.available;
|
|
1064
|
-
|
|
1065
|
-
|
|
1034
|
+
logger.info(`[BillingService] Native billing ${payload.available ? "available" : "unavailable"}`);
|
|
1035
|
+
resolved = true;
|
|
1066
1036
|
resolve(payload.available);
|
|
1067
1037
|
}
|
|
1068
1038
|
);
|
|
@@ -1070,15 +1040,15 @@ var BillingService = class {
|
|
|
1070
1040
|
NativeBridge.checkIAPAvailability();
|
|
1071
1041
|
}, 100);
|
|
1072
1042
|
setTimeout(() => {
|
|
1073
|
-
if (!
|
|
1074
|
-
|
|
1075
|
-
|
|
1043
|
+
if (!resolved) {
|
|
1044
|
+
logger.warn("[BillingService] Native initialization timeout");
|
|
1045
|
+
resolved = true;
|
|
1076
1046
|
resolve(false);
|
|
1077
1047
|
}
|
|
1078
1048
|
}, 5e3);
|
|
1079
1049
|
} catch (error) {
|
|
1080
|
-
|
|
1081
|
-
|
|
1050
|
+
logger.error("[BillingService] Native initialization failed:", error?.message);
|
|
1051
|
+
resolved = true;
|
|
1082
1052
|
resolve(false);
|
|
1083
1053
|
}
|
|
1084
1054
|
});
|
|
@@ -1088,12 +1058,12 @@ var BillingService = class {
|
|
|
1088
1058
|
*/
|
|
1089
1059
|
async initializeWeb() {
|
|
1090
1060
|
if (!this.config.stripePublishableKey) {
|
|
1091
|
-
|
|
1061
|
+
logger.error("[BillingService] Stripe publishable key not provided");
|
|
1092
1062
|
return false;
|
|
1093
1063
|
}
|
|
1094
1064
|
try {
|
|
1095
1065
|
if (typeof window === "undefined") {
|
|
1096
|
-
|
|
1066
|
+
logger.error("[BillingService] Window is undefined (not in browser)");
|
|
1097
1067
|
return false;
|
|
1098
1068
|
}
|
|
1099
1069
|
if (!window.Stripe) {
|
|
@@ -1101,15 +1071,14 @@ var BillingService = class {
|
|
|
1101
1071
|
}
|
|
1102
1072
|
if (window.Stripe) {
|
|
1103
1073
|
this.stripe = window.Stripe(this.config.stripePublishableKey);
|
|
1104
|
-
|
|
1105
|
-
this.log("info", "[BillingService] Web billing initialized");
|
|
1074
|
+
logger.info("[BillingService] Web billing initialized");
|
|
1106
1075
|
return true;
|
|
1107
1076
|
} else {
|
|
1108
|
-
|
|
1077
|
+
logger.error("[BillingService] Stripe not available after loading");
|
|
1109
1078
|
return false;
|
|
1110
1079
|
}
|
|
1111
1080
|
} catch (error) {
|
|
1112
|
-
|
|
1081
|
+
logger.error("[BillingService] Stripe initialization failed:", error?.message);
|
|
1113
1082
|
return false;
|
|
1114
1083
|
}
|
|
1115
1084
|
}
|
|
@@ -1134,11 +1103,11 @@ var BillingService = class {
|
|
|
1134
1103
|
script.src = "https://js.stripe.com/v3/";
|
|
1135
1104
|
script.async = true;
|
|
1136
1105
|
script.onload = () => {
|
|
1137
|
-
|
|
1106
|
+
logger.info("[BillingService] Stripe.js loaded");
|
|
1138
1107
|
resolve();
|
|
1139
1108
|
};
|
|
1140
1109
|
script.onerror = () => {
|
|
1141
|
-
|
|
1110
|
+
logger.error("[BillingService] Failed to load Stripe.js");
|
|
1142
1111
|
reject(new Error("Failed to load Stripe.js"));
|
|
1143
1112
|
};
|
|
1144
1113
|
try {
|
|
@@ -1149,12 +1118,9 @@ var BillingService = class {
|
|
|
1149
1118
|
});
|
|
1150
1119
|
}
|
|
1151
1120
|
/**
|
|
1152
|
-
* Check if billing is available
|
|
1121
|
+
* Check if billing is available based on current config and platform state
|
|
1153
1122
|
*/
|
|
1154
1123
|
isAvailable() {
|
|
1155
|
-
if (!this.isInitialized) {
|
|
1156
|
-
return false;
|
|
1157
|
-
}
|
|
1158
1124
|
if (this.platform === "native" /* NATIVE */) {
|
|
1159
1125
|
return this.nativeAvailable;
|
|
1160
1126
|
} else if (this.platform === "web" /* WEB */) {
|
|
@@ -1163,12 +1129,10 @@ var BillingService = class {
|
|
|
1163
1129
|
return false;
|
|
1164
1130
|
}
|
|
1165
1131
|
/**
|
|
1166
|
-
* Get available products
|
|
1132
|
+
* Get available products. Auto-initializes on first call.
|
|
1167
1133
|
*/
|
|
1168
1134
|
async getProducts() {
|
|
1169
|
-
|
|
1170
|
-
throw new Error("BillingService not initialized. Call initialize() first.");
|
|
1171
|
-
}
|
|
1135
|
+
await this.initialize();
|
|
1172
1136
|
if (this.platform === "native" /* NATIVE */) {
|
|
1173
1137
|
return await this.getProductsNative();
|
|
1174
1138
|
} else if (this.platform === "web" /* WEB */) {
|
|
@@ -1180,38 +1144,31 @@ var BillingService = class {
|
|
|
1180
1144
|
* Get products from native IAP
|
|
1181
1145
|
*/
|
|
1182
1146
|
async getProductsNative() {
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
}
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
this.log("info", `[BillingService] Fetched ${this.products.length} native products`);
|
|
1209
|
-
resolve(this.products);
|
|
1210
|
-
}).catch((error) => {
|
|
1211
|
-
this.log("error", "[BillingService] Failed to fetch native products:", error?.message);
|
|
1212
|
-
reject(error);
|
|
1213
|
-
});
|
|
1214
|
-
});
|
|
1147
|
+
if (!this.config.gameId || !this.config.checkoutUrl) {
|
|
1148
|
+
const error = new Error("gameId and checkoutUrl required for native purchases");
|
|
1149
|
+
logger.error("[BillingService]", error.message);
|
|
1150
|
+
throw error;
|
|
1151
|
+
}
|
|
1152
|
+
const response = await fetch(
|
|
1153
|
+
`${this.config.checkoutUrl}/get-native-packages?game_id=${this.config.gameId}`
|
|
1154
|
+
);
|
|
1155
|
+
if (!response.ok) {
|
|
1156
|
+
throw new Error(`Failed to fetch native products: ${response.status}`);
|
|
1157
|
+
}
|
|
1158
|
+
const data = await response.json();
|
|
1159
|
+
if (!data.packages || !Array.isArray(data.packages)) {
|
|
1160
|
+
throw new Error("Invalid response format: missing packages array");
|
|
1161
|
+
}
|
|
1162
|
+
const products = data.packages.map((pkg) => ({
|
|
1163
|
+
productId: pkg.productId,
|
|
1164
|
+
title: pkg.package_name,
|
|
1165
|
+
description: `${pkg.game_name} - ${pkg.package_name}`,
|
|
1166
|
+
price: pkg.price_cents / 100,
|
|
1167
|
+
localizedPrice: pkg.price_display,
|
|
1168
|
+
currency: "USD"
|
|
1169
|
+
}));
|
|
1170
|
+
logger.info(`[BillingService] Fetched ${products.length} native products`);
|
|
1171
|
+
return products;
|
|
1215
1172
|
}
|
|
1216
1173
|
/**
|
|
1217
1174
|
* Get products from web API (Stripe)
|
|
@@ -1219,7 +1176,7 @@ var BillingService = class {
|
|
|
1219
1176
|
async getProductsWeb() {
|
|
1220
1177
|
if (!this.config.checkoutUrl || !this.config.gameId) {
|
|
1221
1178
|
const error = new Error("checkoutUrl and gameId required for web purchases");
|
|
1222
|
-
|
|
1179
|
+
logger.error("[BillingService]", error.message);
|
|
1223
1180
|
throw error;
|
|
1224
1181
|
}
|
|
1225
1182
|
try {
|
|
@@ -1232,7 +1189,7 @@ var BillingService = class {
|
|
|
1232
1189
|
if (!data.packages || !Array.isArray(data.packages)) {
|
|
1233
1190
|
throw new Error("Invalid response format: missing packages array");
|
|
1234
1191
|
}
|
|
1235
|
-
|
|
1192
|
+
const products = data.packages.map((pkg) => ({
|
|
1236
1193
|
productId: pkg.priceId || pkg.productId,
|
|
1237
1194
|
// Prefer priceId for Stripe
|
|
1238
1195
|
title: pkg.package_name,
|
|
@@ -1241,23 +1198,21 @@ var BillingService = class {
|
|
|
1241
1198
|
localizedPrice: pkg.price_display,
|
|
1242
1199
|
currency: "USD"
|
|
1243
1200
|
}));
|
|
1244
|
-
|
|
1245
|
-
return
|
|
1201
|
+
logger.info(`[BillingService] Fetched ${products.length} web products`);
|
|
1202
|
+
return products;
|
|
1246
1203
|
} catch (error) {
|
|
1247
|
-
|
|
1204
|
+
logger.error("[BillingService] Failed to fetch web products:", error?.message);
|
|
1248
1205
|
throw error;
|
|
1249
1206
|
}
|
|
1250
1207
|
}
|
|
1251
1208
|
/**
|
|
1252
|
-
* Purchase a product
|
|
1209
|
+
* Purchase a product. Auto-initializes on first call.
|
|
1253
1210
|
* @param productId - The product ID (priceId for web/Stripe, productId for native)
|
|
1254
1211
|
* @param options - Optional purchase options
|
|
1255
1212
|
* @param options.elementId - For web: DOM element ID to mount Stripe checkout (default: 'stripe-checkout-element')
|
|
1256
1213
|
*/
|
|
1257
1214
|
async purchase(productId, options) {
|
|
1258
|
-
|
|
1259
|
-
throw new Error("BillingService not initialized. Call initialize() first.");
|
|
1260
|
-
}
|
|
1215
|
+
await this.initialize();
|
|
1261
1216
|
if (!this.isAvailable()) {
|
|
1262
1217
|
throw new Error("Billing is not available on this platform");
|
|
1263
1218
|
}
|
|
@@ -1277,7 +1232,7 @@ var BillingService = class {
|
|
|
1277
1232
|
reject(new Error("userId is required for native purchases"));
|
|
1278
1233
|
return;
|
|
1279
1234
|
}
|
|
1280
|
-
|
|
1235
|
+
logger.info(`[BillingService] Purchasing: ${productId}`);
|
|
1281
1236
|
const previousCompleteCallback = this.onPurchaseCompleteCallback;
|
|
1282
1237
|
const previousErrorCallback = this.onPurchaseErrorCallback;
|
|
1283
1238
|
const cleanup = () => {
|
|
@@ -1351,14 +1306,14 @@ var BillingService = class {
|
|
|
1351
1306
|
throw new Error("No client_secret returned from checkout session");
|
|
1352
1307
|
}
|
|
1353
1308
|
await this.mountCheckoutElement(client_secret, elementId || "stripe-checkout-element");
|
|
1354
|
-
|
|
1309
|
+
logger.info(`[BillingService] Checkout session created: ${id}`);
|
|
1355
1310
|
return {
|
|
1356
1311
|
success: true,
|
|
1357
1312
|
productId,
|
|
1358
1313
|
transactionId: id
|
|
1359
1314
|
};
|
|
1360
1315
|
} catch (error) {
|
|
1361
|
-
|
|
1316
|
+
logger.error("[BillingService] Web purchase failed:", error?.message);
|
|
1362
1317
|
return {
|
|
1363
1318
|
success: false,
|
|
1364
1319
|
productId,
|
|
@@ -1380,7 +1335,7 @@ var BillingService = class {
|
|
|
1380
1335
|
}
|
|
1381
1336
|
try {
|
|
1382
1337
|
if (this.checkoutElement) {
|
|
1383
|
-
|
|
1338
|
+
logger.info("[BillingService] Unmounting existing checkout element");
|
|
1384
1339
|
this.unmountCheckoutElement();
|
|
1385
1340
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
1386
1341
|
}
|
|
@@ -1389,15 +1344,15 @@ var BillingService = class {
|
|
|
1389
1344
|
throw new Error(`Element with id "${elementId}" not found in the DOM`);
|
|
1390
1345
|
}
|
|
1391
1346
|
container.innerHTML = "";
|
|
1392
|
-
|
|
1347
|
+
logger.info("[BillingService] Creating new checkout instance");
|
|
1393
1348
|
this.checkoutElement = await this.stripe.initEmbeddedCheckout({
|
|
1394
1349
|
clientSecret
|
|
1395
1350
|
});
|
|
1396
|
-
|
|
1351
|
+
logger.info("[BillingService] Mounting checkout element to DOM");
|
|
1397
1352
|
this.checkoutElement.mount(`#${elementId}`);
|
|
1398
1353
|
this.setupCheckoutEventListeners();
|
|
1399
1354
|
} catch (error) {
|
|
1400
|
-
|
|
1355
|
+
logger.error("[BillingService] Failed to mount checkout:", error?.message);
|
|
1401
1356
|
throw error;
|
|
1402
1357
|
}
|
|
1403
1358
|
}
|
|
@@ -1418,7 +1373,7 @@ var BillingService = class {
|
|
|
1418
1373
|
transactionId: sessionId || void 0
|
|
1419
1374
|
});
|
|
1420
1375
|
}
|
|
1421
|
-
|
|
1376
|
+
logger.info("[BillingService] Payment completed");
|
|
1422
1377
|
urlParams.delete("payment");
|
|
1423
1378
|
urlParams.delete("session_id");
|
|
1424
1379
|
const newUrl = `${window.location.pathname}${urlParams.toString() ? "?" + urlParams.toString() : ""}`;
|
|
@@ -1437,7 +1392,7 @@ var BillingService = class {
|
|
|
1437
1392
|
this.checkoutElement.destroy();
|
|
1438
1393
|
}
|
|
1439
1394
|
} catch (error) {
|
|
1440
|
-
|
|
1395
|
+
logger.warn("[BillingService] Error unmounting checkout element:", error?.message);
|
|
1441
1396
|
}
|
|
1442
1397
|
this.checkoutElement = null;
|
|
1443
1398
|
}
|
|
@@ -1465,9 +1420,8 @@ var BillingService = class {
|
|
|
1465
1420
|
NativeBridge.off("PURCHASE_ERROR" /* PURCHASE_ERROR */);
|
|
1466
1421
|
}
|
|
1467
1422
|
this.unmountCheckoutElement();
|
|
1468
|
-
this.
|
|
1423
|
+
this.initPromise = null;
|
|
1469
1424
|
this.nativeAvailable = false;
|
|
1470
|
-
this.products = [];
|
|
1471
1425
|
this.stripe = null;
|
|
1472
1426
|
this.onPurchaseCompleteCallback = void 0;
|
|
1473
1427
|
this.onPurchaseErrorCallback = void 0;
|
|
@@ -1639,10 +1593,10 @@ var HyveClient = class {
|
|
|
1639
1593
|
gameId = null;
|
|
1640
1594
|
adsService;
|
|
1641
1595
|
playgamaService = null;
|
|
1596
|
+
playgamaInitPromise = null;
|
|
1597
|
+
crazyGamesService = null;
|
|
1598
|
+
crazyGamesInitPromise = null;
|
|
1642
1599
|
billingService;
|
|
1643
|
-
billingConfig;
|
|
1644
|
-
// Store callbacks to preserve them when recreating BillingService
|
|
1645
|
-
billingCallbacks = {};
|
|
1646
1600
|
storageMode;
|
|
1647
1601
|
cloudStorageAdapter;
|
|
1648
1602
|
localStorageAdapter;
|
|
@@ -1668,17 +1622,33 @@ var HyveClient = class {
|
|
|
1668
1622
|
}
|
|
1669
1623
|
if (typeof window !== "undefined" && PlaygamaService.isPlaygamaDomain()) {
|
|
1670
1624
|
this.playgamaService = new PlaygamaService();
|
|
1671
|
-
this.playgamaService.initialize().then((success) => {
|
|
1625
|
+
this.playgamaInitPromise = this.playgamaService.initialize().then((success) => {
|
|
1672
1626
|
logger.info("Playgama Bridge initialized:", success);
|
|
1627
|
+
return success;
|
|
1628
|
+
});
|
|
1629
|
+
}
|
|
1630
|
+
if (typeof window !== "undefined" && CrazyGamesService.isCrazyGamesDomain()) {
|
|
1631
|
+
this.crazyGamesService = new CrazyGamesService();
|
|
1632
|
+
this.crazyGamesInitPromise = this.crazyGamesService.initialize().then((success) => {
|
|
1633
|
+
logger.info("CrazyGames SDK initialized:", success);
|
|
1634
|
+
return success;
|
|
1673
1635
|
});
|
|
1674
1636
|
}
|
|
1675
|
-
this.billingConfig = config?.billing || {};
|
|
1676
|
-
this.billingService = new BillingService(this.billingConfig);
|
|
1677
1637
|
this.storageMode = config?.storageMode || "cloud";
|
|
1678
1638
|
this.cloudStorageAdapter = new CloudStorageAdapter(
|
|
1679
1639
|
(endpoint, options) => this.callApi(endpoint, options)
|
|
1680
1640
|
);
|
|
1681
1641
|
this.localStorageAdapter = new LocalStorageAdapter(() => this.getUserId());
|
|
1642
|
+
if (typeof window !== "undefined") {
|
|
1643
|
+
this._parseUrlAuth();
|
|
1644
|
+
}
|
|
1645
|
+
const billingConfig = {
|
|
1646
|
+
checkoutUrl: this.apiBaseUrl,
|
|
1647
|
+
userId: this.userId ?? void 0,
|
|
1648
|
+
gameId: this.gameId ? Number(this.gameId) : void 0,
|
|
1649
|
+
...config?.billing
|
|
1650
|
+
};
|
|
1651
|
+
this.billingService = new BillingService(billingConfig);
|
|
1682
1652
|
const envSource = config?.isDev !== void 0 ? "explicit config" : "auto-detected from parent URL";
|
|
1683
1653
|
logger.info("==========================================");
|
|
1684
1654
|
logger.info("HyveClient Initialized");
|
|
@@ -1690,28 +1660,27 @@ var HyveClient = class {
|
|
|
1690
1660
|
`(${envSource})`
|
|
1691
1661
|
);
|
|
1692
1662
|
logger.info("API Base URL:", this.apiBaseUrl);
|
|
1693
|
-
logger.info("Ads enabled:", this.adsService.isEnabled());
|
|
1694
1663
|
logger.info("Playgama platform:", this.playgamaService !== null);
|
|
1664
|
+
logger.info("CrazyGames platform:", this.crazyGamesService !== null);
|
|
1695
1665
|
logger.info(
|
|
1696
1666
|
"Billing configured:",
|
|
1697
|
-
Object.keys(
|
|
1667
|
+
!!config?.billing && Object.keys(config.billing).length > 0
|
|
1698
1668
|
);
|
|
1699
1669
|
logger.info("Storage mode:", this.storageMode);
|
|
1670
|
+
logger.info("Authenticated:", this.jwtToken !== null);
|
|
1700
1671
|
logger.debug("Config:", {
|
|
1701
1672
|
isDev: this.telemetryConfig.isDev,
|
|
1702
1673
|
hasCustomApiUrl: !!config?.apiBaseUrl,
|
|
1703
|
-
|
|
1704
|
-
billingConfigured: Object.keys(this.billingConfig).length > 0,
|
|
1674
|
+
billingConfigured: !!config?.billing && Object.keys(config.billing).length > 0,
|
|
1705
1675
|
storageMode: this.storageMode
|
|
1706
1676
|
});
|
|
1707
1677
|
logger.info("==========================================");
|
|
1708
1678
|
}
|
|
1709
1679
|
/**
|
|
1710
|
-
*
|
|
1711
|
-
*
|
|
1712
|
-
* @returns Promise resolving to boolean indicating success
|
|
1680
|
+
* Parses JWT and game ID from the current window URL and stores them on the client.
|
|
1681
|
+
* Called automatically during construction.
|
|
1713
1682
|
*/
|
|
1714
|
-
|
|
1683
|
+
_parseUrlAuth(urlParams) {
|
|
1715
1684
|
try {
|
|
1716
1685
|
const params = urlParams ? parseUrlParams(urlParams) : parseUrlParams(window.location.search);
|
|
1717
1686
|
if (params.hyveAccess) {
|
|
@@ -1736,27 +1705,11 @@ var HyveClient = class {
|
|
|
1736
1705
|
}
|
|
1737
1706
|
if (this.jwtToken) {
|
|
1738
1707
|
logger.info("Authentication successful via JWT");
|
|
1739
|
-
return true;
|
|
1740
|
-
}
|
|
1741
|
-
const authResult = verifyAuthentication({
|
|
1742
|
-
hyveToken: params.hyveToken,
|
|
1743
|
-
signature: params.signature,
|
|
1744
|
-
message: params.message
|
|
1745
|
-
});
|
|
1746
|
-
if (authResult.isValid && authResult.address) {
|
|
1747
|
-
this.userId = authResult.address;
|
|
1748
|
-
logger.info("Authentication successful:", authResult.address);
|
|
1749
|
-
logger.info("Authentication method:", authResult.method);
|
|
1750
|
-
return true;
|
|
1751
1708
|
} else {
|
|
1752
|
-
logger.
|
|
1753
|
-
this.userId = null;
|
|
1754
|
-
return false;
|
|
1709
|
+
logger.info("No hyve-access JWT token in URL \u2014 unauthenticated");
|
|
1755
1710
|
}
|
|
1756
1711
|
} catch (error) {
|
|
1757
|
-
logger.error("
|
|
1758
|
-
this.userId = null;
|
|
1759
|
-
return false;
|
|
1712
|
+
logger.error("Error parsing URL auth:", error);
|
|
1760
1713
|
}
|
|
1761
1714
|
}
|
|
1762
1715
|
/**
|
|
@@ -1773,7 +1726,7 @@ var HyveClient = class {
|
|
|
1773
1726
|
*/
|
|
1774
1727
|
async sendTelemetry(eventLocation, eventCategory, eventAction, eventSubCategory, eventSubAction, eventDetails, platformId) {
|
|
1775
1728
|
if (!this.jwtToken) {
|
|
1776
|
-
logger.error("JWT token required.
|
|
1729
|
+
logger.error("JWT token required. Ensure hyve-access and game-id are present in the URL.");
|
|
1777
1730
|
return false;
|
|
1778
1731
|
}
|
|
1779
1732
|
if (!this.gameId) {
|
|
@@ -1845,7 +1798,7 @@ var HyveClient = class {
|
|
|
1845
1798
|
async callApi(endpoint, options = {}) {
|
|
1846
1799
|
if (!this.jwtToken) {
|
|
1847
1800
|
throw new Error(
|
|
1848
|
-
"No JWT token available.
|
|
1801
|
+
"No JWT token available. Ensure hyve-access and game-id are present in the URL."
|
|
1849
1802
|
);
|
|
1850
1803
|
}
|
|
1851
1804
|
try {
|
|
@@ -1975,6 +1928,13 @@ var HyveClient = class {
|
|
|
1975
1928
|
const storageMode = mode || this.storageMode;
|
|
1976
1929
|
return storageMode === "local" ? this.localStorageAdapter : this.cloudStorageAdapter;
|
|
1977
1930
|
}
|
|
1931
|
+
/**
|
|
1932
|
+
* Returns the current game ID or throws if not available.
|
|
1933
|
+
*/
|
|
1934
|
+
requireGameId() {
|
|
1935
|
+
const gameId = this.requireGameId();
|
|
1936
|
+
return gameId;
|
|
1937
|
+
}
|
|
1978
1938
|
/**
|
|
1979
1939
|
* Save persistent game data
|
|
1980
1940
|
* @param key Data key
|
|
@@ -1983,10 +1943,7 @@ var HyveClient = class {
|
|
|
1983
1943
|
* @returns Promise resolving to save response
|
|
1984
1944
|
*/
|
|
1985
1945
|
async saveGameData(key, value, storage) {
|
|
1986
|
-
const gameId = this.
|
|
1987
|
-
if (!gameId) {
|
|
1988
|
-
throw new Error("game-id is required for persistent storage. Call authenticateFromUrl first.");
|
|
1989
|
-
}
|
|
1946
|
+
const gameId = this.requireGameId();
|
|
1990
1947
|
const storageMode = storage || this.storageMode;
|
|
1991
1948
|
logger.debug(`Saving game data to ${storageMode}: ${gameId}/${key}`);
|
|
1992
1949
|
const adapter = this.getStorageAdapter(storage);
|
|
@@ -2001,10 +1958,7 @@ var HyveClient = class {
|
|
|
2001
1958
|
* @returns Promise resolving to save response
|
|
2002
1959
|
*/
|
|
2003
1960
|
async batchSaveGameData(items, storage) {
|
|
2004
|
-
const gameId = this.
|
|
2005
|
-
if (!gameId) {
|
|
2006
|
-
throw new Error("game-id is required for persistent storage. Call authenticateFromUrl first.");
|
|
2007
|
-
}
|
|
1961
|
+
const gameId = this.requireGameId();
|
|
2008
1962
|
const storageMode = storage || this.storageMode;
|
|
2009
1963
|
logger.debug(`Batch saving ${items.length} game data entries to ${storageMode} for game: ${gameId}`);
|
|
2010
1964
|
const adapter = this.getStorageAdapter(storage);
|
|
@@ -2019,10 +1973,7 @@ var HyveClient = class {
|
|
|
2019
1973
|
* @returns Promise resolving to game data item or null if not found
|
|
2020
1974
|
*/
|
|
2021
1975
|
async getGameData(key, storage) {
|
|
2022
|
-
const gameId = this.
|
|
2023
|
-
if (!gameId) {
|
|
2024
|
-
throw new Error("game-id is required for persistent storage. Call authenticateFromUrl first.");
|
|
2025
|
-
}
|
|
1976
|
+
const gameId = this.requireGameId();
|
|
2026
1977
|
const storageMode = storage || this.storageMode;
|
|
2027
1978
|
logger.debug(`Getting game data from ${storageMode}: ${gameId}/${key}`);
|
|
2028
1979
|
const adapter = this.getStorageAdapter(storage);
|
|
@@ -2041,10 +1992,7 @@ var HyveClient = class {
|
|
|
2041
1992
|
* @returns Promise resolving to array of game data items
|
|
2042
1993
|
*/
|
|
2043
1994
|
async getMultipleGameData(keys, storage) {
|
|
2044
|
-
const gameId = this.
|
|
2045
|
-
if (!gameId) {
|
|
2046
|
-
throw new Error("game-id is required for persistent storage. Call authenticateFromUrl first.");
|
|
2047
|
-
}
|
|
1995
|
+
const gameId = this.requireGameId();
|
|
2048
1996
|
const storageMode = storage || this.storageMode;
|
|
2049
1997
|
logger.debug(`Getting ${keys.length} game data entries from ${storageMode} for game: ${gameId}`);
|
|
2050
1998
|
const adapter = this.getStorageAdapter(storage);
|
|
@@ -2059,10 +2007,7 @@ var HyveClient = class {
|
|
|
2059
2007
|
* @returns Promise resolving to boolean indicating if data was deleted
|
|
2060
2008
|
*/
|
|
2061
2009
|
async deleteGameData(key, storage) {
|
|
2062
|
-
const gameId = this.
|
|
2063
|
-
if (!gameId) {
|
|
2064
|
-
throw new Error("game-id is required for persistent storage. Call authenticateFromUrl first.");
|
|
2065
|
-
}
|
|
2010
|
+
const gameId = this.requireGameId();
|
|
2066
2011
|
const storageMode = storage || this.storageMode;
|
|
2067
2012
|
logger.debug(`Deleting game data from ${storageMode}: ${gameId}/${key}`);
|
|
2068
2013
|
const adapter = this.getStorageAdapter(storage);
|
|
@@ -2081,10 +2026,7 @@ var HyveClient = class {
|
|
|
2081
2026
|
* @returns Promise resolving to number of entries deleted
|
|
2082
2027
|
*/
|
|
2083
2028
|
async deleteMultipleGameData(keys, storage) {
|
|
2084
|
-
const gameId = this.
|
|
2085
|
-
if (!gameId) {
|
|
2086
|
-
throw new Error("game-id is required for persistent storage. Call authenticateFromUrl first.");
|
|
2087
|
-
}
|
|
2029
|
+
const gameId = this.requireGameId();
|
|
2088
2030
|
const storageMode = storage || this.storageMode;
|
|
2089
2031
|
logger.debug(`Deleting ${keys.length} game data entries from ${storageMode} for game: ${gameId}`);
|
|
2090
2032
|
const adapter = this.getStorageAdapter(storage);
|
|
@@ -2121,66 +2063,82 @@ var HyveClient = class {
|
|
|
2121
2063
|
* @returns Promise resolving to ad result
|
|
2122
2064
|
*/
|
|
2123
2065
|
async showAd(type) {
|
|
2124
|
-
if (this.
|
|
2125
|
-
if (
|
|
2126
|
-
|
|
2066
|
+
if (this.crazyGamesService) {
|
|
2067
|
+
if (this.crazyGamesInitPromise) {
|
|
2068
|
+
await this.crazyGamesInitPromise;
|
|
2069
|
+
}
|
|
2070
|
+
if (this.crazyGamesService.isInitialized()) {
|
|
2071
|
+
const { onBeforeAd, onAfterAd, onRewardEarned } = this.adsService.getCallbacks();
|
|
2072
|
+
if (type === "rewarded") {
|
|
2073
|
+
return this.crazyGamesService.showRewarded({
|
|
2074
|
+
onBeforeAd: () => onBeforeAd("rewarded"),
|
|
2075
|
+
onAfterAd: () => onAfterAd("rewarded"),
|
|
2076
|
+
onRewardEarned
|
|
2077
|
+
});
|
|
2078
|
+
}
|
|
2079
|
+
return this.crazyGamesService.showInterstitial({
|
|
2080
|
+
onBeforeAd: () => onBeforeAd(type),
|
|
2081
|
+
onAfterAd: () => onAfterAd(type)
|
|
2082
|
+
});
|
|
2083
|
+
}
|
|
2084
|
+
}
|
|
2085
|
+
if (this.playgamaService) {
|
|
2086
|
+
if (this.playgamaInitPromise) {
|
|
2087
|
+
await this.playgamaInitPromise;
|
|
2088
|
+
}
|
|
2089
|
+
if (this.playgamaService.isInitialized()) {
|
|
2090
|
+
const { onBeforeAd, onAfterAd, onRewardEarned } = this.adsService.getCallbacks();
|
|
2091
|
+
if (type === "rewarded") {
|
|
2092
|
+
return this.playgamaService.showRewarded({
|
|
2093
|
+
onBeforeAd: () => onBeforeAd("rewarded"),
|
|
2094
|
+
onAfterAd: () => onAfterAd("rewarded"),
|
|
2095
|
+
onRewardEarned
|
|
2096
|
+
});
|
|
2097
|
+
}
|
|
2098
|
+
return this.playgamaService.showInterstitial({
|
|
2099
|
+
onBeforeAd: () => onBeforeAd(type),
|
|
2100
|
+
onAfterAd: () => onAfterAd(type)
|
|
2101
|
+
});
|
|
2102
|
+
}
|
|
2127
2103
|
}
|
|
2128
2104
|
return this.adsService.show(type);
|
|
2129
2105
|
}
|
|
2130
2106
|
/**
|
|
2131
|
-
*
|
|
2132
|
-
*
|
|
2107
|
+
* Notifies CrazyGames that gameplay has started.
|
|
2108
|
+
* No-op on other platforms.
|
|
2133
2109
|
*/
|
|
2134
|
-
|
|
2135
|
-
|
|
2110
|
+
async gameplayStart() {
|
|
2111
|
+
if (this.crazyGamesService) {
|
|
2112
|
+
if (this.crazyGamesInitPromise) await this.crazyGamesInitPromise;
|
|
2113
|
+
this.crazyGamesService.gameplayStart();
|
|
2114
|
+
}
|
|
2136
2115
|
}
|
|
2137
2116
|
/**
|
|
2138
|
-
*
|
|
2139
|
-
*
|
|
2117
|
+
* Notifies CrazyGames that gameplay has stopped.
|
|
2118
|
+
* No-op on other platforms.
|
|
2140
2119
|
*/
|
|
2141
|
-
|
|
2142
|
-
|
|
2120
|
+
async gameplayStop() {
|
|
2121
|
+
if (this.crazyGamesService) {
|
|
2122
|
+
if (this.crazyGamesInitPromise) await this.crazyGamesInitPromise;
|
|
2123
|
+
this.crazyGamesService.gameplayStop();
|
|
2124
|
+
}
|
|
2143
2125
|
}
|
|
2144
2126
|
/**
|
|
2145
|
-
*
|
|
2146
|
-
*
|
|
2127
|
+
* Triggers a celebration effect on the CrazyGames website for significant achievements.
|
|
2128
|
+
* No-op on other platforms.
|
|
2147
2129
|
*/
|
|
2148
|
-
|
|
2149
|
-
this.
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
}
|
|
2153
|
-
logger.info("Billing configuration updated");
|
|
2130
|
+
async happytime() {
|
|
2131
|
+
if (this.crazyGamesService) {
|
|
2132
|
+
if (this.crazyGamesInitPromise) await this.crazyGamesInitPromise;
|
|
2133
|
+
this.crazyGamesService.happytime();
|
|
2134
|
+
}
|
|
2154
2135
|
}
|
|
2155
2136
|
/**
|
|
2156
|
-
*
|
|
2157
|
-
*
|
|
2158
|
-
* @returns Promise resolving to boolean indicating success
|
|
2137
|
+
* Check if ads are ready to show
|
|
2138
|
+
* @returns Boolean indicating if ads have initialized successfully
|
|
2159
2139
|
*/
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
...this.billingConfig
|
|
2163
|
-
};
|
|
2164
|
-
if (!mergedConfig.userId && this.userId) {
|
|
2165
|
-
mergedConfig.userId = this.userId;
|
|
2166
|
-
}
|
|
2167
|
-
if (!mergedConfig.gameId && this.gameId) {
|
|
2168
|
-
mergedConfig.gameId = Number(this.gameId);
|
|
2169
|
-
}
|
|
2170
|
-
if (!mergedConfig.checkoutUrl) {
|
|
2171
|
-
mergedConfig.checkoutUrl = this.apiBaseUrl;
|
|
2172
|
-
}
|
|
2173
|
-
this.billingService = new BillingService(mergedConfig);
|
|
2174
|
-
if (this.billingCallbacks.onComplete) {
|
|
2175
|
-
this.billingService.onPurchaseComplete(this.billingCallbacks.onComplete);
|
|
2176
|
-
}
|
|
2177
|
-
if (this.billingCallbacks.onError) {
|
|
2178
|
-
this.billingService.onPurchaseError(this.billingCallbacks.onError);
|
|
2179
|
-
}
|
|
2180
|
-
if (this.billingCallbacks.onLog) {
|
|
2181
|
-
this.billingService.onLog(this.billingCallbacks.onLog);
|
|
2182
|
-
}
|
|
2183
|
-
return await this.billingService.initialize();
|
|
2140
|
+
areAdsReady() {
|
|
2141
|
+
return this.adsService.isReady();
|
|
2184
2142
|
}
|
|
2185
2143
|
/**
|
|
2186
2144
|
* Get the billing platform
|
|
@@ -2217,7 +2175,6 @@ var HyveClient = class {
|
|
|
2217
2175
|
* @param callback Function to call on purchase completion
|
|
2218
2176
|
*/
|
|
2219
2177
|
onPurchaseComplete(callback) {
|
|
2220
|
-
this.billingCallbacks.onComplete = callback;
|
|
2221
2178
|
this.billingService.onPurchaseComplete(callback);
|
|
2222
2179
|
}
|
|
2223
2180
|
/**
|
|
@@ -2225,7 +2182,6 @@ var HyveClient = class {
|
|
|
2225
2182
|
* @param callback Function to call on purchase error
|
|
2226
2183
|
*/
|
|
2227
2184
|
onPurchaseError(callback) {
|
|
2228
|
-
this.billingCallbacks.onError = callback;
|
|
2229
2185
|
this.billingService.onPurchaseError(callback);
|
|
2230
2186
|
}
|
|
2231
2187
|
/**
|
|
@@ -2234,14 +2190,6 @@ var HyveClient = class {
|
|
|
2234
2190
|
unmountBillingCheckout() {
|
|
2235
2191
|
this.billingService.unmountCheckoutElement();
|
|
2236
2192
|
}
|
|
2237
|
-
/**
|
|
2238
|
-
* Register a callback to receive billing logs
|
|
2239
|
-
* @param callback Function to call with log messages
|
|
2240
|
-
*/
|
|
2241
|
-
onBillingLog(callback) {
|
|
2242
|
-
this.billingCallbacks.onLog = callback;
|
|
2243
|
-
this.billingService.onLog(callback);
|
|
2244
|
-
}
|
|
2245
2193
|
};
|
|
2246
2194
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2247
2195
|
0 && (module.exports = {
|
|
@@ -2249,6 +2197,7 @@ var HyveClient = class {
|
|
|
2249
2197
|
BillingPlatform,
|
|
2250
2198
|
BillingService,
|
|
2251
2199
|
CloudStorageAdapter,
|
|
2200
|
+
CrazyGamesService,
|
|
2252
2201
|
HyveClient,
|
|
2253
2202
|
LocalStorageAdapter,
|
|
2254
2203
|
Logger,
|
|
@@ -2256,11 +2205,7 @@ var HyveClient = class {
|
|
|
2256
2205
|
NativeMessageType,
|
|
2257
2206
|
PlaygamaService,
|
|
2258
2207
|
generateUUID,
|
|
2259
|
-
handleVerifyMessage,
|
|
2260
2208
|
isDomainAllowed,
|
|
2261
2209
|
logger,
|
|
2262
|
-
parseUrlParams
|
|
2263
|
-
validateSignature,
|
|
2264
|
-
verifyAuthentication,
|
|
2265
|
-
verifyHyveToken
|
|
2210
|
+
parseUrlParams
|
|
2266
2211
|
});
|