@cshah18/sdk 4.0.0 → 4.2.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.
@@ -4,9 +4,9 @@ import { ApiClient } from "./api-client";
4
4
  */
5
5
  export interface AnalyticsEvent {
6
6
  event: string;
7
- productId: string;
8
7
  timestamp: string;
9
- sessionId: string;
8
+ productId?: string;
9
+ sessionId?: string;
10
10
  context?: {
11
11
  pageUrl?: string;
12
12
  userAgent?: string;
@@ -31,6 +31,10 @@ export declare class AnalyticsClient {
31
31
  * Send event to backend analytics endpoint using unified ApiClient
32
32
  */
33
33
  private sendEvent;
34
+ /**
35
+ * Track page view event
36
+ */
37
+ trackPageView(): Promise<void>;
34
38
  /**
35
39
  * Track custom event (extensible for future use)
36
40
  */
@@ -1,4 +1,4 @@
1
- import { ApiRequestOptions, ApiResponse, ApiClientConfig, ProductRewardData, ProductPrimaryGroupData, GroupJoinResponseData, GroupInviteResponseData, AuthStrategy, ShareChannel, Contact, CheckoutValidationData } from "./types";
1
+ import { ApiRequestOptions, ApiResponse, ApiClientConfig, ProductRewardData, ProductPrimaryGroupData, GroupJoinResponseData, GroupInviteResponseData, AuthStrategy, ShareChannel, Contact, CheckoutValidationData, CheckoutConfirmData } from "./types";
2
2
  /**
3
3
  * HTTP client for communicating with CoBuy API
4
4
  *
@@ -158,5 +158,5 @@ export declare class ApiClient {
158
158
  * @param checkoutRef - The checkout reference ID from the prepare step
159
159
  * @returns Promise with success status
160
160
  */
161
- confirmCheckout(groupId: string, checkoutRef: string): Promise<ApiResponse<void>>;
161
+ confirmCheckout(groupId: string, checkoutRef: string, data?: CheckoutConfirmData): Promise<ApiResponse<void>>;
162
162
  }
@@ -1,4 +1,4 @@
1
- import { CoBuySDK, CoBuyInitOptions, RenderWidgetOptions, ModalOptions, Contact, CheckoutValidationData } from "./types";
1
+ import { CoBuySDK, CoBuyInitOptions, RenderWidgetOptions, ModalOptions, Contact, CheckoutValidationData, CheckoutConfirmData } from "./types";
2
2
  import { ApiClient } from "./api-client";
3
3
  import { AnalyticsClient } from "./analytics";
4
4
  import { SocketManager } from "./socket";
@@ -119,14 +119,31 @@ export declare class CoBuy implements CoBuySDK {
119
119
  *
120
120
  * @param groupId - The group ID to confirm checkout for
121
121
  * @param checkoutRef - The checkout reference ID from the prepare step
122
+ /**
123
+ * Confirm checkout for a group
124
+ * Should be called after validateCheckout to complete the checkout process.
125
+ *
126
+ * @param groupId The group ID to confirm checkout for
127
+ * @param checkoutRef The checkout reference from the prepare step
128
+ * @param data Optional order details (order_id, order_total, discount_applied, currency, payment_method, metadata)
122
129
  *
123
130
  * @example
124
131
  * ```typescript
125
132
  * // Confirm checkout for a group
126
133
  * await CoBuy.confirmCheckout('fae238ae-7468-47e9-9eec-b6d52fe3b012', 'chk_9a6d8750-ed60-4795-a207-2abe955e8509');
134
+ *
135
+ * // Confirm checkout with order details
136
+ * await CoBuy.confirmCheckout('fae238ae-7468-47e9-9eec-b6d52fe3b012', 'chk_9a6d8750-ed60-4795-a207-2abe955e8509', {
137
+ * order_id: 'ORDER-1001',
138
+ * order_total: 100.00,
139
+ * discount_applied: 20.00,
140
+ * currency: 'USD',
141
+ * payment_method: 'card',
142
+ * metadata: { coupon_code: 'WELCOME20', customer_id: 'cust_123' }
143
+ * });
127
144
  * ```
128
145
  */
129
- confirmCheckout(groupId: string, checkoutRef: string): Promise<void>;
146
+ confirmCheckout(groupId: string, checkoutRef: string, data?: CheckoutConfirmData): Promise<void>;
130
147
  /**
131
148
  * Get the initialized API client instance
132
149
  */
@@ -0,0 +1,74 @@
1
+ import { InternalConfig, GroupFulfilledEvent, Reward } from "./types";
2
+ export interface GroupRealtimeSnapshot {
3
+ groupId: string;
4
+ productId: string;
5
+ participants: number;
6
+ maxParticipants?: number;
7
+ progressPercent?: number;
8
+ expiresAt?: string;
9
+ reward?: Reward | null;
10
+ status?: string;
11
+ timeLeftSeconds?: number;
12
+ }
13
+ export type GroupChannelEvent = {
14
+ type: "update";
15
+ snapshot: GroupRealtimeSnapshot;
16
+ } | {
17
+ type: "fulfilled";
18
+ snapshot: GroupRealtimeSnapshot;
19
+ };
20
+ export type GroupChannelListener = (event: GroupChannelEvent) => void;
21
+ /**
22
+ * Centralized real-time channel manager for all group-aware UI surfaces.
23
+ * Ensures a single socket connection, one room subscription per group, and
24
+ * shared event dispatch to interested listeners.
25
+ */
26
+ export declare class GroupChannelManager {
27
+ private readonly config;
28
+ private readonly sdkVersion;
29
+ private readonly onFulfilledCallback?;
30
+ private socket;
31
+ private readonly logger;
32
+ private readonly listeners;
33
+ private readonly groupRefCount;
34
+ private readonly groupToProduct;
35
+ private readonly latestByGroup;
36
+ private readonly latestByProduct;
37
+ private isConnecting;
38
+ constructor(config: InternalConfig, sdkVersion: string, onFulfilledCallback?: ((event: GroupFulfilledEvent) => void) | undefined);
39
+ /**
40
+ * Start socket connection if not already connected.
41
+ */
42
+ connect(): void;
43
+ /**
44
+ * Subscribe to realtime updates for a group. Multiple callers are reference-counted.
45
+ */
46
+ subscribeToGroup(groupId: string, productId: string): void;
47
+ /**
48
+ * Unsubscribe from a group when no listeners remain.
49
+ */
50
+ unsubscribeFromGroup(groupId: string): void;
51
+ /**
52
+ * Switch from one group room to another, ensuring only one active subscription.
53
+ */
54
+ switchGroup(oldGroupId: string | null, newGroupId: string, productId: string): void;
55
+ /**
56
+ * Register for realtime events. Returns an unsubscribe function.
57
+ */
58
+ addListener(listener: GroupChannelListener): () => void;
59
+ /**
60
+ * Retrieve the latest snapshot for a product if one exists.
61
+ */
62
+ getLatestSnapshotForProduct(productId: string): GroupRealtimeSnapshot | null;
63
+ /**
64
+ * Disconnect and clear state.
65
+ */
66
+ disconnect(): void;
67
+ private handleGroupUpdate;
68
+ private handleGroupFulfilled;
69
+ private cacheSnapshot;
70
+ private broadcast;
71
+ private normalizeSnapshot;
72
+ private pickString;
73
+ private pickNumber;
74
+ }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Offline Redemption Utilities
3
+ * Helpers for constructing QR URLs, copying codes, and downloading QR images
4
+ */
5
+ import { OfflineRedemption } from "./types";
6
+ /**
7
+ * Build full S3 URL for QR code image
8
+ * @param qrCodeData - S3 key from offline_redemption.qr_code_data
9
+ * @returns Full HTTPS URL to the QR code image
10
+ */
11
+ export declare function buildQRUrl(qrCodeData: string): string;
12
+ /**
13
+ * Copy redemption code to clipboard
14
+ * @param code - Redemption code to copy
15
+ * @returns Promise that resolves when copy is complete
16
+ */
17
+ export declare function copyRedemptionCode(code: string): Promise<boolean>;
18
+ /**
19
+ * Download QR code image
20
+ * @param qrUrl - Full URL to the QR code image
21
+ * @param filename - Name for the downloaded file
22
+ */
23
+ export declare function downloadQRCode(qrUrl: string, filename?: string): Promise<boolean>;
24
+ /**
25
+ * Validate offline redemption data
26
+ * @param redemption - Offline redemption object
27
+ * @returns true if data is valid and complete
28
+ */
29
+ export declare function isValidOfflineRedemption(redemption: unknown): redemption is OfflineRedemption;
30
+ /**
31
+ * Format expiry date for display
32
+ * @param isoDate - ISO 8601 date string
33
+ * @returns Formatted date string (e.g., "Feb 20, 2026 at 10:21 AM")
34
+ */
35
+ export declare function formatExpiryDate(isoDate: string): string;
36
+ /**
37
+ * Check if offline redemption is still valid
38
+ * @param expiryDate - ISO 8601 expiry date string
39
+ * @returns true if redemption has not expired
40
+ */
41
+ export declare function isRedemptionValid(expiryDate: string): boolean;
@@ -11,9 +11,10 @@ export interface SocketHandlers {
11
11
  export declare class SocketManager {
12
12
  private readonly config;
13
13
  private readonly sdkVersion;
14
+ private readonly sessionId;
14
15
  private socket;
15
16
  private readonly logger;
16
- constructor(config: InternalConfig, sdkVersion: string);
17
+ constructor(config: InternalConfig, sdkVersion: string, sessionId: string);
17
18
  private dispatchWindowEvent;
18
19
  private formatPayload;
19
20
  /**
@@ -161,14 +161,78 @@ export interface CheckoutValidationData {
161
161
  reward?: CheckoutValidationReward;
162
162
  message?: string;
163
163
  }
164
+ /**
165
+ * Optional metadata for checkout confirmation
166
+ */
167
+ export interface CheckoutConfirmMetadata {
168
+ coupon_code?: string;
169
+ customer_id?: string;
170
+ note?: string;
171
+ [key: string]: string | number | boolean | undefined;
172
+ }
173
+ /**
174
+ * Optional order details for checkout confirmation
175
+ */
176
+ export interface CheckoutConfirmData {
177
+ order_id?: string;
178
+ order_total?: number;
179
+ discount_applied?: number;
180
+ currency?: string;
181
+ payment_method?: "cod" | "card" | string;
182
+ metadata?: CheckoutConfirmMetadata;
183
+ }
184
+ /**
185
+ * Offline redemption information for fulfilled groups
186
+ */
187
+ export interface OfflineRedemption {
188
+ member_id: string;
189
+ qr_code_value: string;
190
+ redemption_code: string;
191
+ qr_code_data: string;
192
+ offline_expires_at: string;
193
+ }
194
+ /**
195
+ * Group information in fulfillment event
196
+ */
197
+ export interface FulfilledGroup {
198
+ id: string;
199
+ group_name: string;
200
+ description?: string;
201
+ campaign_creative_name?: string;
202
+ campaign_id: string;
203
+ campaign_name: string;
204
+ campaign_status: string;
205
+ participants_count: number;
206
+ max_participants: number;
207
+ status: string;
208
+ expiry_at: string;
209
+ timeLeftSeconds: number;
210
+ }
211
+ /**
212
+ * Frozen reward information in fulfillment event
213
+ */
214
+ export interface FrozenReward {
215
+ reward: Reward;
216
+ group_id: string;
217
+ frozen_at: string;
218
+ product_id: string;
219
+ eligibility?: {
220
+ isEligible: boolean;
221
+ };
222
+ }
164
223
  /**
165
224
  * Socket payload emitted when a group reaches fulfillment
225
+ * Includes all details needed for offline redemption
166
226
  */
167
227
  export interface GroupFulfilledEvent {
168
- groupId: string;
169
- productId: string;
170
- participants: number;
171
- reward: Reward;
228
+ group: FulfilledGroup;
229
+ frozen_reward: FrozenReward;
230
+ product_id: string;
231
+ offline_redemption?: OfflineRedemption;
232
+ groupId?: string;
233
+ productId?: string;
234
+ participants?: number;
235
+ reward?: Reward;
172
236
  }
173
237
  /**
174
238
  * Socket payload emitted when a new group is created
@@ -302,6 +366,10 @@ export interface ModalOptions {
302
366
  timeAgo: string;
303
367
  color: "pink" | "purple" | "blue" | "green" | "orange";
304
368
  }>;
369
+ /**
370
+ * Offline redemption data for fulfilled groups
371
+ */
372
+ offlineRedemption?: OfflineRedemption;
305
373
  /**
306
374
  * Optional callback when modal opens
307
375
  */
@@ -393,6 +461,7 @@ export interface ProductPrimaryGroupData {
393
461
  status: string;
394
462
  expiry_at: string;
395
463
  timeLeftSeconds: number;
464
+ offline_redemption?: OfflineRedemption;
396
465
  }
397
466
  /**
398
467
  * Group join response data
@@ -414,6 +483,7 @@ export interface GroupJoinResponseData {
414
483
  };
415
484
  isPrimary: boolean;
416
485
  product_id: string;
486
+ offline_redemption?: OfflineRedemption;
417
487
  }
418
488
  export type ShareChannel = "whatsapp" | "facebook" | "email" | "sms" | "copy_link" | "tiktok" | "x" | "other";
419
489
  export interface GroupInviteResponseData {
@@ -531,8 +601,9 @@ export interface CoBuySDK {
531
601
  * Finalizes the checkout and confirms the order
532
602
  * @param _groupId The group ID to confirm checkout for
533
603
  * @param _checkoutRef The checkout reference ID from the prepare step
604
+ * @param _data (optional) Additional order details (order_id, order_total, discount_applied, currency, payment_method, metadata)
534
605
  */
535
- confirmCheckout(_groupId: string, _checkoutRef: string): Promise<void>;
606
+ confirmCheckout(_groupId: string, _checkoutRef: string, _data?: CheckoutConfirmData): Promise<void>;
536
607
  /**
537
608
  * Retrieve stored checkout reference for a product in this session
538
609
  * Returns both the storage key and value; key/value null if not found or storage unavailable
@@ -2,6 +2,7 @@ import { ApiClient } from "../../core/api-client";
2
2
  import { ProductPrimaryGroupData, GroupJoinResponseData } from "../../core/types";
3
3
  export interface GroupListItem {
4
4
  groupId: string;
5
+ name?: string;
5
6
  timeLabel: string;
6
7
  timeLeftSeconds?: number;
7
8
  joined: number;
@@ -1,4 +1,5 @@
1
1
  import { ApiClient } from "../../core/api-client";
2
+ import { OfflineRedemption } from "../../core/types";
2
3
  import { AnalyticsClient } from "../../core/analytics";
3
4
  import { SocketManager } from "../../core/socket";
4
5
  import "./styles/styles.css";
@@ -16,6 +17,7 @@ export interface LobbyModalData {
16
17
  shareMessage?: string;
17
18
  activities?: ActivityItem[];
18
19
  isLocked?: boolean;
20
+ offlineRedemption?: OfflineRedemption;
19
21
  }
20
22
  export interface ActivityItem {
21
23
  emoji: string;
@@ -58,6 +60,18 @@ export declare class LobbyModal {
58
60
  * Default activity items
59
61
  */
60
62
  private getDefaultActivities;
63
+ /**
64
+ * Create entrance animation overlay
65
+ */
66
+ private createEntranceAnimation;
67
+ /**
68
+ * Show entrance animation and auto-hide after duration
69
+ */
70
+ private showEntranceAnimation;
71
+ /**
72
+ * Hide entrance animation with exit effect
73
+ */
74
+ private hideEntranceAnimation;
61
75
  /**
62
76
  * Create the modal DOM structure
63
77
  */
@@ -83,9 +97,29 @@ export declare class LobbyModal {
83
97
  */
84
98
  private createTitleSection;
85
99
  /**
86
- * Create link section
100
+ * Create connected section (subtitle + link + share)
101
+ */
102
+ private createConnectedSection;
103
+ /**
104
+ * Create link section (just the link box and share button, no wrapper)
87
105
  */
88
106
  private createLinkSection;
107
+ /**
108
+ * Create offline redemption section
109
+ */
110
+ private createOfflineRedemptionSection;
111
+ /**
112
+ * Handle copying offline redemption code
113
+ */
114
+ private copyOfflineRedemptionCode;
115
+ /**
116
+ * Handle downloading offline QR code
117
+ */
118
+ private downloadOfflineQR;
119
+ /**
120
+ * Inject styles for offline redemption section
121
+ */
122
+ private injectOfflineRedemptionStyles;
89
123
  /**
90
124
  * Create offer section
91
125
  */
@@ -230,6 +264,10 @@ export declare class LobbyModal {
230
264
  * Show/hide link section based on lock state
231
265
  */
232
266
  private updateLinkVisibility;
267
+ /**
268
+ * Update offline redemption visibility when group is fulfilled
269
+ */
270
+ private updateOfflineRedemptionVisibility;
233
271
  /**
234
272
  * Subscribe to socket events
235
273
  */
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Offline Redemption Modal
3
+ * Displays QR code and redemption code for fulfilled groups
4
+ */
5
+ import { OfflineRedemption } from "../../core/types";
6
+ export declare class OfflineRedemptionModal {
7
+ private modalElement;
8
+ private offlineRedemption;
9
+ private onClose?;
10
+ constructor(offlineRedemption: OfflineRedemption, onClose?: () => void);
11
+ /**
12
+ * Open the modal
13
+ */
14
+ open(): void;
15
+ /**
16
+ * Close the modal
17
+ */
18
+ close(): void;
19
+ /**
20
+ * Create the modal DOM structure
21
+ */
22
+ private createModalStructure;
23
+ /**
24
+ * Handle download QR button click
25
+ */
26
+ private handleDownloadQR;
27
+ /**
28
+ * Handle copy code button click
29
+ */
30
+ private handleCopyCode;
31
+ /**
32
+ * Inject modal styles
33
+ */
34
+ private injectStyles;
35
+ }
@@ -29,6 +29,8 @@ export declare class WidgetRoot {
29
29
  private lastGroupDataRefreshTime;
30
30
  private readonly GROUP_REFRESH_DEBOUNCE;
31
31
  private groupExpiryRefreshTriggered;
32
+ private offlineRedemption;
33
+ private offlineRedemptionModal;
32
34
  constructor(config: InternalConfig, apiClient: ApiClient | null, analyticsClient?: AnalyticsClient | null);
33
35
  /** Subscribe once to backend socket events routed through the host page */
34
36
  private subscribeToSocketEvents;
@@ -104,6 +106,10 @@ export declare class WidgetRoot {
104
106
  * Handle CTA button click with analytics and modal opening
105
107
  */
106
108
  private handleCTAClick;
109
+ /**
110
+ * Open offline redemption modal when user clicks "Redeem In-store"
111
+ */
112
+ private openOfflineRedemptionModal;
107
113
  /**
108
114
  * Emit checkout intent without performing navigation
109
115
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cshah18/sdk",
3
- "version": "4.0.0",
3
+ "version": "4.2.0",
4
4
  "description": "CoBuy Embedded SDK for browser JavaScript integration",
5
5
  "type": "module",
6
6
  "main": "dist/cobuy-sdk.umd.js",
@@ -1,126 +0,0 @@
1
- import { InternalConfig } from "./types";
2
- export interface GroupPayload {
3
- id: string;
4
- group_name: string;
5
- participants_count: number;
6
- max_participants: number;
7
- status: string;
8
- expiry_at: Date | string;
9
- timeLeftSeconds: number;
10
- }
11
- export interface MembershipPayload {
12
- id: string;
13
- session_id: string;
14
- created_at: Date | string;
15
- }
16
- export interface GroupMemberJoinedPayload {
17
- group: GroupPayload;
18
- membership: MembershipPayload;
19
- product_id?: string;
20
- isPrimary?: boolean;
21
- }
22
- export interface GroupCreatedPayload {
23
- group: GroupPayload;
24
- membership: MembershipPayload;
25
- product_id?: string;
26
- }
27
- export interface GroupFulfilledPayload {
28
- group: GroupPayload;
29
- frozen_reward: unknown;
30
- product_id?: string | null;
31
- }
32
- export interface SocketClientConfig {
33
- serverUrl: string;
34
- merchantKey?: string;
35
- sdkVersion?: string;
36
- sessionId?: string;
37
- debug?: boolean;
38
- }
39
- export interface SocketEventHandlers {
40
- onGroupMemberJoined?: (payload: GroupMemberJoinedPayload) => void;
41
- onGroupCreated?: (payload: GroupCreatedPayload) => void;
42
- onGroupFulfilled?: (payload: GroupFulfilledPayload) => void;
43
- onConnect?: () => void;
44
- onDisconnect?: (reason: string) => void;
45
- onError?: (error: Error) => void;
46
- }
47
- /**
48
- * Frontend Socket.IO client for real-time group updates
49
- * Manages connection to the backend /sdk namespace and handles group subscriptions
50
- */
51
- export declare class SocketClient {
52
- private socket;
53
- private logger;
54
- private config;
55
- private handlers;
56
- private currentGroupId;
57
- private currentProductId;
58
- private maxReconnectAttempts;
59
- constructor(config: SocketClientConfig);
60
- /**
61
- * Connect to the Socket.IO server
62
- * @param handlers - Event handlers for socket events
63
- */
64
- connect(handlers: SocketEventHandlers): Promise<void>;
65
- /**
66
- * Register event listeners for group updates
67
- */
68
- private registerEventListeners;
69
- /**
70
- * Subscribe to a specific group's updates
71
- * @param groupId - The group ID to subscribe to
72
- */
73
- subscribeToGroup(groupId: string): void;
74
- /**
75
- * Unsubscribe from a specific group's updates
76
- * @param groupId - The group ID to unsubscribe from
77
- */
78
- unsubscribeFromGroup(groupId: string): void;
79
- /**
80
- * Subscribe to product availability updates (groups being created for the product)
81
- * @param productId - The product ID to watch for
82
- */
83
- subscribeToProductAvailable(productId: string): void;
84
- /**
85
- * Unsubscribe from product availability updates
86
- * @param productId - The product ID to stop watching
87
- */
88
- unsubscribeFromProductAvailable(productId: string): void;
89
- /**
90
- * Check if socket is connected
91
- */
92
- isConnected(): boolean;
93
- /**
94
- * Get current socket ID
95
- */
96
- getSocketId(): string | null;
97
- /**
98
- * Get currently subscribed group ID
99
- */
100
- getCurrentGroupId(): string | null;
101
- /**
102
- * Get currently subscribed product ID
103
- */
104
- getCurrentProductId(): string | null;
105
- /**
106
- * Disconnect from the server
107
- */
108
- disconnect(): void;
109
- /**
110
- * Force reconnection to the server
111
- */
112
- reconnect(): void;
113
- /**
114
- * Update authentication credentials
115
- * @param merchantKey - New merchant key
116
- */
117
- updateAuth(merchantKey: string): void;
118
- }
119
- /**
120
- * Create a socket client from SDK config
121
- * @param config - Internal SDK configuration
122
- * @param sdkVersion - SDK version
123
- * @param sessionId - Session ID from CoBuy instance
124
- * @returns Configured SocketClient instance
125
- */
126
- export declare function createSocketClient(config: InternalConfig, sdkVersion: string, sessionId: string): SocketClient;