@warriorteam/redai-zalo-sdk 1.2.0 → 1.4.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.
Files changed (77) hide show
  1. package/README.md +563 -550
  2. package/dist/index.d.ts +0 -2
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +1 -5
  5. package/dist/index.js.map +1 -1
  6. package/dist/services/article.service.d.ts +1 -1
  7. package/dist/services/article.service.d.ts.map +1 -1
  8. package/dist/services/article.service.js +24 -16
  9. package/dist/services/article.service.js.map +1 -1
  10. package/dist/services/auth.service.d.ts +1 -0
  11. package/dist/services/auth.service.d.ts.map +1 -1
  12. package/dist/services/auth.service.js +23 -9
  13. package/dist/services/auth.service.js.map +1 -1
  14. package/dist/services/consultation.service.d.ts +63 -16
  15. package/dist/services/consultation.service.d.ts.map +1 -1
  16. package/dist/services/consultation.service.js +264 -49
  17. package/dist/services/consultation.service.js.map +1 -1
  18. package/dist/services/general-message.service.d.ts +2 -25
  19. package/dist/services/general-message.service.d.ts.map +1 -1
  20. package/dist/services/general-message.service.js +11 -112
  21. package/dist/services/general-message.service.js.map +1 -1
  22. package/dist/services/group-management.service.d.ts +1 -1
  23. package/dist/services/group-management.service.d.ts.map +1 -1
  24. package/dist/services/group-management.service.js +59 -27
  25. package/dist/services/group-management.service.js.map +1 -1
  26. package/dist/services/group-message.service.d.ts +1 -1
  27. package/dist/services/group-message.service.d.ts.map +1 -1
  28. package/dist/services/group-message.service.js +49 -23
  29. package/dist/services/group-message.service.js.map +1 -1
  30. package/dist/services/message-management.service.d.ts +21 -2
  31. package/dist/services/message-management.service.d.ts.map +1 -1
  32. package/dist/services/message-management.service.js +83 -7
  33. package/dist/services/message-management.service.js.map +1 -1
  34. package/dist/services/oa.service.d.ts +1 -0
  35. package/dist/services/oa.service.d.ts.map +1 -1
  36. package/dist/services/oa.service.js +23 -5
  37. package/dist/services/oa.service.js.map +1 -1
  38. package/dist/services/user.service.d.ts +108 -18
  39. package/dist/services/user.service.d.ts.map +1 -1
  40. package/dist/services/user.service.js +260 -59
  41. package/dist/services/user.service.js.map +1 -1
  42. package/dist/services/video-upload.service.d.ts +1 -1
  43. package/dist/services/video-upload.service.d.ts.map +1 -1
  44. package/dist/services/video-upload.service.js +11 -8
  45. package/dist/services/video-upload.service.js.map +1 -1
  46. package/dist/services/zns.service.d.ts +88 -21
  47. package/dist/services/zns.service.d.ts.map +1 -1
  48. package/dist/services/zns.service.js +157 -42
  49. package/dist/services/zns.service.js.map +1 -1
  50. package/dist/types/group.d.ts +5 -5
  51. package/dist/types/group.d.ts.map +1 -1
  52. package/dist/types/user.d.ts +155 -12
  53. package/dist/types/user.d.ts.map +1 -1
  54. package/dist/types/user.js.map +1 -1
  55. package/dist/types/zns.d.ts +67 -33
  56. package/dist/types/zns.d.ts.map +1 -1
  57. package/dist/zalo-sdk.d.ts +3 -11
  58. package/dist/zalo-sdk.d.ts.map +1 -1
  59. package/dist/zalo-sdk.js +0 -10
  60. package/dist/zalo-sdk.js.map +1 -1
  61. package/docs/API_REFERENCE.md +680 -0
  62. package/docs/AUTHENTICATION.md +709 -0
  63. package/docs/CONSULTATION_SERVICE.md +512 -330
  64. package/docs/GROUP_MANAGEMENT.md +2 -2
  65. package/docs/MESSAGE_SERVICES.md +1224 -0
  66. package/docs/TAG_MANAGEMENT.md +1462 -0
  67. package/docs/USER_MANAGEMENT.md +481 -0
  68. package/docs/ZNS_SERVICE.md +985 -0
  69. package/package.json +1 -1
  70. package/dist/services/tag.service.d.ts +0 -144
  71. package/dist/services/tag.service.d.ts.map +0 -1
  72. package/dist/services/tag.service.js +0 -184
  73. package/dist/services/tag.service.js.map +0 -1
  74. package/dist/services/user-management.service.d.ts +0 -117
  75. package/dist/services/user-management.service.d.ts.map +0 -1
  76. package/dist/services/user-management.service.js +0 -239
  77. package/dist/services/user-management.service.js.map +0 -1
@@ -0,0 +1,985 @@
1
+ # RedAI Zalo SDK - ZNS Service Guide
2
+
3
+ ## Tổng quan
4
+
5
+ **Zalo Notification Service (ZNS)** là dịch vụ gửi tin nhắn thông báo chính thức từ doanh nghiệp đến khách hàng thông qua các template được duyệt trước. ZNS phù hợp cho:
6
+
7
+ - 📋 **Thông báo đơn hàng** - Xác nhận, cập nhật trạng thái đơn hàng
8
+ - 🎫 **Thông báo lịch hẹn** - Nhắc nhở cuộc hẹn, booking
9
+ - 💰 **Thông báo thanh toán** - Hóa đơn, biên lai, giao dịch
10
+ - 📢 **Thông báo khuyến mại** - Ưu đãi, chương trình đặc biệt
11
+ - ⚠️ **Cảnh báo bảo mật** - OTP, xác thực 2FA
12
+ - 📊 **Báo cáo định kỳ** - Báo cáo tài chính, thống kê
13
+
14
+ ---
15
+
16
+ ## Khởi tạo ZNS Service
17
+
18
+ ```typescript
19
+ import { ZaloSDK } from "@warriorteam/redai-zalo-sdk";
20
+
21
+ const zalo = new ZaloSDK({
22
+ appId: "your-oa-app-id",
23
+ appSecret: "your-oa-app-secret",
24
+ debug: true
25
+ });
26
+
27
+ // Access ZNS service
28
+ const znsService = zalo.zns;
29
+ ```
30
+
31
+ ---
32
+
33
+ ## Gửi Tin nhắn ZNS
34
+
35
+ ### 1. Gửi tin nhắn cơ bản
36
+
37
+ ```typescript
38
+ // Gửi thông báo đơn hàng
39
+ const znsResponse = await zalo.zns.sendMessage(accessToken, {
40
+ phone: "0123456789", // Số điện thoại người nhận
41
+ template_id: "your-template-id", // ID template đã được duyệt
42
+ template_data: {
43
+ customer_name: "Nguyễn Văn An",
44
+ order_id: "DH001234",
45
+ total_amount: "500,000",
46
+ delivery_date: "15/12/2024"
47
+ },
48
+ tracking_id: "tracking_001" // ID để track (tùy chọn)
49
+ });
50
+
51
+ console.log("Message sent:", znsResponse.msg_id);
52
+ ```
53
+
54
+ ### 2. Gửi với chế độ development
55
+
56
+ ```typescript
57
+ // Test trong môi trường development
58
+ const testResponse = await zalo.zns.sendMessage(accessToken, {
59
+ phone: "0123456789",
60
+ template_id: "template_id",
61
+ template_data: {
62
+ customer_name: "Test User",
63
+ order_id: "TEST001"
64
+ },
65
+ mode: "development" // Không trừ quota, chỉ test
66
+ });
67
+ ```
68
+
69
+ ### 3. Gửi tin nhắn có mã hóa (Hash Phone)
70
+
71
+ ```typescript
72
+ // Hash phone number để bảo mật
73
+ import crypto from 'crypto';
74
+
75
+ function hashPhone(phone: string, secretKey: string): string {
76
+ return crypto
77
+ .createHmac('sha256', secretKey)
78
+ .update(phone)
79
+ .digest('hex');
80
+ }
81
+
82
+ const hashedPhone = hashPhone("0123456789", "your-secret-key");
83
+
84
+ const response = await zalo.zns.sendMessage(accessToken, {
85
+ phone: hashedPhone,
86
+ template_id: "template_id",
87
+ template_data: { name: "User" },
88
+ is_hash_phone: true // Báo cho Zalo biết phone đã được hash
89
+ });
90
+ ```
91
+
92
+ ---
93
+
94
+ ## Quản lý Templates
95
+
96
+ ### 1. Lấy danh sách templates
97
+
98
+ ```typescript
99
+ const templates = await zalo.zns.getTemplateList(accessToken, {
100
+ offset: 0,
101
+ limit: 20,
102
+ status: 1 // 1: active, 2: pending, 3: rejected
103
+ });
104
+
105
+ console.log("Total templates:", templates.total);
106
+ templates.data.forEach(template => {
107
+ console.log(`Template ID: ${template.templateId}`);
108
+ console.log(`Name: ${template.templateName}`);
109
+ console.log(`Status: ${template.status}`);
110
+ console.log(`Content: ${template.previewContent}`);
111
+ });
112
+ ```
113
+
114
+ ### 2. Tạo template mới
115
+
116
+ ```typescript
117
+ const newTemplate = await zalo.zns.createTemplate(accessToken, {
118
+ templateName: "Xác nhận đơn hàng",
119
+ templateContent: `Xin chào {{customer_name}},
120
+
121
+ Đơn hàng {{order_id}} của bạn đã được xác nhận.
122
+ Tổng tiền: {{total_amount}} VNĐ
123
+ Ngày giao hàng dự kiến: {{delivery_date}}
124
+
125
+ Cảm ơn bạn đã mua hàng!`,
126
+ templateType: 3, // 1: OTP, 2: Thông báo, 3: Xác nhận, 4: Khuyến mại
127
+ timeout: 3600, // Thời gian hiệu lực (seconds)
128
+ previewData: JSON.stringify({
129
+ customer_name: "Nguyễn Văn A",
130
+ order_id: "DH001",
131
+ total_amount: "299,000",
132
+ delivery_date: "20/12/2024"
133
+ }),
134
+ hasHashPhone: false, // Có hỗ trợ hash phone không
135
+ hasAttachment: false // Có đính kèm file không
136
+ });
137
+
138
+ console.log("Template created:", newTemplate.templateId);
139
+ console.log("Status:", newTemplate.status); // 2: pending approval
140
+ ```
141
+
142
+ ### 3. Cập nhật template
143
+
144
+ ```typescript
145
+ const updatedTemplate = await zalo.zns.updateTemplate(accessToken, "template-id", {
146
+ templateName: "Xác nhận đơn hàng - Updated",
147
+ templateContent: `Kính chào {{customer_name}},
148
+
149
+ Đơn hàng #{{order_id}} đã được xác nhận.
150
+ Giá trị: {{total_amount}} VNĐ
151
+ Giao hàng: {{delivery_date}}
152
+
153
+ Hotline: 1800-xxx-xxx`,
154
+ previewData: JSON.stringify({
155
+ customer_name: "Khách hàng",
156
+ order_id: "DH999",
157
+ total_amount: "1,500,000",
158
+ delivery_date: "25/12/2024"
159
+ })
160
+ });
161
+ ```
162
+
163
+ ### 4. Kiểm tra trạng thái template
164
+
165
+ ```typescript
166
+ const templateStatus = await zalo.zns.getTemplateStatus(accessToken, "template-id");
167
+
168
+ console.log("Template Status:", templateStatus.status);
169
+ // 1: Active (đã duyệt)
170
+ // 2: Pending (chờ duyệt)
171
+ // 3: Rejected (bị từ chối)
172
+ // 4: Disabled (đã tắt)
173
+
174
+ if (templateStatus.status === 3) {
175
+ console.log("Rejection reason:", templateStatus.rejectReason);
176
+ }
177
+ ```
178
+
179
+ ---
180
+
181
+ ## Quản lý Quota
182
+
183
+ ### 1. Kiểm tra quota hiện tại
184
+
185
+ ```typescript
186
+ const quotaInfo = await zalo.zns.getQuotaInfo(accessToken);
187
+
188
+ console.log("Remaining quota:", quotaInfo.remainingQuota);
189
+ console.log("Daily quota:", quotaInfo.dailyQuota);
190
+ console.log("Monthly quota:", quotaInfo.monthlyQuota);
191
+
192
+ // Quota theo loại template
193
+ quotaInfo.quotaByType?.forEach(quota => {
194
+ console.log(`Type ${quota.type}: ${quota.remaining}/${quota.total}`);
195
+ });
196
+ ```
197
+
198
+ ### 2. Kiểm tra quota trước khi gửi
199
+
200
+ ```typescript
201
+ async function sendZNSWithQuotaCheck(
202
+ accessToken: string,
203
+ request: ZNSMessageRequest
204
+ ): Promise<boolean> {
205
+
206
+ // Kiểm tra quota trước
207
+ const quota = await zalo.zns.getQuotaInfo(accessToken);
208
+
209
+ if (quota.remainingQuota <= 0) {
210
+ console.log("⚠️ Không đủ quota để gửi tin nhắn");
211
+ return false;
212
+ }
213
+
214
+ if (quota.remainingQuota < 10) {
215
+ console.log(`⚠️ Cảnh báo: Chỉ còn ${quota.remainingQuota} quota`);
216
+ }
217
+
218
+ try {
219
+ const result = await zalo.zns.sendMessage(accessToken, request);
220
+ console.log(`✅ Gửi thành công. Quota còn lại: ${quota.remainingQuota - 1}`);
221
+ return true;
222
+ } catch (error) {
223
+ console.error("❌ Gửi thất bại:", error);
224
+ return false;
225
+ }
226
+ }
227
+ ```
228
+
229
+ ---
230
+
231
+ ## Template Data & Variables
232
+
233
+ ### 1. Template Variables
234
+
235
+ Templates hỗ trợ các biến động với format `{{variable_name}}`:
236
+
237
+ ```typescript
238
+ // Template content
239
+ const templateContent = `
240
+ Xin chào {{customer_name}},
241
+
242
+ Mã OTP của bạn là: {{otp_code}}
243
+ Mã này có hiệu lực trong {{validity_minutes}} phút.
244
+
245
+ Vui lòng không chia sẻ mã này với bất kỳ ai.
246
+ `;
247
+
248
+ // Template data
249
+ const templateData = {
250
+ customer_name: "Nguyễn Văn A",
251
+ otp_code: "123456",
252
+ validity_minutes: "5"
253
+ };
254
+ ```
255
+
256
+ ### 2. Data Type Handling
257
+
258
+ ```typescript
259
+ // Số và ngày tháng nên format thành string
260
+ const templateData = {
261
+ // ✅ Đúng
262
+ amount: "1,500,000",
263
+ date: "25/12/2024",
264
+ phone: "0123-456-789",
265
+
266
+ // ❌ Sai - sẽ gây lỗi
267
+ amount: 1500000,
268
+ date: new Date(),
269
+ phone: 123456789
270
+ };
271
+ ```
272
+
273
+ ### 3. Conditional Variables
274
+
275
+ ```typescript
276
+ // Template với logic điều kiện
277
+ const templateContent = `
278
+ {{#if is_vip}}
279
+ Kính gửi Khách hàng VIP {{customer_name}},
280
+ {{else}}
281
+ Xin chào {{customer_name}},
282
+ {{/if}}
283
+
284
+ Đơn hàng {{order_id}} đã được {{status}}.
285
+
286
+ {{#if has_discount}}
287
+ Bạn được giảm {{discount_amount}} cho đơn hàng này.
288
+ {{/if}}
289
+
290
+ Cảm ơn bạn!
291
+ `;
292
+
293
+ const templateData = {
294
+ customer_name: "Nguyễn Văn A",
295
+ order_id: "DH001",
296
+ status: "xác nhận",
297
+ is_vip: true,
298
+ has_discount: true,
299
+ discount_amount: "50,000 VNĐ"
300
+ };
301
+ ```
302
+
303
+ ---
304
+
305
+ ## Use Cases & Examples
306
+
307
+ ### 1. E-commerce Order Notifications
308
+
309
+ ```typescript
310
+ class OrderNotificationService {
311
+ constructor(private zalo: ZaloSDK, private accessToken: string) {}
312
+
313
+ async sendOrderConfirmation(order: Order) {
314
+ return await this.zalo.zns.sendMessage(this.accessToken, {
315
+ phone: order.customer.phone,
316
+ template_id: "order_confirmation_template",
317
+ template_data: {
318
+ customer_name: order.customer.name,
319
+ order_id: order.id,
320
+ total_amount: this.formatCurrency(order.totalAmount),
321
+ items_summary: this.getItemsSummary(order.items),
322
+ delivery_date: this.formatDate(order.estimatedDelivery),
323
+ tracking_url: `https://mystore.com/track/${order.id}`
324
+ },
325
+ tracking_id: `order_${order.id}`
326
+ });
327
+ }
328
+
329
+ async sendShippingUpdate(order: Order, status: string) {
330
+ return await this.zalo.zns.sendMessage(this.accessToken, {
331
+ phone: order.customer.phone,
332
+ template_id: "shipping_update_template",
333
+ template_data: {
334
+ customer_name: order.customer.name,
335
+ order_id: order.id,
336
+ status: status,
337
+ tracking_code: order.trackingCode,
338
+ estimated_delivery: this.formatDate(order.estimatedDelivery)
339
+ }
340
+ });
341
+ }
342
+
343
+ private formatCurrency(amount: number): string {
344
+ return new Intl.NumberFormat('vi-VN').format(amount) + ' VNĐ';
345
+ }
346
+
347
+ private formatDate(date: Date): string {
348
+ return date.toLocaleDateString('vi-VN');
349
+ }
350
+
351
+ private getItemsSummary(items: OrderItem[]): string {
352
+ return items.map(item => `${item.name} x${item.quantity}`).join(', ');
353
+ }
354
+ }
355
+ ```
356
+
357
+ ### 2. Booking & Appointment System
358
+
359
+ ```typescript
360
+ class BookingNotificationService {
361
+ constructor(private zalo: ZaloSDK, private accessToken: string) {}
362
+
363
+ async sendBookingConfirmation(booking: Booking) {
364
+ return await this.zalo.zns.sendMessage(this.accessToken, {
365
+ phone: booking.customer.phone,
366
+ template_id: "booking_confirmation_template",
367
+ template_data: {
368
+ customer_name: booking.customer.name,
369
+ service_name: booking.service.name,
370
+ booking_date: this.formatDateTime(booking.appointmentTime),
371
+ location: booking.location.address,
372
+ staff_name: booking.staff.name,
373
+ booking_id: booking.id,
374
+ cancel_url: `https://mybusiness.com/cancel/${booking.id}`
375
+ }
376
+ });
377
+ }
378
+
379
+ async sendReminder(booking: Booking, hoursUntilAppointment: number) {
380
+ return await this.zalo.zns.sendMessage(this.accessToken, {
381
+ phone: booking.customer.phone,
382
+ template_id: "appointment_reminder_template",
383
+ template_data: {
384
+ customer_name: booking.customer.name,
385
+ service_name: booking.service.name,
386
+ appointment_time: this.formatDateTime(booking.appointmentTime),
387
+ hours_until: hoursUntilAppointment.toString(),
388
+ location: booking.location.address,
389
+ contact_phone: "1800-xxx-xxx"
390
+ }
391
+ });
392
+ }
393
+
394
+ private formatDateTime(date: Date): string {
395
+ return date.toLocaleString('vi-VN');
396
+ }
397
+ }
398
+ ```
399
+
400
+ ### 3. Payment & Financial Notifications
401
+
402
+ ```typescript
403
+ class PaymentNotificationService {
404
+ constructor(private zalo: ZaloSDK, private accessToken: string) {}
405
+
406
+ async sendPaymentConfirmation(payment: Payment) {
407
+ return await this.zalo.zns.sendMessage(this.accessToken, {
408
+ phone: payment.customer.phone,
409
+ template_id: "payment_confirmation_template",
410
+ template_data: {
411
+ customer_name: payment.customer.name,
412
+ amount: this.formatCurrency(payment.amount),
413
+ payment_method: payment.method,
414
+ transaction_id: payment.transactionId,
415
+ payment_date: this.formatDateTime(payment.createdAt),
416
+ merchant_name: payment.merchant.name,
417
+ receipt_url: `https://pay.com/receipt/${payment.id}`
418
+ }
419
+ });
420
+ }
421
+
422
+ async sendInvoice(invoice: Invoice) {
423
+ return await this.zalo.zns.sendMessage(this.accessToken, {
424
+ phone: invoice.customer.phone,
425
+ template_id: "invoice_template",
426
+ template_data: {
427
+ customer_name: invoice.customer.name,
428
+ invoice_number: invoice.number,
429
+ amount: this.formatCurrency(invoice.totalAmount),
430
+ due_date: this.formatDate(invoice.dueDate),
431
+ payment_url: `https://pay.com/invoice/${invoice.id}`,
432
+ company_name: invoice.company.name
433
+ }
434
+ });
435
+ }
436
+ }
437
+ ```
438
+
439
+ ### 4. OTP & Security Messages
440
+
441
+ ```typescript
442
+ class SecurityNotificationService {
443
+ constructor(private zalo: ZaloSDK, private accessToken: string) {}
444
+
445
+ async sendOTP(phone: string, otpCode: string, purpose: string) {
446
+ return await this.zalo.zns.sendMessage(this.accessToken, {
447
+ phone: phone,
448
+ template_id: "otp_template",
449
+ template_data: {
450
+ otp_code: otpCode,
451
+ purpose: purpose,
452
+ validity_minutes: "5",
453
+ app_name: "MyApp"
454
+ }
455
+ });
456
+ }
457
+
458
+ async sendSecurityAlert(user: User, activity: string, location: string) {
459
+ return await this.zalo.zns.sendMessage(this.accessToken, {
460
+ phone: user.phone,
461
+ template_id: "security_alert_template",
462
+ template_data: {
463
+ customer_name: user.name,
464
+ activity: activity,
465
+ time: this.formatDateTime(new Date()),
466
+ location: location,
467
+ ip_address: this.getCurrentIP(),
468
+ support_phone: "1800-xxx-xxx"
469
+ }
470
+ });
471
+ }
472
+ }
473
+ ```
474
+
475
+ ---
476
+
477
+ ## Batch Processing
478
+
479
+ ### 1. Gửi tin nhắn hàng loạt
480
+
481
+ ```typescript
482
+ class BatchZNSService {
483
+ constructor(private zalo: ZaloSDK, private accessToken: string) {}
484
+
485
+ async sendBatchMessages(
486
+ templateId: string,
487
+ recipients: Array<{phone: string, data: Record<string, any>}>
488
+ ) {
489
+ const results = [];
490
+ const batchSize = 10; // Gửi 10 tin mỗi lần để tránh rate limit
491
+
492
+ for (let i = 0; i < recipients.length; i += batchSize) {
493
+ const batch = recipients.slice(i, i + batchSize);
494
+
495
+ const batchPromises = batch.map(async (recipient) => {
496
+ try {
497
+ const result = await this.zalo.zns.sendMessage(this.accessToken, {
498
+ phone: recipient.phone,
499
+ template_id: templateId,
500
+ template_data: recipient.data,
501
+ tracking_id: `batch_${Date.now()}_${i}`
502
+ });
503
+
504
+ return {
505
+ phone: recipient.phone,
506
+ success: true,
507
+ messageId: result.msg_id
508
+ };
509
+ } catch (error) {
510
+ return {
511
+ phone: recipient.phone,
512
+ success: false,
513
+ error: error.message
514
+ };
515
+ }
516
+ });
517
+
518
+ const batchResults = await Promise.all(batchPromises);
519
+ results.push(...batchResults);
520
+
521
+ // Delay giữa các batch để tránh rate limit
522
+ if (i + batchSize < recipients.length) {
523
+ await this.delay(1000); // 1 second delay
524
+ }
525
+ }
526
+
527
+ return results;
528
+ }
529
+
530
+ private delay(ms: number): Promise<void> {
531
+ return new Promise(resolve => setTimeout(resolve, ms));
532
+ }
533
+ }
534
+
535
+ // Usage
536
+ const batchService = new BatchZNSService(zalo, accessToken);
537
+
538
+ const recipients = [
539
+ {
540
+ phone: "0123456789",
541
+ data: { name: "Nguyễn Văn A", code: "ABC123" }
542
+ },
543
+ {
544
+ phone: "0987654321",
545
+ data: { name: "Trần Thị B", code: "XYZ789" }
546
+ }
547
+ // ... more recipients
548
+ ];
549
+
550
+ const results = await batchService.sendBatchMessages("template_id", recipients);
551
+ console.log(`Sent ${results.filter(r => r.success).length}/${results.length} messages successfully`);
552
+ ```
553
+
554
+ ---
555
+
556
+ ## Error Handling
557
+
558
+ ### 1. Common ZNS Errors
559
+
560
+ ```typescript
561
+ try {
562
+ await zalo.zns.sendMessage(accessToken, request);
563
+ } catch (error) {
564
+ switch (error.code) {
565
+ case -216:
566
+ console.error("Invalid access token");
567
+ break;
568
+ case -223:
569
+ console.error("Quota exceeded");
570
+ break;
571
+ case -224:
572
+ console.error("Template not found or not approved");
573
+ break;
574
+ case -225:
575
+ console.error("Invalid phone number");
576
+ break;
577
+ case -226:
578
+ console.error("Invalid template data");
579
+ break;
580
+ case -227:
581
+ console.error("Template content mismatch");
582
+ break;
583
+ default:
584
+ console.error("Unexpected error:", error.message);
585
+ }
586
+ }
587
+ ```
588
+
589
+ ### 2. Retry Logic với Exponential Backoff
590
+
591
+ ```typescript
592
+ class ZNSRetryService {
593
+ constructor(private zalo: ZaloSDK, private accessToken: string) {}
594
+
595
+ async sendWithRetry(
596
+ request: ZNSMessageRequest,
597
+ maxRetries = 3
598
+ ): Promise<any> {
599
+ let lastError;
600
+
601
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
602
+ try {
603
+ return await this.zalo.zns.sendMessage(this.accessToken, request);
604
+ } catch (error) {
605
+ lastError = error;
606
+
607
+ // Không retry với một số lỗi cố định
608
+ if (this.shouldNotRetry(error.code)) {
609
+ throw error;
610
+ }
611
+
612
+ if (attempt < maxRetries) {
613
+ const delay = Math.pow(2, attempt) * 1000; // Exponential backoff
614
+ console.log(`Attempt ${attempt} failed, retrying in ${delay}ms...`);
615
+ await new Promise(resolve => setTimeout(resolve, delay));
616
+ }
617
+ }
618
+ }
619
+
620
+ throw lastError;
621
+ }
622
+
623
+ private shouldNotRetry(errorCode: number): boolean {
624
+ // Không retry với các lỗi sau
625
+ const nonRetryableCodes = [-224, -225, -226, -227]; // Template/data errors
626
+ return nonRetryableCodes.includes(errorCode);
627
+ }
628
+ }
629
+ ```
630
+
631
+ ---
632
+
633
+ ## Monitoring & Analytics
634
+
635
+ ### 1. Message Tracking
636
+
637
+ ```typescript
638
+ class ZNSAnalyticsService {
639
+ constructor(private zalo: ZaloSDK, private accessToken: string) {}
640
+
641
+ async trackMessageDelivery(trackingId: string) {
642
+ // Implement your tracking logic
643
+ const deliveryStatus = await this.getDeliveryStatus(trackingId);
644
+
645
+ return {
646
+ trackingId,
647
+ status: deliveryStatus.status, // sent, delivered, failed
648
+ sentAt: deliveryStatus.sentAt,
649
+ deliveredAt: deliveryStatus.deliveredAt,
650
+ failureReason: deliveryStatus.failureReason
651
+ };
652
+ }
653
+
654
+ async generateQuotaReport(period: 'daily' | 'monthly') {
655
+ const quota = await this.zalo.zns.getQuotaInfo(this.accessToken);
656
+
657
+ return {
658
+ period,
659
+ totalQuota: quota.dailyQuota,
660
+ usedQuota: quota.dailyQuota - quota.remainingQuota,
661
+ remainingQuota: quota.remainingQuota,
662
+ utilizationRate: ((quota.dailyQuota - quota.remainingQuota) / quota.dailyQuota * 100).toFixed(2) + '%'
663
+ };
664
+ }
665
+ }
666
+ ```
667
+
668
+ ### 2. Performance Monitoring
669
+
670
+ ```typescript
671
+ class ZNSPerformanceMonitor {
672
+ private metrics = {
673
+ totalSent: 0,
674
+ successful: 0,
675
+ failed: 0,
676
+ averageResponseTime: 0
677
+ };
678
+
679
+ async sendMessageWithMonitoring(
680
+ zalo: ZaloSDK,
681
+ accessToken: string,
682
+ request: ZNSMessageRequest
683
+ ) {
684
+ const startTime = Date.now();
685
+
686
+ try {
687
+ const result = await zalo.zns.sendMessage(accessToken, request);
688
+
689
+ this.metrics.totalSent++;
690
+ this.metrics.successful++;
691
+ this.updateResponseTime(Date.now() - startTime);
692
+
693
+ console.log(`✅ Message sent successfully. Success rate: ${this.getSuccessRate()}%`);
694
+ return result;
695
+
696
+ } catch (error) {
697
+ this.metrics.totalSent++;
698
+ this.metrics.failed++;
699
+
700
+ console.log(`❌ Message failed. Success rate: ${this.getSuccessRate()}%`);
701
+ throw error;
702
+ }
703
+ }
704
+
705
+ private getSuccessRate(): number {
706
+ if (this.metrics.totalSent === 0) return 0;
707
+ return (this.metrics.successful / this.metrics.totalSent * 100).toFixed(2);
708
+ }
709
+
710
+ private updateResponseTime(responseTime: number) {
711
+ this.metrics.averageResponseTime =
712
+ (this.metrics.averageResponseTime * (this.metrics.successful - 1) + responseTime) / this.metrics.successful;
713
+ }
714
+
715
+ getMetrics() {
716
+ return { ...this.metrics };
717
+ }
718
+ }
719
+ ```
720
+
721
+ ---
722
+
723
+ ## Best Practices
724
+
725
+ ### 1. Template Design
726
+
727
+ ```typescript
728
+ // ✅ Template tốt - rõ ràng, súc tích
729
+ const goodTemplate = `
730
+ Xin chào {{customer_name}},
731
+
732
+ Đơn hàng {{order_id}} đã được xác nhận thành công.
733
+ • Tổng tiền: {{total_amount}} VNĐ
734
+ • Ngày giao: {{delivery_date}}
735
+ • Mã vận đơn: {{tracking_code}}
736
+
737
+ Cảm ơn bạn đã tin tưởng!
738
+ Hotline: {{support_phone}}
739
+ `;
740
+
741
+ // ❌ Template kém - quá dài, không có cấu trúc
742
+ const badTemplate = `
743
+ Kính chào quý khách hàng {{customer_name}} của công ty chúng tôi. Chúng tôi xin thông báo rằng đơn hàng có mã số {{order_id}} của quý khách đã được xác nhận và đang trong quá trình xử lý...
744
+ `;
745
+ ```
746
+
747
+ ### 2. Phone Number Validation
748
+
749
+ ```typescript
750
+ function validatePhoneNumber(phone: string): boolean {
751
+ // Vietnamese phone number regex
752
+ const phoneRegex = /^(0[3-9][0-9]{8})$/;
753
+ return phoneRegex.test(phone.replace(/[\s\-\.]/g, ''));
754
+ }
755
+
756
+ function formatPhoneNumber(phone: string): string {
757
+ // Remove all non-digits
758
+ const cleaned = phone.replace(/\D/g, '');
759
+
760
+ // Add country code if missing
761
+ if (cleaned.length === 10 && cleaned.startsWith('0')) {
762
+ return cleaned;
763
+ } else if (cleaned.length === 9) {
764
+ return '0' + cleaned;
765
+ }
766
+
767
+ throw new Error('Invalid phone number format');
768
+ }
769
+ ```
770
+
771
+ ### 3. Template Data Validation
772
+
773
+ ```typescript
774
+ function validateTemplateData(template: string, data: Record<string, any>): boolean {
775
+ // Extract variables from template
776
+ const variables = template.match(/\{\{(\w+)\}\}/g)?.map(match =>
777
+ match.replace(/\{\{|\}\}/g, '')
778
+ ) || [];
779
+
780
+ // Check if all variables are provided
781
+ const missingVars = variables.filter(variable => !(variable in data));
782
+
783
+ if (missingVars.length > 0) {
784
+ throw new Error(`Missing template variables: ${missingVars.join(', ')}`);
785
+ }
786
+
787
+ // Validate data types (all should be strings)
788
+ const invalidTypes = Object.entries(data)
789
+ .filter(([key, value]) => typeof value !== 'string')
790
+ .map(([key]) => key);
791
+
792
+ if (invalidTypes.length > 0) {
793
+ throw new Error(`Invalid data types for: ${invalidTypes.join(', ')}. All values must be strings.`);
794
+ }
795
+
796
+ return true;
797
+ }
798
+ ```
799
+
800
+ ---
801
+
802
+ ## Testing ZNS
803
+
804
+ ### 1. Development Mode Testing
805
+
806
+ ```typescript
807
+ // Test template trong development mode
808
+ async function testTemplate(templateId: string) {
809
+ const testData = {
810
+ customer_name: "Nguyễn Test",
811
+ order_id: "TEST001",
812
+ total_amount: "100,000"
813
+ };
814
+
815
+ try {
816
+ const result = await zalo.zns.sendMessage(accessToken, {
817
+ phone: "0123456789", // Test phone number
818
+ template_id: templateId,
819
+ template_data: testData,
820
+ mode: "development" // Không trừ quota
821
+ });
822
+
823
+ console.log("✅ Template test successful:", result.msg_id);
824
+ return true;
825
+ } catch (error) {
826
+ console.error("❌ Template test failed:", error.message);
827
+ return false;
828
+ }
829
+ }
830
+ ```
831
+
832
+ ### 2. Unit Tests
833
+
834
+ ```typescript
835
+ // zns.test.ts
836
+ import { ZaloSDK } from '@warriorteam/redai-zalo-sdk';
837
+
838
+ describe('ZNS Service', () => {
839
+ const zalo = new ZaloSDK({
840
+ appId: 'test_app_id',
841
+ appSecret: 'test_app_secret'
842
+ });
843
+
844
+ it('should send ZNS message successfully', async () => {
845
+ const mockResponse = {
846
+ error: 0,
847
+ message: "Success",
848
+ data: { msg_id: "test_msg_id" }
849
+ };
850
+
851
+ // Mock the API call
852
+ jest.spyOn(zalo.zns, 'sendMessage').mockResolvedValue(mockResponse);
853
+
854
+ const result = await zalo.zns.sendMessage('test_token', {
855
+ phone: '0123456789',
856
+ template_id: 'test_template',
857
+ template_data: { name: 'Test User' }
858
+ });
859
+
860
+ expect(result.msg_id).toBe('test_msg_id');
861
+ });
862
+ });
863
+ ```
864
+
865
+ ---
866
+
867
+ ## Production Checklist
868
+
869
+ ### 1. Trước khi Go Live
870
+
871
+ - [ ] Templates đã được Zalo duyệt
872
+ - [ ] Test tất cả templates trong development mode
873
+ - [ ] Setup monitoring và logging
874
+ - [ ] Implement error handling và retry logic
875
+ - [ ] Validate phone numbers trước khi gửi
876
+ - [ ] Setup quota monitoring và alerts
877
+ - [ ] Backup và recovery plan
878
+ - [ ] Performance testing với volume cao
879
+
880
+ ### 2. Monitoring Production
881
+
882
+ ```typescript
883
+ // Production monitoring setup
884
+ class ProductionZNSMonitor {
885
+ constructor(private zalo: ZaloSDK, private accessToken: string) {}
886
+
887
+ async healthCheck(): Promise<boolean> {
888
+ try {
889
+ const quota = await this.zalo.zns.getQuotaInfo(this.accessToken);
890
+
891
+ // Alert nếu quota thấp
892
+ if (quota.remainingQuota < 100) {
893
+ this.sendAlert(`⚠️ ZNS Quota thấp: ${quota.remainingQuota} còn lại`);
894
+ }
895
+
896
+ return true;
897
+ } catch (error) {
898
+ this.sendAlert(`❌ ZNS Health check failed: ${error.message}`);
899
+ return false;
900
+ }
901
+ }
902
+
903
+ private sendAlert(message: string) {
904
+ // Send to your monitoring system (Slack, email, etc.)
905
+ console.error(message);
906
+ }
907
+ }
908
+ ```
909
+
910
+ ---
911
+
912
+ ## Troubleshooting
913
+
914
+ ### 1. Common Issues
915
+
916
+ **Q: Template không gửi được**
917
+ ```
918
+ A: Kiểm tra:
919
+ - Template đã được duyệt chưa (status = 1)
920
+ - Template data có đúng format không
921
+ - Phone number có hợp lệ không
922
+ - Còn quota không
923
+ ```
924
+
925
+ **Q: "Template content mismatch" error**
926
+ ```
927
+ A: Template data phải khớp chính xác với template content
928
+ - Tên biến phải giống nhau
929
+ - Không được thiếu biến
930
+ - Tất cả giá trị phải là string
931
+ ```
932
+
933
+ ### 2. Debug ZNS Issues
934
+
935
+ ```typescript
936
+ async function debugZNSMessage(
937
+ zalo: ZaloSDK,
938
+ accessToken: string,
939
+ request: ZNSMessageRequest
940
+ ) {
941
+ console.log("🔍 Debug ZNS Request:");
942
+ console.log("Phone:", request.phone);
943
+ console.log("Template ID:", request.template_id);
944
+ console.log("Template Data:", JSON.stringify(request.template_data, null, 2));
945
+
946
+ // Check quota first
947
+ try {
948
+ const quota = await zalo.zns.getQuotaInfo(accessToken);
949
+ console.log("📊 Quota Info:", quota);
950
+ } catch (error) {
951
+ console.error("❌ Failed to get quota:", error);
952
+ }
953
+
954
+ // Check template status
955
+ try {
956
+ const template = await zalo.zns.getTemplateStatus(accessToken, request.template_id);
957
+ console.log("📝 Template Status:", template);
958
+ } catch (error) {
959
+ console.error("❌ Failed to get template status:", error);
960
+ }
961
+
962
+ // Attempt to send
963
+ try {
964
+ const result = await zalo.zns.sendMessage(accessToken, request);
965
+ console.log("✅ Message sent successfully:", result);
966
+ return result;
967
+ } catch (error) {
968
+ console.error("❌ Failed to send message:", error);
969
+ throw error;
970
+ }
971
+ }
972
+ ```
973
+
974
+ ---
975
+
976
+ ## Next Steps
977
+
978
+ Sau khi làm chủ ZNS Service:
979
+
980
+ 1. **[Message Services](./MESSAGE_SERVICES.md)** - Tìm hiểu các loại tin nhắn khác
981
+ 2. **[Webhook Events](./WEBHOOK_EVENTS.md)** - Xử lý ZNS delivery events
982
+ 3. **[User Management](./USER_MANAGEMENT.md)** - Quản lý danh sách khách hàng
983
+ 4. **[Error Handling](./ERROR_HANDLING.md)** - Xử lý lỗi toàn diện
984
+
985
+ Tham khảo **[API Reference](./API_REFERENCE.md)** để biết chi tiết về tất cả ZNS methods.