@cshah18/sdk 2.0.0 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cobuy-sdk.esm.js +320 -39
- package/dist/cobuy-sdk.esm.js.map +1 -1
- package/dist/cobuy-sdk.umd.js +320 -39
- package/dist/cobuy-sdk.umd.js.map +1 -1
- package/dist/types/core/api-client.d.ts +36 -1
- package/dist/types/core/cobuy.d.ts +54 -1
- package/dist/types/core/endpoints.d.ts +3 -0
- package/dist/types/core/types.d.ts +43 -8
- package/dist/types/ui/lobby/lobby-modal.d.ts +1 -2
- package/dist/types/ui/widget/widget-root.d.ts +2 -1
- package/package.json +1 -1
package/dist/cobuy-sdk.esm.js
CHANGED
|
@@ -1305,6 +1305,21 @@ class LobbyModal {
|
|
|
1305
1305
|
this.socketListenerRegistered = false;
|
|
1306
1306
|
this.currentGroupId = null;
|
|
1307
1307
|
this.shareOverlay = null;
|
|
1308
|
+
/**
|
|
1309
|
+
* Unsubscribe from socket events - testing out flow will remove once we have conviction
|
|
1310
|
+
*/
|
|
1311
|
+
// private unsubscribeFromSocketEvents(): void {
|
|
1312
|
+
// if (typeof window === "undefined" || !this.socketListenerRegistered) {
|
|
1313
|
+
// return;
|
|
1314
|
+
// }
|
|
1315
|
+
// window.removeEventListener("group:fulfilled", this.handleSocketGroupUpdate as EventListener);
|
|
1316
|
+
// window.removeEventListener(
|
|
1317
|
+
// "group:member:joined",
|
|
1318
|
+
// this.handleSocketGroupUpdate as EventListener,
|
|
1319
|
+
// );
|
|
1320
|
+
// window.removeEventListener("group:created", this.handleSocketGroupUpdate as EventListener);
|
|
1321
|
+
// this.socketListenerRegistered = false;
|
|
1322
|
+
// }
|
|
1308
1323
|
/**
|
|
1309
1324
|
* Handle socket group update events
|
|
1310
1325
|
*/
|
|
@@ -2326,11 +2341,11 @@ class LobbyModal {
|
|
|
2326
2341
|
// Stop timers and animations
|
|
2327
2342
|
this.stopTimer();
|
|
2328
2343
|
this.stopActivityAnimation();
|
|
2329
|
-
// Unsubscribe from socket events and group
|
|
2330
|
-
this.unsubscribeFromSocketEvents();
|
|
2331
|
-
if (this.socketManager && this.currentGroupId) {
|
|
2332
|
-
|
|
2333
|
-
}
|
|
2344
|
+
// // Unsubscribe from socket events and group - testing out flow will remove once we have conviction
|
|
2345
|
+
// this.unsubscribeFromSocketEvents();
|
|
2346
|
+
// if (this.socketManager && this.currentGroupId) {
|
|
2347
|
+
// this.socketManager.unsubscribeFromGroup(this.currentGroupId);
|
|
2348
|
+
// }
|
|
2334
2349
|
// Remove modal from DOM
|
|
2335
2350
|
document.body.removeChild(this.modalElement);
|
|
2336
2351
|
this.modalElement = null;
|
|
@@ -2445,18 +2460,6 @@ class LobbyModal {
|
|
|
2445
2460
|
window.addEventListener("group:created", this.handleSocketGroupUpdate);
|
|
2446
2461
|
this.socketListenerRegistered = true;
|
|
2447
2462
|
}
|
|
2448
|
-
/**
|
|
2449
|
-
* Unsubscribe from socket events
|
|
2450
|
-
*/
|
|
2451
|
-
unsubscribeFromSocketEvents() {
|
|
2452
|
-
if (typeof window === "undefined" || !this.socketListenerRegistered) {
|
|
2453
|
-
return;
|
|
2454
|
-
}
|
|
2455
|
-
window.removeEventListener("group:fulfilled", this.handleSocketGroupUpdate);
|
|
2456
|
-
window.removeEventListener("group:member:joined", this.handleSocketGroupUpdate);
|
|
2457
|
-
window.removeEventListener("group:created", this.handleSocketGroupUpdate);
|
|
2458
|
-
this.socketListenerRegistered = false;
|
|
2459
|
-
}
|
|
2460
2463
|
/**
|
|
2461
2464
|
* Create activity item from socket event data
|
|
2462
2465
|
*/
|
|
@@ -2582,6 +2585,7 @@ class WidgetRoot {
|
|
|
2582
2585
|
this.groupListModal = null;
|
|
2583
2586
|
this.lastGroupDataRefreshTime = 0;
|
|
2584
2587
|
this.GROUP_REFRESH_DEBOUNCE = 1000; // 1 second min between refreshes
|
|
2588
|
+
this.groupExpiryRefreshTriggered = false; // Track if expiry refresh already triggered for current group
|
|
2585
2589
|
/** Handle backend fulfillment notifications */
|
|
2586
2590
|
this.handleGroupFulfilledEvent = (event) => {
|
|
2587
2591
|
const detail = event.detail;
|
|
@@ -2595,9 +2599,10 @@ class WidgetRoot {
|
|
|
2595
2599
|
};
|
|
2596
2600
|
/** Handle realtime group creation/join events by refreshing group UI */
|
|
2597
2601
|
this.handleGroupUpdatedEvent = (event) => {
|
|
2602
|
+
var _a;
|
|
2598
2603
|
const detail = event.detail || {};
|
|
2599
|
-
const productId = detail.
|
|
2600
|
-
const groupId = detail.
|
|
2604
|
+
const productId = detail.product_id;
|
|
2605
|
+
const groupId = (_a = detail.group) === null || _a === void 0 ? void 0 : _a.id;
|
|
2601
2606
|
if (productId && this.currentProductId && productId !== this.currentProductId) {
|
|
2602
2607
|
return;
|
|
2603
2608
|
}
|
|
@@ -2863,14 +2868,10 @@ class WidgetRoot {
|
|
|
2863
2868
|
footer.style.display = "flex";
|
|
2864
2869
|
footer.style.justifyContent = "flex-end";
|
|
2865
2870
|
footer.style.alignItems = "center";
|
|
2866
|
-
const viewAllLink = this.createInlineViewAllLink();
|
|
2867
|
-
if (viewAllLink) {
|
|
2868
|
-
footer.appendChild(viewAllLink);
|
|
2869
|
-
}
|
|
2870
2871
|
root.appendChild(footer);
|
|
2871
2872
|
return root;
|
|
2872
2873
|
}
|
|
2873
|
-
/** Update countdown text */
|
|
2874
|
+
/** Update countdown text and trigger refresh when group expires */
|
|
2874
2875
|
updateTimer(el, expiryMs, remaining) {
|
|
2875
2876
|
const now = Date.now();
|
|
2876
2877
|
const diff = Math.max(0, Math.floor((expiryMs - now) / 1000));
|
|
@@ -2884,6 +2885,12 @@ class WidgetRoot {
|
|
|
2884
2885
|
else {
|
|
2885
2886
|
el.textContent = "✨ Discount activated!";
|
|
2886
2887
|
el.classList.add("activated");
|
|
2888
|
+
// Trigger group refresh only once when expiry is reached, regardless of modal state
|
|
2889
|
+
if (!this.groupExpiryRefreshTriggered && diff <= 0) {
|
|
2890
|
+
this.groupExpiryRefreshTriggered = true;
|
|
2891
|
+
this.logger.info("[Expiry] Group expired, refreshing group data");
|
|
2892
|
+
void this.refreshGroupDataFromRealtime();
|
|
2893
|
+
}
|
|
2887
2894
|
}
|
|
2888
2895
|
}
|
|
2889
2896
|
/** Build and attach the shared "View all Groups" link section */
|
|
@@ -2995,6 +3002,7 @@ class WidgetRoot {
|
|
|
2995
3002
|
this.frozenReward = null;
|
|
2996
3003
|
this.currentGroupId = null;
|
|
2997
3004
|
this.currentRewardData = null;
|
|
3005
|
+
this.groupExpiryRefreshTriggered = false; // Reset expiry refresh flag for new render
|
|
2998
3006
|
// Listen for realtime fulfillment updates once per page
|
|
2999
3007
|
this.subscribeToSocketEvents();
|
|
3000
3008
|
// Host safety + idempotency markers
|
|
@@ -3409,7 +3417,6 @@ class WidgetRoot {
|
|
|
3409
3417
|
}
|
|
3410
3418
|
}
|
|
3411
3419
|
createWidget(rewardData, container, options) {
|
|
3412
|
-
var _a, _b;
|
|
3413
3420
|
const isFulfilled = this.groupFulfilled;
|
|
3414
3421
|
const activeReward = this.frozenReward || (rewardData === null || rewardData === void 0 ? void 0 : rewardData.reward) || null;
|
|
3415
3422
|
const wrapper = document.createElement("div");
|
|
@@ -3459,7 +3466,7 @@ class WidgetRoot {
|
|
|
3459
3466
|
rewardLine.setAttribute("aria-label", rewardText ? `Reward locked in: ${rewardText}` : "Reward available for this group");
|
|
3460
3467
|
rewardLine.title = rewardLine.textContent;
|
|
3461
3468
|
}
|
|
3462
|
-
else if (
|
|
3469
|
+
else if (activeReward) {
|
|
3463
3470
|
const rewardText = this.formatRewardText(activeReward);
|
|
3464
3471
|
rewardLine.textContent = rewardText
|
|
3465
3472
|
? `Save up to ${rewardText} with CoBuy`
|
|
@@ -3467,12 +3474,6 @@ class WidgetRoot {
|
|
|
3467
3474
|
rewardLine.setAttribute("aria-label", `Eligible for CoBuy reward: ${rewardLine.textContent}`);
|
|
3468
3475
|
rewardLine.title = rewardLine.textContent;
|
|
3469
3476
|
}
|
|
3470
|
-
else if (rewardData && !((_b = rewardData.eligibility) === null || _b === void 0 ? void 0 : _b.isEligible)) {
|
|
3471
|
-
rewardLine.textContent = "Join with CoBuy to unlock rewards";
|
|
3472
|
-
rewardLine.setAttribute("aria-label", "Join with CoBuy to unlock rewards");
|
|
3473
|
-
rewardLine.title = "Join with CoBuy to unlock rewards";
|
|
3474
|
-
rewardLine.style.color = "#6b7280";
|
|
3475
|
-
}
|
|
3476
3477
|
else {
|
|
3477
3478
|
rewardLine.textContent = "CoBuy offer loading or unavailable";
|
|
3478
3479
|
rewardLine.setAttribute("aria-label", "CoBuy offer loading or unavailable");
|
|
@@ -3876,7 +3877,6 @@ class WidgetRoot {
|
|
|
3876
3877
|
* Format reward text for rendering in external container
|
|
3877
3878
|
*/
|
|
3878
3879
|
formatRewardForContainer(rewardData) {
|
|
3879
|
-
var _a;
|
|
3880
3880
|
const reward = this.frozenReward || (rewardData === null || rewardData === void 0 ? void 0 : rewardData.reward);
|
|
3881
3881
|
if (this.groupFulfilled) {
|
|
3882
3882
|
const rewardText = this.formatRewardText(reward);
|
|
@@ -3886,12 +3886,7 @@ class WidgetRoot {
|
|
|
3886
3886
|
return "Join with CoBuy to unlock rewards";
|
|
3887
3887
|
}
|
|
3888
3888
|
const rewardText = this.formatRewardText(reward);
|
|
3889
|
-
|
|
3890
|
-
return rewardText ? `Save up to ${rewardText} with CoBuy` : "CoBuy reward available";
|
|
3891
|
-
}
|
|
3892
|
-
else {
|
|
3893
|
-
return "Join with CoBuy to unlock rewards";
|
|
3894
|
-
}
|
|
3889
|
+
return rewardText ? `Save up to ${rewardText} with CoBuy` : "CoBuy reward available";
|
|
3895
3890
|
}
|
|
3896
3891
|
}
|
|
3897
3892
|
|
|
@@ -3907,6 +3902,9 @@ const API_ENDPOINTS = {
|
|
|
3907
3902
|
GROUP_CREATE_AND_JOIN: "/v1/sdk/groups/new/join",
|
|
3908
3903
|
PRODUCT_ACTIVE_GROUPS: "/v1/sdk/products/:productId/groups/active",
|
|
3909
3904
|
GROUP_INVITE: "/v1/sdk/groups/:groupId/invite",
|
|
3905
|
+
GROUP_CHECKOUT_PREPARE: "/v1/sdk/groups/:groupId/checkout/prepare",
|
|
3906
|
+
GROUP_CHECKOUT_VALIDATE: "/v1/sdk/groups/:groupId/checkout/validate",
|
|
3907
|
+
GROUP_CHECKOUT_CONFIRM: "/v1/sdk/groups/:groupId/checkout/confirm",
|
|
3910
3908
|
};
|
|
3911
3909
|
/**
|
|
3912
3910
|
* Build full API URL from base URL and endpoint path
|
|
@@ -4361,6 +4359,100 @@ class ApiClient {
|
|
|
4361
4359
|
},
|
|
4362
4360
|
};
|
|
4363
4361
|
}
|
|
4362
|
+
/**
|
|
4363
|
+
* Prepare checkout for a group
|
|
4364
|
+
*
|
|
4365
|
+
* Signals the backend that the user is proceeding to checkout for a specific group.
|
|
4366
|
+
* This allows the backend to prepare order data, lock pricing, and track conversion.
|
|
4367
|
+
* Returns a checkout reference to be used in subsequent validate and confirm calls.
|
|
4368
|
+
*
|
|
4369
|
+
* @param groupId - The group ID to prepare checkout for
|
|
4370
|
+
* @returns Promise with success status and checkout reference
|
|
4371
|
+
*/
|
|
4372
|
+
async prepareCheckout(groupId) {
|
|
4373
|
+
var _a, _b;
|
|
4374
|
+
const endpoint = buildApiUrl("", API_ENDPOINTS.GROUP_CHECKOUT_PREPARE, { groupId });
|
|
4375
|
+
this.logger.info(`Preparing checkout for group: ${groupId}`);
|
|
4376
|
+
const response = await this.post(endpoint);
|
|
4377
|
+
if (response.success && ((_b = (_a = response.data) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b.checkout_ref)) {
|
|
4378
|
+
this.logger.info(`Checkout prepared successfully with ref: ${response.data.data.checkout_ref}`);
|
|
4379
|
+
return {
|
|
4380
|
+
success: true,
|
|
4381
|
+
data: {
|
|
4382
|
+
checkout_ref: response.data.data.checkout_ref,
|
|
4383
|
+
},
|
|
4384
|
+
};
|
|
4385
|
+
}
|
|
4386
|
+
return {
|
|
4387
|
+
success: false,
|
|
4388
|
+
error: response.error || {
|
|
4389
|
+
message: "Failed to prepare checkout",
|
|
4390
|
+
code: "PREPARE_CHECKOUT_ERROR",
|
|
4391
|
+
},
|
|
4392
|
+
};
|
|
4393
|
+
}
|
|
4394
|
+
/**
|
|
4395
|
+
* Validate checkout for a group
|
|
4396
|
+
*
|
|
4397
|
+
* Validates the checkout reference and confirms the order can proceed.
|
|
4398
|
+
* This should be called after prepareCheckout to verify the checkout state.
|
|
4399
|
+
*
|
|
4400
|
+
* @param groupId - The group ID to validate checkout for
|
|
4401
|
+
* @param checkoutRef - The checkout reference ID from the prepare step
|
|
4402
|
+
* @returns Promise with success status
|
|
4403
|
+
*/
|
|
4404
|
+
async validateCheckout(groupId, checkoutRef) {
|
|
4405
|
+
var _a;
|
|
4406
|
+
const endpoint = buildApiUrl("", API_ENDPOINTS.GROUP_CHECKOUT_VALIDATE, { groupId });
|
|
4407
|
+
this.logger.info(`Validating checkout for group: ${groupId}`);
|
|
4408
|
+
const response = await this.post(endpoint, {
|
|
4409
|
+
checkout_ref: checkoutRef,
|
|
4410
|
+
});
|
|
4411
|
+
if (response.success) {
|
|
4412
|
+
this.logger.info("Checkout validated successfully");
|
|
4413
|
+
return {
|
|
4414
|
+
success: true,
|
|
4415
|
+
data: (_a = response.data) === null || _a === void 0 ? void 0 : _a.data,
|
|
4416
|
+
};
|
|
4417
|
+
}
|
|
4418
|
+
return {
|
|
4419
|
+
success: false,
|
|
4420
|
+
error: response.error || {
|
|
4421
|
+
message: "Failed to validate checkout",
|
|
4422
|
+
code: "VALIDATE_CHECKOUT_ERROR",
|
|
4423
|
+
},
|
|
4424
|
+
};
|
|
4425
|
+
}
|
|
4426
|
+
/**
|
|
4427
|
+
* Confirm checkout for a group
|
|
4428
|
+
*
|
|
4429
|
+
* Finalizes the checkout and confirms the order.
|
|
4430
|
+
* This should be called after validateCheckout to complete the checkout process.
|
|
4431
|
+
*
|
|
4432
|
+
* @param groupId - The group ID to confirm checkout for
|
|
4433
|
+
* @param checkoutRef - The checkout reference ID from the prepare step
|
|
4434
|
+
* @returns Promise with success status
|
|
4435
|
+
*/
|
|
4436
|
+
async confirmCheckout(groupId, checkoutRef) {
|
|
4437
|
+
const endpoint = buildApiUrl("", API_ENDPOINTS.GROUP_CHECKOUT_CONFIRM, { groupId });
|
|
4438
|
+
this.logger.info(`Confirming checkout for group: ${groupId}`);
|
|
4439
|
+
const response = await this.post(endpoint, {
|
|
4440
|
+
checkout_ref: checkoutRef,
|
|
4441
|
+
});
|
|
4442
|
+
if (response.success) {
|
|
4443
|
+
this.logger.info("Checkout confirmed successfully");
|
|
4444
|
+
return {
|
|
4445
|
+
success: true,
|
|
4446
|
+
};
|
|
4447
|
+
}
|
|
4448
|
+
return {
|
|
4449
|
+
success: false,
|
|
4450
|
+
error: response.error || {
|
|
4451
|
+
message: "Failed to confirm checkout",
|
|
4452
|
+
code: "CONFIRM_CHECKOUT_ERROR",
|
|
4453
|
+
},
|
|
4454
|
+
};
|
|
4455
|
+
}
|
|
4364
4456
|
}
|
|
4365
4457
|
|
|
4366
4458
|
/**
|
|
@@ -8755,6 +8847,7 @@ class CoBuy {
|
|
|
8755
8847
|
this.analyticsClient = null;
|
|
8756
8848
|
this.sessionId = "";
|
|
8757
8849
|
this.SESSION_STORAGE_KEY = "cobuy_sdk_session_id";
|
|
8850
|
+
this.CHECKOUT_REF_PREFIX = "cobuy_checkout_ref";
|
|
8758
8851
|
this.lobbyModal = null;
|
|
8759
8852
|
this.modals = new Map();
|
|
8760
8853
|
this.socketManager = null;
|
|
@@ -8798,12 +8891,97 @@ class CoBuy {
|
|
|
8798
8891
|
// Fallback for environments without crypto.randomUUID
|
|
8799
8892
|
return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
8800
8893
|
}
|
|
8894
|
+
/**
|
|
8895
|
+
* Store checkout reference for a product/group (session-level)
|
|
8896
|
+
* Storage key format: cobuy_checkout_ref_<sessionId>_<productId>_<groupId>
|
|
8897
|
+
*/
|
|
8898
|
+
storeCheckoutRef(productId, groupId, checkoutRef) {
|
|
8899
|
+
if (typeof window === "undefined" || !window.localStorage) {
|
|
8900
|
+
this.logger.warn("[SDK] localStorage not available, cannot store checkout reference");
|
|
8901
|
+
return;
|
|
8902
|
+
}
|
|
8903
|
+
const storageKey = `${this.CHECKOUT_REF_PREFIX}_${this.sessionId}_${productId}_${groupId}`;
|
|
8904
|
+
try {
|
|
8905
|
+
window.localStorage.setItem(storageKey, checkoutRef);
|
|
8906
|
+
this.logger.debug(`[SDK] Stored checkout reference: ${storageKey}`);
|
|
8907
|
+
}
|
|
8908
|
+
catch (e) {
|
|
8909
|
+
this.logger.warn("[SDK] Could not store checkout reference to localStorage", e);
|
|
8910
|
+
}
|
|
8911
|
+
}
|
|
8912
|
+
/**
|
|
8913
|
+
* Retrieve checkout reference for a product/group (session-level)
|
|
8914
|
+
* If groupId provided, try exact match first; otherwise fall back to any key for that product
|
|
8915
|
+
* Returns both the matched storage key and the checkout reference value (or null if not found)
|
|
8916
|
+
*/
|
|
8917
|
+
getCheckoutRef(productId, groupId) {
|
|
8918
|
+
if (typeof window === "undefined" || !window.localStorage) {
|
|
8919
|
+
return { key: null, checkoutRef: null, groupId: null };
|
|
8920
|
+
}
|
|
8921
|
+
const basePrefix = `${this.CHECKOUT_REF_PREFIX}_${this.sessionId}_${productId}`;
|
|
8922
|
+
// Try exact match if groupId supplied
|
|
8923
|
+
if (groupId) {
|
|
8924
|
+
const exactKey = `${basePrefix}_${groupId}`;
|
|
8925
|
+
const exactRef = window.localStorage.getItem(exactKey);
|
|
8926
|
+
if (exactRef) {
|
|
8927
|
+
this.logger.debug(`[SDK] Retrieved checkout reference: ${exactKey}`);
|
|
8928
|
+
return { key: exactKey, checkoutRef: exactRef, groupId };
|
|
8929
|
+
}
|
|
8930
|
+
}
|
|
8931
|
+
// Fallback: find any checkout ref for this product (ignore group)
|
|
8932
|
+
for (let i = 0; i < window.localStorage.length; i++) {
|
|
8933
|
+
const key = window.localStorage.key(i);
|
|
8934
|
+
if (key && key.startsWith(basePrefix)) {
|
|
8935
|
+
const ref = window.localStorage.getItem(key);
|
|
8936
|
+
if (ref) {
|
|
8937
|
+
this.logger.debug(`[SDK] Retrieved checkout reference via prefix: ${key}`);
|
|
8938
|
+
const parsedGroupId = key.startsWith(`${basePrefix}_`) ? key.substring(basePrefix.length + 1) : null;
|
|
8939
|
+
return { key, checkoutRef: ref, groupId: parsedGroupId };
|
|
8940
|
+
}
|
|
8941
|
+
}
|
|
8942
|
+
}
|
|
8943
|
+
return { key: null, checkoutRef: null, groupId: null };
|
|
8944
|
+
}
|
|
8801
8945
|
/**
|
|
8802
8946
|
* Get the current session ID (core SDK concept)
|
|
8803
8947
|
*/
|
|
8804
8948
|
getSessionId() {
|
|
8805
8949
|
return this.sessionId;
|
|
8806
8950
|
}
|
|
8951
|
+
/**
|
|
8952
|
+
* Prepare checkout for a group if not already prepared
|
|
8953
|
+
* Checks localStorage to prevent duplicate calls for the same product/group/session
|
|
8954
|
+
*/
|
|
8955
|
+
async prepareCheckoutIfNotDone(productId, groupId) {
|
|
8956
|
+
var _a;
|
|
8957
|
+
// Check if checkout already prepared for this product/group (session)
|
|
8958
|
+
const { checkoutRef: existingRef } = this.getCheckoutRef(productId, groupId);
|
|
8959
|
+
if (existingRef) {
|
|
8960
|
+
this.logger.debug(`[SDK] Checkout already prepared for product ${productId} (session), skipping`);
|
|
8961
|
+
return;
|
|
8962
|
+
}
|
|
8963
|
+
if (!this.apiClient) {
|
|
8964
|
+
this.logger.warn("[SDK] API client not available, cannot prepare checkout");
|
|
8965
|
+
return;
|
|
8966
|
+
}
|
|
8967
|
+
try {
|
|
8968
|
+
this.logger.info(`[SDK] Preparing checkout for product: ${productId}, group: ${groupId}`);
|
|
8969
|
+
// Call prepareCheckout to get the checkout reference
|
|
8970
|
+
const response = await this.apiClient.prepareCheckout(groupId);
|
|
8971
|
+
if (response.success && ((_a = response.data) === null || _a === void 0 ? void 0 : _a.checkout_ref)) {
|
|
8972
|
+
const checkoutRef = response.data.checkout_ref;
|
|
8973
|
+
// Store the checkout reference for this specific product and group
|
|
8974
|
+
this.storeCheckoutRef(productId, groupId, checkoutRef);
|
|
8975
|
+
this.logger.info(`[SDK] Checkout prepared and stored for ${productId}:${groupId} - Ref: ${checkoutRef}`);
|
|
8976
|
+
}
|
|
8977
|
+
else {
|
|
8978
|
+
this.logger.error("[SDK] Failed to prepare checkout", response.error);
|
|
8979
|
+
}
|
|
8980
|
+
}
|
|
8981
|
+
catch (error) {
|
|
8982
|
+
this.logger.error("[SDK] Error preparing checkout", error);
|
|
8983
|
+
}
|
|
8984
|
+
}
|
|
8807
8985
|
/**
|
|
8808
8986
|
* Initialize the SDK with configuration
|
|
8809
8987
|
*/
|
|
@@ -8864,6 +9042,21 @@ class CoBuy {
|
|
|
8864
9042
|
},
|
|
8865
9043
|
onGroupMemberJoined: (payload) => {
|
|
8866
9044
|
var _a;
|
|
9045
|
+
const group = payload.group;
|
|
9046
|
+
const productId = payload.product_id;
|
|
9047
|
+
const groupId = group === null || group === void 0 ? void 0 : group.id;
|
|
9048
|
+
const maxParticipants = group === null || group === void 0 ? void 0 : group.max_participants;
|
|
9049
|
+
const participantsCount = group === null || group === void 0 ? void 0 : group.participants_count;
|
|
9050
|
+
const status = group === null || group === void 0 ? void 0 : group.status;
|
|
9051
|
+
const isFulfilled = status === "fulfilled" ||
|
|
9052
|
+
(maxParticipants !== undefined &&
|
|
9053
|
+
participantsCount !== undefined &&
|
|
9054
|
+
participantsCount >= maxParticipants);
|
|
9055
|
+
if (isFulfilled && productId && groupId) {
|
|
9056
|
+
// Prepare checkout when group is fulfilled (if not already prepared)
|
|
9057
|
+
this.prepareCheckoutIfNotDone(productId, groupId);
|
|
9058
|
+
}
|
|
9059
|
+
// Emit user-defined callback if provided
|
|
8867
9060
|
if ((_a = config.events) === null || _a === void 0 ? void 0 : _a.onGroupMemberJoined) {
|
|
8868
9061
|
config.events.onGroupMemberJoined(payload);
|
|
8869
9062
|
}
|
|
@@ -8972,6 +9165,17 @@ class CoBuy {
|
|
|
8972
9165
|
this.lobbyModal = modal;
|
|
8973
9166
|
// Open the modal
|
|
8974
9167
|
modal.open(options.groupId);
|
|
9168
|
+
// Prepare checkout if group is fulfilled
|
|
9169
|
+
if (options.groupId) {
|
|
9170
|
+
const isFulfilled = options.status === "complete" ||
|
|
9171
|
+
options.progress === 100 ||
|
|
9172
|
+
(options.currentMembers !== undefined &&
|
|
9173
|
+
options.totalMembers !== undefined &&
|
|
9174
|
+
options.currentMembers >= options.totalMembers);
|
|
9175
|
+
if (isFulfilled) {
|
|
9176
|
+
this.prepareCheckoutIfNotDone(options.productId, options.groupId);
|
|
9177
|
+
}
|
|
9178
|
+
}
|
|
8975
9179
|
// Call callback if provided
|
|
8976
9180
|
if (options.onOpen) {
|
|
8977
9181
|
options.onOpen(options.productId);
|
|
@@ -9053,6 +9257,83 @@ class CoBuy {
|
|
|
9053
9257
|
this.logger.error("Error setting contact information", error);
|
|
9054
9258
|
}
|
|
9055
9259
|
}
|
|
9260
|
+
/**
|
|
9261
|
+
* Validate checkout for a group
|
|
9262
|
+
*
|
|
9263
|
+
* Validates the checkout reference and confirms the order can proceed.
|
|
9264
|
+
* Should be called after prepareCheckout to verify the checkout state.
|
|
9265
|
+
*
|
|
9266
|
+
* @param groupId - The group ID to validate checkout for
|
|
9267
|
+
* @param checkoutRef - The checkout reference ID from the prepare step
|
|
9268
|
+
*
|
|
9269
|
+
* @example
|
|
9270
|
+
* ```typescript
|
|
9271
|
+
* // Validate checkout for a group
|
|
9272
|
+
* await CoBuy.validateCheckout('fae238ae-7468-47e9-9eec-b6d52fe3b012', 'chk_9a6d8750-ed60-4795-a207-2abe955e8509');
|
|
9273
|
+
* ```
|
|
9274
|
+
*/
|
|
9275
|
+
async validateCheckout(groupId, checkoutRef) {
|
|
9276
|
+
if (!this.configManager.isInitialized()) {
|
|
9277
|
+
this.logger.warn("SDK not initialized, cannot validate checkout");
|
|
9278
|
+
return null;
|
|
9279
|
+
}
|
|
9280
|
+
if (!this.apiClient) {
|
|
9281
|
+
this.logger.error("API client not available");
|
|
9282
|
+
return null;
|
|
9283
|
+
}
|
|
9284
|
+
try {
|
|
9285
|
+
const response = await this.apiClient.validateCheckout(groupId, checkoutRef);
|
|
9286
|
+
if (response.success) {
|
|
9287
|
+
this.logger.info(`Checkout validated successfully for group: ${groupId}`);
|
|
9288
|
+
return response.data || null;
|
|
9289
|
+
}
|
|
9290
|
+
else {
|
|
9291
|
+
this.logger.error("Failed to validate checkout", response.error);
|
|
9292
|
+
return null;
|
|
9293
|
+
}
|
|
9294
|
+
}
|
|
9295
|
+
catch (error) {
|
|
9296
|
+
this.logger.error("Error validating checkout", error);
|
|
9297
|
+
return null;
|
|
9298
|
+
}
|
|
9299
|
+
}
|
|
9300
|
+
/**
|
|
9301
|
+
* Confirm checkout for a group
|
|
9302
|
+
*
|
|
9303
|
+
* Finalizes the checkout and confirms the order.
|
|
9304
|
+
* Should be called after validateCheckout to complete the checkout process.
|
|
9305
|
+
*
|
|
9306
|
+
* @param groupId - The group ID to confirm checkout for
|
|
9307
|
+
* @param checkoutRef - The checkout reference ID from the prepare step
|
|
9308
|
+
*
|
|
9309
|
+
* @example
|
|
9310
|
+
* ```typescript
|
|
9311
|
+
* // Confirm checkout for a group
|
|
9312
|
+
* await CoBuy.confirmCheckout('fae238ae-7468-47e9-9eec-b6d52fe3b012', 'chk_9a6d8750-ed60-4795-a207-2abe955e8509');
|
|
9313
|
+
* ```
|
|
9314
|
+
*/
|
|
9315
|
+
async confirmCheckout(groupId, checkoutRef) {
|
|
9316
|
+
if (!this.configManager.isInitialized()) {
|
|
9317
|
+
this.logger.warn("SDK not initialized, cannot confirm checkout");
|
|
9318
|
+
return;
|
|
9319
|
+
}
|
|
9320
|
+
if (!this.apiClient) {
|
|
9321
|
+
this.logger.error("API client not available");
|
|
9322
|
+
return;
|
|
9323
|
+
}
|
|
9324
|
+
try {
|
|
9325
|
+
const response = await this.apiClient.confirmCheckout(groupId, checkoutRef);
|
|
9326
|
+
if (response.success) {
|
|
9327
|
+
this.logger.info(`Checkout confirmed successfully for group: ${groupId}`);
|
|
9328
|
+
}
|
|
9329
|
+
else {
|
|
9330
|
+
this.logger.error("Failed to confirm checkout", response.error);
|
|
9331
|
+
}
|
|
9332
|
+
}
|
|
9333
|
+
catch (error) {
|
|
9334
|
+
this.logger.error("Error confirming checkout", error);
|
|
9335
|
+
}
|
|
9336
|
+
}
|
|
9056
9337
|
/**
|
|
9057
9338
|
* Get the initialized API client instance
|
|
9058
9339
|
*/
|