@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.mjs
CHANGED
|
@@ -24,7 +24,7 @@ var Logger = class _Logger {
|
|
|
24
24
|
}
|
|
25
25
|
} else if (typeof window !== "undefined") {
|
|
26
26
|
try {
|
|
27
|
-
enabled = process
|
|
27
|
+
enabled = process?.env.NODE_ENV !== "production";
|
|
28
28
|
} catch (e) {
|
|
29
29
|
enabled = true;
|
|
30
30
|
}
|
|
@@ -124,136 +124,15 @@ var Logger = class _Logger {
|
|
|
124
124
|
var logger = new Logger();
|
|
125
125
|
|
|
126
126
|
// src/utils/auth.ts
|
|
127
|
-
import { ethers } from "ethers";
|
|
128
127
|
function parseUrlParams(searchParams) {
|
|
129
128
|
const params = typeof searchParams === "string" ? new URLSearchParams(searchParams) : searchParams;
|
|
130
129
|
return {
|
|
131
|
-
signature: params.get("signature") || "",
|
|
132
|
-
message: params.get("message") || "",
|
|
133
130
|
gameStartTab: params.get("game_start_tab") || "",
|
|
134
|
-
hyveToken: params.get("hyve-token") || "",
|
|
135
131
|
platform: params.get("platform") || "",
|
|
136
132
|
hyveAccess: params.get("hyve-access") || "",
|
|
137
133
|
gameId: params.get("game-id") || ""
|
|
138
134
|
};
|
|
139
135
|
}
|
|
140
|
-
function validateSignature(signature, message) {
|
|
141
|
-
try {
|
|
142
|
-
const recoveredAddress = ethers.verifyMessage(message, signature);
|
|
143
|
-
return !!recoveredAddress;
|
|
144
|
-
} catch (error) {
|
|
145
|
-
logger.error("Signature validation error:", error);
|
|
146
|
-
return false;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
function handleVerifyMessage(signature, message) {
|
|
150
|
-
try {
|
|
151
|
-
const metadata = JSON.parse(decodeURI(message));
|
|
152
|
-
if (!metadata.expiration || metadata.expiration < Date.now()) {
|
|
153
|
-
return false;
|
|
154
|
-
}
|
|
155
|
-
const userAddress = metadata.userId || metadata.address;
|
|
156
|
-
if (!userAddress) {
|
|
157
|
-
return false;
|
|
158
|
-
}
|
|
159
|
-
const byteMessage = new TextEncoder().encode(message);
|
|
160
|
-
const addressThatSignedMessage = ethers.verifyMessage(
|
|
161
|
-
byteMessage,
|
|
162
|
-
signature
|
|
163
|
-
);
|
|
164
|
-
if (!addressThatSignedMessage) {
|
|
165
|
-
return false;
|
|
166
|
-
}
|
|
167
|
-
if (addressThatSignedMessage.toLowerCase() !== userAddress.toLowerCase()) {
|
|
168
|
-
return false;
|
|
169
|
-
}
|
|
170
|
-
return userAddress;
|
|
171
|
-
} catch (error) {
|
|
172
|
-
logger.error("Error verifying message:", error);
|
|
173
|
-
return false;
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
function verifyHyveToken(hyveToken, maxAgeSec = 600) {
|
|
177
|
-
try {
|
|
178
|
-
const parts = hyveToken.split(".");
|
|
179
|
-
if (parts.length !== 4) {
|
|
180
|
-
logger.error(
|
|
181
|
-
"Invalid hyve-token format: expected 4 parts, got",
|
|
182
|
-
parts.length
|
|
183
|
-
);
|
|
184
|
-
return false;
|
|
185
|
-
}
|
|
186
|
-
const [signature, address, randomBase64, timestampStr] = parts;
|
|
187
|
-
if (!signature || !address || !randomBase64 || !timestampStr) {
|
|
188
|
-
logger.error("Missing hyve-token components");
|
|
189
|
-
return false;
|
|
190
|
-
}
|
|
191
|
-
const message = `${address}.${randomBase64}.${timestampStr}`;
|
|
192
|
-
const recoveredAddress = ethers.verifyMessage(message, signature);
|
|
193
|
-
if (recoveredAddress.toLowerCase() !== address.toLowerCase()) {
|
|
194
|
-
logger.error("Hyve-token signature verification failed");
|
|
195
|
-
return false;
|
|
196
|
-
}
|
|
197
|
-
const timestamp = parseInt(timestampStr, 10);
|
|
198
|
-
if (!Number.isFinite(timestamp)) {
|
|
199
|
-
logger.error("Invalid hyve-token timestamp");
|
|
200
|
-
return false;
|
|
201
|
-
}
|
|
202
|
-
const now = Math.floor(Date.now() / 1e3);
|
|
203
|
-
if (now - timestamp > maxAgeSec) {
|
|
204
|
-
logger.error(
|
|
205
|
-
`Hyve-token expired (age: ${now - timestamp}s, max: ${maxAgeSec}s)`
|
|
206
|
-
);
|
|
207
|
-
return false;
|
|
208
|
-
}
|
|
209
|
-
return address;
|
|
210
|
-
} catch (error) {
|
|
211
|
-
logger.error("Hyve-token verification error:", error);
|
|
212
|
-
return false;
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
function verifyAuthentication(params, maxAgeSec = 600) {
|
|
216
|
-
if (params.hyveToken) {
|
|
217
|
-
const modernAddress = verifyHyveToken(params.hyveToken, maxAgeSec);
|
|
218
|
-
if (modernAddress) {
|
|
219
|
-
return {
|
|
220
|
-
isValid: true,
|
|
221
|
-
address: modernAddress,
|
|
222
|
-
method: "modern"
|
|
223
|
-
};
|
|
224
|
-
} else {
|
|
225
|
-
return {
|
|
226
|
-
isValid: false,
|
|
227
|
-
address: null,
|
|
228
|
-
method: "modern",
|
|
229
|
-
error: "Modern token verification failed"
|
|
230
|
-
};
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
if (params.signature && params.message) {
|
|
234
|
-
const legacyAddress = handleVerifyMessage(params.signature, params.message);
|
|
235
|
-
if (legacyAddress) {
|
|
236
|
-
return {
|
|
237
|
-
isValid: true,
|
|
238
|
-
address: legacyAddress,
|
|
239
|
-
method: "legacy"
|
|
240
|
-
};
|
|
241
|
-
} else {
|
|
242
|
-
return {
|
|
243
|
-
isValid: false,
|
|
244
|
-
address: null,
|
|
245
|
-
method: "legacy",
|
|
246
|
-
error: "Legacy token verification failed"
|
|
247
|
-
};
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
return {
|
|
251
|
-
isValid: false,
|
|
252
|
-
address: null,
|
|
253
|
-
method: "none",
|
|
254
|
-
error: "No authentication tokens provided"
|
|
255
|
-
};
|
|
256
|
-
}
|
|
257
136
|
function isDomainAllowed(allowedDomains, hostname) {
|
|
258
137
|
logger.debug("Checking hostname:", hostname);
|
|
259
138
|
if (!allowedDomains) return true;
|
|
@@ -294,9 +173,7 @@ var NativeBridge = class {
|
|
|
294
173
|
* Checks if the app is running inside a React Native WebView
|
|
295
174
|
*/
|
|
296
175
|
static isNativeContext() {
|
|
297
|
-
|
|
298
|
-
console.log(`[NativeBridge] isNativeContext check: ${isNative}`);
|
|
299
|
-
return isNative;
|
|
176
|
+
return typeof window !== "undefined" && "ReactNativeWebView" in window;
|
|
300
177
|
}
|
|
301
178
|
/**
|
|
302
179
|
* Initializes the native bridge message listener
|
|
@@ -305,55 +182,37 @@ var NativeBridge = class {
|
|
|
305
182
|
static initialize() {
|
|
306
183
|
if (this.isInitialized) {
|
|
307
184
|
logger.debug("[NativeBridge] Already initialized");
|
|
308
|
-
console.log("[NativeBridge] \u26A0 Already initialized, skipping");
|
|
309
185
|
return;
|
|
310
186
|
}
|
|
311
187
|
if (typeof window === "undefined") {
|
|
312
188
|
logger.warn("[NativeBridge] Window not available, skipping initialization");
|
|
313
|
-
console.warn("[NativeBridge] \u26A0 Window not available, skipping initialization");
|
|
314
189
|
return;
|
|
315
190
|
}
|
|
316
|
-
const isInIframe = window !== window.parent;
|
|
317
|
-
console.log("[NativeBridge] ========== INITIALIZING ==========");
|
|
318
|
-
console.log(`[NativeBridge] Running in iframe: ${isInIframe}`);
|
|
319
|
-
console.log("[NativeBridge] Setting up message listeners on window and document");
|
|
320
191
|
const boundHandler = this.handleNativeMessage.bind(this);
|
|
321
192
|
window.addEventListener("message", boundHandler);
|
|
322
193
|
document.addEventListener("message", boundHandler);
|
|
323
|
-
if (isInIframe) {
|
|
324
|
-
console.log("[NativeBridge] Setting up parent window message listener for iframe context");
|
|
325
|
-
}
|
|
326
194
|
this.isInitialized = true;
|
|
327
|
-
console.log("[NativeBridge] \u2713 Event listeners attached to window and document");
|
|
328
|
-
console.log("[NativeBridge] \u2713 Initialized and ready to receive messages");
|
|
329
195
|
logger.info("[NativeBridge] Initialized and listening for native messages");
|
|
330
196
|
}
|
|
331
197
|
/**
|
|
332
198
|
* Handles incoming messages from React Native
|
|
333
199
|
*/
|
|
334
200
|
static handleNativeMessage(event) {
|
|
335
|
-
console.log("[NativeBridge] [HANDLER] handleNativeMessage called");
|
|
336
|
-
console.log("[NativeBridge] [HANDLER] Event source:", event.source === window ? "window" : "other");
|
|
337
|
-
console.log("[NativeBridge] [HANDLER] Event data:", event.data);
|
|
338
201
|
try {
|
|
339
202
|
let data;
|
|
340
203
|
if (typeof event.data === "string") {
|
|
341
204
|
try {
|
|
342
205
|
data = JSON.parse(event.data);
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
logger.debug("[NativeBridge] Failed to parse message, not JSON:", event.data);
|
|
206
|
+
} catch {
|
|
207
|
+
logger.debug("[NativeBridge] Ignoring non-JSON message");
|
|
346
208
|
return;
|
|
347
209
|
}
|
|
348
210
|
} else if (typeof event.data === "object" && event.data !== null) {
|
|
349
211
|
data = event.data;
|
|
350
|
-
logger.debug("[NativeBridge] Received message object:", data);
|
|
351
212
|
} else {
|
|
352
|
-
logger.debug("[NativeBridge] Received invalid message type:", typeof event.data);
|
|
353
213
|
return;
|
|
354
214
|
}
|
|
355
|
-
if (!data
|
|
356
|
-
logger.debug("[NativeBridge] Received message without type, ignoring");
|
|
215
|
+
if (!data?.type) {
|
|
357
216
|
return;
|
|
358
217
|
}
|
|
359
218
|
const nativeResponseTypes = [
|
|
@@ -365,22 +224,16 @@ var NativeBridge = class {
|
|
|
365
224
|
"PURCHASE_ERROR" /* PURCHASE_ERROR */
|
|
366
225
|
];
|
|
367
226
|
if (!nativeResponseTypes.includes(data.type)) {
|
|
368
|
-
logger.debug(`[NativeBridge] Ignoring non-native-response message: ${data.type}`);
|
|
369
227
|
return;
|
|
370
228
|
}
|
|
371
|
-
console.log("[NativeBridge] [HANDLER] Message type:", data.type);
|
|
372
229
|
const handler = this.handlers.get(data.type);
|
|
373
230
|
if (handler) {
|
|
374
|
-
|
|
375
|
-
logger.info(`[NativeBridge] Handling message: ${data.type}`, data.payload);
|
|
231
|
+
logger.debug(`[NativeBridge] Handling message: ${data.type}`);
|
|
376
232
|
handler(data.payload);
|
|
377
|
-
console.log(`[NativeBridge] [HANDLER] \u2713 Handler completed for: ${data.type}`);
|
|
378
233
|
} else {
|
|
379
|
-
console.warn(`[NativeBridge] [HANDLER] \u26A0 No handler registered for: ${data.type}`);
|
|
380
234
|
logger.warn(`[NativeBridge] No handler registered for: ${data.type}`);
|
|
381
235
|
}
|
|
382
236
|
} catch (error) {
|
|
383
|
-
console.error("[NativeBridge] [HANDLER] \u274C Error handling native message:", error);
|
|
384
237
|
logger.error("[NativeBridge] Error handling native message:", error);
|
|
385
238
|
}
|
|
386
239
|
}
|
|
@@ -390,27 +243,15 @@ var NativeBridge = class {
|
|
|
390
243
|
* @param payload Optional payload data
|
|
391
244
|
*/
|
|
392
245
|
static send(type, payload) {
|
|
393
|
-
console.log(`[NativeBridge] [SEND] Attempting to send message: ${type}`);
|
|
394
246
|
if (!this.isNativeContext()) {
|
|
395
|
-
|
|
396
|
-
logger.debug(
|
|
397
|
-
`[NativeBridge] Not in native context, skipping message: ${type}`
|
|
398
|
-
);
|
|
247
|
+
logger.debug(`[NativeBridge] Not in native context, skipping message: ${type}`);
|
|
399
248
|
return;
|
|
400
249
|
}
|
|
401
|
-
console.log(`[NativeBridge] [SEND] \u2713 In native context, sending message`);
|
|
402
250
|
try {
|
|
403
|
-
const message = {
|
|
404
|
-
type,
|
|
405
|
-
payload,
|
|
406
|
-
timestamp: Date.now()
|
|
407
|
-
};
|
|
408
|
-
console.log(`[NativeBridge] [SEND] Message object:`, message);
|
|
251
|
+
const message = { type, payload, timestamp: Date.now() };
|
|
409
252
|
window.ReactNativeWebView.postMessage(JSON.stringify(message));
|
|
410
|
-
console.log(`[NativeBridge] [SEND] \u2713 Message sent to native: ${type}`);
|
|
411
253
|
logger.debug(`[NativeBridge] Sent message to native: ${type}`, payload);
|
|
412
254
|
} catch (error) {
|
|
413
|
-
console.error(`[NativeBridge] [SEND] \u274C\u274C Error sending message to native:`, error);
|
|
414
255
|
logger.error(`[NativeBridge] Error sending message to native:`, error);
|
|
415
256
|
}
|
|
416
257
|
}
|
|
@@ -420,10 +261,7 @@ var NativeBridge = class {
|
|
|
420
261
|
* @param handler Function to call when message is received
|
|
421
262
|
*/
|
|
422
263
|
static on(type, handler) {
|
|
423
|
-
console.log(`[NativeBridge] [ON] Registering handler for: ${type}`);
|
|
424
264
|
this.handlers.set(type, handler);
|
|
425
|
-
console.log(`[NativeBridge] [ON] \u2713 Handler registered. Total handlers:`, this.handlers.size);
|
|
426
|
-
console.log(`[NativeBridge] [ON] All registered types:`, Array.from(this.handlers.keys()));
|
|
427
265
|
logger.debug(`[NativeBridge] Registered handler for: ${type}`);
|
|
428
266
|
}
|
|
429
267
|
/**
|
|
@@ -573,7 +411,6 @@ function generateUUID() {
|
|
|
573
411
|
// src/services/ads.ts
|
|
574
412
|
var AdsService = class {
|
|
575
413
|
config = {
|
|
576
|
-
enabled: false,
|
|
577
414
|
sound: "on",
|
|
578
415
|
debug: false,
|
|
579
416
|
onBeforeAd: () => {
|
|
@@ -583,87 +420,69 @@ var AdsService = class {
|
|
|
583
420
|
onRewardEarned: () => {
|
|
584
421
|
}
|
|
585
422
|
};
|
|
586
|
-
|
|
423
|
+
// Cached init promise — ensures adConfig() is only called once
|
|
424
|
+
initPromise = null;
|
|
587
425
|
ready = false;
|
|
588
426
|
/**
|
|
589
|
-
*
|
|
590
|
-
*
|
|
427
|
+
* Optionally configure the ads service.
|
|
428
|
+
* Not required — ads work without calling this.
|
|
591
429
|
*/
|
|
592
430
|
configure(config) {
|
|
593
431
|
this.config = {
|
|
594
432
|
...this.config,
|
|
595
433
|
...config,
|
|
596
|
-
onBeforeAd: config.onBeforeAd
|
|
597
|
-
onAfterAd: config.onAfterAd
|
|
598
|
-
onRewardEarned: config.onRewardEarned
|
|
434
|
+
onBeforeAd: config.onBeforeAd ?? this.config.onBeforeAd,
|
|
435
|
+
onAfterAd: config.onAfterAd ?? this.config.onAfterAd,
|
|
436
|
+
onRewardEarned: config.onRewardEarned ?? this.config.onRewardEarned
|
|
599
437
|
};
|
|
600
438
|
if (this.config.debug) {
|
|
601
|
-
|
|
602
|
-
enabled: this.config.enabled,
|
|
603
|
-
sound: this.config.sound
|
|
604
|
-
});
|
|
605
|
-
}
|
|
606
|
-
if (this.config.enabled && !this.initialized) {
|
|
607
|
-
this.initialize();
|
|
439
|
+
logger.debug("[AdsService] Configuration updated:", { sound: this.config.sound });
|
|
608
440
|
}
|
|
609
441
|
}
|
|
610
442
|
/**
|
|
611
|
-
* Initialize the
|
|
443
|
+
* Initialize the Google H5 Ads system.
|
|
444
|
+
* Returns a cached promise — safe to call multiple times.
|
|
612
445
|
*/
|
|
613
446
|
initialize() {
|
|
614
|
-
if (this.
|
|
615
|
-
|
|
616
|
-
if (
|
|
617
|
-
console.log("[AdsService] Ads disabled, skipping initialization");
|
|
618
|
-
}
|
|
619
|
-
return;
|
|
620
|
-
}
|
|
621
|
-
if (!window.adConfig || !window.adBreak) {
|
|
622
|
-
console.warn("[AdsService] Google Ads SDK not found. Ads will not be available.");
|
|
623
|
-
return;
|
|
624
|
-
}
|
|
625
|
-
if (this.config.debug) {
|
|
626
|
-
console.log("[AdsService] Initializing ads system...");
|
|
627
|
-
}
|
|
628
|
-
const googleConfig = {
|
|
629
|
-
sound: this.config.sound,
|
|
630
|
-
preloadAdBreaks: "on",
|
|
631
|
-
onReady: () => {
|
|
632
|
-
this.ready = true;
|
|
447
|
+
if (this.initPromise) return this.initPromise;
|
|
448
|
+
this.initPromise = new Promise((resolve) => {
|
|
449
|
+
if (!window.adConfig || !window.adBreak) {
|
|
633
450
|
if (this.config.debug) {
|
|
634
|
-
|
|
451
|
+
logger.debug("[AdsService] Google Ads SDK not found \u2014 ads unavailable");
|
|
635
452
|
}
|
|
453
|
+
resolve(false);
|
|
454
|
+
return;
|
|
636
455
|
}
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
456
|
+
if (this.config.debug) {
|
|
457
|
+
logger.debug("[AdsService] Initializing ads system...");
|
|
458
|
+
}
|
|
459
|
+
window.adConfig({
|
|
460
|
+
sound: this.config.sound,
|
|
461
|
+
preloadAdBreaks: "on",
|
|
462
|
+
onReady: () => {
|
|
463
|
+
this.ready = true;
|
|
464
|
+
if (this.config.debug) {
|
|
465
|
+
logger.debug("[AdsService] Ads ready");
|
|
466
|
+
}
|
|
467
|
+
resolve(true);
|
|
468
|
+
}
|
|
469
|
+
});
|
|
470
|
+
setTimeout(() => resolve(false), 5e3);
|
|
471
|
+
});
|
|
472
|
+
return this.initPromise;
|
|
640
473
|
}
|
|
641
474
|
/**
|
|
642
|
-
* Show an ad
|
|
643
|
-
* Returns immediately if ads are disabled
|
|
475
|
+
* Show an ad. Auto-initializes on the first call.
|
|
476
|
+
* Returns immediately with success: false if ads are disabled or unavailable.
|
|
644
477
|
*/
|
|
645
478
|
async show(type) {
|
|
646
479
|
const requestedAt = Date.now();
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
console.log("[AdsService] Ads disabled, skipping ad request");
|
|
650
|
-
}
|
|
480
|
+
const ready = await this.initialize();
|
|
481
|
+
if (!ready || !window.adBreak) {
|
|
651
482
|
return {
|
|
652
483
|
success: false,
|
|
653
484
|
type,
|
|
654
|
-
error: new Error("Ads
|
|
655
|
-
requestedAt,
|
|
656
|
-
completedAt: Date.now()
|
|
657
|
-
};
|
|
658
|
-
}
|
|
659
|
-
if (!this.initialized) {
|
|
660
|
-
this.initialize();
|
|
661
|
-
}
|
|
662
|
-
if (!this.ready || !window.adBreak) {
|
|
663
|
-
return {
|
|
664
|
-
success: false,
|
|
665
|
-
type,
|
|
666
|
-
error: new Error("Ads not ready"),
|
|
485
|
+
error: new Error("Ads not available"),
|
|
667
486
|
requestedAt,
|
|
668
487
|
completedAt: Date.now()
|
|
669
488
|
};
|
|
@@ -671,7 +490,7 @@ var AdsService = class {
|
|
|
671
490
|
return this.showAdBreak(type);
|
|
672
491
|
}
|
|
673
492
|
/**
|
|
674
|
-
* Show an ad break
|
|
493
|
+
* Show an ad break via the Google H5 API.
|
|
675
494
|
*/
|
|
676
495
|
async showAdBreak(type) {
|
|
677
496
|
const requestedAt = Date.now();
|
|
@@ -679,81 +498,48 @@ var AdsService = class {
|
|
|
679
498
|
const googleType = type === "rewarded" ? "reward" : type === "preroll" ? "start" : "next";
|
|
680
499
|
const adName = `${type}-ad-${Date.now()}`;
|
|
681
500
|
if (this.config.debug) {
|
|
682
|
-
|
|
501
|
+
logger.debug(`[AdsService] Showing ${type} ad`);
|
|
683
502
|
}
|
|
684
503
|
this.config.onBeforeAd(type);
|
|
685
504
|
const adBreakConfig = {
|
|
686
505
|
type: googleType,
|
|
687
506
|
name: adName,
|
|
688
|
-
beforeAd: () => {
|
|
689
|
-
if (this.config.debug) {
|
|
690
|
-
console.log("[AdsService] Ad started");
|
|
691
|
-
}
|
|
692
|
-
},
|
|
693
507
|
afterAd: () => {
|
|
694
|
-
if (this.config.debug) {
|
|
695
|
-
console.log("[AdsService] Ad finished");
|
|
696
|
-
}
|
|
697
508
|
this.config.onAfterAd(type);
|
|
698
509
|
},
|
|
699
510
|
adBreakDone: (info) => {
|
|
700
511
|
const completedAt = Date.now();
|
|
701
|
-
|
|
702
|
-
if (type === "rewarded") {
|
|
703
|
-
success = info?.breakStatus === "viewed";
|
|
704
|
-
} else {
|
|
705
|
-
success = info?.breakStatus !== "error";
|
|
706
|
-
}
|
|
512
|
+
const success = type === "rewarded" ? info?.breakStatus === "viewed" : info?.breakStatus !== "error";
|
|
707
513
|
const error = info?.breakStatus === "error" && info?.error ? new Error(info.error) : void 0;
|
|
708
514
|
if (this.config.debug) {
|
|
709
|
-
|
|
710
|
-
success,
|
|
711
|
-
status: info?.breakStatus
|
|
712
|
-
});
|
|
515
|
+
logger.debug("[AdsService] Ad break done:", { success, status: info?.breakStatus });
|
|
713
516
|
}
|
|
714
|
-
|
|
715
|
-
success,
|
|
716
|
-
type,
|
|
717
|
-
error,
|
|
718
|
-
requestedAt,
|
|
719
|
-
completedAt
|
|
720
|
-
};
|
|
721
|
-
resolve(result);
|
|
517
|
+
resolve({ success, type, error, requestedAt, completedAt });
|
|
722
518
|
}
|
|
723
519
|
};
|
|
724
520
|
if (type === "rewarded") {
|
|
725
|
-
adBreakConfig.beforeReward = (showAdFn) =>
|
|
726
|
-
|
|
727
|
-
console.log("[AdsService] beforeReward callback");
|
|
728
|
-
}
|
|
729
|
-
showAdFn();
|
|
730
|
-
};
|
|
731
|
-
adBreakConfig.adViewed = () => {
|
|
732
|
-
if (this.config.debug) {
|
|
733
|
-
console.log("[AdsService] Rewarded ad watched successfully");
|
|
734
|
-
}
|
|
735
|
-
this.config.onRewardEarned();
|
|
736
|
-
};
|
|
737
|
-
adBreakConfig.adDismissed = () => {
|
|
738
|
-
if (this.config.debug) {
|
|
739
|
-
console.log("[AdsService] Rewarded ad dismissed");
|
|
740
|
-
}
|
|
741
|
-
};
|
|
521
|
+
adBreakConfig.beforeReward = (showAdFn) => showAdFn();
|
|
522
|
+
adBreakConfig.adViewed = () => this.config.onRewardEarned();
|
|
742
523
|
}
|
|
743
524
|
window.adBreak(adBreakConfig);
|
|
744
525
|
});
|
|
745
526
|
}
|
|
746
527
|
/**
|
|
747
|
-
*
|
|
528
|
+
* Returns the configured ad lifecycle callbacks.
|
|
529
|
+
* Used by platform-specific providers (e.g. Playgama) to fire the same hooks.
|
|
748
530
|
*/
|
|
749
|
-
|
|
750
|
-
return
|
|
531
|
+
getCallbacks() {
|
|
532
|
+
return {
|
|
533
|
+
onBeforeAd: this.config.onBeforeAd,
|
|
534
|
+
onAfterAd: this.config.onAfterAd,
|
|
535
|
+
onRewardEarned: this.config.onRewardEarned
|
|
536
|
+
};
|
|
751
537
|
}
|
|
752
538
|
/**
|
|
753
|
-
* Check if ads are ready to show
|
|
539
|
+
* Check if ads have successfully initialized and are ready to show.
|
|
754
540
|
*/
|
|
755
541
|
isReady() {
|
|
756
|
-
return this.
|
|
542
|
+
return this.ready;
|
|
757
543
|
}
|
|
758
544
|
};
|
|
759
545
|
|
|
@@ -800,16 +586,17 @@ var PlaygamaService = class {
|
|
|
800
586
|
this.initialized = true;
|
|
801
587
|
return true;
|
|
802
588
|
} catch (error) {
|
|
803
|
-
|
|
589
|
+
logger.warn("[PlaygamaService] Failed to initialize:", error);
|
|
804
590
|
return false;
|
|
805
591
|
}
|
|
806
592
|
}
|
|
807
593
|
isInitialized() {
|
|
808
594
|
return this.initialized;
|
|
809
595
|
}
|
|
810
|
-
async showInterstitial() {
|
|
596
|
+
async showInterstitial(callbacks) {
|
|
811
597
|
const requestedAt = Date.now();
|
|
812
|
-
|
|
598
|
+
const bridge = window.bridge;
|
|
599
|
+
if (!this.initialized || !bridge) {
|
|
813
600
|
return {
|
|
814
601
|
success: false,
|
|
815
602
|
type: "interstitial",
|
|
@@ -818,13 +605,26 @@ var PlaygamaService = class {
|
|
|
818
605
|
completedAt: Date.now()
|
|
819
606
|
};
|
|
820
607
|
}
|
|
608
|
+
if (!bridge.advertisement.isInterstitialSupported) {
|
|
609
|
+
return {
|
|
610
|
+
success: false,
|
|
611
|
+
type: "interstitial",
|
|
612
|
+
error: new Error("Interstitial ads not supported on this platform"),
|
|
613
|
+
requestedAt,
|
|
614
|
+
completedAt: Date.now()
|
|
615
|
+
};
|
|
616
|
+
}
|
|
821
617
|
return new Promise((resolve) => {
|
|
822
618
|
const handler = (state) => {
|
|
823
|
-
if (state === "
|
|
824
|
-
|
|
619
|
+
if (state === "opened") {
|
|
620
|
+
callbacks?.onBeforeAd?.();
|
|
621
|
+
} else if (state === "closed") {
|
|
622
|
+
bridge.advertisement.off(bridge.EVENT_NAME.INTERSTITIAL_STATE_CHANGED, handler);
|
|
623
|
+
callbacks?.onAfterAd?.();
|
|
825
624
|
resolve({ success: true, type: "interstitial", requestedAt, completedAt: Date.now() });
|
|
826
625
|
} else if (state === "failed") {
|
|
827
|
-
|
|
626
|
+
bridge.advertisement.off(bridge.EVENT_NAME.INTERSTITIAL_STATE_CHANGED, handler);
|
|
627
|
+
callbacks?.onAfterAd?.();
|
|
828
628
|
resolve({
|
|
829
629
|
success: false,
|
|
830
630
|
type: "interstitial",
|
|
@@ -834,13 +634,14 @@ var PlaygamaService = class {
|
|
|
834
634
|
});
|
|
835
635
|
}
|
|
836
636
|
};
|
|
837
|
-
|
|
838
|
-
|
|
637
|
+
bridge.advertisement.on(bridge.EVENT_NAME.INTERSTITIAL_STATE_CHANGED, handler);
|
|
638
|
+
bridge.advertisement.showInterstitial();
|
|
839
639
|
});
|
|
840
640
|
}
|
|
841
|
-
async showRewarded() {
|
|
641
|
+
async showRewarded(callbacks) {
|
|
842
642
|
const requestedAt = Date.now();
|
|
843
|
-
|
|
643
|
+
const bridge = window.bridge;
|
|
644
|
+
if (!this.initialized || !bridge) {
|
|
844
645
|
return {
|
|
845
646
|
success: false,
|
|
846
647
|
type: "rewarded",
|
|
@@ -849,13 +650,26 @@ var PlaygamaService = class {
|
|
|
849
650
|
completedAt: Date.now()
|
|
850
651
|
};
|
|
851
652
|
}
|
|
653
|
+
if (!bridge.advertisement.isRewardedSupported) {
|
|
654
|
+
return {
|
|
655
|
+
success: false,
|
|
656
|
+
type: "rewarded",
|
|
657
|
+
error: new Error("Rewarded ads not supported on this platform"),
|
|
658
|
+
requestedAt,
|
|
659
|
+
completedAt: Date.now()
|
|
660
|
+
};
|
|
661
|
+
}
|
|
852
662
|
return new Promise((resolve) => {
|
|
853
663
|
let rewarded = false;
|
|
854
664
|
const handler = (state) => {
|
|
855
|
-
if (state === "
|
|
665
|
+
if (state === "opened") {
|
|
666
|
+
callbacks?.onBeforeAd?.();
|
|
667
|
+
} else if (state === "rewarded") {
|
|
856
668
|
rewarded = true;
|
|
669
|
+
callbacks?.onRewardEarned?.();
|
|
857
670
|
} else if (state === "closed" || state === "failed") {
|
|
858
|
-
|
|
671
|
+
bridge.advertisement.off(bridge.EVENT_NAME.REWARDED_STATE_CHANGED, handler);
|
|
672
|
+
callbacks?.onAfterAd?.();
|
|
859
673
|
resolve({
|
|
860
674
|
success: rewarded,
|
|
861
675
|
type: "rewarded",
|
|
@@ -865,8 +679,8 @@ var PlaygamaService = class {
|
|
|
865
679
|
});
|
|
866
680
|
}
|
|
867
681
|
};
|
|
868
|
-
|
|
869
|
-
|
|
682
|
+
bridge.advertisement.on(bridge.EVENT_NAME.REWARDED_STATE_CHANGED, handler);
|
|
683
|
+
bridge.advertisement.showRewarded();
|
|
870
684
|
});
|
|
871
685
|
}
|
|
872
686
|
loadScript() {
|
|
@@ -884,6 +698,176 @@ var PlaygamaService = class {
|
|
|
884
698
|
}
|
|
885
699
|
};
|
|
886
700
|
|
|
701
|
+
// src/services/crazygames.ts
|
|
702
|
+
var CRAZYGAMES_SDK_CDN = "https://sdk.crazygames.com/crazygames-sdk-v2.js";
|
|
703
|
+
var CrazyGamesService = class {
|
|
704
|
+
initialized = false;
|
|
705
|
+
/**
|
|
706
|
+
* Detects if the game is running on the CrazyGames platform.
|
|
707
|
+
* Games on CrazyGames run inside an iframe, so we check document.referrer
|
|
708
|
+
* and attempt to read the parent frame location.
|
|
709
|
+
*/
|
|
710
|
+
static isCrazyGamesDomain() {
|
|
711
|
+
try {
|
|
712
|
+
if (document.referrer.includes("crazygames.com")) {
|
|
713
|
+
return true;
|
|
714
|
+
}
|
|
715
|
+
if (window !== window.top) {
|
|
716
|
+
try {
|
|
717
|
+
if (window.parent.location.hostname.includes("crazygames.com")) {
|
|
718
|
+
return true;
|
|
719
|
+
}
|
|
720
|
+
} catch {
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
return false;
|
|
724
|
+
} catch {
|
|
725
|
+
return false;
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
/**
|
|
729
|
+
* Loads the CrazyGames SDK from CDN and confirms the environment is 'crazygames'.
|
|
730
|
+
* Safe to call multiple times — resolves immediately if already initialized.
|
|
731
|
+
*/
|
|
732
|
+
async initialize() {
|
|
733
|
+
if (this.initialized) return true;
|
|
734
|
+
try {
|
|
735
|
+
await this.loadScript();
|
|
736
|
+
const sdk = window.CrazyGames?.SDK;
|
|
737
|
+
if (!sdk) {
|
|
738
|
+
logger.warn("[CrazyGamesService] SDK not found after script load");
|
|
739
|
+
return false;
|
|
740
|
+
}
|
|
741
|
+
const env = await sdk.getEnvironment();
|
|
742
|
+
if (env !== "crazygames" && env !== "local") {
|
|
743
|
+
logger.warn("[CrazyGamesService] Unexpected environment:", env);
|
|
744
|
+
return false;
|
|
745
|
+
}
|
|
746
|
+
this.initialized = true;
|
|
747
|
+
return true;
|
|
748
|
+
} catch (error) {
|
|
749
|
+
logger.warn("[CrazyGamesService] Failed to initialize:", error);
|
|
750
|
+
return false;
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
isInitialized() {
|
|
754
|
+
return this.initialized;
|
|
755
|
+
}
|
|
756
|
+
/**
|
|
757
|
+
* Shows a midgame (interstitial) ad via the CrazyGames SDK.
|
|
758
|
+
*/
|
|
759
|
+
async showInterstitial(callbacks) {
|
|
760
|
+
const requestedAt = Date.now();
|
|
761
|
+
const sdk = window.CrazyGames?.SDK;
|
|
762
|
+
if (!this.initialized || !sdk) {
|
|
763
|
+
return {
|
|
764
|
+
success: false,
|
|
765
|
+
type: "interstitial",
|
|
766
|
+
error: new Error("CrazyGames SDK not initialized"),
|
|
767
|
+
requestedAt,
|
|
768
|
+
completedAt: Date.now()
|
|
769
|
+
};
|
|
770
|
+
}
|
|
771
|
+
return new Promise((resolve) => {
|
|
772
|
+
sdk.ad.requestAd("midgame", {
|
|
773
|
+
adStarted: () => {
|
|
774
|
+
callbacks?.onBeforeAd?.();
|
|
775
|
+
},
|
|
776
|
+
adFinished: () => {
|
|
777
|
+
callbacks?.onAfterAd?.();
|
|
778
|
+
resolve({ success: true, type: "interstitial", requestedAt, completedAt: Date.now() });
|
|
779
|
+
},
|
|
780
|
+
adError: (error) => {
|
|
781
|
+
callbacks?.onAfterAd?.();
|
|
782
|
+
resolve({
|
|
783
|
+
success: false,
|
|
784
|
+
type: "interstitial",
|
|
785
|
+
error: new Error(`CrazyGames interstitial ad error: ${error}`),
|
|
786
|
+
requestedAt,
|
|
787
|
+
completedAt: Date.now()
|
|
788
|
+
});
|
|
789
|
+
}
|
|
790
|
+
});
|
|
791
|
+
});
|
|
792
|
+
}
|
|
793
|
+
/**
|
|
794
|
+
* Shows a rewarded ad via the CrazyGames SDK.
|
|
795
|
+
* Resolves with success: true only when adFinished fires (ad was fully watched).
|
|
796
|
+
*/
|
|
797
|
+
async showRewarded(callbacks) {
|
|
798
|
+
const requestedAt = Date.now();
|
|
799
|
+
const sdk = window.CrazyGames?.SDK;
|
|
800
|
+
if (!this.initialized || !sdk) {
|
|
801
|
+
return {
|
|
802
|
+
success: false,
|
|
803
|
+
type: "rewarded",
|
|
804
|
+
error: new Error("CrazyGames SDK not initialized"),
|
|
805
|
+
requestedAt,
|
|
806
|
+
completedAt: Date.now()
|
|
807
|
+
};
|
|
808
|
+
}
|
|
809
|
+
return new Promise((resolve) => {
|
|
810
|
+
sdk.ad.requestAd("rewarded", {
|
|
811
|
+
adStarted: () => {
|
|
812
|
+
callbacks?.onBeforeAd?.();
|
|
813
|
+
},
|
|
814
|
+
adFinished: () => {
|
|
815
|
+
callbacks?.onRewardEarned?.();
|
|
816
|
+
callbacks?.onAfterAd?.();
|
|
817
|
+
resolve({ success: true, type: "rewarded", requestedAt, completedAt: Date.now() });
|
|
818
|
+
},
|
|
819
|
+
adError: (error) => {
|
|
820
|
+
callbacks?.onAfterAd?.();
|
|
821
|
+
resolve({
|
|
822
|
+
success: false,
|
|
823
|
+
type: "rewarded",
|
|
824
|
+
error: new Error(`CrazyGames rewarded ad error: ${error}`),
|
|
825
|
+
requestedAt,
|
|
826
|
+
completedAt: Date.now()
|
|
827
|
+
});
|
|
828
|
+
}
|
|
829
|
+
});
|
|
830
|
+
});
|
|
831
|
+
}
|
|
832
|
+
/**
|
|
833
|
+
* Notifies CrazyGames that gameplay has started.
|
|
834
|
+
* Call when the player actively begins or resumes playing.
|
|
835
|
+
*/
|
|
836
|
+
gameplayStart() {
|
|
837
|
+
if (!this.initialized) return;
|
|
838
|
+
window.CrazyGames?.SDK.game.gameplayStart();
|
|
839
|
+
}
|
|
840
|
+
/**
|
|
841
|
+
* Notifies CrazyGames that gameplay has stopped.
|
|
842
|
+
* Call during menu access, level completion, or pausing.
|
|
843
|
+
*/
|
|
844
|
+
gameplayStop() {
|
|
845
|
+
if (!this.initialized) return;
|
|
846
|
+
window.CrazyGames?.SDK.game.gameplayStop();
|
|
847
|
+
}
|
|
848
|
+
/**
|
|
849
|
+
* Triggers a celebration effect on the CrazyGames website.
|
|
850
|
+
* Use sparingly for significant achievements (boss defeat, personal record, etc.)
|
|
851
|
+
*/
|
|
852
|
+
happytime() {
|
|
853
|
+
if (!this.initialized) return;
|
|
854
|
+
window.CrazyGames?.SDK.game.happytime();
|
|
855
|
+
}
|
|
856
|
+
loadScript() {
|
|
857
|
+
return new Promise((resolve, reject) => {
|
|
858
|
+
if (window.CrazyGames?.SDK) {
|
|
859
|
+
resolve();
|
|
860
|
+
return;
|
|
861
|
+
}
|
|
862
|
+
const script = document.createElement("script");
|
|
863
|
+
script.src = CRAZYGAMES_SDK_CDN;
|
|
864
|
+
script.onload = () => resolve();
|
|
865
|
+
script.onerror = () => reject(new Error("Failed to load CrazyGames SDK script"));
|
|
866
|
+
document.head.appendChild(script);
|
|
867
|
+
});
|
|
868
|
+
}
|
|
869
|
+
};
|
|
870
|
+
|
|
887
871
|
// src/services/billing.ts
|
|
888
872
|
var BillingPlatform = /* @__PURE__ */ ((BillingPlatform3) => {
|
|
889
873
|
BillingPlatform3["WEB"] = "web";
|
|
@@ -894,41 +878,28 @@ var BillingPlatform = /* @__PURE__ */ ((BillingPlatform3) => {
|
|
|
894
878
|
var BillingService = class {
|
|
895
879
|
config;
|
|
896
880
|
platform = "unknown" /* UNKNOWN */;
|
|
897
|
-
|
|
881
|
+
initPromise = null;
|
|
898
882
|
nativeAvailable = false;
|
|
899
|
-
products = [];
|
|
900
883
|
// Stripe instance for web payments
|
|
901
884
|
stripe = null;
|
|
902
885
|
checkoutElement = null;
|
|
903
886
|
// Callbacks for purchase events
|
|
904
887
|
onPurchaseCompleteCallback;
|
|
905
888
|
onPurchaseErrorCallback;
|
|
906
|
-
// Callback for logging (so external UI can capture logs)
|
|
907
|
-
onLogCallback;
|
|
908
889
|
constructor(config) {
|
|
909
890
|
this.config = config;
|
|
910
891
|
this.detectPlatform();
|
|
911
892
|
}
|
|
912
893
|
/**
|
|
913
|
-
*
|
|
914
|
-
* Useful for displaying logs in a UI
|
|
915
|
-
*/
|
|
916
|
-
onLog(callback) {
|
|
917
|
-
this.onLogCallback = callback;
|
|
918
|
-
}
|
|
919
|
-
/**
|
|
920
|
-
* Internal logging method that calls both logger and custom callback
|
|
894
|
+
* Update billing configuration. Resets initialization so next call re-inits with new config.
|
|
921
895
|
*/
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
}
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
if (this.onLogCallback) {
|
|
930
|
-
this.onLogCallback(level, message, data);
|
|
931
|
-
}
|
|
896
|
+
configure(config) {
|
|
897
|
+
this.config = {
|
|
898
|
+
...this.config,
|
|
899
|
+
...config
|
|
900
|
+
};
|
|
901
|
+
this.initPromise = null;
|
|
902
|
+
logger.info("[BillingService] Configuration updated");
|
|
932
903
|
}
|
|
933
904
|
/**
|
|
934
905
|
* Detects if running on web or native platform
|
|
@@ -938,13 +909,13 @@ var BillingService = class {
|
|
|
938
909
|
const hasWindow = typeof window !== "undefined";
|
|
939
910
|
if (isNative) {
|
|
940
911
|
this.platform = "native" /* NATIVE */;
|
|
941
|
-
|
|
912
|
+
logger.info("[BillingService] Platform: NATIVE");
|
|
942
913
|
} else if (hasWindow) {
|
|
943
914
|
this.platform = "web" /* WEB */;
|
|
944
|
-
|
|
915
|
+
logger.info("[BillingService] Platform: WEB");
|
|
945
916
|
} else {
|
|
946
917
|
this.platform = "unknown" /* UNKNOWN */;
|
|
947
|
-
|
|
918
|
+
logger.warn("[BillingService] Platform: UNKNOWN");
|
|
948
919
|
}
|
|
949
920
|
}
|
|
950
921
|
/**
|
|
@@ -954,38 +925,40 @@ var BillingService = class {
|
|
|
954
925
|
return this.platform;
|
|
955
926
|
}
|
|
956
927
|
/**
|
|
957
|
-
* Initialize the billing service
|
|
958
|
-
*
|
|
928
|
+
* Initialize the billing service. Idempotent — returns cached promise on subsequent calls.
|
|
929
|
+
* Called automatically by getProducts() and purchase().
|
|
959
930
|
*/
|
|
960
|
-
|
|
961
|
-
if (this.
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
931
|
+
initialize() {
|
|
932
|
+
if (this.initPromise) return this.initPromise;
|
|
933
|
+
logger.info(`[BillingService] Initializing for ${this.platform} platform...`);
|
|
934
|
+
this.initPromise = (async () => {
|
|
935
|
+
try {
|
|
936
|
+
if (this.platform === "native" /* NATIVE */) {
|
|
937
|
+
return await this.initializeNative();
|
|
938
|
+
} else if (this.platform === "web" /* WEB */) {
|
|
939
|
+
return await this.initializeWeb();
|
|
940
|
+
}
|
|
941
|
+
logger.error("[BillingService] Cannot initialize: unknown platform");
|
|
942
|
+
return false;
|
|
943
|
+
} catch (error) {
|
|
944
|
+
logger.error("[BillingService] Initialization failed:", error?.message);
|
|
945
|
+
return false;
|
|
970
946
|
}
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
} catch (error) {
|
|
974
|
-
this.log("error", "[BillingService] Initialization failed:", error?.message);
|
|
975
|
-
return false;
|
|
976
|
-
}
|
|
947
|
+
})();
|
|
948
|
+
return this.initPromise;
|
|
977
949
|
}
|
|
978
950
|
/**
|
|
979
951
|
* Initialize native billing
|
|
980
952
|
*/
|
|
981
953
|
async initializeNative() {
|
|
982
954
|
return new Promise((resolve) => {
|
|
955
|
+
let resolved = false;
|
|
983
956
|
try {
|
|
984
957
|
NativeBridge.initialize();
|
|
985
958
|
NativeBridge.on(
|
|
986
959
|
"PURCHASE_COMPLETE" /* PURCHASE_COMPLETE */,
|
|
987
960
|
(payload) => {
|
|
988
|
-
|
|
961
|
+
logger.info("[BillingService] Purchase complete:", payload.productId);
|
|
989
962
|
const result = {
|
|
990
963
|
success: true,
|
|
991
964
|
productId: payload.productId,
|
|
@@ -1000,7 +973,7 @@ var BillingService = class {
|
|
|
1000
973
|
NativeBridge.on(
|
|
1001
974
|
"PURCHASE_ERROR" /* PURCHASE_ERROR */,
|
|
1002
975
|
(payload) => {
|
|
1003
|
-
|
|
976
|
+
logger.error("[BillingService] Purchase error:", payload.message);
|
|
1004
977
|
const result = {
|
|
1005
978
|
success: false,
|
|
1006
979
|
productId: payload.productId || "unknown",
|
|
@@ -1018,8 +991,8 @@ var BillingService = class {
|
|
|
1018
991
|
"IAP_AVAILABILITY_RESULT" /* IAP_AVAILABILITY_RESULT */,
|
|
1019
992
|
(payload) => {
|
|
1020
993
|
this.nativeAvailable = payload.available;
|
|
1021
|
-
|
|
1022
|
-
|
|
994
|
+
logger.info(`[BillingService] Native billing ${payload.available ? "available" : "unavailable"}`);
|
|
995
|
+
resolved = true;
|
|
1023
996
|
resolve(payload.available);
|
|
1024
997
|
}
|
|
1025
998
|
);
|
|
@@ -1027,15 +1000,15 @@ var BillingService = class {
|
|
|
1027
1000
|
NativeBridge.checkIAPAvailability();
|
|
1028
1001
|
}, 100);
|
|
1029
1002
|
setTimeout(() => {
|
|
1030
|
-
if (!
|
|
1031
|
-
|
|
1032
|
-
|
|
1003
|
+
if (!resolved) {
|
|
1004
|
+
logger.warn("[BillingService] Native initialization timeout");
|
|
1005
|
+
resolved = true;
|
|
1033
1006
|
resolve(false);
|
|
1034
1007
|
}
|
|
1035
1008
|
}, 5e3);
|
|
1036
1009
|
} catch (error) {
|
|
1037
|
-
|
|
1038
|
-
|
|
1010
|
+
logger.error("[BillingService] Native initialization failed:", error?.message);
|
|
1011
|
+
resolved = true;
|
|
1039
1012
|
resolve(false);
|
|
1040
1013
|
}
|
|
1041
1014
|
});
|
|
@@ -1045,12 +1018,12 @@ var BillingService = class {
|
|
|
1045
1018
|
*/
|
|
1046
1019
|
async initializeWeb() {
|
|
1047
1020
|
if (!this.config.stripePublishableKey) {
|
|
1048
|
-
|
|
1021
|
+
logger.error("[BillingService] Stripe publishable key not provided");
|
|
1049
1022
|
return false;
|
|
1050
1023
|
}
|
|
1051
1024
|
try {
|
|
1052
1025
|
if (typeof window === "undefined") {
|
|
1053
|
-
|
|
1026
|
+
logger.error("[BillingService] Window is undefined (not in browser)");
|
|
1054
1027
|
return false;
|
|
1055
1028
|
}
|
|
1056
1029
|
if (!window.Stripe) {
|
|
@@ -1058,15 +1031,14 @@ var BillingService = class {
|
|
|
1058
1031
|
}
|
|
1059
1032
|
if (window.Stripe) {
|
|
1060
1033
|
this.stripe = window.Stripe(this.config.stripePublishableKey);
|
|
1061
|
-
|
|
1062
|
-
this.log("info", "[BillingService] Web billing initialized");
|
|
1034
|
+
logger.info("[BillingService] Web billing initialized");
|
|
1063
1035
|
return true;
|
|
1064
1036
|
} else {
|
|
1065
|
-
|
|
1037
|
+
logger.error("[BillingService] Stripe not available after loading");
|
|
1066
1038
|
return false;
|
|
1067
1039
|
}
|
|
1068
1040
|
} catch (error) {
|
|
1069
|
-
|
|
1041
|
+
logger.error("[BillingService] Stripe initialization failed:", error?.message);
|
|
1070
1042
|
return false;
|
|
1071
1043
|
}
|
|
1072
1044
|
}
|
|
@@ -1091,11 +1063,11 @@ var BillingService = class {
|
|
|
1091
1063
|
script.src = "https://js.stripe.com/v3/";
|
|
1092
1064
|
script.async = true;
|
|
1093
1065
|
script.onload = () => {
|
|
1094
|
-
|
|
1066
|
+
logger.info("[BillingService] Stripe.js loaded");
|
|
1095
1067
|
resolve();
|
|
1096
1068
|
};
|
|
1097
1069
|
script.onerror = () => {
|
|
1098
|
-
|
|
1070
|
+
logger.error("[BillingService] Failed to load Stripe.js");
|
|
1099
1071
|
reject(new Error("Failed to load Stripe.js"));
|
|
1100
1072
|
};
|
|
1101
1073
|
try {
|
|
@@ -1106,12 +1078,9 @@ var BillingService = class {
|
|
|
1106
1078
|
});
|
|
1107
1079
|
}
|
|
1108
1080
|
/**
|
|
1109
|
-
* Check if billing is available
|
|
1081
|
+
* Check if billing is available based on current config and platform state
|
|
1110
1082
|
*/
|
|
1111
1083
|
isAvailable() {
|
|
1112
|
-
if (!this.isInitialized) {
|
|
1113
|
-
return false;
|
|
1114
|
-
}
|
|
1115
1084
|
if (this.platform === "native" /* NATIVE */) {
|
|
1116
1085
|
return this.nativeAvailable;
|
|
1117
1086
|
} else if (this.platform === "web" /* WEB */) {
|
|
@@ -1120,12 +1089,10 @@ var BillingService = class {
|
|
|
1120
1089
|
return false;
|
|
1121
1090
|
}
|
|
1122
1091
|
/**
|
|
1123
|
-
* Get available products
|
|
1092
|
+
* Get available products. Auto-initializes on first call.
|
|
1124
1093
|
*/
|
|
1125
1094
|
async getProducts() {
|
|
1126
|
-
|
|
1127
|
-
throw new Error("BillingService not initialized. Call initialize() first.");
|
|
1128
|
-
}
|
|
1095
|
+
await this.initialize();
|
|
1129
1096
|
if (this.platform === "native" /* NATIVE */) {
|
|
1130
1097
|
return await this.getProductsNative();
|
|
1131
1098
|
} else if (this.platform === "web" /* WEB */) {
|
|
@@ -1137,38 +1104,31 @@ var BillingService = class {
|
|
|
1137
1104
|
* Get products from native IAP
|
|
1138
1105
|
*/
|
|
1139
1106
|
async getProductsNative() {
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
}
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
this.log("info", `[BillingService] Fetched ${this.products.length} native products`);
|
|
1166
|
-
resolve(this.products);
|
|
1167
|
-
}).catch((error) => {
|
|
1168
|
-
this.log("error", "[BillingService] Failed to fetch native products:", error?.message);
|
|
1169
|
-
reject(error);
|
|
1170
|
-
});
|
|
1171
|
-
});
|
|
1107
|
+
if (!this.config.gameId || !this.config.checkoutUrl) {
|
|
1108
|
+
const error = new Error("gameId and checkoutUrl required for native purchases");
|
|
1109
|
+
logger.error("[BillingService]", error.message);
|
|
1110
|
+
throw error;
|
|
1111
|
+
}
|
|
1112
|
+
const response = await fetch(
|
|
1113
|
+
`${this.config.checkoutUrl}/get-native-packages?game_id=${this.config.gameId}`
|
|
1114
|
+
);
|
|
1115
|
+
if (!response.ok) {
|
|
1116
|
+
throw new Error(`Failed to fetch native products: ${response.status}`);
|
|
1117
|
+
}
|
|
1118
|
+
const data = await response.json();
|
|
1119
|
+
if (!data.packages || !Array.isArray(data.packages)) {
|
|
1120
|
+
throw new Error("Invalid response format: missing packages array");
|
|
1121
|
+
}
|
|
1122
|
+
const products = data.packages.map((pkg) => ({
|
|
1123
|
+
productId: pkg.productId,
|
|
1124
|
+
title: pkg.package_name,
|
|
1125
|
+
description: `${pkg.game_name} - ${pkg.package_name}`,
|
|
1126
|
+
price: pkg.price_cents / 100,
|
|
1127
|
+
localizedPrice: pkg.price_display,
|
|
1128
|
+
currency: "USD"
|
|
1129
|
+
}));
|
|
1130
|
+
logger.info(`[BillingService] Fetched ${products.length} native products`);
|
|
1131
|
+
return products;
|
|
1172
1132
|
}
|
|
1173
1133
|
/**
|
|
1174
1134
|
* Get products from web API (Stripe)
|
|
@@ -1176,7 +1136,7 @@ var BillingService = class {
|
|
|
1176
1136
|
async getProductsWeb() {
|
|
1177
1137
|
if (!this.config.checkoutUrl || !this.config.gameId) {
|
|
1178
1138
|
const error = new Error("checkoutUrl and gameId required for web purchases");
|
|
1179
|
-
|
|
1139
|
+
logger.error("[BillingService]", error.message);
|
|
1180
1140
|
throw error;
|
|
1181
1141
|
}
|
|
1182
1142
|
try {
|
|
@@ -1189,7 +1149,7 @@ var BillingService = class {
|
|
|
1189
1149
|
if (!data.packages || !Array.isArray(data.packages)) {
|
|
1190
1150
|
throw new Error("Invalid response format: missing packages array");
|
|
1191
1151
|
}
|
|
1192
|
-
|
|
1152
|
+
const products = data.packages.map((pkg) => ({
|
|
1193
1153
|
productId: pkg.priceId || pkg.productId,
|
|
1194
1154
|
// Prefer priceId for Stripe
|
|
1195
1155
|
title: pkg.package_name,
|
|
@@ -1198,23 +1158,21 @@ var BillingService = class {
|
|
|
1198
1158
|
localizedPrice: pkg.price_display,
|
|
1199
1159
|
currency: "USD"
|
|
1200
1160
|
}));
|
|
1201
|
-
|
|
1202
|
-
return
|
|
1161
|
+
logger.info(`[BillingService] Fetched ${products.length} web products`);
|
|
1162
|
+
return products;
|
|
1203
1163
|
} catch (error) {
|
|
1204
|
-
|
|
1164
|
+
logger.error("[BillingService] Failed to fetch web products:", error?.message);
|
|
1205
1165
|
throw error;
|
|
1206
1166
|
}
|
|
1207
1167
|
}
|
|
1208
1168
|
/**
|
|
1209
|
-
* Purchase a product
|
|
1169
|
+
* Purchase a product. Auto-initializes on first call.
|
|
1210
1170
|
* @param productId - The product ID (priceId for web/Stripe, productId for native)
|
|
1211
1171
|
* @param options - Optional purchase options
|
|
1212
1172
|
* @param options.elementId - For web: DOM element ID to mount Stripe checkout (default: 'stripe-checkout-element')
|
|
1213
1173
|
*/
|
|
1214
1174
|
async purchase(productId, options) {
|
|
1215
|
-
|
|
1216
|
-
throw new Error("BillingService not initialized. Call initialize() first.");
|
|
1217
|
-
}
|
|
1175
|
+
await this.initialize();
|
|
1218
1176
|
if (!this.isAvailable()) {
|
|
1219
1177
|
throw new Error("Billing is not available on this platform");
|
|
1220
1178
|
}
|
|
@@ -1234,7 +1192,7 @@ var BillingService = class {
|
|
|
1234
1192
|
reject(new Error("userId is required for native purchases"));
|
|
1235
1193
|
return;
|
|
1236
1194
|
}
|
|
1237
|
-
|
|
1195
|
+
logger.info(`[BillingService] Purchasing: ${productId}`);
|
|
1238
1196
|
const previousCompleteCallback = this.onPurchaseCompleteCallback;
|
|
1239
1197
|
const previousErrorCallback = this.onPurchaseErrorCallback;
|
|
1240
1198
|
const cleanup = () => {
|
|
@@ -1308,14 +1266,14 @@ var BillingService = class {
|
|
|
1308
1266
|
throw new Error("No client_secret returned from checkout session");
|
|
1309
1267
|
}
|
|
1310
1268
|
await this.mountCheckoutElement(client_secret, elementId || "stripe-checkout-element");
|
|
1311
|
-
|
|
1269
|
+
logger.info(`[BillingService] Checkout session created: ${id}`);
|
|
1312
1270
|
return {
|
|
1313
1271
|
success: true,
|
|
1314
1272
|
productId,
|
|
1315
1273
|
transactionId: id
|
|
1316
1274
|
};
|
|
1317
1275
|
} catch (error) {
|
|
1318
|
-
|
|
1276
|
+
logger.error("[BillingService] Web purchase failed:", error?.message);
|
|
1319
1277
|
return {
|
|
1320
1278
|
success: false,
|
|
1321
1279
|
productId,
|
|
@@ -1337,7 +1295,7 @@ var BillingService = class {
|
|
|
1337
1295
|
}
|
|
1338
1296
|
try {
|
|
1339
1297
|
if (this.checkoutElement) {
|
|
1340
|
-
|
|
1298
|
+
logger.info("[BillingService] Unmounting existing checkout element");
|
|
1341
1299
|
this.unmountCheckoutElement();
|
|
1342
1300
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
1343
1301
|
}
|
|
@@ -1346,15 +1304,15 @@ var BillingService = class {
|
|
|
1346
1304
|
throw new Error(`Element with id "${elementId}" not found in the DOM`);
|
|
1347
1305
|
}
|
|
1348
1306
|
container.innerHTML = "";
|
|
1349
|
-
|
|
1307
|
+
logger.info("[BillingService] Creating new checkout instance");
|
|
1350
1308
|
this.checkoutElement = await this.stripe.initEmbeddedCheckout({
|
|
1351
1309
|
clientSecret
|
|
1352
1310
|
});
|
|
1353
|
-
|
|
1311
|
+
logger.info("[BillingService] Mounting checkout element to DOM");
|
|
1354
1312
|
this.checkoutElement.mount(`#${elementId}`);
|
|
1355
1313
|
this.setupCheckoutEventListeners();
|
|
1356
1314
|
} catch (error) {
|
|
1357
|
-
|
|
1315
|
+
logger.error("[BillingService] Failed to mount checkout:", error?.message);
|
|
1358
1316
|
throw error;
|
|
1359
1317
|
}
|
|
1360
1318
|
}
|
|
@@ -1375,7 +1333,7 @@ var BillingService = class {
|
|
|
1375
1333
|
transactionId: sessionId || void 0
|
|
1376
1334
|
});
|
|
1377
1335
|
}
|
|
1378
|
-
|
|
1336
|
+
logger.info("[BillingService] Payment completed");
|
|
1379
1337
|
urlParams.delete("payment");
|
|
1380
1338
|
urlParams.delete("session_id");
|
|
1381
1339
|
const newUrl = `${window.location.pathname}${urlParams.toString() ? "?" + urlParams.toString() : ""}`;
|
|
@@ -1394,7 +1352,7 @@ var BillingService = class {
|
|
|
1394
1352
|
this.checkoutElement.destroy();
|
|
1395
1353
|
}
|
|
1396
1354
|
} catch (error) {
|
|
1397
|
-
|
|
1355
|
+
logger.warn("[BillingService] Error unmounting checkout element:", error?.message);
|
|
1398
1356
|
}
|
|
1399
1357
|
this.checkoutElement = null;
|
|
1400
1358
|
}
|
|
@@ -1422,9 +1380,8 @@ var BillingService = class {
|
|
|
1422
1380
|
NativeBridge.off("PURCHASE_ERROR" /* PURCHASE_ERROR */);
|
|
1423
1381
|
}
|
|
1424
1382
|
this.unmountCheckoutElement();
|
|
1425
|
-
this.
|
|
1383
|
+
this.initPromise = null;
|
|
1426
1384
|
this.nativeAvailable = false;
|
|
1427
|
-
this.products = [];
|
|
1428
1385
|
this.stripe = null;
|
|
1429
1386
|
this.onPurchaseCompleteCallback = void 0;
|
|
1430
1387
|
this.onPurchaseErrorCallback = void 0;
|
|
@@ -1596,10 +1553,10 @@ var HyveClient = class {
|
|
|
1596
1553
|
gameId = null;
|
|
1597
1554
|
adsService;
|
|
1598
1555
|
playgamaService = null;
|
|
1556
|
+
playgamaInitPromise = null;
|
|
1557
|
+
crazyGamesService = null;
|
|
1558
|
+
crazyGamesInitPromise = null;
|
|
1599
1559
|
billingService;
|
|
1600
|
-
billingConfig;
|
|
1601
|
-
// Store callbacks to preserve them when recreating BillingService
|
|
1602
|
-
billingCallbacks = {};
|
|
1603
1560
|
storageMode;
|
|
1604
1561
|
cloudStorageAdapter;
|
|
1605
1562
|
localStorageAdapter;
|
|
@@ -1625,17 +1582,33 @@ var HyveClient = class {
|
|
|
1625
1582
|
}
|
|
1626
1583
|
if (typeof window !== "undefined" && PlaygamaService.isPlaygamaDomain()) {
|
|
1627
1584
|
this.playgamaService = new PlaygamaService();
|
|
1628
|
-
this.playgamaService.initialize().then((success) => {
|
|
1585
|
+
this.playgamaInitPromise = this.playgamaService.initialize().then((success) => {
|
|
1629
1586
|
logger.info("Playgama Bridge initialized:", success);
|
|
1587
|
+
return success;
|
|
1588
|
+
});
|
|
1589
|
+
}
|
|
1590
|
+
if (typeof window !== "undefined" && CrazyGamesService.isCrazyGamesDomain()) {
|
|
1591
|
+
this.crazyGamesService = new CrazyGamesService();
|
|
1592
|
+
this.crazyGamesInitPromise = this.crazyGamesService.initialize().then((success) => {
|
|
1593
|
+
logger.info("CrazyGames SDK initialized:", success);
|
|
1594
|
+
return success;
|
|
1630
1595
|
});
|
|
1631
1596
|
}
|
|
1632
|
-
this.billingConfig = config?.billing || {};
|
|
1633
|
-
this.billingService = new BillingService(this.billingConfig);
|
|
1634
1597
|
this.storageMode = config?.storageMode || "cloud";
|
|
1635
1598
|
this.cloudStorageAdapter = new CloudStorageAdapter(
|
|
1636
1599
|
(endpoint, options) => this.callApi(endpoint, options)
|
|
1637
1600
|
);
|
|
1638
1601
|
this.localStorageAdapter = new LocalStorageAdapter(() => this.getUserId());
|
|
1602
|
+
if (typeof window !== "undefined") {
|
|
1603
|
+
this._parseUrlAuth();
|
|
1604
|
+
}
|
|
1605
|
+
const billingConfig = {
|
|
1606
|
+
checkoutUrl: this.apiBaseUrl,
|
|
1607
|
+
userId: this.userId ?? void 0,
|
|
1608
|
+
gameId: this.gameId ? Number(this.gameId) : void 0,
|
|
1609
|
+
...config?.billing
|
|
1610
|
+
};
|
|
1611
|
+
this.billingService = new BillingService(billingConfig);
|
|
1639
1612
|
const envSource = config?.isDev !== void 0 ? "explicit config" : "auto-detected from parent URL";
|
|
1640
1613
|
logger.info("==========================================");
|
|
1641
1614
|
logger.info("HyveClient Initialized");
|
|
@@ -1647,28 +1620,27 @@ var HyveClient = class {
|
|
|
1647
1620
|
`(${envSource})`
|
|
1648
1621
|
);
|
|
1649
1622
|
logger.info("API Base URL:", this.apiBaseUrl);
|
|
1650
|
-
logger.info("Ads enabled:", this.adsService.isEnabled());
|
|
1651
1623
|
logger.info("Playgama platform:", this.playgamaService !== null);
|
|
1624
|
+
logger.info("CrazyGames platform:", this.crazyGamesService !== null);
|
|
1652
1625
|
logger.info(
|
|
1653
1626
|
"Billing configured:",
|
|
1654
|
-
Object.keys(
|
|
1627
|
+
!!config?.billing && Object.keys(config.billing).length > 0
|
|
1655
1628
|
);
|
|
1656
1629
|
logger.info("Storage mode:", this.storageMode);
|
|
1630
|
+
logger.info("Authenticated:", this.jwtToken !== null);
|
|
1657
1631
|
logger.debug("Config:", {
|
|
1658
1632
|
isDev: this.telemetryConfig.isDev,
|
|
1659
1633
|
hasCustomApiUrl: !!config?.apiBaseUrl,
|
|
1660
|
-
|
|
1661
|
-
billingConfigured: Object.keys(this.billingConfig).length > 0,
|
|
1634
|
+
billingConfigured: !!config?.billing && Object.keys(config.billing).length > 0,
|
|
1662
1635
|
storageMode: this.storageMode
|
|
1663
1636
|
});
|
|
1664
1637
|
logger.info("==========================================");
|
|
1665
1638
|
}
|
|
1666
1639
|
/**
|
|
1667
|
-
*
|
|
1668
|
-
*
|
|
1669
|
-
* @returns Promise resolving to boolean indicating success
|
|
1640
|
+
* Parses JWT and game ID from the current window URL and stores them on the client.
|
|
1641
|
+
* Called automatically during construction.
|
|
1670
1642
|
*/
|
|
1671
|
-
|
|
1643
|
+
_parseUrlAuth(urlParams) {
|
|
1672
1644
|
try {
|
|
1673
1645
|
const params = urlParams ? parseUrlParams(urlParams) : parseUrlParams(window.location.search);
|
|
1674
1646
|
if (params.hyveAccess) {
|
|
@@ -1693,27 +1665,11 @@ var HyveClient = class {
|
|
|
1693
1665
|
}
|
|
1694
1666
|
if (this.jwtToken) {
|
|
1695
1667
|
logger.info("Authentication successful via JWT");
|
|
1696
|
-
return true;
|
|
1697
|
-
}
|
|
1698
|
-
const authResult = verifyAuthentication({
|
|
1699
|
-
hyveToken: params.hyveToken,
|
|
1700
|
-
signature: params.signature,
|
|
1701
|
-
message: params.message
|
|
1702
|
-
});
|
|
1703
|
-
if (authResult.isValid && authResult.address) {
|
|
1704
|
-
this.userId = authResult.address;
|
|
1705
|
-
logger.info("Authentication successful:", authResult.address);
|
|
1706
|
-
logger.info("Authentication method:", authResult.method);
|
|
1707
|
-
return true;
|
|
1708
1668
|
} else {
|
|
1709
|
-
logger.
|
|
1710
|
-
this.userId = null;
|
|
1711
|
-
return false;
|
|
1669
|
+
logger.info("No hyve-access JWT token in URL \u2014 unauthenticated");
|
|
1712
1670
|
}
|
|
1713
1671
|
} catch (error) {
|
|
1714
|
-
logger.error("
|
|
1715
|
-
this.userId = null;
|
|
1716
|
-
return false;
|
|
1672
|
+
logger.error("Error parsing URL auth:", error);
|
|
1717
1673
|
}
|
|
1718
1674
|
}
|
|
1719
1675
|
/**
|
|
@@ -1730,7 +1686,7 @@ var HyveClient = class {
|
|
|
1730
1686
|
*/
|
|
1731
1687
|
async sendTelemetry(eventLocation, eventCategory, eventAction, eventSubCategory, eventSubAction, eventDetails, platformId) {
|
|
1732
1688
|
if (!this.jwtToken) {
|
|
1733
|
-
logger.error("JWT token required.
|
|
1689
|
+
logger.error("JWT token required. Ensure hyve-access and game-id are present in the URL.");
|
|
1734
1690
|
return false;
|
|
1735
1691
|
}
|
|
1736
1692
|
if (!this.gameId) {
|
|
@@ -1802,7 +1758,7 @@ var HyveClient = class {
|
|
|
1802
1758
|
async callApi(endpoint, options = {}) {
|
|
1803
1759
|
if (!this.jwtToken) {
|
|
1804
1760
|
throw new Error(
|
|
1805
|
-
"No JWT token available.
|
|
1761
|
+
"No JWT token available. Ensure hyve-access and game-id are present in the URL."
|
|
1806
1762
|
);
|
|
1807
1763
|
}
|
|
1808
1764
|
try {
|
|
@@ -1932,6 +1888,13 @@ var HyveClient = class {
|
|
|
1932
1888
|
const storageMode = mode || this.storageMode;
|
|
1933
1889
|
return storageMode === "local" ? this.localStorageAdapter : this.cloudStorageAdapter;
|
|
1934
1890
|
}
|
|
1891
|
+
/**
|
|
1892
|
+
* Returns the current game ID or throws if not available.
|
|
1893
|
+
*/
|
|
1894
|
+
requireGameId() {
|
|
1895
|
+
const gameId = this.requireGameId();
|
|
1896
|
+
return gameId;
|
|
1897
|
+
}
|
|
1935
1898
|
/**
|
|
1936
1899
|
* Save persistent game data
|
|
1937
1900
|
* @param key Data key
|
|
@@ -1940,10 +1903,7 @@ var HyveClient = class {
|
|
|
1940
1903
|
* @returns Promise resolving to save response
|
|
1941
1904
|
*/
|
|
1942
1905
|
async saveGameData(key, value, storage) {
|
|
1943
|
-
const gameId = this.
|
|
1944
|
-
if (!gameId) {
|
|
1945
|
-
throw new Error("game-id is required for persistent storage. Call authenticateFromUrl first.");
|
|
1946
|
-
}
|
|
1906
|
+
const gameId = this.requireGameId();
|
|
1947
1907
|
const storageMode = storage || this.storageMode;
|
|
1948
1908
|
logger.debug(`Saving game data to ${storageMode}: ${gameId}/${key}`);
|
|
1949
1909
|
const adapter = this.getStorageAdapter(storage);
|
|
@@ -1958,10 +1918,7 @@ var HyveClient = class {
|
|
|
1958
1918
|
* @returns Promise resolving to save response
|
|
1959
1919
|
*/
|
|
1960
1920
|
async batchSaveGameData(items, storage) {
|
|
1961
|
-
const gameId = this.
|
|
1962
|
-
if (!gameId) {
|
|
1963
|
-
throw new Error("game-id is required for persistent storage. Call authenticateFromUrl first.");
|
|
1964
|
-
}
|
|
1921
|
+
const gameId = this.requireGameId();
|
|
1965
1922
|
const storageMode = storage || this.storageMode;
|
|
1966
1923
|
logger.debug(`Batch saving ${items.length} game data entries to ${storageMode} for game: ${gameId}`);
|
|
1967
1924
|
const adapter = this.getStorageAdapter(storage);
|
|
@@ -1976,10 +1933,7 @@ var HyveClient = class {
|
|
|
1976
1933
|
* @returns Promise resolving to game data item or null if not found
|
|
1977
1934
|
*/
|
|
1978
1935
|
async getGameData(key, storage) {
|
|
1979
|
-
const gameId = this.
|
|
1980
|
-
if (!gameId) {
|
|
1981
|
-
throw new Error("game-id is required for persistent storage. Call authenticateFromUrl first.");
|
|
1982
|
-
}
|
|
1936
|
+
const gameId = this.requireGameId();
|
|
1983
1937
|
const storageMode = storage || this.storageMode;
|
|
1984
1938
|
logger.debug(`Getting game data from ${storageMode}: ${gameId}/${key}`);
|
|
1985
1939
|
const adapter = this.getStorageAdapter(storage);
|
|
@@ -1998,10 +1952,7 @@ var HyveClient = class {
|
|
|
1998
1952
|
* @returns Promise resolving to array of game data items
|
|
1999
1953
|
*/
|
|
2000
1954
|
async getMultipleGameData(keys, storage) {
|
|
2001
|
-
const gameId = this.
|
|
2002
|
-
if (!gameId) {
|
|
2003
|
-
throw new Error("game-id is required for persistent storage. Call authenticateFromUrl first.");
|
|
2004
|
-
}
|
|
1955
|
+
const gameId = this.requireGameId();
|
|
2005
1956
|
const storageMode = storage || this.storageMode;
|
|
2006
1957
|
logger.debug(`Getting ${keys.length} game data entries from ${storageMode} for game: ${gameId}`);
|
|
2007
1958
|
const adapter = this.getStorageAdapter(storage);
|
|
@@ -2016,10 +1967,7 @@ var HyveClient = class {
|
|
|
2016
1967
|
* @returns Promise resolving to boolean indicating if data was deleted
|
|
2017
1968
|
*/
|
|
2018
1969
|
async deleteGameData(key, storage) {
|
|
2019
|
-
const gameId = this.
|
|
2020
|
-
if (!gameId) {
|
|
2021
|
-
throw new Error("game-id is required for persistent storage. Call authenticateFromUrl first.");
|
|
2022
|
-
}
|
|
1970
|
+
const gameId = this.requireGameId();
|
|
2023
1971
|
const storageMode = storage || this.storageMode;
|
|
2024
1972
|
logger.debug(`Deleting game data from ${storageMode}: ${gameId}/${key}`);
|
|
2025
1973
|
const adapter = this.getStorageAdapter(storage);
|
|
@@ -2038,10 +1986,7 @@ var HyveClient = class {
|
|
|
2038
1986
|
* @returns Promise resolving to number of entries deleted
|
|
2039
1987
|
*/
|
|
2040
1988
|
async deleteMultipleGameData(keys, storage) {
|
|
2041
|
-
const gameId = this.
|
|
2042
|
-
if (!gameId) {
|
|
2043
|
-
throw new Error("game-id is required for persistent storage. Call authenticateFromUrl first.");
|
|
2044
|
-
}
|
|
1989
|
+
const gameId = this.requireGameId();
|
|
2045
1990
|
const storageMode = storage || this.storageMode;
|
|
2046
1991
|
logger.debug(`Deleting ${keys.length} game data entries from ${storageMode} for game: ${gameId}`);
|
|
2047
1992
|
const adapter = this.getStorageAdapter(storage);
|
|
@@ -2078,66 +2023,82 @@ var HyveClient = class {
|
|
|
2078
2023
|
* @returns Promise resolving to ad result
|
|
2079
2024
|
*/
|
|
2080
2025
|
async showAd(type) {
|
|
2081
|
-
if (this.
|
|
2082
|
-
if (
|
|
2083
|
-
|
|
2026
|
+
if (this.crazyGamesService) {
|
|
2027
|
+
if (this.crazyGamesInitPromise) {
|
|
2028
|
+
await this.crazyGamesInitPromise;
|
|
2029
|
+
}
|
|
2030
|
+
if (this.crazyGamesService.isInitialized()) {
|
|
2031
|
+
const { onBeforeAd, onAfterAd, onRewardEarned } = this.adsService.getCallbacks();
|
|
2032
|
+
if (type === "rewarded") {
|
|
2033
|
+
return this.crazyGamesService.showRewarded({
|
|
2034
|
+
onBeforeAd: () => onBeforeAd("rewarded"),
|
|
2035
|
+
onAfterAd: () => onAfterAd("rewarded"),
|
|
2036
|
+
onRewardEarned
|
|
2037
|
+
});
|
|
2038
|
+
}
|
|
2039
|
+
return this.crazyGamesService.showInterstitial({
|
|
2040
|
+
onBeforeAd: () => onBeforeAd(type),
|
|
2041
|
+
onAfterAd: () => onAfterAd(type)
|
|
2042
|
+
});
|
|
2043
|
+
}
|
|
2044
|
+
}
|
|
2045
|
+
if (this.playgamaService) {
|
|
2046
|
+
if (this.playgamaInitPromise) {
|
|
2047
|
+
await this.playgamaInitPromise;
|
|
2048
|
+
}
|
|
2049
|
+
if (this.playgamaService.isInitialized()) {
|
|
2050
|
+
const { onBeforeAd, onAfterAd, onRewardEarned } = this.adsService.getCallbacks();
|
|
2051
|
+
if (type === "rewarded") {
|
|
2052
|
+
return this.playgamaService.showRewarded({
|
|
2053
|
+
onBeforeAd: () => onBeforeAd("rewarded"),
|
|
2054
|
+
onAfterAd: () => onAfterAd("rewarded"),
|
|
2055
|
+
onRewardEarned
|
|
2056
|
+
});
|
|
2057
|
+
}
|
|
2058
|
+
return this.playgamaService.showInterstitial({
|
|
2059
|
+
onBeforeAd: () => onBeforeAd(type),
|
|
2060
|
+
onAfterAd: () => onAfterAd(type)
|
|
2061
|
+
});
|
|
2062
|
+
}
|
|
2084
2063
|
}
|
|
2085
2064
|
return this.adsService.show(type);
|
|
2086
2065
|
}
|
|
2087
2066
|
/**
|
|
2088
|
-
*
|
|
2089
|
-
*
|
|
2067
|
+
* Notifies CrazyGames that gameplay has started.
|
|
2068
|
+
* No-op on other platforms.
|
|
2090
2069
|
*/
|
|
2091
|
-
|
|
2092
|
-
|
|
2070
|
+
async gameplayStart() {
|
|
2071
|
+
if (this.crazyGamesService) {
|
|
2072
|
+
if (this.crazyGamesInitPromise) await this.crazyGamesInitPromise;
|
|
2073
|
+
this.crazyGamesService.gameplayStart();
|
|
2074
|
+
}
|
|
2093
2075
|
}
|
|
2094
2076
|
/**
|
|
2095
|
-
*
|
|
2096
|
-
*
|
|
2077
|
+
* Notifies CrazyGames that gameplay has stopped.
|
|
2078
|
+
* No-op on other platforms.
|
|
2097
2079
|
*/
|
|
2098
|
-
|
|
2099
|
-
|
|
2080
|
+
async gameplayStop() {
|
|
2081
|
+
if (this.crazyGamesService) {
|
|
2082
|
+
if (this.crazyGamesInitPromise) await this.crazyGamesInitPromise;
|
|
2083
|
+
this.crazyGamesService.gameplayStop();
|
|
2084
|
+
}
|
|
2100
2085
|
}
|
|
2101
2086
|
/**
|
|
2102
|
-
*
|
|
2103
|
-
*
|
|
2087
|
+
* Triggers a celebration effect on the CrazyGames website for significant achievements.
|
|
2088
|
+
* No-op on other platforms.
|
|
2104
2089
|
*/
|
|
2105
|
-
|
|
2106
|
-
this.
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
}
|
|
2110
|
-
logger.info("Billing configuration updated");
|
|
2090
|
+
async happytime() {
|
|
2091
|
+
if (this.crazyGamesService) {
|
|
2092
|
+
if (this.crazyGamesInitPromise) await this.crazyGamesInitPromise;
|
|
2093
|
+
this.crazyGamesService.happytime();
|
|
2094
|
+
}
|
|
2111
2095
|
}
|
|
2112
2096
|
/**
|
|
2113
|
-
*
|
|
2114
|
-
*
|
|
2115
|
-
* @returns Promise resolving to boolean indicating success
|
|
2097
|
+
* Check if ads are ready to show
|
|
2098
|
+
* @returns Boolean indicating if ads have initialized successfully
|
|
2116
2099
|
*/
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
...this.billingConfig
|
|
2120
|
-
};
|
|
2121
|
-
if (!mergedConfig.userId && this.userId) {
|
|
2122
|
-
mergedConfig.userId = this.userId;
|
|
2123
|
-
}
|
|
2124
|
-
if (!mergedConfig.gameId && this.gameId) {
|
|
2125
|
-
mergedConfig.gameId = Number(this.gameId);
|
|
2126
|
-
}
|
|
2127
|
-
if (!mergedConfig.checkoutUrl) {
|
|
2128
|
-
mergedConfig.checkoutUrl = this.apiBaseUrl;
|
|
2129
|
-
}
|
|
2130
|
-
this.billingService = new BillingService(mergedConfig);
|
|
2131
|
-
if (this.billingCallbacks.onComplete) {
|
|
2132
|
-
this.billingService.onPurchaseComplete(this.billingCallbacks.onComplete);
|
|
2133
|
-
}
|
|
2134
|
-
if (this.billingCallbacks.onError) {
|
|
2135
|
-
this.billingService.onPurchaseError(this.billingCallbacks.onError);
|
|
2136
|
-
}
|
|
2137
|
-
if (this.billingCallbacks.onLog) {
|
|
2138
|
-
this.billingService.onLog(this.billingCallbacks.onLog);
|
|
2139
|
-
}
|
|
2140
|
-
return await this.billingService.initialize();
|
|
2100
|
+
areAdsReady() {
|
|
2101
|
+
return this.adsService.isReady();
|
|
2141
2102
|
}
|
|
2142
2103
|
/**
|
|
2143
2104
|
* Get the billing platform
|
|
@@ -2174,7 +2135,6 @@ var HyveClient = class {
|
|
|
2174
2135
|
* @param callback Function to call on purchase completion
|
|
2175
2136
|
*/
|
|
2176
2137
|
onPurchaseComplete(callback) {
|
|
2177
|
-
this.billingCallbacks.onComplete = callback;
|
|
2178
2138
|
this.billingService.onPurchaseComplete(callback);
|
|
2179
2139
|
}
|
|
2180
2140
|
/**
|
|
@@ -2182,7 +2142,6 @@ var HyveClient = class {
|
|
|
2182
2142
|
* @param callback Function to call on purchase error
|
|
2183
2143
|
*/
|
|
2184
2144
|
onPurchaseError(callback) {
|
|
2185
|
-
this.billingCallbacks.onError = callback;
|
|
2186
2145
|
this.billingService.onPurchaseError(callback);
|
|
2187
2146
|
}
|
|
2188
2147
|
/**
|
|
@@ -2191,20 +2150,13 @@ var HyveClient = class {
|
|
|
2191
2150
|
unmountBillingCheckout() {
|
|
2192
2151
|
this.billingService.unmountCheckoutElement();
|
|
2193
2152
|
}
|
|
2194
|
-
/**
|
|
2195
|
-
* Register a callback to receive billing logs
|
|
2196
|
-
* @param callback Function to call with log messages
|
|
2197
|
-
*/
|
|
2198
|
-
onBillingLog(callback) {
|
|
2199
|
-
this.billingCallbacks.onLog = callback;
|
|
2200
|
-
this.billingService.onLog(callback);
|
|
2201
|
-
}
|
|
2202
2153
|
};
|
|
2203
2154
|
export {
|
|
2204
2155
|
AdsService,
|
|
2205
2156
|
BillingPlatform,
|
|
2206
2157
|
BillingService,
|
|
2207
2158
|
CloudStorageAdapter,
|
|
2159
|
+
CrazyGamesService,
|
|
2208
2160
|
HyveClient,
|
|
2209
2161
|
LocalStorageAdapter,
|
|
2210
2162
|
Logger,
|
|
@@ -2212,11 +2164,7 @@ export {
|
|
|
2212
2164
|
NativeMessageType,
|
|
2213
2165
|
PlaygamaService,
|
|
2214
2166
|
generateUUID,
|
|
2215
|
-
handleVerifyMessage,
|
|
2216
2167
|
isDomainAllowed,
|
|
2217
2168
|
logger,
|
|
2218
|
-
parseUrlParams
|
|
2219
|
-
validateSignature,
|
|
2220
|
-
verifyAuthentication,
|
|
2221
|
-
verifyHyveToken
|
|
2169
|
+
parseUrlParams
|
|
2222
2170
|
};
|