@warriorteam/redai-zalo-sdk
Version:
Comprehensive TypeScript/JavaScript SDK for Zalo APIs - Official Account v3.0, ZNS with Full Type Safety, Consultation Service, Broadcast Service, Group Messaging with List APIs, Social APIs, Enhanced Article Management, Promotion Service v3.0 with Multip
380 lines (323 loc) • 10.6 kB
Markdown
# Zalo Promotion API v3.0 Migration Guide
## Tổng quan thay đổi
Promotion Service đã được cập nhật để tương thích với **Zalo Official Account API v3.0**. Đây là những thay đổi chính:
### 🔄 Thay đổi URL API
- **Cũ**: `https://openapi.zalo.me/v2.0/oa/message/promotion`
- **Mới**: `https://openapi.zalo.me/v3.0/oa/message/promotion`
### 🏗️ Cấu trúc Message Elements hoàn toàn mới
#### Cũ (v2.0):
```typescript
elements: [{
title: string;
subtitle?: string;
image_url?: string;
buttons?: Array<{...}>;
}]
```
#### Mới (v3.0):
```typescript
elements: [
{
type: "banner",
attachment_id: "...", // hoặc image_url
},
{
type: "header",
content: "💥💥Ưu đãi thành viên Platinum💥💥"
},
{
type: "text",
align: "left",
content: "Nội dung chi tiết..."
},
{
type: "table",
content: [
{ key: "Voucher", value: "VC09279222" },
{ key: "Hạn sử dụng", value: "30/12/2023" }
]
}
]
```
### 🎯 Các Element Types được hỗ trợ
1. **Banner Element**
```typescript
{
type: "banner",
attachment_id?: string, // ID từ API upload ảnh
image_url?: string // URL trực tiếp (chỉ dùng 1 trong 2)
}
```
2. **Header Element**
```typescript
{
type: "header",
content: string, // Tối đa 100 ký tự
align?: "left" | "center" | "right" // Mặc định: left
}
```
3. **Text Element**
```typescript
{
type: "text",
content: string, // Tối đa 1000 ký tự
align?: "left" | "center" | "right" // Mặc định: left
}
```
4. **Table Element**
```typescript
{
type: "table",
content: Array<{
key: string, // Tối đa 25 ký tự
value: string // Tối đa 100 ký tự
}> // Tối đa 5 phần tử
}
```
### 🔘 Cấu trúc Buttons mới
#### Cũ (v2.0):
```typescript
buttons: [{
type: string;
title: string;
url?: string;
payload?: string;
}]
```
#### Mới (v3.0):
```typescript
buttons: [{
title: string, // Tối đa 35 ký tự
image_icon?: string, // URL hoặc attachment_id hoặc "default"
type: string, // VD: "oa.open.url", "oa.query.hide"
payload: object | string // Tùy thuộc vào type
}]
```
**Payload format theo type:**
- `oa.open.url`: `{ url: "https://..." }`
- `oa.query.show`: `"#callback_data"` (string, max 1000 chars)
- `oa.query.hide`: `"#callback_data"` (string, max 1000 chars)
- `oa.open.sms`: `{ content: "text", phone_code: "84xxx" }` (content max 160 chars)
- `oa.open.phone`: `{ phone_code: "84xxx" }`
**TypeScript Support:**
```typescript
// Typed button interfaces
interface OpenUrlButton {
title: string; // Max 100 characters
type: "oa.open.url";
payload: { url: string };
}
interface QueryHideButton {
title: string; // Max 100 characters
type: "oa.query.hide";
payload: string; // Max 1000 characters
}
// Union type for all buttons
type PromotionButton = OpenUrlButton | QueryShowButton | QueryHideButton | OpenSmsButton | OpenPhoneButton;
// Flexible input interface
interface PromotionButtonInput {
title: string;
imageIcon?: string;
type: "oa.open.url" | "oa.query.show" | "oa.query.hide" | "oa.open.sms" | "oa.open.phone";
payload: any; // SDK auto-converts to proper format
}
```
### 🌐 Hỗ trợ đa ngôn ngữ
Thêm thuộc tính `language` trong payload:
```typescript
{
template_type: "promotion",
language: "VI" | "EN", // Mặc định: VI
elements: [...],
buttons: [...]
}
```
## 🎨 Banner Configuration
### Cách sử dụng Banner
Từ phiên bản mới, bạn có thể truyền banner theo 2 cách:
#### 1. Sử dụng Attachment ID (Recommended)
```typescript
{
banner: { attachment_id: "your_attachment_id" }
}
```
#### 2. Sử dụng Image URL
```typescript
{
banner: { image_url: "https://example.com/banner.jpg" }
}
```
### ⚠️ Lưu ý quan trọng:
- **Chỉ sử dụng một trong hai**: `attachment_id` HOẶC `image_url`, không được dùng cả hai
- **Attachment ID được khuyến nghị** vì ảnh đã được upload lên Zalo server, tốc độ tải nhanh hơn
- **Image URL** phù hợp khi bạn muốn sử dụng ảnh từ server riêng
### Validation
SDK sẽ tự động validate và báo lỗi nếu:
- Không có `attachment_id` hoặc `image_url`
- Có cả `attachment_id` và `image_url` cùng lúc
---
### 🚀 Tính năng mới: Multiple Users với Tracking
#### Gửi đến nhiều users cùng lúc
- **Sequential Mode**: Gửi tuần tự với delay tùy chỉnh
- **Parallel Mode**: Gửi song song để tối ưu tốc độ
- **Real-time Tracking**: Callback progress theo thời gian thực
- **Error Isolation**: Lỗi của 1 user không ảnh hưởng các user khác
- **Statistics**: Thống kê chi tiết success rate và execution time
#### Callback Tracking
```typescript
{
onProgress: (progress) => {
// Real-time progress: completed/total, success/failed
// Estimated time remaining, current user ID
},
onUserComplete: (userResult) => {
// Callback cho từng user hoàn thành
// Success/failure status, message ID, error details
}
}
```
## 📝 Cách sử dụng mới
### 1. Gửi Promotion theo format chuẩn docs
```typescript
import { PromotionService } from "./services/promotion.service";
const promotionService = new PromotionService(zaloClient);
// Gửi promotion message theo đúng format docs v3.0
await promotionService.sendCustomPromotion(
accessToken,
{ user_id: "user_id" },
{
banner: { attachment_id: "your_attachment_id" }, // Hoặc { image_url: "https://example.com/banner.jpg" }
headerContent: "💥💥Ưu đãi thành viên Platinum💥💥",
textContent: "Ưu đãi dành riêng cho khách hàng...",
tableData: [
{ key: "Voucher", value: "VC09279222" },
{ key: "Hạn sử dụng", value: "30/12/2023" }
],
footerText: "Áp dụng tất cả cửa hàng trên toàn quốc",
buttons: [
{
title: "Tham khảo chương trình",
type: "oa.open.url",
payload: { url: "https://oa.zalo.me/home" }
}
],
language: "VI"
}
);
```
### 2. Gửi Product Promotion (đã cập nhật)
```typescript
await promotionService.sendProductPromotion(
accessToken,
{ user_id: "user_id" },
{
title: "iPhone 15 Pro Max",
description: "Siêu phẩm công nghệ mới nhất...",
attachmentId: "product_image_id", // Thay vì imageUrl
originalPrice: 34990000,
discountPrice: 29990000,
discountPercent: 14,
validUntil: "31/12/2024",
productUrl: "https://example.com/product"
}
);
```
### 3. Gửi Event Notification (đã cập nhật)
```typescript
await promotionService.sendEventNotification(
accessToken,
{ user_id: "user_id" },
{
title: "Hội thảo Digital Marketing 2024",
description: "Khám phá xu hướng marketing...",
attachmentId: "event_image_id",
eventDate: "15/01/2024 - 9:00 AM",
location: "Trung tâm Hội nghị Quốc gia, Hà Nội",
registrationUrl: "https://example.com/register"
}
);
```
### 4. Gửi Promotion đến nhiều Users với Tracking
```typescript
// Gửi tuần tự với tracking
const result = await promotionService.sendCustomPromotionToMultipleUsers(
accessToken,
["user1", "user2", "user3", "user4"],
{
banner: { attachment_id: "your_attachment_id" }, // Hoặc { image_url: "https://example.com/banner.jpg" }
headerContent: "🎉 Khuyến mãi đặc biệt!",
textContent: "Ưu đãi độc quyền dành riêng cho bạn",
tableData: [
{ key: "Mã giảm giá", value: "SAVE50" },
{ key: "Giảm giá", value: "50%" }
],
buttons: [
{
title: "Mua ngay",
type: "oa.open.url",
payload: { url: "https://shop.example.com" }
}
]
},
{
mode: "sequential", // hoặc "parallel"
delayMs: 2000, // Delay 2s giữa các tin
continueOnError: true,
// Real-time progress tracking
onProgress: (progress) => {
console.log(`Progress: ${progress.completed}/${progress.total}`);
console.log(`Success: ${progress.successful}, Failed: ${progress.failed}`);
},
// Callback cho từng user
onUserComplete: (userResult) => {
if (userResult.success) {
console.log(`✅ Sent to ${userResult.userId}`);
} else {
console.log(`❌ Failed to ${userResult.userId}: ${userResult.error?.message}`);
}
}
}
);
console.log(`📊 Final Result:`);
console.log(`Total: ${result.total}, Success: ${result.successful}, Failed: ${result.failed}`);
console.log(`Success Rate: ${result.successRate}%, Time: ${result.executionTime}ms`);
```
### 5. Gửi Song Song (Parallel Mode)
```typescript
// Gửi song song để tối ưu tốc độ
const result = await promotionService.sendCustomPromotionToMultipleUsers(
accessToken,
userIds,
promotionData,
{
mode: "parallel", // Gửi tất cả cùng lúc
continueOnError: true,
onProgress: (progress) => {
console.log(`⚡ Parallel: ${progress.completed}/${progress.total}`);
}
}
);
```
## ⚠️ Breaking Changes
1. **URL API**: Phải cập nhật từ v2.0 lên v3.0
2. **Elements Structure**: Hoàn toàn khác, không tương thích ngược
3. **Buttons Payload**: Từ `string` thành `object`
4. **Image Handling**: Ưu tiên `attachment_id` thay vì `image_url`
5. **Validation**: Thêm validation cho elements và buttons
## 🔍 Validation Rules
- **Header content**: Tối đa 100 ký tự
- **Text content**: Tối đa 1000 ký tự mỗi đoạn, tối đa 2 đoạn text
- **Table**: Tối đa 5 phần tử, key ≤ 25 ký tự, value ≤ 100 ký tự
- **Button title**: Tối đa 35 ký tự, tối đa 4 buttons
- **Sending time**: Chỉ từ 8:00 - 22:00 hàng ngày
## 📚 Examples
Xem file `examples/promotion-examples.ts` để có ví dụ chi tiết về cách sử dụng tất cả các tính năng mới.
## 🚀 Migration Checklist
- [ ] Cập nhật URL API từ v2.0 lên v3.0
- [ ] Thay đổi cấu trúc elements theo format mới
- [ ] Cập nhật cấu trúc buttons với payload object
- [ ] Thêm support cho attachment_id
- [ ] Thêm language parameter
- [ ] Test với các element types khác nhau
- [ ] Kiểm tra validation rules mới
- [ ] Cập nhật documentation và examples