Triển khai send guard WhatsApp 24 giờ cho phản hồi trong queue
Phản hồi WhatsApp trong queue có thể vượt qua cửa sổ 24 giờ khi chờ duyệt, retry hoặc worker nhận job. Một send guard đáng tin cậy phải đọc lại state ở server ngay trước lúc gửi, tính hạn từ tin nhắn người dùng mới nhất đã xác minh và chặn free-form text đã hết hạn. Hãy dùng cây quyết định service và utility message cho category và pricing; bài này chỉ tập trung vào biên thực thi ngăn gửi nhầm.
Điểm chính
- Lưu hai mốc hết hạn và
state_versiontăng đơn điệu, không chỉ một biến boolean. - Chỉ inbound message của người dùng đã xác minh và deduplicate mới được đặt lại cửa sổ 24 giờ.
- Sau khi lấy job, worker đọc lại state trong cùng transaction hoặc lock rồi mới chọn đường gửi.
- Draft đã hết hạn phải dừng và route lại, không âm thầm đổi thành template.
- Kiểm thử event trùng, event sai thứ tự, worker cạnh tranh và ranh giới chính xác đến giây.
Xây send guard quanh phản hồi trong queue
Trang giá Business Platform chính thức nêu rằng tin nhắn người dùng mở hoặc đặt lại cửa sổ 24 giờ. Ở lớp triển khai, biên cho phép này phải là điều kiện cuối cùng trước khi gửi. Category và thay đổi pricing tháng 10/2026 đã được giải thích trong hướng dẫn hiện có nên không lặp lại ở đây.
Với mỗi job trong queue, sender phải trả lời ba câu hỏi triển khai:
- Đã đọc phiên bản state mới nhất chưa? Worker cũ không được ghi đè cửa sổ đã cập nhật.
- Cửa sổ còn mở tại thời điểm gửi thật không? Không tái sử dụng kết quả lúc tạo hoặc duyệt draft.
- Đường gửi đã chọn còn được phép không? Sau khi cửa sổ đóng, chỉ dùng template đã duyệt và khớp đúng intent; nếu không thì dừng và trả cho agent.
Lưu hai đồng hồ thay vì một trạng thái
Các timestamp rõ ràng cho phép retry, job trễ và chuyển agent được đánh giá lại.
| Giá trị | Điều gì mở hoặc đặt lại | Điều gì được kiểm soát |
|---|---|---|
service_window_expires_at | Mỗi tin nhắn inbound của người dùng | Có được gửi service reply không dùng template không |
free_entry_expires_at | Người dùng vào từ quảng cáo Click-to-WhatsApp hoặc nút CTA Facebook Page hợp lệ | Giao tin có được miễn phí trong 72 giờ không |
last_user_message_id | Mỗi tin nhắn người dùng đã chấp nhận | Idempotency và bằng chứng audit |
state_version | Mỗi event làm đổi trạng thái | Chặn job cũ ghi đè trạng thái mới |
Free entry point 72 giờ không phải cửa sổ gửi kéo dài 72 giờ. Tin nhắn người dùng mới có thể đặt lại quyền gửi 24 giờ, còn hạn miễn phí vẫn là một ranh giới riêng.
Triển khai send guard
Đây là ví dụ TypeScript ở lớp ứng dụng, không phải schema payload của Meta. Quyền gửi và phí dự kiến được trả về riêng:
type WindowState = {
serviceWindowExpiresAt: Date | null;
freeEntryExpiresAt: Date | null;
};
function evaluateWhatsAppSend(state: WindowState, now: Date) {
const serviceWindowOpen =
state.serviceWindowExpiresAt !== null && now < state.serviceWindowExpiresAt;
const freeEntryActive =
state.freeEntryExpiresAt !== null && now < state.freeEntryExpiresAt;
return {
maySendNonTemplate: serviceWindowOpen,
expectedDeliveryCharge: serviceWindowOpen && !freeEntryActive,
requiredPath: serviceWindowOpen ? "service" : "approved_template",
} as const;
}
Hãy dùng kết quả như chốt kiểm tra cuối cùng, không chỉ để hiển thị UI. Một phản hồi soạn lúc 10:00 có thể chờ duyệt đến sau khi cửa sổ đóng. Khi nhận job, sender phải đọc lại trạng thái hiện tại rồi chọn:
- Gửi service reply không dùng template khi cửa sổ còn mở.
- Chuyển sang template đã được phê duyệt khi cửa sổ đóng và ý định phù hợp.
- Dừng gửi và trả hội thoại cho agent nếu cả hai đường đều không hợp lệ.
Không tự động biến nội dung tự do thành template hoặc cắt nội dung để vừa template. Category, biến và phê duyệt template là các contract riêng.
Không kéo dài cửa sổ bằng event sai
Chỉ tin nhắn do người dùng gửi mới được đặt lại cửa sổ. Delivery receipt, status update, bản nháp của agent, ghi chú nội bộ, retry và echo của tin nhắn doanh nghiệp không được kéo dài nó.
Checklist xử lý:
- Xác minh webhook của provider trước khi thay đổi state.
- Loại trùng bằng message ID hoặc event ID ổn định.
- Xác nhận đây là inbound message do người dùng gửi, không phải receipt hoặc echo.
- Đặt
service_window_expires_atbằng thời gian tin nhắn đã chấp nhận cộng 24 giờ. - Chỉ tạo hạn miễn phí 72 giờ cho referral đủ điều kiện.
- Tăng
state_versionvà lưu event nguồn để audit. - Tính lại cả hai đồng hồ ngay trước mỗi lần gửi.
Không cộng 24 giờ vào hạn cũ. Nếu người dùng gửi lúc 09:00 và 12:00, hạn mới là 12:00 ngày hôm sau.
Kiểm thử biên queue và thứ tự event
Dùng fixed clock để kiểm thử chính xác đến một giây.
| Tình huống | Kết quả mong đợi |
|---|---|
| Người dùng gửi 10:00; phản hồi 09:59:59 hôm sau | Cho phép phản hồi không dùng template |
| Phản hồi đúng 10:00 hôm sau | Coi cửa sổ đã đóng |
| Người dùng gửi tiếp lúc 09:50 hôm sau | Hạn mới là 09:50 của ngày kế tiếp |
| Duyệt trước hạn nhưng job được lấy sau hạn | Chặn và route lại |
| Cùng một tin nhắn người dùng được giao hai lần | Deduplicate để chỉ chuyển state một lần |
| Tin nhắn cũ đến sau tin nhắn mới | Giữ hạn mới hơn, không làm state lùi lại |
| Hai worker cùng lấy một phản hồi | Chỉ một worker qua kiểm tra version và gửi |
Sau khi chạy thật, đối chiếu dự báo với status và pricing record chính thức. Hướng dẫn theo dõi phí service message tách volume giao tin khỏi mức giá theo thị trường. Nếu đang chọn Meta Business Agent hay AI riêng, xem so sánh giá và kiến trúc.
UnifyPort phù hợp ở đâu
State machine trên áp dụng cho WhatsApp Business Platform chính thức. Giao diện không chính thức của UnifyPort là một đường khác cho messaging account thông thường và không cung cấp trạng thái cửa sổ hay Pricing Analytics của Meta.
Trên đường riêng này, tin nhắn WhatsApp inbound đến dưới dạng event chuẩn hóa message.received. Nếu webhook endpoint có signing_secret, hãy xác minh X-Device-Timestamp và X-Device-Signature trên raw request body trước khi xử lý. Phản hồi chuẩn được hỗ trợ dùng POST /v1/messages. Xem tài liệu giao webhook và chữ ký cùng event message.received.
Không đưa event UnifyPort vào state machine tính phí Cloud API rồi nói rằng Meta đã phân loại nó. Nếu dùng cả hai đường, hãy lưu transport hoặc control_plane và tách sổ theo dõi.
Giới hạn và đánh đổi
WhatsApp Business Platform chính thức phù hợp khi cần template đã duyệt, campaign, attribution Click-to-WhatsApp, analytics của Meta hoặc dịch vụ BSP. Record chính thức là nguồn quyết định phí thực tế của delivery chính thức.
Giao diện không chính thức không phê duyệt template, không kéo dài cửa sổ Meta, không cung cấp Pricing Analytics và không thay đổi chính sách WhatsApp. Giá trị của nó là đường kết nối khác cho messaging account thông thường và event đa nền tảng được chuẩn hóa. Cả hai đường đều cần idempotency, kiểm soát concurrency và audit log.
Giá và quy tắc có thể thay đổi. Hãy kiểm tra lại tài liệu chính thức trước ngày 1/10 và không hard-code mức giá dự báo vào logic chuyển state.
FAQ
Send guard nên dùng timestamp nào?
Dùng thời điểm của tin nhắn người dùng mới nhất đã xác minh và được hệ thống chấp nhận, không dùng lúc nhận webhook, lúc tạo job hoặc đồng hồ đếm ngược trên UI.
Webhook sai thứ tự có làm cửa sổ ngắn lại không?
Không nên. Deduplicate bằng event ID ổn định và chỉ cập nhật hạn cùng state_version khi inbound message mới hơn record hiện tại.
Phản hồi trong queue hết hạn trước khi gửi thì sao?
Dừng free-form text và route lại. Chỉ chuyển sang template khi intent thực sự khớp một template đã duyệt; nếu không, trả hội thoại cho agent.
Có thể tự động đổi free-form text hết hạn thành template không?
Không được đổi âm thầm. Category, variable và approval của template là contract riêng cần được chọn và xác minh rõ ràng.
Làm sao tránh event trùng gây gửi hai lần?
Lưu message ID hoặc event ID, rồi để worker kiểm tra idempotency key và state_version trong transaction hoặc lock trước khi gửi.
Bước tiếp theo
Hãy làm đúng biên inbound trước: đọc tài liệu giao webhook và chữ ký, rồi kiểm thử event trùng hoặc sai thứ tự không làm cửa sổ bị kéo dài nhầm.
Nguồn
Nguồn chính thức được kiểm tra ngày 27/7/2026: