@basedone/core 0.2.0 → 0.2.2
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/{chunk-4GAKANLT.mjs → chunk-Z5OW2FDP.mjs} +218 -0
- package/dist/ecommerce.d.mts +461 -1
- package/dist/ecommerce.d.ts +461 -1
- package/dist/ecommerce.js +218 -0
- package/dist/ecommerce.mjs +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +218 -0
- package/dist/index.mjs +1 -1
- package/lib/ecommerce/FLASH_SALES.md +340 -0
- package/lib/ecommerce/QUICK_REFERENCE.md +26 -0
- package/lib/ecommerce/README.md +32 -0
- package/lib/ecommerce/client/customer.ts +249 -0
- package/lib/ecommerce/types/entities.ts +69 -0
- package/lib/ecommerce/types/requests.ts +79 -0
- package/lib/ecommerce/types/responses.ts +148 -0
- package/package.json +1 -1
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
# Flash Sales SDK Documentation
|
|
2
|
+
|
|
3
|
+
This guide shows you how to use the `@basedone/core/ecommerce` SDK to fetch and display active flash sales.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Flash sales are time-limited deals with discounted products. The SDK provides a simple method to fetch all currently active flash sales with their products, pricing, and countdown information.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
import { CustomerEcommerceClient } from "@basedone/core/ecommerce";
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Basic Usage
|
|
16
|
+
|
|
17
|
+
### Initialize the Client
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
const client = new CustomerEcommerceClient({
|
|
21
|
+
baseURL: "https://your-api-domain.com",
|
|
22
|
+
authToken: "your-auth-token", // Optional for public endpoints
|
|
23
|
+
});
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Fetch Active Flash Sales
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
// Get all active flash sales
|
|
30
|
+
const result = await client.getActiveFlashSales();
|
|
31
|
+
|
|
32
|
+
console.log(`Found ${result.flashSales.length} active flash sales`);
|
|
33
|
+
|
|
34
|
+
// Access the first flash sale
|
|
35
|
+
const featuredSale = result.flashSales[0];
|
|
36
|
+
console.log(`Sale: ${featuredSale.name}`);
|
|
37
|
+
console.log(`Ends at: ${featuredSale.endsAt}`);
|
|
38
|
+
|
|
39
|
+
// Access countdown timer info
|
|
40
|
+
if (result.timeRemaining) {
|
|
41
|
+
console.log(`Time remaining: ${result.timeRemaining.remainingSeconds} seconds`);
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### With Parameters
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
// Limit the number of flash sales returned
|
|
49
|
+
const result = await client.getActiveFlashSales({
|
|
50
|
+
limit: 5, // Maximum 50
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// Filter by merchant (optional)
|
|
54
|
+
const result = await client.getActiveFlashSales({
|
|
55
|
+
limit: 10,
|
|
56
|
+
merchantId: "merchant_123",
|
|
57
|
+
});
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Response Structure
|
|
61
|
+
|
|
62
|
+
### ActiveFlashSalesResponse
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
{
|
|
66
|
+
flashSales: FlashSale[]; // Array of active flash sales
|
|
67
|
+
timeRemaining: { // Countdown info for featured sale
|
|
68
|
+
endsAt: string; // ISO timestamp
|
|
69
|
+
remainingSeconds: number; // Seconds until end
|
|
70
|
+
} | null;
|
|
71
|
+
serverTime: string; // Server timestamp for sync
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### FlashSale
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
{
|
|
79
|
+
id: string; // Flash sale ID
|
|
80
|
+
name: string; // Sale name (e.g., "Summer Flash Sale")
|
|
81
|
+
description?: string; // Optional description
|
|
82
|
+
startsAt: string; // ISO timestamp
|
|
83
|
+
endsAt: string; // ISO timestamp
|
|
84
|
+
badgeText?: string; // Badge text (e.g., "Mall")
|
|
85
|
+
badgeColor?: string; // Badge color hex (e.g., "#facc15")
|
|
86
|
+
priority: number; // Display priority (higher = first)
|
|
87
|
+
merchant?: { // Merchant info (null for admin sales)
|
|
88
|
+
id: string;
|
|
89
|
+
name: string;
|
|
90
|
+
} | null;
|
|
91
|
+
items: FlashSaleItem[]; // Products in this sale
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### FlashSaleItem
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
{
|
|
99
|
+
id: string; // Item ID
|
|
100
|
+
productId: string; // Product ID
|
|
101
|
+
salePrice: string; // Sale price in USDC (e.g., "17.90")
|
|
102
|
+
originalPrice: string; // Original price in USDC (e.g., "30.00")
|
|
103
|
+
discountPercent: number; // Discount percentage (e.g., 42)
|
|
104
|
+
maxQuantity: number | null; // Max available (null = unlimited)
|
|
105
|
+
soldQuantity: number; // Already sold count
|
|
106
|
+
remainingQuantity: number | null; // Remaining (null if unlimited)
|
|
107
|
+
limitPerCustomer: number | null; // Max per customer
|
|
108
|
+
product: { // Full product details
|
|
109
|
+
id: string;
|
|
110
|
+
title: string;
|
|
111
|
+
description?: string;
|
|
112
|
+
images: string[];
|
|
113
|
+
priceUSDC: string;
|
|
114
|
+
inventory: number | null;
|
|
115
|
+
soldCount: number;
|
|
116
|
+
averageRating: number | null;
|
|
117
|
+
reviewCount: number;
|
|
118
|
+
merchant?: {
|
|
119
|
+
id: string;
|
|
120
|
+
name: string;
|
|
121
|
+
};
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Example: Display Flash Sales
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
import { CustomerEcommerceClient } from "@basedone/core/ecommerce";
|
|
130
|
+
|
|
131
|
+
const client = new CustomerEcommerceClient({
|
|
132
|
+
baseURL: "https://api.example.com",
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
async function displayFlashSales() {
|
|
136
|
+
try {
|
|
137
|
+
const result = await client.getActiveFlashSales({ limit: 10 });
|
|
138
|
+
|
|
139
|
+
if (result.flashSales.length === 0) {
|
|
140
|
+
console.log("No active flash sales");
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Display each flash sale
|
|
145
|
+
result.flashSales.forEach((sale) => {
|
|
146
|
+
console.log(`\n⚡ ${sale.name}`);
|
|
147
|
+
console.log(` Badge: ${sale.badgeText || "Flash Deal"}`);
|
|
148
|
+
console.log(` Ends: ${new Date(sale.endsAt).toLocaleString()}`);
|
|
149
|
+
console.log(` Products: ${sale.items.length}`);
|
|
150
|
+
|
|
151
|
+
// Display products
|
|
152
|
+
sale.items.forEach((item) => {
|
|
153
|
+
console.log(` - ${item.product.title}`);
|
|
154
|
+
console.log(` Original: $${item.originalPrice} → Sale: $${item.salePrice}`);
|
|
155
|
+
console.log(` Discount: ${item.discountPercent}% off`);
|
|
156
|
+
|
|
157
|
+
if (item.remainingQuantity !== null) {
|
|
158
|
+
console.log(` Only ${item.remainingQuantity} left!`);
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
// Display countdown
|
|
164
|
+
if (result.timeRemaining) {
|
|
165
|
+
const hours = Math.floor(result.timeRemaining.remainingSeconds / 3600);
|
|
166
|
+
const minutes = Math.floor((result.timeRemaining.remainingSeconds % 3600) / 60);
|
|
167
|
+
const seconds = result.timeRemaining.remainingSeconds % 60;
|
|
168
|
+
console.log(`\n⏰ Time remaining: ${hours}h ${minutes}m ${seconds}s`);
|
|
169
|
+
}
|
|
170
|
+
} catch (error) {
|
|
171
|
+
console.error("Error fetching flash sales:", error);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
displayFlashSales();
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## Example: React Component
|
|
179
|
+
|
|
180
|
+
```typescript
|
|
181
|
+
import { useState, useEffect } from "react";
|
|
182
|
+
import { CustomerEcommerceClient, ActiveFlashSalesResponse } from "@basedone/core/ecommerce";
|
|
183
|
+
|
|
184
|
+
function FlashSalesList() {
|
|
185
|
+
const [flashSales, setFlashSales] = useState<ActiveFlashSalesResponse | null>(null);
|
|
186
|
+
const [loading, setLoading] = useState(true);
|
|
187
|
+
|
|
188
|
+
useEffect(() => {
|
|
189
|
+
const client = new CustomerEcommerceClient({
|
|
190
|
+
baseURL: process.env.NEXT_PUBLIC_API_URL || "",
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
client
|
|
194
|
+
.getActiveFlashSales({ limit: 10 })
|
|
195
|
+
.then((data) => {
|
|
196
|
+
setFlashSales(data);
|
|
197
|
+
setLoading(false);
|
|
198
|
+
})
|
|
199
|
+
.catch((error) => {
|
|
200
|
+
console.error("Failed to load flash sales:", error);
|
|
201
|
+
setLoading(false);
|
|
202
|
+
});
|
|
203
|
+
}, []);
|
|
204
|
+
|
|
205
|
+
if (loading) return <div>Loading flash sales...</div>;
|
|
206
|
+
if (!flashSales || flashSales.flashSales.length === 0) {
|
|
207
|
+
return <div>No active flash sales</div>;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return (
|
|
211
|
+
<div>
|
|
212
|
+
{flashSales.flashSales.map((sale) => (
|
|
213
|
+
<div key={sale.id} className="flash-sale-card">
|
|
214
|
+
<h2>{sale.name}</h2>
|
|
215
|
+
{sale.badgeText && (
|
|
216
|
+
<span style={{ backgroundColor: sale.badgeColor || "#f97316" }}>
|
|
217
|
+
{sale.badgeText}
|
|
218
|
+
</span>
|
|
219
|
+
)}
|
|
220
|
+
<p>Ends: {new Date(sale.endsAt).toLocaleString()}</p>
|
|
221
|
+
|
|
222
|
+
<div className="products">
|
|
223
|
+
{sale.items.map((item) => (
|
|
224
|
+
<div key={item.id} className="product-card">
|
|
225
|
+
<img src={item.product.images[0]} alt={item.product.title} />
|
|
226
|
+
<h3>{item.product.title}</h3>
|
|
227
|
+
<div className="pricing">
|
|
228
|
+
<span className="original-price">${item.originalPrice}</span>
|
|
229
|
+
<span className="sale-price">${item.salePrice}</span>
|
|
230
|
+
<span className="discount">-{item.discountPercent}%</span>
|
|
231
|
+
</div>
|
|
232
|
+
{item.remainingQuantity !== null && item.remainingQuantity <= 10 && (
|
|
233
|
+
<div className="scarcity">Only {item.remainingQuantity} left!</div>
|
|
234
|
+
)}
|
|
235
|
+
</div>
|
|
236
|
+
))}
|
|
237
|
+
</div>
|
|
238
|
+
</div>
|
|
239
|
+
))}
|
|
240
|
+
</div>
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
## Example: Countdown Timer
|
|
246
|
+
|
|
247
|
+
```typescript
|
|
248
|
+
import { useState, useEffect } from "react";
|
|
249
|
+
import { CustomerEcommerceClient } from "@basedone/core/ecommerce";
|
|
250
|
+
|
|
251
|
+
function CountdownTimer() {
|
|
252
|
+
const [timeLeft, setTimeLeft] = useState({ hours: 0, minutes: 0, seconds: 0 });
|
|
253
|
+
const [serverTime, setServerTime] = useState<Date | null>(null);
|
|
254
|
+
|
|
255
|
+
useEffect(() => {
|
|
256
|
+
const client = new CustomerEcommerceClient({
|
|
257
|
+
baseURL: process.env.NEXT_PUBLIC_API_URL || "",
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
// Fetch flash sales and sync with server time
|
|
261
|
+
client.getActiveFlashSales().then((result) => {
|
|
262
|
+
if (result.timeRemaining) {
|
|
263
|
+
const serverTimeDate = new Date(result.serverTime);
|
|
264
|
+
setServerTime(serverTimeDate);
|
|
265
|
+
|
|
266
|
+
// Calculate offset for accurate countdown
|
|
267
|
+
const offset = serverTimeDate.getTime() - Date.now();
|
|
268
|
+
const endTime = new Date(result.timeRemaining.endsAt).getTime();
|
|
269
|
+
|
|
270
|
+
const updateTimer = () => {
|
|
271
|
+
const now = Date.now() + offset;
|
|
272
|
+
const remaining = Math.max(0, endTime - now);
|
|
273
|
+
|
|
274
|
+
setTimeLeft({
|
|
275
|
+
hours: Math.floor(remaining / (1000 * 60 * 60)),
|
|
276
|
+
minutes: Math.floor((remaining % (1000 * 60 * 60)) / (1000 * 60)),
|
|
277
|
+
seconds: Math.floor((remaining % (1000 * 60)) / 1000),
|
|
278
|
+
});
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
updateTimer();
|
|
282
|
+
const interval = setInterval(updateTimer, 1000);
|
|
283
|
+
return () => clearInterval(interval);
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
}, []);
|
|
287
|
+
|
|
288
|
+
return (
|
|
289
|
+
<div className="countdown">
|
|
290
|
+
{timeLeft.hours.toString().padStart(2, "0")}:
|
|
291
|
+
{timeLeft.minutes.toString().padStart(2, "0")}:
|
|
292
|
+
{timeLeft.seconds.toString().padStart(2, "0")}
|
|
293
|
+
</div>
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
## Error Handling
|
|
299
|
+
|
|
300
|
+
```typescript
|
|
301
|
+
try {
|
|
302
|
+
const result = await client.getActiveFlashSales();
|
|
303
|
+
// Use result...
|
|
304
|
+
} catch (error) {
|
|
305
|
+
if (error instanceof EcommerceApiError) {
|
|
306
|
+
console.error("API Error:", error.message);
|
|
307
|
+
console.error("Status:", error.status);
|
|
308
|
+
} else {
|
|
309
|
+
console.error("Unexpected error:", error);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
## Notes
|
|
315
|
+
|
|
316
|
+
- Flash sales are automatically filtered to only show active sales (between `startsAt` and `endsAt`)
|
|
317
|
+
- The `timeRemaining` field provides countdown info for the featured/first flash sale
|
|
318
|
+
- Use `serverTime` to sync client-side countdown timers accurately
|
|
319
|
+
- Products in flash sales are automatically filtered to only include active products
|
|
320
|
+
- Discount percentages are pre-calculated for convenience
|
|
321
|
+
- The endpoint caches responses for 30 seconds due to time-sensitive nature
|
|
322
|
+
|
|
323
|
+
## API Endpoint
|
|
324
|
+
|
|
325
|
+
The SDK method calls:
|
|
326
|
+
```
|
|
327
|
+
GET /api/marketplace/flash-sales/active?limit={limit}&merchantId={merchantId}
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
## Related Types
|
|
331
|
+
|
|
332
|
+
```typescript
|
|
333
|
+
import type {
|
|
334
|
+
ActiveFlashSalesResponse,
|
|
335
|
+
FlashSale,
|
|
336
|
+
FlashSaleItem,
|
|
337
|
+
ListActiveFlashSalesParams,
|
|
338
|
+
} from "@basedone/core/ecommerce";
|
|
339
|
+
```
|
|
340
|
+
|
|
@@ -57,6 +57,16 @@ await client.createReview("prod_123", {
|
|
|
57
57
|
comment: "Love it!",
|
|
58
58
|
});
|
|
59
59
|
|
|
60
|
+
// Shop Following
|
|
61
|
+
const followStatus = await client.isFollowingMerchant("merchant_123");
|
|
62
|
+
await client.followMerchant("merchant_123");
|
|
63
|
+
await client.unfollowMerchant("merchant_123");
|
|
64
|
+
const following = await client.getFollowedMerchants({ limit: 20 });
|
|
65
|
+
|
|
66
|
+
// Merchant Profile & Products (public)
|
|
67
|
+
const merchant = await client.getMerchantProfile("merchant_123");
|
|
68
|
+
const products = await client.getMerchantProducts("merchant_123", { sortBy: "popular" });
|
|
69
|
+
|
|
60
70
|
// Calculate discounts
|
|
61
71
|
const discounts = await client.calculateCartDiscounts({
|
|
62
72
|
items: [{ productId: "prod_123", quantity: 2 }],
|
|
@@ -67,6 +77,17 @@ const tax = await client.calculateTax({
|
|
|
67
77
|
items: [{ productId: "prod_123", quantity: 2 }],
|
|
68
78
|
shippingAddress: { country: "US", region: "NY" },
|
|
69
79
|
});
|
|
80
|
+
|
|
81
|
+
// Get merchant storefront profile
|
|
82
|
+
const merchant = await client.getMerchantProfile("merchant_123");
|
|
83
|
+
console.log(merchant.merchant.name, merchant.merchant.productCount);
|
|
84
|
+
|
|
85
|
+
// List merchant products with search/sort
|
|
86
|
+
const merchantProducts = await client.getMerchantProducts("merchant_123", {
|
|
87
|
+
search: "laptop",
|
|
88
|
+
sortBy: "popular",
|
|
89
|
+
limit: 20,
|
|
90
|
+
});
|
|
70
91
|
```
|
|
71
92
|
|
|
72
93
|
## Merchant Client
|
|
@@ -200,6 +221,11 @@ import type {
|
|
|
200
221
|
PaymentMethod,
|
|
201
222
|
CreateOrderRequest,
|
|
202
223
|
ListProductsParams,
|
|
224
|
+
// Merchant storefront types
|
|
225
|
+
PublicMerchantProfile,
|
|
226
|
+
PublicMerchantProfileResponse,
|
|
227
|
+
MerchantProductsResponse,
|
|
228
|
+
ListMerchantProductsParams,
|
|
203
229
|
} from "@basedone/core/ecommerce";
|
|
204
230
|
```
|
|
205
231
|
|
package/lib/ecommerce/README.md
CHANGED
|
@@ -68,6 +68,17 @@ if (order.escrow) {
|
|
|
68
68
|
// After depositing, confirm the transaction
|
|
69
69
|
await client.confirmEscrowDeposit(order.orders[0].id);
|
|
70
70
|
}
|
|
71
|
+
|
|
72
|
+
// Browse a merchant's storefront
|
|
73
|
+
const merchantProfile = await client.getMerchantProfile("merchant_123");
|
|
74
|
+
console.log(`${merchantProfile.merchant.name} - ${merchantProfile.merchant.productCount} products`);
|
|
75
|
+
|
|
76
|
+
// Get merchant products with search and sort
|
|
77
|
+
const merchantProducts = await client.getMerchantProducts("merchant_123", {
|
|
78
|
+
search: "laptop",
|
|
79
|
+
sortBy: "popular", // popular, latest, top_sales, price_asc, price_desc, rating
|
|
80
|
+
limit: 20,
|
|
81
|
+
});
|
|
71
82
|
```
|
|
72
83
|
|
|
73
84
|
### Merchant Client
|
|
@@ -142,6 +153,16 @@ await client.updateOrderStatus("ord_123", {
|
|
|
142
153
|
- `getActiveBanners(params?)` - Get active promotional banners
|
|
143
154
|
- `trackBanner(bannerId, request)` - Track banner impression/click
|
|
144
155
|
|
|
156
|
+
#### Merchant Storefront (Public)
|
|
157
|
+
- `getMerchantProfile(merchantId)` - Get public merchant profile
|
|
158
|
+
- `getMerchantProducts(merchantId, params?)` - Get merchant's products with filtering
|
|
159
|
+
|
|
160
|
+
#### Shop Following
|
|
161
|
+
- `isFollowingMerchant(merchantId)` - Check if following a merchant
|
|
162
|
+
- `followMerchant(merchantId)` - Follow a merchant
|
|
163
|
+
- `unfollowMerchant(merchantId)` - Unfollow a merchant
|
|
164
|
+
- `getFollowedMerchants(params?)` - Get list of followed merchants
|
|
165
|
+
|
|
145
166
|
### Merchant APIs
|
|
146
167
|
|
|
147
168
|
#### Profile
|
|
@@ -319,6 +340,11 @@ import type {
|
|
|
319
340
|
PaymentMethod,
|
|
320
341
|
CreateOrderRequest,
|
|
321
342
|
ListProductsParams,
|
|
343
|
+
// Merchant storefront types
|
|
344
|
+
PublicMerchantProfile,
|
|
345
|
+
PublicMerchantProfileResponse,
|
|
346
|
+
MerchantProductsResponse,
|
|
347
|
+
ListMerchantProductsParams,
|
|
322
348
|
// ... and many more
|
|
323
349
|
} from "@basedone/core/ecommerce";
|
|
324
350
|
```
|
|
@@ -375,6 +401,12 @@ const client = new CustomerEcommerceClient({
|
|
|
375
401
|
});
|
|
376
402
|
```
|
|
377
403
|
|
|
404
|
+
## Additional Documentation
|
|
405
|
+
|
|
406
|
+
- **[Flash Sales Guide](./FLASH_SALES.md)** - Complete guide for fetching and displaying flash sales
|
|
407
|
+
- **[Usage Examples](./USAGE_EXAMPLES.md)** - More detailed code examples
|
|
408
|
+
- **[Quick Reference](./QUICK_REFERENCE.md)** - API method quick reference
|
|
409
|
+
|
|
378
410
|
## Support
|
|
379
411
|
|
|
380
412
|
For issues or questions, please open an issue on GitHub or contact support.
|
|
@@ -20,6 +20,10 @@ import type {
|
|
|
20
20
|
CalculateTaxRequest,
|
|
21
21
|
ListActiveBannersParams,
|
|
22
22
|
TrackBannerRequest,
|
|
23
|
+
SendMessageRequest,
|
|
24
|
+
ListActiveFlashSalesParams,
|
|
25
|
+
ListMerchantProductsParams,
|
|
26
|
+
ListFollowingParams,
|
|
23
27
|
|
|
24
28
|
// Response types
|
|
25
29
|
ListProductsResponse,
|
|
@@ -39,6 +43,15 @@ import type {
|
|
|
39
43
|
ListBannersResponse,
|
|
40
44
|
SuccessResponse,
|
|
41
45
|
ProductDiscountsResponse,
|
|
46
|
+
CustomerMessagesResponse,
|
|
47
|
+
MessageStatsResponse,
|
|
48
|
+
MessageResponse,
|
|
49
|
+
ActiveFlashSalesResponse,
|
|
50
|
+
PublicMerchantProfileResponse,
|
|
51
|
+
MerchantProductsResponse,
|
|
52
|
+
FollowStatusResponse,
|
|
53
|
+
FollowActionResponse,
|
|
54
|
+
ListFollowingResponse,
|
|
42
55
|
} from "../types";
|
|
43
56
|
|
|
44
57
|
/**
|
|
@@ -518,5 +531,241 @@ export class CustomerEcommerceClient extends BaseEcommerceClient {
|
|
|
518
531
|
async trackBanner(bannerId: string, request: TrackBannerRequest): Promise<SuccessResponse> {
|
|
519
532
|
return this.post(`/api/marketplace/banners/${bannerId}/track`, request);
|
|
520
533
|
}
|
|
534
|
+
|
|
535
|
+
// ============================================================================
|
|
536
|
+
// Messages API
|
|
537
|
+
// ============================================================================
|
|
538
|
+
|
|
539
|
+
/**
|
|
540
|
+
* List messages for customer
|
|
541
|
+
*
|
|
542
|
+
* Returns all conversations grouped by order, where each order represents
|
|
543
|
+
* a conversation with a merchant.
|
|
544
|
+
*
|
|
545
|
+
* @returns List of conversations with messages and stats
|
|
546
|
+
*
|
|
547
|
+
* @example
|
|
548
|
+
* ```typescript
|
|
549
|
+
* const result = await client.listMessages();
|
|
550
|
+
* console.log("Unread:", result.stats.unread);
|
|
551
|
+
* result.conversations.forEach(conv => {
|
|
552
|
+
* console.log(`Order ${conv.orderNumber}: ${conv.unreadCount} unread`);
|
|
553
|
+
* });
|
|
554
|
+
* ```
|
|
555
|
+
*/
|
|
556
|
+
async listMessages(): Promise<CustomerMessagesResponse> {
|
|
557
|
+
return this.get("/api/marketplace/messages");
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
/**
|
|
561
|
+
* Get message stats (unread count)
|
|
562
|
+
*
|
|
563
|
+
* Lightweight endpoint for notification badges.
|
|
564
|
+
*
|
|
565
|
+
* @returns Unread message count
|
|
566
|
+
*
|
|
567
|
+
* @example
|
|
568
|
+
* ```typescript
|
|
569
|
+
* const stats = await client.getMessageStats();
|
|
570
|
+
* console.log("Unread messages:", stats.unread);
|
|
571
|
+
* ```
|
|
572
|
+
*/
|
|
573
|
+
async getMessageStats(): Promise<MessageStatsResponse> {
|
|
574
|
+
return this.get("/api/marketplace/messages/stats");
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
/**
|
|
578
|
+
* Send a message to a merchant about an order
|
|
579
|
+
*
|
|
580
|
+
* @param request - Message data including orderId, recipientId (merchant user ID), and message
|
|
581
|
+
* @returns Sent message
|
|
582
|
+
*
|
|
583
|
+
* @example
|
|
584
|
+
* ```typescript
|
|
585
|
+
* await client.sendMessage({
|
|
586
|
+
* orderId: "ord_123",
|
|
587
|
+
* recipientId: "user_merchant_456",
|
|
588
|
+
* message: "When will my order ship?"
|
|
589
|
+
* });
|
|
590
|
+
* ```
|
|
591
|
+
*/
|
|
592
|
+
async sendMessage(request: SendMessageRequest): Promise<MessageResponse> {
|
|
593
|
+
return this.post("/api/marketplace/messages/send", request);
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
/**
|
|
597
|
+
* Mark a message as read
|
|
598
|
+
*
|
|
599
|
+
* @param messageId - Message ID
|
|
600
|
+
* @returns Updated message
|
|
601
|
+
*
|
|
602
|
+
* @example
|
|
603
|
+
* ```typescript
|
|
604
|
+
* await client.markMessageAsRead("msg_123");
|
|
605
|
+
* ```
|
|
606
|
+
*/
|
|
607
|
+
async markMessageAsRead(messageId: string): Promise<MessageResponse> {
|
|
608
|
+
return this.patch(`/api/marketplace/messages/${messageId}/read`, {});
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
// ============================================================================
|
|
612
|
+
// Flash Sales API
|
|
613
|
+
// ============================================================================
|
|
614
|
+
|
|
615
|
+
/**
|
|
616
|
+
* Get active flash sales
|
|
617
|
+
*
|
|
618
|
+
* Returns currently running flash sales with their discounted products.
|
|
619
|
+
* Includes countdown timer information for UI display.
|
|
620
|
+
*
|
|
621
|
+
* @param params - Query parameters
|
|
622
|
+
* @returns List of active flash sales with time remaining
|
|
623
|
+
*
|
|
624
|
+
* @example
|
|
625
|
+
* ```typescript
|
|
626
|
+
* const result = await client.getActiveFlashSales({ limit: 5 });
|
|
627
|
+
*
|
|
628
|
+
* result.flashSales.forEach(sale => {
|
|
629
|
+
* console.log(`${sale.name} - ends at ${sale.endsAt}`);
|
|
630
|
+
* sale.items.forEach(item => {
|
|
631
|
+
* console.log(` ${item.product.title}: $${item.salePrice} (${item.discountPercent}% off)`);
|
|
632
|
+
* });
|
|
633
|
+
* });
|
|
634
|
+
*
|
|
635
|
+
* // Use timeRemaining for countdown UI
|
|
636
|
+
* if (result.timeRemaining) {
|
|
637
|
+
* console.log(`Featured sale ends in ${result.timeRemaining.remainingSeconds} seconds`);
|
|
638
|
+
* }
|
|
639
|
+
* ```
|
|
640
|
+
*/
|
|
641
|
+
async getActiveFlashSales(params?: ListActiveFlashSalesParams): Promise<ActiveFlashSalesResponse> {
|
|
642
|
+
const queryString = params ? buildQueryString(params) : "";
|
|
643
|
+
return this.get(`/api/marketplace/flash-sales/active${queryString}`);
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
// ============================================================================
|
|
647
|
+
// Merchant Storefront API
|
|
648
|
+
// ============================================================================
|
|
649
|
+
|
|
650
|
+
/**
|
|
651
|
+
* Get public merchant profile
|
|
652
|
+
*
|
|
653
|
+
* Fetches public information about a merchant for the storefront page.
|
|
654
|
+
*
|
|
655
|
+
* @param merchantId - Merchant ID
|
|
656
|
+
* @returns Public merchant profile with stats
|
|
657
|
+
*
|
|
658
|
+
* @example
|
|
659
|
+
* ```typescript
|
|
660
|
+
* const result = await client.getMerchantProfile("merchant_123");
|
|
661
|
+
* console.log(result.merchant.name, result.merchant.productCount);
|
|
662
|
+
* ```
|
|
663
|
+
*/
|
|
664
|
+
async getMerchantProfile(merchantId: string): Promise<PublicMerchantProfileResponse> {
|
|
665
|
+
return this.get(`/api/marketplace/merchants/${merchantId}`);
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
/**
|
|
669
|
+
* List merchant products
|
|
670
|
+
*
|
|
671
|
+
* Fetches products for a specific merchant with search, filter, and sort options.
|
|
672
|
+
*
|
|
673
|
+
* @param merchantId - Merchant ID
|
|
674
|
+
* @param params - Query parameters for filtering and pagination
|
|
675
|
+
* @returns Paginated list of products
|
|
676
|
+
*
|
|
677
|
+
* @example
|
|
678
|
+
* ```typescript
|
|
679
|
+
* const result = await client.getMerchantProducts("merchant_123", {
|
|
680
|
+
* search: "laptop",
|
|
681
|
+
* sortBy: "popular",
|
|
682
|
+
* limit: 20,
|
|
683
|
+
* });
|
|
684
|
+
*
|
|
685
|
+
* result.items.forEach(product => {
|
|
686
|
+
* console.log(product.title, product.priceUSDC);
|
|
687
|
+
* });
|
|
688
|
+
* ```
|
|
689
|
+
*/
|
|
690
|
+
async getMerchantProducts(
|
|
691
|
+
merchantId: string,
|
|
692
|
+
params?: ListMerchantProductsParams
|
|
693
|
+
): Promise<MerchantProductsResponse> {
|
|
694
|
+
const queryString = params ? buildQueryString(params) : "";
|
|
695
|
+
return this.get(`/api/marketplace/merchants/${merchantId}/products${queryString}`);
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
// ============================================================================
|
|
699
|
+
// Shop Following API
|
|
700
|
+
// ============================================================================
|
|
701
|
+
|
|
702
|
+
/**
|
|
703
|
+
* Check if following a merchant
|
|
704
|
+
*
|
|
705
|
+
* @param merchantId - Merchant ID
|
|
706
|
+
* @returns Follow status with follow date if applicable
|
|
707
|
+
*
|
|
708
|
+
* @example
|
|
709
|
+
* ```typescript
|
|
710
|
+
* const status = await client.isFollowingMerchant("merchant_123");
|
|
711
|
+
* if (status.isFollowing) {
|
|
712
|
+
* console.log(`Following since ${status.followedAt}`);
|
|
713
|
+
* }
|
|
714
|
+
* ```
|
|
715
|
+
*/
|
|
716
|
+
async isFollowingMerchant(merchantId: string): Promise<FollowStatusResponse> {
|
|
717
|
+
return this.get(`/api/marketplace/merchants/${merchantId}/follow`);
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
/**
|
|
721
|
+
* Follow a merchant
|
|
722
|
+
*
|
|
723
|
+
* @param merchantId - Merchant ID to follow
|
|
724
|
+
* @returns Follow action result
|
|
725
|
+
*
|
|
726
|
+
* @example
|
|
727
|
+
* ```typescript
|
|
728
|
+
* const result = await client.followMerchant("merchant_123");
|
|
729
|
+
* console.log(result.message); // "Now following Awesome Store"
|
|
730
|
+
* ```
|
|
731
|
+
*/
|
|
732
|
+
async followMerchant(merchantId: string): Promise<FollowActionResponse> {
|
|
733
|
+
return this.post(`/api/marketplace/merchants/${merchantId}/follow`);
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
/**
|
|
737
|
+
* Unfollow a merchant
|
|
738
|
+
*
|
|
739
|
+
* @param merchantId - Merchant ID to unfollow
|
|
740
|
+
* @returns Unfollow action result
|
|
741
|
+
*
|
|
742
|
+
* @example
|
|
743
|
+
* ```typescript
|
|
744
|
+
* const result = await client.unfollowMerchant("merchant_123");
|
|
745
|
+
* console.log(result.message); // "Unfollowed successfully"
|
|
746
|
+
* ```
|
|
747
|
+
*/
|
|
748
|
+
async unfollowMerchant(merchantId: string): Promise<FollowActionResponse> {
|
|
749
|
+
return this.delete(`/api/marketplace/merchants/${merchantId}/follow`);
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
/**
|
|
753
|
+
* Get list of followed merchants
|
|
754
|
+
*
|
|
755
|
+
* @param params - Query parameters for pagination and sorting
|
|
756
|
+
* @returns Paginated list of followed merchants with their info
|
|
757
|
+
*
|
|
758
|
+
* @example
|
|
759
|
+
* ```typescript
|
|
760
|
+
* const result = await client.getFollowedMerchants({ limit: 20, sortBy: "recent" });
|
|
761
|
+
* result.items.forEach(item => {
|
|
762
|
+
* console.log(`${item.merchant.name} - followed on ${item.followedAt}`);
|
|
763
|
+
* });
|
|
764
|
+
* ```
|
|
765
|
+
*/
|
|
766
|
+
async getFollowedMerchants(params?: ListFollowingParams): Promise<ListFollowingResponse> {
|
|
767
|
+
const queryString = params ? buildQueryString(params) : "";
|
|
768
|
+
return this.get(`/api/marketplace/following${queryString}`);
|
|
769
|
+
}
|
|
521
770
|
}
|
|
522
771
|
|