@bzbs/react-api-client 1.4.14 → 1.4.15

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 CHANGED
@@ -1,8 +1,11 @@
1
1
  # @bzbs/react-api-client
2
2
 
3
- A TypeScript API client library for integrating with Buzzebees services. Provides strongly-typed methods for authentication, campaigns, profiles, shopping carts, notifications, and more.
3
+ A TypeScript library providing a type-safe API client for Buzzebees loyalty and reward services. Supports authentication, campaigns, user profiles, shopping carts, notifications, stamps, consent management, and more.
4
4
 
5
- **Version:** 1.4.12 | **License:** ISC | **Author:** Buzzebees Co., Ltd.
5
+ **Version:** 1.4.14 | **License:** ISC | **Author:** Buzzebees Co., Ltd.
6
+ **Repository:** [Azure DevOps](https://dev.azure.com/buzzebees/Buzzebees/_git/React_API_Client)
7
+
8
+ ---
6
9
 
7
10
  ## Table of Contents
8
11
 
@@ -10,6 +13,9 @@ A TypeScript API client library for integrating with Buzzebees services. Provide
10
13
  - [Quick Start](#quick-start)
11
14
  - [Configuration](#configuration)
12
15
  - [Architecture](#architecture)
16
+ - [BzbsService Properties](#bzbsservice-properties)
17
+ - [Response Handling](#response-handling)
18
+ - [Complete API Endpoint Table](#complete-api-endpoint-table)
13
19
  - [API Reference](#api-reference)
14
20
  - [AuthenticateApi](#authenticateapi)
15
21
  - [CampaignApi](#campaignapi)
@@ -31,10 +37,11 @@ A TypeScript API client library for integrating with Buzzebees services. Provide
31
37
  - [RequestHelpApi (Forum)](#requesthelpapi-forum)
32
38
  - [SettingApi](#settingapi)
33
39
  - [Blob](#blob)
34
- - [Response Handling](#response-handling)
35
- - [Error Handling](#error-handling)
36
- - [Models](#models)
37
- - [Development](#development)
40
+ - [Models Reference](#models-reference)
41
+ - [BaseService Utilities](#baseservice-utilities)
42
+ - [Testing Guide](#testing-guide)
43
+ - [Development Commands](#development-commands)
44
+ - [Adding a New API](#adding-a-new-api)
38
45
  - [Build Output](#build-output)
39
46
 
40
47
  ---
@@ -45,6 +52,14 @@ A TypeScript API client library for integrating with Buzzebees services. Provide
45
52
  npm install @bzbs/react-api-client
46
53
  ```
47
54
 
55
+ Peer dependency: `axios`
56
+
57
+ ```bash
58
+ npm install axios
59
+ ```
60
+
61
+ ---
62
+
48
63
  ## Quick Start
49
64
 
50
65
  ```typescript
@@ -81,6 +96,8 @@ if (result.type === 'success') {
81
96
  }
82
97
  ```
83
98
 
99
+ ---
100
+
84
101
  ## Configuration
85
102
 
86
103
  ### Constructor
@@ -122,8 +139,6 @@ axiosClient.interceptors.request.use((config) => {
122
139
 
123
140
  ### Dynamic URL Updates
124
141
 
125
- You can update base URLs at runtime:
126
-
127
142
  ```typescript
128
143
  bzbsService.setBaseUrl('https://new-api.buzzebees.com/api/');
129
144
  bzbsService.setBlobUrl('https://new-blob.buzzebees.com/');
@@ -134,9 +149,10 @@ bzbsService.setLineUrl('https://api.line.me/');
134
149
 
135
150
  ## Architecture
136
151
 
137
- The library follows a layered service architecture:
138
-
139
152
  ```
153
+ Consumer Code
154
+
155
+
140
156
  BzbsService (entry point)
141
157
  ├── AuthenticateApi → auth/
142
158
  ├── BadgeApi → profile/me/badges
@@ -158,12 +174,18 @@ BzbsService (entry point)
158
174
  ├── RequestHelpApi → buzz/, profile/me/help
159
175
  ├── SettingApi → setting/
160
176
  └── Blob → blob storage
177
+
178
+
179
+ BaseService ← Abstract HTTP layer
180
+
181
+
182
+ AxiosInstance ← Consumer-injected, fully configurable
161
183
  ```
162
184
 
163
185
  All API classes extend `BaseService`, which provides:
164
186
  - HTTP methods: `get`, `post`, `put`, `delete`, `patch`
165
- - Automatic response normalization
166
- - Error handling with typed responses
187
+ - Automatic response normalization (handles both `{ Success, Data }` and `{ success, data }` formats)
188
+ - Error handling with typed `ServiceResponse<T>`
167
189
  - URL construction utilities
168
190
  - FormData creation utility
169
191
 
@@ -200,11 +222,92 @@ src/
200
222
 
201
223
  ---
202
224
 
203
- ## API Reference
225
+ ## BzbsService Properties
204
226
 
205
- Every API method returns `Promise<ServiceResponse<T>>`. All methods accept an optional `requestOptions` parameter as the last argument for custom headers or query params.
227
+ | Property | Type | Description |
228
+ |---|---|---|
229
+ | `authApi` | `AuthenticateApi` | Authentication, login, OTP, session management |
230
+ | `badgeApi` | `BadgeApi` | User badges and missions |
231
+ | `campaignApi` | `CampaignApi` | Campaigns, redemption, favorites |
232
+ | `cartApi` | `CartApi` | Shopping cart |
233
+ | `categoryApi` | `CategoryApi` | Campaign categories |
234
+ | `consentApi` | `ConsentApi` | PDPA / privacy consent |
235
+ | `couponApi` | `CouponApi` | Coupon code processing |
236
+ | `dashboardApi` | `DashboardApi` | Dashboard content |
237
+ | `historyApi` | `HistoryApi` | Redemption history, voucher usage |
238
+ | `lineApi` | `LineApi \| undefined` | LINE OAuth (only if `baseLineUrl` provided) |
239
+ | `notificationApi` | `NotificationApi` | Push notifications |
240
+ | `placeApi` | `PlaceApi` | Locations / stores |
241
+ | `pointLogApi` | `PointLogApi` | Point transaction logs |
242
+ | `profileApi` | `ProfileApi` | User profile, points, account |
243
+ | `registerApi` | `RegistrationApi` | User registration |
244
+ | `addressApi` | `AddressApi` | Addresses, provinces, districts |
245
+ | `stampApi` | `StampApi` | Stamp cards |
246
+ | `forumApi` | `RequestHelpApi` | Help/support forum (Buzz) |
247
+ | `settingApi` | `SettingApi` | Web landing / cart settings |
248
+ | `blob` | `Blob` | Blob storage, maintenance, consent versions |
206
249
 
207
- ### RequestOptions
250
+ ---
251
+
252
+ ## Response Handling
253
+
254
+ All API methods return `Promise<ServiceResponse<T>>`, a discriminated union:
255
+
256
+ ```typescript
257
+ type ServiceResponse<T> = SuccessResponse<T> | ErrorResponse;
258
+
259
+ type SuccessResponse<T> = {
260
+ type: 'success';
261
+ model: T; // The typed response data
262
+ response: AxiosResponse;
263
+ };
264
+
265
+ type ErrorResponse = ClientError | ServerError;
266
+
267
+ type ServerError = {
268
+ type: 'server-error';
269
+ error: BzbsErrorResponse; // { requestId, error: { id, message, code, type } }
270
+ statusCode: number;
271
+ response: AxiosResponse;
272
+ };
273
+
274
+ type ClientError = {
275
+ type: 'client-error';
276
+ message: string;
277
+ details?: any;
278
+ };
279
+ ```
280
+
281
+ ### Usage Pattern
282
+
283
+ ```typescript
284
+ const result = await bzbsService.campaignApi.campaigns({ config: 'main' });
285
+
286
+ switch (result.type) {
287
+ case 'success':
288
+ console.log(result.model); // Campaign[]
289
+ break;
290
+
291
+ case 'server-error':
292
+ console.error(result.error.error?.message);
293
+ console.error('Status:', result.statusCode);
294
+ break;
295
+
296
+ case 'client-error':
297
+ console.error(result.message);
298
+ break;
299
+ }
300
+ ```
301
+
302
+ ### Response Normalization
303
+
304
+ The library automatically handles both uppercase and lowercase API response formats:
305
+ - `{ Success: true, Data: ... }` and `{ success: true, data: ... }` are both supported
306
+ - HTTP 204 responses return an empty object as the model
307
+
308
+ ### Custom Request Headers
309
+
310
+ Pass `RequestOptions` as the last argument to any API method:
208
311
 
209
312
  ```typescript
210
313
  type RequestOptions = {
@@ -213,13 +316,122 @@ type RequestOptions = {
213
316
  data?: any;
214
317
  baseUrl?: string;
215
318
  };
319
+
320
+ const response = await bzbsService.profileApi.profile(undefined, {
321
+ headers: { 'Accept-Language': 'th' },
322
+ });
216
323
  ```
217
324
 
218
325
  ---
219
326
 
327
+ ## Complete API Endpoint Table
328
+
329
+ > All endpoints are relative to `baseUrl` (constructor param), except `LineApi` (uses `baseLineUrl`) and `Blob` (uses `baseBlobUrl`).
330
+ >
331
+ > **Feature Tags:** `#auth` `#campaign` `#cart` `#category` `#consent` `#coupon` `#dashboard` `#history` `#line` `#notification` `#place` `#points` `#profile` `#registration` `#address` `#stamp` `#forum` `#setting` `#blob`
332
+
333
+ | Service | Method | HTTP | Endpoint | Key Parameters | Return Model | Tags |
334
+ |---|---|---|---|---|---|---|
335
+ | `authApi` | `deviceLogin` | POST | `auth/device_login` | appId, uuid, deviceLocale, os, platform, deviceToken, macAddress; opt: otp, refcode, contact_number | `LoginResponse` | `#auth` |
336
+ | `authApi` | `facebookLogin` | POST | `auth/login` | accessToken, appId, uuid, deviceLocale, os, platform, deviceToken, macAddress | `LoginResponse` | `#auth` |
337
+ | `authApi` | `googleLogin` | POST | `auth/google_login` | idToken, appId, uuid, deviceLocale, os, platform, deviceToken, macAddress | `LoginResponse` | `#auth` |
338
+ | `authApi` | `lineLogin` | POST | `auth/line_login` | idToken, lineAccessToken, authorizationCode, appId, uuid, os, platform, deviceToken, macAddress | `LoginResponse` | `#auth #line` |
339
+ | `authApi` | `appleLogin` | POST | `auth/apple_login` | idToken, refreshToken, appId, uuid, os, platform, deviceToken, macAddress | `LoginResponse` | `#auth` |
340
+ | `authApi` | `appleToken` | POST | `auth/apple_token` | authorizationCode, idToken, appId, os, platform, macAddress, clientVersion | `AppleToken` | `#auth` |
341
+ | `authApi` | `usernamePasswordLogin` | POST | `auth/bzbs_login` | username, password, appId, uuid, deviceLocale, os, platform, deviceToken, macAddress | `LoginResponse` | `#auth` |
342
+ | `authApi` | `connectLine` | POST | `auth/line_login` | idToken, lineAccessToken, authorizationCode, appId, uuid, os, platform, deviceToken, macAddress | `LoginResponse` | `#auth #line` |
343
+ | `authApi` | `connectFacebook` | POST | `auth/login` | accessToken, appId, uuid, os, platform, deviceToken, macAddress | `LoginResponse` | `#auth` |
344
+ | `authApi` | `connectGoogle` | POST | `auth/google_login` | idToken, appId, uuid, os, platform, deviceToken, macAddress | `LoginResponse` | `#auth` |
345
+ | `authApi` | `connectApple` | POST | `auth/apple_login` | idToken, refreshToken, appId, uuid, os, platform, deviceToken, macAddress | `LoginResponse` | `#auth` |
346
+ | `authApi` | `logout` | POST | `auth/logout` | uuid | `unknown` | `#auth` |
347
+ | `authApi` | `forgetPassword` | GET | `profile/{contact}/forget_password` | contact, type (`email`\|`contact_number`) | `ForgetPasswordResponse` | `#auth #profile` |
348
+ | `authApi` | `resetPassword` | POST | `profile/{contact}/forget_password` | contact, refCode, newPassword; opt: otp | `StatusResponse` | `#auth #profile` |
349
+ | `authApi` | `otp` | GET | `auth/otp` | uuid, appId, contactNumber; opt: channel | `OtpResponse` | `#auth` |
350
+ | `authApi` | `otpV2` | POST | `auth/otp` | appId, contactNumber; opt: channel | `OtpResponse` | `#auth` |
351
+ | `authApi` | `otpEmail` | GET | `auth/otp_email` | uuid, appId, email; opt: channel | `OtpResponse` | `#auth` |
352
+ | `authApi` | `confirmOtp` | POST | `auth/bzbs_authen` | otp, refCode, contactNumber | `ConfirmOtpResponse` | `#auth` |
353
+ | `authApi` | `validateOtp` | POST | `auth/validate_otp` | appId, otp, refCode, type; opt: contactNumber, email, use, channel | `ValidateOtpResponse` | `#auth` |
354
+ | `authApi` | `resume` | POST | `auth/device_resume` | uuid, deviceAppId, os, platform, macAddress, deviceNotificationEnabled, clientVersion, deviceToken | `ResumeResponse` | `#auth` |
355
+ | `authApi` | `updateDevice` | POST | `auth/update_device` | uuid, deviceAppId, os, platform, macAddress, deviceNotificationEnabled, clientVersion, deviceToken | `ResumeResponse` | `#auth` |
356
+ | `authApi` | `version` | GET | `auth/version` | clientVersion | `Version` | `#auth` |
357
+ | `authApi` | `versionRaw` | GET | `auth/version` | clientVersion | `any` | `#auth` |
358
+ | `badgeApi` | `badges` | GET | `profile/me/badges` | opt: badgeId | `Badge[]` | `#profile` |
359
+ | `campaignApi` | `campaigns` | GET | `campaign` | config; opt: cat, byConfig, skip, top, locale, keyword, startDate, sponsorId, maxPoints, minPoints, sortBy, center, hashTags, locationAgencyId | `Campaign[]` | `#campaign` |
360
+ | `campaignApi` | `favoriteCampaigns` | GET | `profile/me/favourite_campaign` | opt: skip, top, locale | `Campaign[]` | `#campaign #profile` |
361
+ | `campaignApi` | `campaignDetails` | GET | `campaign/{id}` | id; opt: deviceLocale | `CampaignDetail` | `#campaign` |
362
+ | `campaignApi` | `addToFavorite` | POST | `campaign/{id}/favourite` | id | `FavoriteResponse` | `#campaign` |
363
+ | `campaignApi` | `removeFromFavorite` | DELETE | `campaign/{id}/favourite` | id | `FavoriteResponse` | `#campaign` |
364
+ | `campaignApi` | `redeem` | POST | `campaign/{id}/redeem` | id; opt: addressKey, contactNumber, pointUnit, spPoints | `RedeemResponse` | `#campaign #points` |
365
+ | `campaignApi` | `bulkRedeem` | POST | `campaign/{id}/bulkredeem` | id, quantity; opt: addressKey, contactNumber, pointUnit, spPoints | `RedeemResponse` | `#campaign #points` |
366
+ | `cartApi` | `addCart` | POST | `cart/{id}/add` | id; opt: mode, qty, sideCampaignJson | `CartCountResponse` | `#cart #campaign` |
367
+ | `cartApi` | `cartCount` | GET | `cart/count` | opt: options | `CartCountResponse` | `#cart` |
368
+ | `cartApi` | `cartAccess` | POST | `setting` | errorUrl, successUrl, returnUrl, appId, appName | `CartAccessResponse` | `#cart #setting` |
369
+ | `categoryApi` | `categories` | GET | `campaigncat/menu` | config; opt: byConfig | `Category[]` | `#category #campaign` |
370
+ | `consentApi` | `consent` | GET | `consent` | opt: options | `Consent` | `#consent` |
371
+ | `consentApi` | `updateConsent` | POST | `consent` | opt: termsAndConditions, dataPrivacy, marketingOption, consentAge, email, sms, notification, line, analyticsBuzzebeesCookies, analyticsFirebaseCookies, analyticsGoogleCookies, analyticsMetaCookies, analyticsOtherCookies, functionalCookies, marketingCookies, necessaryCookies | `Consent` | `#consent` |
372
+ | `consentApi` | `unconsent` | POST | `consent/unconsent` | opt: options | `unknown` | `#consent` |
373
+ | `couponApi` | `processCodes` | POST | `coupon/process` | codes (string) | `CouponResponse` | `#coupon #campaign` |
374
+ | `dashboardApi` | `mainDashboard` | GET | `dashboard/main` | appName, locale | `Dashboard[]` | `#dashboard` |
375
+ | `dashboardApi` | `subDashboard` | GET | `dashboard/{dashboardName}` | dashboardName, locale | `Dashboard[]` | `#dashboard` |
376
+ | `historyApi` | `redeemHistories` | GET | `redeem` | byConfig, config, skip, top; opt: locale, startDate, endDate | `Purchase[]` | `#history #campaign` |
377
+ | `historyApi` | `use` | POST | `redeem/{redeemKey}/use` | redeemKey | `UseCampaignResponse` | `#history #campaign` |
378
+ | `lineApi` | `lineAuth` | POST | `oauth2/v2.1/token` _(baseLineUrl)_ | grantType, code, clientId, clientSecret, redirectUrl | `LineAuthResponse` | `#line #auth` |
379
+ | `notificationApi` | `notifications` | GET | `noti` | mode (`new`\|`all`), sortBy (`createdate_desc`\|`createdate_asc`); opt: top, skip | `Notification[]` | `#notification` |
380
+ | `notificationApi` | `read` | POST | `noti/read` | ids (comma-separated) | `unknown` | `#notification` |
381
+ | `placeApi` | `placeList` | GET | `place` | agencyId, center, distance, top; opt: provinceCode, category, mode, requiredCampaign, keyword, isFavourite | `Place[]` | `#place` |
382
+ | `placeApi` | `place` | GET | `place/{id}` | id, agencyId; opt: requiredCampaign, isFavourite | `Place` | `#place` |
383
+ | `placeApi` | `addToFavourite` | POST | `place/{id}/favourite` | id | `unknown` | `#place` |
384
+ | `placeApi` | `removeFromFavourite` | POST | `place/{id}/unfavourite` | id | `unknown` | `#place` |
385
+ | `pointLogApi` | `getPointLog` | GET | `log/points` | month; opt: type, lastRowKey, top | `PointLog[]` | `#points #profile` |
386
+ | `profileApi` | `profile` | GET | `profile/me` | opt: options | `ProfileResponse` | `#profile` |
387
+ | `profileApi` | `updateProfile` | POST | `profile/me` | opt: profileImage (File), firstName, lastName, contactNumber, email, notification, locale, title, gender, birthDate, address, subdistrictCode, districtCode, provinceCode, countryCode, zipCode, idCard, passport, maritalStatus, displayName, latitude, longitude, income, interests, region, occupation, remark, ... | `ProfileResponse` | `#profile` |
388
+ | `profileApi` | `changePassword` | POST | `profile/me/change_password` | current, change | `StatusResponse` | `#profile #auth` |
389
+ | `profileApi` | `updateShipping` | POST | `profile/me/shipping` | opt: shippingFirstName, shippingLastName, shippingProvinceCode, shippingDistrictCode, shippingSubDistrictCode, shippingZipCode, shippingAddress, shippingContactNumber, ... | `unknown` | `#profile #address` |
390
+ | `profileApi` | `changeContactNumber` | POST | `auth/change_authen` | contactNumber, otp, refCode; opt: idCard | `ConfirmOtpResponse` | `#profile #auth` |
391
+ | `profileApi` | `changeContactNumberV2` | POST | `profile/me/contact_number` | contactNumber, otp, refCode | `any` | `#profile #auth` |
392
+ | `profileApi` | `changeContactNumberV3` | POST | `profile/{userId}/contact_number` | userId, contactNumber, otp, refCode | `any` | `#profile #auth` |
393
+ | `profileApi` | `points` | GET | `profile/me/updated_points` | opt: options | `UpdatedPoints` | `#profile #points` |
394
+ | `profileApi` | `expiringPoints` | GET | `profile/me/allexpiring_points` | opt: options | `ExpiringPoints` | `#profile #points` |
395
+ | `profileApi` | `deactivate` | POST | `profile/me/deactivate` | opt: options | `unknown` | `#profile` |
396
+ | `registerApi` | `validateRegister` | POST | `auth/validate_register` | appId, username, email, contactNumber | `OtpResponse` | `#registration #auth` |
397
+ | `registerApi` | `register` | POST | `auth/register` | appId, uuid, macAddress, os, platform, clientVersion, deviceNotificationEnable, username, password, confirmPassword, firstName, lastName, contactNumber, otp, refCode, options; opt: address, gender, birthdate, email, refUserCode, termAndConditionVersion, dataPrivacyVersion, marketingOptionsVersion, consentAge, emailMarketing, smsMarketing, notificationMarketing, lineMarketing, phoneMarketing | `RegistrationResponse` | `#registration #auth` |
398
+ | `addressApi` | `zipCodes` | GET | `main/postcode` | opt: zipCode | `ZipCode[]` | `#address` |
399
+ | `addressApi` | `provinces` | GET | `main/province` | opt: options | `Province[]` | `#address` |
400
+ | `addressApi` | `districts` | GET | `main/district` | opt: provinceCode | `District[]` | `#address` |
401
+ | `addressApi` | `subDistricts` | GET | `main/subdistrict` | opt: provinceCode, districtCode | `SubDistrict[]` | `#address` |
402
+ | `addressApi` | `userAddresses` | GET | `profile/me/addresses` | opt: options | `Address[]` | `#address #profile` |
403
+ | `addressApi` | `updateAddress` | POST | `profile/me/address` | opt: name, addressName, firstName, lastName, address, zipcode, provinceCode, provinceName, districtCode, districtName, subDistrictCode, subDistrictName, contactNumber, countryCode, countryName, isDefault, rowKey | `Address` | `#address #profile` |
404
+ | `addressApi` | `deleteAddress` | DELETE | `profile/me/address` | opt: rowKey | `unknown` | `#address #profile` |
405
+ | `addressApi` | `userTaxAddresses` | GET | `profile/me/taxes` | opt: options | `Address[]` | `#address #profile` |
406
+ | `addressApi` | `updateTaxAddress` | POST | `profile/me/tax` | opt: rowKey, taxId, isDefault, personType, title, name, firstName, lastName, email, contactNumber, companyName, address, addressName, floor, building, moo, road, room, soi, village, districtCode, provinceName, subDistrictCode, zipcode | `Address` | `#address #profile` |
407
+ | `addressApi` | `deleteTaxAddress` | DELETE | `profile/me/tax` | opt: rowKey | `unknown` | `#address #profile` |
408
+ | `stampApi` | `createStamp` | POST | `stamp/create` | opt: imei, issuer, os, platform | `CreateStampResponse` | `#stamp` |
409
+ | `stampApi` | `stamps` | GET | `stamp` | options | `Stamp[]` | `#stamp` |
410
+ | `stampApi` | `stampProfile` | GET | `stamp/{id}/profile` | id, cardId | `StampProfileResponse` | `#stamp` |
411
+ | `forumApi` | `helpCode` | POST | `profile/me/help` | os, platform, clientVersion | `RequestHelpCode` | `#forum #profile` |
412
+ | `forumApi` | `requestHelpList` | GET | `buzz/{requestId}/list` | requestId | `ChatMessage[]` | `#forum` |
413
+ | `forumApi` | `requestDetail` | GET | `buzz/{buzzKey}` | buzzKey | `ChatMessage` | `#forum` |
414
+ | `forumApi` | `postRequestHelp` | POST | `buzz/{requestId}/buzz` | requestId, message; opt: image (File) | `ChatMessage` | `#forum` |
415
+ | `forumApi` | `comments` | GET | `buzz/{buzzKey}/comments` | buzzKey; opt: lastRowKey | `ChatMessage[]` | `#forum` |
416
+ | `forumApi` | `postComment` | POST | `buzz/{buzzKey}/comments` | buzzKey, message; opt: image (File) | `ChatMessage` | `#forum` |
417
+ | `forumApi` | `like` | POST | `buzz/{buzzKey}/like` | buzzKey | `LikeForumResponse` | `#forum` |
418
+ | `forumApi` | `unlike` | DELETE | `buzz/{buzzKey}/like` | buzzKey | `LikeForumResponse` | `#forum` |
419
+ | `settingApi` | `accessKey` | POST | `setting` | data (JSON: app_id, campaign_id, locale, return_url, version; opt: redeem_key) | `AccessTokenResponse` | `#setting #campaign` |
420
+ | `blob` | `consentVersion` | GET | `pdpaconsent/{appId}/version` _(baseBlobUrl)_ | appId | `ConsentVersion` | `#blob #consent` |
421
+ | `blob` | `maintenance` | GET | `config/maintenance/{appId}.json` _(baseBlobUrl)_ | appId | `Maintenance` | `#blob` |
422
+ | `blob` | `blob` | GET | `{path}` _(baseBlobUrl)_ | path | `unknown` | `#blob` |
423
+
424
+ ---
425
+
426
+ ## API Reference
427
+
428
+ Every method accepts an optional `requestOptions` as the last argument for custom headers or query params.
429
+
430
+ ---
431
+
220
432
  ### AuthenticateApi
221
433
 
222
- Accessed via `bzbsService.authApi`.
434
+ `bzbsService.authApi` — Authentication, login providers, OTP, session management.
223
435
 
224
436
  | Method | Description | Returns |
225
437
  |---|---|---|
@@ -230,26 +442,27 @@ Accessed via `bzbsService.authApi`.
230
442
  | `appleLogin(params)` | Login with Apple ID token + refresh token | `LoginResponse` |
231
443
  | `appleToken(params)` | Request Apple refresh token | `AppleToken` |
232
444
  | `usernamePasswordLogin(params)` | Login with username/password | `LoginResponse` |
233
- | `connectLine(params)` | Connect/login with LINE | `LoginResponse` |
234
- | `connectFacebook(params)` | Connect/login with Facebook | `LoginResponse` |
235
- | `connectGoogle(params)` | Connect/login with Google | `LoginResponse` |
236
- | `connectApple(params)` | Connect/login with Apple | `LoginResponse` |
445
+ | `connectLine(params)` | Connect/link LINE to existing account | `LoginResponse` |
446
+ | `connectFacebook(params)` | Connect/link Facebook to existing account | `LoginResponse` |
447
+ | `connectGoogle(params)` | Connect/link Google to existing account | `LoginResponse` |
448
+ | `connectApple(params)` | Connect/link Apple to existing account | `LoginResponse` |
237
449
  | `logout(params)` | Logout user | `unknown` |
238
450
  | `forgetPassword(params)` | Send forget password request | `ForgetPasswordResponse` |
239
- | `resetPassword(params)` | Reset user password | `StatusResponse` |
240
- | `otp(params)` | Request OTP via GET | `OtpResponse` |
241
- | `otpV2(params)` | Request OTP via POST | `OtpResponse` |
451
+ | `resetPassword(params)` | Reset user password with OTP | `StatusResponse` |
452
+ | `otp(params)` | Request OTP (GET) | `OtpResponse` |
453
+ | `otpV2(params)` | Request OTP (POST) | `OtpResponse` |
242
454
  | `otpEmail(params)` | Request OTP via email | `OtpResponse` |
243
- | `confirmOtp(params)` | Confirm OTP | `ConfirmOtpResponse` |
244
- | `validateOtp(params)` | Validate OTP | `ValidateOtpResponse` |
245
- | `resume(params)` | Refresh token (session resume) | `ResumeResponse` |
246
- | `updateDevice(params)` | Update device token for push notifications | `ResumeResponse` |
455
+ | `confirmOtp(params)` | Confirm OTP for authentication | `ConfirmOtpResponse` |
456
+ | `validateOtp(params)` | Validate OTP (generic) | `ValidateOtpResponse` |
457
+ | `resume(params)` | Resume/refresh session | `ResumeResponse` |
458
+ | `updateDevice(params)` | Update device push token | `ResumeResponse` |
247
459
  | `version(clientVersion)` | Get app version info | `Version` |
248
460
  | `versionRaw(clientVersion)` | Get raw version info | `any` |
249
461
 
250
- #### Login Example
462
+ #### Examples
251
463
 
252
464
  ```typescript
465
+ // Device login
253
466
  const result = await bzbsService.authApi.deviceLogin({
254
467
  appId: 'your-app-id',
255
468
  uuid: 'device-uuid',
@@ -262,25 +475,14 @@ const result = await bzbsService.authApi.deviceLogin({
262
475
  macAddress: 'device-mac-address',
263
476
  });
264
477
 
265
- if (result.type === 'success') {
266
- const { token, userId, jwt } = result.model;
267
- // Store token for subsequent requests
268
- }
269
- ```
270
-
271
- #### OTP Flow Example
272
-
273
- ```typescript
274
- // 1. Request OTP
275
- const otpResult = await bzbsService.authApi.otp({
276
- uuid: 'device-uuid',
478
+ // OTP flow
479
+ const otpResult = await bzbsService.authApi.otpV2({
277
480
  appId: 'your-app-id',
278
481
  contactNumber: '0812345678',
279
482
  });
280
483
 
281
- // 2. Validate OTP
282
484
  if (otpResult.type === 'success') {
283
- const validateResult = await bzbsService.authApi.validateOtp({
485
+ const validated = await bzbsService.authApi.validateOtp({
284
486
  appId: 'your-app-id',
285
487
  otp: '123456',
286
488
  refCode: otpResult.model.refcode,
@@ -294,7 +496,7 @@ if (otpResult.type === 'success') {
294
496
 
295
497
  ### CampaignApi
296
498
 
297
- Accessed via `bzbsService.campaignApi`.
499
+ `bzbsService.campaignApi` — Campaign listing, details, redemption, and favorites.
298
500
 
299
501
  | Method | Description | Returns |
300
502
  |---|---|---|
@@ -317,14 +519,12 @@ Accessed via `bzbsService.campaignApi`.
317
519
  | `top` | `number` | No | Number of items to retrieve |
318
520
  | `keyword` | `string` | No | Search keyword |
319
521
  | `sortBy` | `string` | No | Sort order |
320
- | `center` | `string` | No | Geo coordinates for location-based search |
522
+ | `center` | `string` | No | Geo coordinates (`lat,lng`) for location-based search |
321
523
  | `hashTags` | `string` | No | Filter by hashtags |
322
524
  | `sponsorId` | `string` | No | Sponsor filter |
323
525
  | `minPoints` | `string` | No | Minimum points filter |
324
526
  | `maxPoints` | `string` | No | Maximum points filter |
325
527
 
326
- #### Example
327
-
328
528
  ```typescript
329
529
  // List campaigns
330
530
  const campaigns = await bzbsService.campaignApi.campaigns({
@@ -333,7 +533,6 @@ const campaigns = await bzbsService.campaignApi.campaigns({
333
533
  skip: 0,
334
534
  top: 20,
335
535
  keyword: 'coffee',
336
- sortBy: 'popular',
337
536
  });
338
537
 
339
538
  // Redeem a campaign
@@ -347,7 +546,7 @@ const redeem = await bzbsService.campaignApi.redeem({
347
546
 
348
547
  ### ProfileApi
349
548
 
350
- Accessed via `bzbsService.profileApi`.
549
+ `bzbsService.profileApi` — User profile management, points, password, contact number.
351
550
 
352
551
  | Method | Description | Returns |
353
552
  |---|---|---|
@@ -360,9 +559,7 @@ Accessed via `bzbsService.profileApi`.
360
559
  | `changeContactNumberV3(params)` | Change contact number by user ID (v3) | `any` |
361
560
  | `points()` | Get user's current points | `UpdatedPoints` |
362
561
  | `expiringPoints()` | Get user's expiring points | `ExpiringPoints` |
363
- | `deactivate()` | Deactivate user profile | `unknown` |
364
-
365
- #### Profile Update Example
562
+ | `deactivate()` | Deactivate user account | `unknown` |
366
563
 
367
564
  ```typescript
368
565
  // Update profile with image
@@ -372,8 +569,8 @@ const result = await bzbsService.profileApi.updateProfile({
372
569
  email: 'john@example.com',
373
570
  contactNumber: '0812345678',
374
571
  gender: 'male',
375
- birthDate: 946684800, // Unix timestamp in seconds
376
- profileImage: fileObject, // File, { uri, name, type }, or object
572
+ birthDate: 946684800, // Unix timestamp in seconds
573
+ profileImage: fileObject,
377
574
  });
378
575
  ```
379
576
 
@@ -381,7 +578,7 @@ const result = await bzbsService.profileApi.updateProfile({
381
578
 
382
579
  ### CartApi
383
580
 
384
- Accessed via `bzbsService.cartApi`.
581
+ `bzbsService.cartApi` — Shopping cart management.
385
582
 
386
583
  | Method | Description | Returns |
387
584
  |---|---|---|
@@ -398,7 +595,7 @@ const count = await bzbsService.cartApi.cartCount();
398
595
 
399
596
  ### CategoryApi
400
597
 
401
- Accessed via `bzbsService.categoryApi`.
598
+ `bzbsService.categoryApi` — Campaign categories.
402
599
 
403
600
  | Method | Description | Returns |
404
601
  |---|---|---|
@@ -415,11 +612,11 @@ const categories = await bzbsService.categoryApi.categories({
415
612
 
416
613
  ### CouponApi
417
614
 
418
- Accessed via `bzbsService.couponApi`.
615
+ `bzbsService.couponApi` — Coupon code processing.
419
616
 
420
617
  | Method | Description | Returns |
421
618
  |---|---|---|
422
- | `processCodes(params)` | Process coupon codes | `CouponResponse` |
619
+ | `processCodes(params)` | Process one or more coupon codes | `CouponResponse` |
423
620
 
424
621
  ```typescript
425
622
  const result = await bzbsService.couponApi.processCodes({
@@ -431,7 +628,7 @@ const result = await bzbsService.couponApi.processCodes({
431
628
 
432
629
  ### NotificationApi
433
630
 
434
- Accessed via `bzbsService.notificationApi`.
631
+ `bzbsService.notificationApi` — Push notification management.
435
632
 
436
633
  | Method | Description | Returns |
437
634
  |---|---|---|
@@ -440,23 +637,20 @@ Accessed via `bzbsService.notificationApi`.
440
637
 
441
638
  ```typescript
442
639
  const notifs = await bzbsService.notificationApi.notifications({
443
- mode: 'new', // 'new' | 'all'
640
+ mode: 'new', // 'new' | 'all'
444
641
  sortBy: 'createdate_desc',
445
642
  top: 20,
446
643
  skip: 0,
447
644
  });
448
645
 
449
- // Mark as read
450
- await bzbsService.notificationApi.read({
451
- ids: 'rowKey1,rowKey2',
452
- });
646
+ await bzbsService.notificationApi.read({ ids: 'rowKey1,rowKey2' });
453
647
  ```
454
648
 
455
649
  ---
456
650
 
457
651
  ### HistoryApi
458
652
 
459
- Accessed via `bzbsService.historyApi`.
653
+ `bzbsService.historyApi` — Redemption history and voucher usage.
460
654
 
461
655
  | Method | Description | Returns |
462
656
  |---|---|---|
@@ -471,7 +665,6 @@ const history = await bzbsService.historyApi.redeemHistories({
471
665
  top: 20,
472
666
  });
473
667
 
474
- // Use a coupon
475
668
  await bzbsService.historyApi.use({ redeemKey: 'ABC123_1' });
476
669
  ```
477
670
 
@@ -479,17 +672,17 @@ await bzbsService.historyApi.use({ redeemKey: 'ABC123_1' });
479
672
 
480
673
  ### RegistrationApi
481
674
 
482
- Accessed via `bzbsService.registerApi`.
675
+ `bzbsService.registerApi` — New user registration with OTP verification.
483
676
 
484
677
  | Method | Description | Returns |
485
678
  |---|---|---|
486
- | `validateRegister(params)` | Validate registration (triggers OTP) | `OtpResponse` |
679
+ | `validateRegister(params)` | Validate registration and trigger OTP | `OtpResponse` |
487
680
  | `register(params)` | Register a new user | `RegistrationResponse` |
488
681
 
489
682
  **Validation Error Codes:**
490
- - `2078` - Duplicate contact number
491
- - `2089` - Duplicate email
492
- - `401` - Duplicate username
683
+ - `2078` Duplicate contact number
684
+ - `2089` Duplicate email
685
+ - `401` Duplicate username
493
686
 
494
687
  ```typescript
495
688
  // Step 1: Validate
@@ -525,7 +718,7 @@ const registration = await bzbsService.registerApi.register({
525
718
 
526
719
  ### AddressApi
527
720
 
528
- Accessed via `bzbsService.addressApi`.
721
+ `bzbsService.addressApi` — User addresses and Thailand geographical data.
529
722
 
530
723
  | Method | Description | Returns |
531
724
  |---|---|---|
@@ -534,22 +727,19 @@ Accessed via `bzbsService.addressApi`.
534
727
  | `districts(params)` | Get districts by province | `District[]` |
535
728
  | `subDistricts(params)` | Get sub-districts by province/district | `SubDistrict[]` |
536
729
  | `userAddresses()` | Get user's saved addresses | `Address[]` |
537
- | `updateAddress(params)` | Create or update an address | `Address` |
730
+ | `updateAddress(params)` | Create or update a delivery address | `Address` |
538
731
  | `deleteAddress(params)` | Delete an address | `unknown` |
539
732
  | `userTaxAddresses()` | Get user's tax addresses | `Address[]` |
540
733
  | `updateTaxAddress(params)` | Create or update a tax address | `Address` |
541
734
  | `deleteTaxAddress(params)` | Delete a tax address | `unknown` |
542
735
 
543
736
  ```typescript
544
- // Get provinces
545
737
  const provinces = await bzbsService.addressApi.provinces();
546
738
 
547
- // Get districts for a province
548
739
  const districts = await bzbsService.addressApi.districts({
549
740
  provinceCode: '10',
550
741
  });
551
742
 
552
- // Save an address
553
743
  await bzbsService.addressApi.updateAddress({
554
744
  firstName: 'John',
555
745
  lastName: 'Doe',
@@ -568,15 +758,14 @@ await bzbsService.addressApi.updateAddress({
568
758
 
569
759
  ### BadgeApi
570
760
 
571
- Accessed via `bzbsService.badgeApi`.
761
+ `bzbsService.badgeApi` — User badges and mission progress.
572
762
 
573
763
  | Method | Description | Returns |
574
764
  |---|---|---|
575
- | `badges(params)` | Get user badges | `Badge[]` |
765
+ | `badges(params)` | Get user badges (opt: filter by badgeId) | `Badge[]` |
576
766
 
577
767
  ```typescript
578
768
  const badges = await bzbsService.badgeApi.badges({});
579
- // Or filter by badge ID
580
769
  const badge = await bzbsService.badgeApi.badges({ badgeId: '123' });
581
770
  ```
582
771
 
@@ -584,7 +773,7 @@ const badge = await bzbsService.badgeApi.badges({ badgeId: '123' });
584
773
 
585
774
  ### ConsentApi
586
775
 
587
- Accessed via `bzbsService.consentApi`.
776
+ `bzbsService.consentApi` — PDPA and marketing consent management.
588
777
 
589
778
  | Method | Description | Returns |
590
779
  |---|---|---|
@@ -592,16 +781,18 @@ Accessed via `bzbsService.consentApi`.
592
781
  | `updateConsent(params)` | Update consent preferences | `Consent` |
593
782
  | `unconsent()` | Withdraw all consent | `unknown` |
594
783
 
595
- ```typescript
596
- // Get consent
597
- const consent = await bzbsService.consentApi.consent();
784
+ **`consentAge`** accepts one of:
785
+ - `'0'` — user has not consented to age verification
786
+ - `'1'` user has consented (age confirmed)
787
+ - The user's actual age as a string (e.g. `'25'`)
598
788
 
599
- // Update consent
789
+ ```typescript
600
790
  await bzbsService.consentApi.updateConsent({
601
791
  termsAndConditions: '1.0',
602
792
  dataPrivacy: '1.0',
603
793
  marketingOption: '1.0',
604
- email: '1', // '0' or '1'
794
+ consentAge: '25', // '0' | '1' | actual age string
795
+ email: '1', // '0' or '1'
605
796
  sms: '1',
606
797
  notification: '1',
607
798
  line: '0',
@@ -612,7 +803,7 @@ await bzbsService.consentApi.updateConsent({
612
803
 
613
804
  ### DashboardApi
614
805
 
615
- Accessed via `bzbsService.dashboardApi`.
806
+ `bzbsService.dashboardApi` — Dashboard content configuration.
616
807
 
617
808
  | Method | Description | Returns |
618
809
  |---|---|---|
@@ -625,7 +816,7 @@ const dashboard = await bzbsService.dashboardApi.mainDashboard({
625
816
  locale: 1054, // Thai locale
626
817
  });
627
818
 
628
- const subDashboard = await bzbsService.dashboardApi.subDashboard({
819
+ const sub = await bzbsService.dashboardApi.subDashboard({
629
820
  dashboardName: 'featured',
630
821
  locale: 1054,
631
822
  });
@@ -635,7 +826,7 @@ const subDashboard = await bzbsService.dashboardApi.subDashboard({
635
826
 
636
827
  ### LineApi
637
828
 
638
- Accessed via `bzbsService.lineApi` (only initialized if `baseLineUrl` is provided).
829
+ `bzbsService.lineApi` LINE OAuth token exchange. Only initialized when `baseLineUrl` is provided. Uses `baseLineUrl` as the base URL.
639
830
 
640
831
  | Method | Description | Returns |
641
832
  |---|---|---|
@@ -657,7 +848,7 @@ if (bzbsService.lineApi) {
657
848
 
658
849
  ### PlaceApi
659
850
 
660
- Accessed via `bzbsService.placeApi`.
851
+ `bzbsService.placeApi` — Location/store management with geo-search.
661
852
 
662
853
  | Method | Description | Returns |
663
854
  |---|---|---|
@@ -680,16 +871,16 @@ const places = await bzbsService.placeApi.placeList({
680
871
 
681
872
  ### PointLogApi
682
873
 
683
- Accessed via `bzbsService.pointLogApi`.
874
+ `bzbsService.pointLogApi` — Point transaction history.
684
875
 
685
876
  | Method | Description | Returns |
686
877
  |---|---|---|
687
- | `getPointLog(params)` | Get point transaction log | `PointLog[]` |
878
+ | `getPointLog(params)` | Get point transaction log by month | `PointLog[]` |
688
879
 
689
880
  ```typescript
690
881
  const logs = await bzbsService.pointLogApi.getPointLog({
691
882
  month: '2025-01',
692
- type: 'earn', // 'earn' | 'burn' | etc.
883
+ type: 'earn',
693
884
  top: 50,
694
885
  });
695
886
  ```
@@ -698,7 +889,7 @@ const logs = await bzbsService.pointLogApi.getPointLog({
698
889
 
699
890
  ### StampApi
700
891
 
701
- Accessed via `bzbsService.stampApi`.
892
+ `bzbsService.stampApi` — Stamp card system.
702
893
 
703
894
  | Method | Description | Returns |
704
895
  |---|---|---|
@@ -719,28 +910,26 @@ const profile = await bzbsService.stampApi.stampProfile({
719
910
 
720
911
  ### RequestHelpApi (Forum)
721
912
 
722
- Accessed via `bzbsService.forumApi`.
913
+ `bzbsService.forumApi` — Help/support forum (Buzz chat system).
723
914
 
724
915
  | Method | Description | Returns |
725
916
  |---|---|---|
726
917
  | `helpCode(params)` | Get help request code | `RequestHelpCode` |
727
- | `requestHelpList(params)` | List help request messages | `ChatMessage[]` |
728
- | `requestDetail(params)` | Get help request detail | `ChatMessage` |
729
- | `postRequestHelp(params)` | Post a help request (supports image) | `ChatMessage` |
918
+ | `requestHelpList(params)` | List messages in a help request | `ChatMessage[]` |
919
+ | `requestDetail(params)` | Get a specific message | `ChatMessage` |
920
+ | `postRequestHelp(params)` | Post a help message (supports image) | `ChatMessage` |
730
921
  | `comments(params)` | Get comments on a post | `ChatMessage[]` |
731
922
  | `postComment(params)` | Post a comment (supports image) | `ChatMessage` |
732
923
  | `like(params)` | Like a post | `LikeForumResponse` |
733
924
  | `unlike(params)` | Unlike a post | `LikeForumResponse` |
734
925
 
735
926
  ```typescript
736
- // Create a help request
737
927
  const helpCode = await bzbsService.forumApi.helpCode({
738
928
  os: 'ios 17.0',
739
929
  platform: 'iPhone',
740
930
  clientVersion: 'ios_myapp1.0.0',
741
931
  });
742
932
 
743
- // Post a message
744
933
  await bzbsService.forumApi.postRequestHelp({
745
934
  requestId: 'req-123',
746
935
  message: 'I need help with my order',
@@ -752,7 +941,7 @@ await bzbsService.forumApi.postRequestHelp({
752
941
 
753
942
  ### SettingApi
754
943
 
755
- Accessed via `bzbsService.settingApi`.
944
+ `bzbsService.settingApi` — Web landing page access tokens.
756
945
 
757
946
  | Method | Description | Returns |
758
947
  |---|---|---|
@@ -774,12 +963,12 @@ const accessKey = await bzbsService.settingApi.accessKey({
774
963
 
775
964
  ### Blob
776
965
 
777
- Accessed via `bzbsService.blob`.
966
+ `bzbsService.blob` Blob storage retrieval. Uses `baseBlobUrl` as base URL.
778
967
 
779
968
  | Method | Description | Returns |
780
969
  |---|---|---|
781
- | `consentVersion(appId)` | Get PDPA consent version | `ConsentVersion` |
782
- | `maintenance(appId)` | Get maintenance configuration | `Maintenance` |
970
+ | `consentVersion(appId)` | Get PDPA consent document version | `ConsentVersion` |
971
+ | `maintenance(appId)` | Get app maintenance configuration | `Maintenance` |
783
972
  | `blob(path)` | Fetch any blob storage path | `unknown` |
784
973
 
785
974
  ```typescript
@@ -789,183 +978,245 @@ const maintenance = await bzbsService.blob.maintenance('your-app-id');
789
978
 
790
979
  ---
791
980
 
792
- ## Response Handling
981
+ ## Models Reference
793
982
 
794
- All API methods return `ServiceResponse<T>`, a discriminated union type:
983
+ All TypeScript interfaces are exported from `@bzbs/react-api-client`:
795
984
 
796
985
  ```typescript
797
- type ServiceResponse<T> = SuccessResponse<T> | ErrorResponse;
986
+ import {
987
+ Campaign,
988
+ CampaignDetail,
989
+ ProfileResponse,
990
+ LoginResponse,
991
+ Purchase,
992
+ // ... etc
993
+ } from '@bzbs/react-api-client';
798
994
  ```
799
995
 
800
- ### Success Response
996
+ ### Authentication Models
801
997
 
802
- ```typescript
803
- type SuccessResponse<T> = {
804
- type: 'success';
805
- model: T; // The typed response data
806
- response: AxiosResponse; // Raw Axios response
807
- };
808
- ```
998
+ | Model | Key Fields |
999
+ |---|---|
1000
+ | `LoginResponse` | Token, UserId, UserCode, Username, Points, Consent fields, ProfileImage, Locale |
1001
+ | `ResumeResponse` | Same structure as LoginResponse |
1002
+ | `AppleToken` | refresh_token, id_token |
1003
+ | `LineAuthResponse` | access_token, token_type, refresh_token, expires_in, scope, id_token |
1004
+ | `OtpResponse` | refcode, channel, expiredate, expireinseconds |
1005
+ | `ConfirmOtpResponse` | status, token |
1006
+ | `ValidateOtpResponse` | validatecode |
1007
+ | `ForgetPasswordResponse` | status, refcode, expiredate, expireinseconds |
1008
+ | `RegistrationResponse` | Registration result with Buzzebees user account |
1009
+ | `Version` | allow_use, has_new_version, welcome_page_times |
1010
+
1011
+ ### Campaign Models
1012
+
1013
+ | Model | Key Fields |
1014
+ |---|---|
1015
+ | `Campaign` | ID, Name, Points, StartDate, EndDate, CategoryId, ImageUrl, Quantity, IsLike |
1016
+ | `CampaignDetail` | All Campaign fields + images (Picture[]), SubCampaigns, conditions, terms |
1017
+ | `Picture` | ID, Type, Sequence, ImageUrl |
1018
+ | `SubCampaign` | ID, Name, styles (SubCampaignStyle[]), quantity, points |
1019
+ | `RedeemResponse` | RedeemKey, PrivilegeMessageEN, PrivilegeMessageTH, UpdatedPoints, ExpiryDate |
1020
+ | `UseCampaignResponse` | PrivilegeMessageEN, PrivilegeMessageTH, redeemKey, points |
1021
+ | `FavoriteResponse` | totalLike, isLike |
1022
+ | `CouponResponse` | success/error results per code |
1023
+ | `Purchase` | RedeemKey, CampaignName, ExpiryDate, Status, Points, barcode, images |
1024
+
1025
+ ### Cart Models
1026
+
1027
+ | Model | Key Fields |
1028
+ |---|---|
1029
+ | `CartCountResponse` | cart_count |
1030
+ | `CartAccessResponse` | success, access key |
809
1031
 
810
- ### Error Response
1032
+ ### Profile & Points Models
811
1033
 
812
- ```typescript
813
- type ErrorResponse = ClientError | ServerError;
1034
+ | Model | Key Fields |
1035
+ |---|---|
1036
+ | `ProfileResponse` | UserId, Name, FirstName, LastName, Email, Contact_Number, address fields, TermAndCondition, DataPrivacy, MarketingOption, ConsentAge (0=not consented, 1=consented, or actual age), EmailMarketing, SMSMarketing, NotificationMarketing, LineMarketing, Token, Jwt, updated_points, Info1–Info10 (60+ fields) |
1037
+ | `UpdatedPoints` | points, time |
1038
+ | `ExpiringPoints` | expiringPoints, expiryDate |
1039
+ | `Badge` | badgeId, name, description, imageUrl, missions (Mission[]) |
1040
+ | `Mission` | missionId, name, current, target, isCompleted |
1041
+ | `PointLog` | UserId, Info, Detail, Points, Type, Timestamp |
814
1042
 
815
- type ServerError = {
816
- type: 'server-error';
817
- error: BzbsErrorResponse; // { requestId, error: { id, message, code, type } }
818
- statusCode: number;
819
- response: AxiosResponse;
820
- };
1043
+ ### Address & Location Models
821
1044
 
822
- type ClientError = {
823
- type: 'client-error';
824
- message: string;
825
- details?: any;
826
- };
827
- ```
1045
+ | Model | Key Fields |
1046
+ |---|---|
1047
+ | `Address` | rowKey, name, firstName, lastName, address, zipcode, province/district/subdistrict codes and names, contactNumber, isDefault, taxId |
1048
+ | `Province` | province_code, province_name_th, province_name_en |
1049
+ | `District` | district_code, district_name_th, district_name_en, province_code |
1050
+ | `SubDistrict` | subdistrict_code, subdistrict_name_th, zip_code |
1051
+ | `ZipCode` | Hierarchical address with zone info |
1052
+ | `Place` | id, name, latitude, longitude, address, workingHours, services, isFavorite |
828
1053
 
829
- ### Usage Pattern
1054
+ ### Consent & Settings Models
830
1055
 
831
- ```typescript
832
- const result = await bzbsService.campaignApi.campaigns({
833
- config: 'campaign_list',
834
- });
1056
+ | Model | Key Fields |
1057
+ |---|---|
1058
+ | `Consent` | TermAndCondition, DataPrivacy, MarketingOption, SMSMarketing, NotificationMarketing, LineMarketing, cookie consent flags |
1059
+ | `ConsentVersion` | version values per consent type |
1060
+ | `Version` | allow_use, has_new_version, welcome_page_times |
1061
+ | `Maintenance` | isUnderMaintenance, message, startDate, endDate |
835
1062
 
836
- switch (result.type) {
837
- case 'success':
838
- // result.model is Campaign[]
839
- console.log(result.model);
840
- break;
1063
+ ### Stamp & Dashboard Models
841
1064
 
842
- case 'server-error':
843
- // Server returned an error
844
- console.error(result.error.error?.message);
845
- console.error('Status:', result.statusCode);
846
- break;
1065
+ | Model | Key Fields |
1066
+ |---|---|
1067
+ | `Stamp` | stampId, cardId, count, maxCount, rewards, expiry |
1068
+ | `StampProfileResponse` | stamp card profile with history |
1069
+ | `CreateStampResponse` | result of stamp creation |
1070
+ | `Dashboard` | id, name, menuItems, images, startDate, endDate |
1071
+ | `Category` | id, name (TH/EN), subcategories, imageUrl |
847
1072
 
848
- case 'client-error':
849
- // Network error or client-side issue
850
- console.error(result.message);
851
- break;
852
- }
853
- ```
1073
+ ### Notification & Forum Models
854
1074
 
855
- ### Response Normalization
1075
+ | Model | Key Fields |
1076
+ |---|---|
1077
+ | `Notification` | notiId, title, message, objectType, imageUrl, expireDate, isRead |
1078
+ | `ChatMessage` | buzzKey, message, images, sender info, likes, comments count, createdDate (65+ fields) |
1079
+ | `LikeForumResponse` | likeCount, isLiked |
1080
+ | `RequestHelpCode` | helpCode |
856
1081
 
857
- The library automatically handles both uppercase and lowercase API response formats:
858
- - `{ Success: true, Data: ... }` and `{ success: true, data: ... }` are both supported
859
- - HTTP 204 responses return an empty object as the model
860
- - Non-standard responses (without success flags) are treated as raw data
1082
+ ### Error & Response Models
1083
+
1084
+ | Model | Key Fields |
1085
+ |---|---|
1086
+ | `BzbsErrorResponse` | requestId, error: { id, message, code, type } |
1087
+ | `StatusResponse` | status |
1088
+ | `AccessTokenResponse` | token key |
1089
+
1090
+ **Common Error Codes:**
1091
+
1092
+ | Code | Description |
1093
+ |---|---|
1094
+ | `401` | Unauthorized / Duplicate username |
1095
+ | `2078` | Duplicate contact number |
1096
+ | `2089` | Duplicate email |
861
1097
 
862
1098
  ---
863
1099
 
864
- ## Error Handling
1100
+ ## BaseService Utilities
865
1101
 
866
- ### BzbsErrorResponse
1102
+ ### createFormData
1103
+
1104
+ Static method for building `multipart/form-data` payloads. Skips `undefined`, `null`, and empty string values — preserves `0` and `false`.
867
1105
 
868
1106
  ```typescript
869
- interface BzbsErrorResponse {
870
- requestId?: string;
871
- error?: {
872
- id?: number;
873
- message?: string;
874
- code?: number;
875
- type?: string;
876
- };
877
- }
1107
+ const formData = BaseService.createFormData(
1108
+ { name: 'John', age: 0, active: false },
1109
+ 'profileImage', // optional file field key
1110
+ imageFile // optional File object
1111
+ );
878
1112
  ```
879
1113
 
880
- ### Common Error Codes
1114
+ ### setBaseUrl / setLineUrl / setBlobUrl
881
1115
 
882
- | Code | Description |
883
- |---|---|
884
- | `401` | Unauthorized / Duplicate username |
885
- | `2078` | Duplicate contact number |
886
- | `2089` | Duplicate email |
1116
+ Update base URLs at runtime (propagates to all sub-services):
1117
+
1118
+ ```typescript
1119
+ bzbsService.setBaseUrl('https://new-api.buzzebees.com/api/');
1120
+ bzbsService.setLineUrl('https://api.line.me/');
1121
+ bzbsService.setBlobUrl('https://new-blob.buzzebees.com/');
1122
+ ```
887
1123
 
888
1124
  ---
889
1125
 
890
- ## Models
1126
+ ## Testing Guide
1127
+
1128
+ Tests are in the `tests/` directory, mirroring `src/` structure.
891
1129
 
892
- All TypeScript interfaces are exported from `@bzbs/react-api-client` and can be imported directly:
1130
+ ### Test Utilities (`tests/helpers/test-utils.ts`)
893
1131
 
894
1132
  ```typescript
895
1133
  import {
896
- Campaign,
897
- CampaignDetail,
898
- ProfileResponse,
899
- LoginResponse,
900
- Purchase,
901
- // ... etc
902
- } from '@bzbs/react-api-client';
1134
+ createMockClient,
1135
+ createSuccessResponse,
1136
+ createRawSuccessResponse,
1137
+ createServerErrorResponse,
1138
+ createAxiosError,
1139
+ createDeviceParams,
1140
+ } from '../helpers/test-utils';
1141
+
1142
+ // Create mocked Axios instance
1143
+ const { client, mockRequest } = createMockClient();
1144
+
1145
+ // Mock a wrapped success response: { Success: true, Data: {...} }
1146
+ mockRequest.mockResolvedValue(createSuccessResponse({ id: '123' }));
1147
+
1148
+ // Mock a raw success (no wrapper)
1149
+ mockRequest.mockResolvedValue(createRawSuccessResponse({ id: '123' }));
1150
+
1151
+ // Mock a server error response
1152
+ mockRequest.mockResolvedValue(createServerErrorResponse());
1153
+
1154
+ // Mock a network error (client error)
1155
+ mockRequest.mockRejectedValue(createAxiosError());
1156
+
1157
+ // Standard device parameters (appId, uuid, os, platform, clientVersion, macAddress, deviceToken, ...)
1158
+ const deviceParams = createDeviceParams();
903
1159
  ```
904
1160
 
905
- ### Key Models
1161
+ ### Test Pattern
906
1162
 
907
- | Model | Description |
908
- |---|---|
909
- | `LoginResponse` | Token, userId, jwt, consent status, points |
910
- | `Campaign` | Campaign list item (ID, name, points, dates, images) |
911
- | `CampaignDetail` | Full campaign details with pictures, subcampaigns, conditions |
912
- | `ProfileResponse` | User profile with personal info, address, shipping, marketing preferences |
913
- | `Purchase` | Redeem history item with usage status, voucher info, delivery status |
914
- | `Address` | User address with full Thailand address structure, tax fields |
915
- | `Notification` | Push notification with object type, category, read status |
916
- | `Place` | Store/place with location, services, working hours |
917
- | `Dashboard` | Dashboard configuration item with images and metadata |
918
- | `Consent` | User consent status for terms, privacy, marketing channels, cookies |
919
- | `ConsentVersion` | PDPA consent version from blob storage |
920
- | `Maintenance` | App maintenance configuration |
921
- | `Badge` | User badge information |
922
- | `Category` | Campaign category |
923
- | `PointLog` | Point transaction record |
924
- | `Stamp` | Stamp card information |
925
- | `StampProfileResponse` | Stamp card profile details |
926
- | `OtpResponse` | OTP request response with reference code |
927
- | `ConfirmOtpResponse` | OTP confirmation result |
928
- | `ValidateOtpResponse` | OTP validation result |
929
- | `RedeemResponse` | Campaign redemption result |
930
- | `FavoriteResponse` | Favorite toggle response |
931
- | `UseCampaignResponse` | Coupon usage result |
932
- | `CartCountResponse` | Cart item count |
933
- | `CartAccessResponse` | Cart web landing access token |
934
- | `AccessTokenResponse` | Web landing access token |
935
- | `LineAuthResponse` | LINE OAuth token response |
936
- | `AppleToken` | Apple refresh token response |
937
- | `ResumeResponse` | Session resume/token refresh response |
938
- | `Version` | App version info |
939
- | `RegistrationResponse` | Registration result |
940
- | `ForgetPasswordResponse` | Password reset request result |
941
- | `StatusResponse` | Generic status response |
942
- | `RequestHelpCode` | Help request code |
943
- | `ChatMessage` | Forum/help chat message |
944
- | `LikeForumResponse` | Forum like/unlike result |
945
- | `Province` | Thailand province |
946
- | `District` | Thailand district |
947
- | `SubDistrict` | Thailand sub-district |
948
- | `ZipCode` | Zip code information |
949
- | `UpdatedPoints` | User points with timestamp |
950
- | `ExpiringPoints` | User expiring points info |
951
- | `PointUnit` | Point service unit configuration |
1163
+ ```typescript
1164
+ import { AuthenticateApi } from '../../src/api/auth/auth-api';
1165
+ import { createMockClient, createSuccessResponse, createDeviceParams } from '../helpers/test-utils';
952
1166
 
953
- ---
1167
+ describe('AuthenticateApi', () => {
1168
+ let api: AuthenticateApi;
1169
+ let mockRequest: jest.Mock;
954
1170
 
955
- ## Development
1171
+ beforeEach(() => {
1172
+ const { client, mockRequest: mock } = createMockClient();
1173
+ mockRequest = mock;
1174
+ api = new AuthenticateApi(client, 'https://api.buzzebees.com/');
1175
+ });
1176
+
1177
+ it('deviceLogin returns success', async () => {
1178
+ const data = { Token: 'abc', UserId: '1' };
1179
+ mockRequest.mockResolvedValue(createSuccessResponse(data));
1180
+
1181
+ const result = await api.deviceLogin(createDeviceParams());
1182
+
1183
+ expect(result.type).toBe('success');
1184
+ if (result.type === 'success') {
1185
+ expect(result.model.Token).toBe('abc');
1186
+ }
1187
+ expect(mockRequest).toHaveBeenCalledWith(
1188
+ expect.objectContaining({ url: 'auth/device_login', method: 'post' })
1189
+ );
1190
+ });
1191
+
1192
+ it('deviceLogin handles server error', async () => {
1193
+ mockRequest.mockResolvedValue(createServerErrorResponse());
956
1194
 
957
- ### Prerequisites
1195
+ const result = await api.deviceLogin(createDeviceParams());
958
1196
 
959
- - Node.js
960
- - npm
1197
+ expect(result.type).toBe('server-error');
1198
+ });
1199
+ });
1200
+ ```
961
1201
 
962
- ### Commands
1202
+ ### Run Tests
1203
+
1204
+ ```bash
1205
+ npm test # Watch mode with coverage
1206
+ npx jest --coverage # Single run with coverage
1207
+ npx jest tests/api/auth # Run specific folder
1208
+ npx jest --testNamePattern otp # Run tests matching pattern
1209
+ ```
1210
+
1211
+ ---
1212
+
1213
+ ## Development Commands
963
1214
 
964
1215
  ```bash
965
1216
  # Install dependencies
966
1217
  npm install
967
1218
 
968
- # Build the library (CJS + ESM + type declarations)
1219
+ # Build (CJS + ESM + type declarations → dist/)
969
1220
  npm run build
970
1221
 
971
1222
  # Run tests in watch mode with coverage
@@ -979,20 +1230,18 @@ npm run lint:fix
979
1230
 
980
1231
  # Format code with Prettier
981
1232
  npm run format
982
- ```
983
-
984
- ### Publishing
985
1233
 
986
- ```bash
987
- npm run patch # Build, bump patch version, publish
988
- npm run minor # Build, bump minor version, publish
989
- npm run major # Build, bump major version, publish
1234
+ # Publish (build + version bump + npm publish)
1235
+ npm run patch # 1.4.14 1.4.15
1236
+ npm run minor # 1.4.14 1.5.0
1237
+ npm run major # 1.4.14 2.0.0
990
1238
  ```
991
1239
 
992
- ### Adding a New API
1240
+ ---
1241
+
1242
+ ## Adding a New API
993
1243
 
994
- 1. Create a new directory under `src/api/` (e.g., `src/api/my-feature/`)
995
- 2. Create an API class that extends `BaseService`:
1244
+ 1. Create `src/api/my-feature/my-feature-api.ts`:
996
1245
 
997
1246
  ```typescript
998
1247
  import { AxiosInstance } from 'axios';
@@ -1005,31 +1254,35 @@ export class MyFeatureApi extends BaseService {
1005
1254
  }
1006
1255
 
1007
1256
  public async getItems(
1008
- params: { id: string; options?: { [key: string]: unknown } },
1257
+ params: { id: string },
1009
1258
  requestOptions?: RequestOptions
1010
1259
  ): Promise<ServiceResponse<MyModel[]>> {
1011
- return await this.get<MyModel[]>(
1012
- `my-feature/${params.id}`,
1013
- { ...params.options },
1014
- requestOptions
1015
- );
1260
+ return await this.get<MyModel[]>(`my-feature/${params.id}`, {}, requestOptions);
1016
1261
  }
1017
1262
  }
1018
1263
  ```
1019
1264
 
1020
- 3. Create model interfaces in `src/models/`
1021
- 4. Export from `src/models/index.ts` and `src/api/index.ts`
1022
- 5. Add the API to `BzbsService` class and its `setBaseUrl()` method
1265
+ 2. Create model interface in `src/models/my-model.ts` and export from `src/models/index.ts`
1266
+ 3. Export from `src/api/index.ts`
1267
+ 4. Add to `BzbsService` constructor and `setBaseUrl()` method in `src/api/bzbs-service.ts`
1023
1268
 
1024
1269
  ---
1025
1270
 
1026
1271
  ## Build Output
1027
1272
 
1028
- The build process (tsup) generates:
1029
-
1030
1273
  | File | Format | Description |
1031
1274
  |---|---|---|
1032
1275
  | `dist/index.js` | CommonJS | For `require()` usage |
1033
1276
  | `dist/index.mjs` | ES Module | For `import` usage |
1034
1277
  | `dist/index.d.ts` | TypeScript | Type declarations |
1035
1278
  | `dist/*.map` | Source Maps | For debugging |
1279
+
1280
+ ### Package Exports
1281
+
1282
+ ```json
1283
+ {
1284
+ "main": "./dist/index.js",
1285
+ "module": "./dist/index.mjs",
1286
+ "types": "./dist/index.d.ts"
1287
+ }
1288
+ ```