Hướng dẫn tích hợp Sapo & Shopee với OpsOne AI
Khi anh/chị bán hàng đa kênh (cửa hàng Sapo, Shopee, Zalo, web), việc nhập tay từng đơn vào OpsOne AI rất tốn thời gian và dễ sai. Tài liệu này hướng dẫn anh/chị thiết lập tích hợp 2 chiều để OpsOne AI tự động kéo đơn từ Sapo về, đẩy trạng thái thanh toán ngược lại, và đồng bộ tồn kho — chỉ cấu hình 1 lần, chạy mãi.
1. Tại sao phải tích hợp?
Nếu anh/chị đang dùng Sapo POS hoặc Sapo Omnichannel (cửa hàng + Shopee + Lazada + TikTok Shop), thì Sapo là nơi nhận đơn. Còn OpsOne AI là nơi anh/chị quản trị tổng thể: công nợ, dòng tiền, kế toán, kho, báo cáo lãi lỗ. Hai hệ phải kết nối, nếu không sẽ xảy ra 3 vấn đề:
- Nhập tay đơn 2 lần — nhân viên gõ lại đơn Sapo vào OpsOne, mỗi ngày 50-200 đơn = mất 2-3 giờ và sai sót.
- Tồn kho lệch — Sapo bán ra nhưng OpsOne không trừ kho → kế toán không biết hàng còn bao nhiêu.
- Công nợ lệch — khách thanh toán trên Sapo nhưng OpsOne vẫn ghi "Chưa thu" → đòi nợ nhầm khách.
Tích hợp Sapo + OpsOne giải quyết cả ba: đơn tự sync về OpsOne theo nhịp anh/chị đặt (TBBV đang để 15 phút), thanh toán đẩy ngược lên Sapo, tồn kho cập nhật 2 chiều.
2. Thuật ngữ cần nhớ
sapo_api_key = abc123…, sapo_token = xyz789…https://app.opsoneai.com/api/sapo-webhook/...sapo_sync_interval, tính bằng phút) lại kéo đơn mới từ Sapo về. Đây là "lưới an toàn" — kể cả khi Webhook bị lỗi, Scheduler vẫn đảm bảo đơn được sync trong tối đa một nhịp.sapo_id để biết nó nguồn gốc từ Sapo.sapo_id để map 1-1 giữa 2 hệ: đơn nào đã sync rồi sẽ không bị Pull trùng lần thứ 2.SP12345 có sapo_id = 987654321 trong DB OpsOne.shopee, lazada, tiktok. Sapo Omnichannel sẽ gắn nhãn này lên đơn để anh/chị biết đơn về từ kênh nào.3. Tích hợp Sapo từng bước
3.1. Lấy API key + Access Token từ Sapo admin
- Đăng nhập trang admin Sapo của anh/chị (vd
https://abc.mysapo.vn/admin). - Vào menu Cấu hình → Thiết lập cửa hàng → API (hoặc Quản lý ứng dụng tuỳ phiên bản Sapo).
- Bấm Tạo ứng dụng mới → đặt tên (ví dụ "OpsOne AI Integration") → cấp quyền: Đơn hàng (read/write), Khách hàng (read), Sản phẩm (read), Tồn kho (read/write).
- Sapo sinh ra 2 mã:
API KeyvàAccess Token. Copy cả hai vào nơi an toàn (không gửi qua chat). - Ghi nhớ thêm tên miền cửa hàng (subdomain trước
.mysapo.vn) — đây làsapo_store.
3.2. Điền credentials vào OpsOne
- Đăng nhập app.opsoneai.com bằng tài khoản admin của project.
- Vào Cài đặt (bánh răng góc dưới sidebar) → tab Tích hợp.
- Khu vực Sapo, điền 3 trường:
sapo_store— tên miền cửa hàng Sapo (vdabcnếu shop làabc.mysapo.vn).sapo_api_key— API Key vừa lấy.sapo_token— Access Token vừa lấy.
- Bấm Lưu. OpsOne mã hóa và lưu vào DB, không hiển thị lại token sau khi lưu.
3.3. Bật cờ sapo_enabled
Sau khi điền credentials, anh/chị cần bật cờ sapo_enabled = 1 cho project. Có 2 cách:
- UI: trong tab Tích hợp có toggle "Kích hoạt đồng bộ Sapo" — bật lên.
- Super admin: nếu là tenant mới, super admin (tenant_id=1) sẽ bật giúp.
3.4. Test kết nối
- Vẫn ở tab Tích hợp, bấm nút "Test kết nối".
- Nếu trả về
OK+ số đơn hàng 7 ngày gần nhất → cấu hình đúng. - Nếu trả về
401 Unauthorized→ token sai hoặc hết hạn, lấy lại từ Sapo. - Nếu trả về
403 Forbidden→ app Sapo chưa cấp đủ quyền (vào lại bước 3.1, mở thêm scope). - Nếu timeout → IP server có thể đang bị Sapo block, liên hệ support OpsOne.
4. Pull đơn từ Sapo
Sau khi bật sapo_enabled, OpsOne tự động chạy Scheduler kéo đơn về theo nhịp sapo_sync_interval. ⚠️ Ô này mặc định 0 = TẮT, nên phải đặt một số phút (TBBV đang để 15) — bật sapo_enabled một mình thì Scheduler chưa kéo gì.
Cơ chế hoạt động
- Scheduler gọi API
GET /orders.jsoncủa Sapo với filtercreated_on_min= lần Pull gần nhất. - Mỗi đơn mới được insert vào bảng
orderscủa OpsOne với:sapo_id= ID đơn trên Sapo (dùng để chống trùng).source = 'sapo'để báo nguồn gốc.payment_statusmap từ Sapo: paid → "Đã thanh toán", partially_paid → "Thanh toán 1 phần", pending → "Chưa thanh toán".items(JSON) chứa danh sách sản phẩm + giá + số lượng (lưu ý: OpsOne không tách thànhorder_itemsriêng).
- Đơn đã sync rồi sẽ bị skip (check
sapo_idđã tồn tại) — không sợ duplicate.
Đồng bộ thủ công (nếu cần kéo gấp)
Nếu anh/chị không muốn chờ tới nhịp kế tiếp (ví dụ vừa có đơn cao điểm cần xử lý ngay):
- Vào module CRM → tab Đơn hàng.
- Bấm nút "Đồng bộ Sapo ngay" ở góc phải.
- Hệ thống chạy Pull ngay lập tức, hiện thông báo "Đã sync X đơn mới" trong 5-30 giây.
5. Push transaction (thanh toán) lên Sapo
Đây là phần "2 chiều" quan trọng nhất. Khi anh/chị thu tiền 1 đơn Sapo trong OpsOne, hệ thống sẽ tự đẩy giao dịch thanh toán đó ngược về Sapo để Sapo cập nhật financial_status = paid.
Điều kiện kích hoạt Push
- Phiếu nhận tiền (Receipt) đã được Approve trong OpsOne.
- Đơn liên quan có
sapo_id != null(tức đơn nguồn gốc Sapo). - Project có
sapo_enabled = 1và credentials hợp lệ.
Luồng Push
Push thất bại — xử lý thế nào?
- Nếu Push lỗi (network, token hết hạn), phiếu nhận tiền vẫn được approve thành công trong OpsOne — lỗi push chỉ log lại, không rollback.
- Anh/chị có thể bấm "Đẩy lại lên Sapo" ở chi tiết phiếu (sau khi xử lý nguyên nhân).
- Scheduler retry tự động sau 15 phút nếu lỗi tạm thời.
6. Đồng bộ tồn kho
Khi đơn Sapo về OpsOne, hệ thống đồng thời sinh các bản ghi trong bảng stock_transactions để Module Kho biết phải trừ tồn kho bao nhiêu sản phẩm.
Quy tắc sinh stock_transactions
- Scheduler quét các đơn có
sapo_id IS NOT NULL(KHÔNG dùngsource = 'sapo'— đây là điểm khác biệt quan trọng). - Mỗi item trong đơn → 1 dòng stock_transactions (loại "xuất bán").
- Dữ liệu này được Module Phân tích kho dùng để tính tốc độ bán, sản phẩm bán chạy, dự báo cần nhập thêm.
sapo_id IS NOT NULL mà không phải source = 'sapo'? Trong quá trình migrate dữ liệu cũ, có một số đơn được tạo bằng tay nhưng vẫn gắn sapo_id (để map về Sapo sau này), và ngược lại đơn Sapo cũ chưa cập nhật cột source. Lọc theo sapo_id IS NOT NULL là nguồn chân lý duy nhất.
Nếu Module Phân tích kho hiển thị số sai
- Kiểm tra Scheduler có chạy không: vào Cài đặt → Scheduler status.
- Chạy thủ công lệnh re-build stock_transactions từ tab Tích hợp.
- Nếu vẫn sai, liên hệ support — có thể cần backfill từ đơn cũ.
7. Tích hợp Shopee (và các sàn khác)
Cách 1 — Qua Sapo Omnichannel (khuyến nghị)
Đây là cách hoạt động ngay hôm nay:
- Anh/chị bật Sapo Omnichannel và kết nối shop Shopee/Lazada/TikTok vào Sapo (phía Sapo có hướng dẫn riêng).
- Mỗi đơn về từ Shopee sẽ được Sapo nhận vào hệ thống, có nhãn
channel = 'shopee'. - OpsOne Pull từ Sapo về như thường lệ — đơn Shopee tự kèm theo nhãn channel để báo cáo phân tích theo kênh.
Cách 2 — Tích hợp Shopee trực tiếp (roadmap)
Một số anh/chị không dùng Sapo Omnichannel, chỉ có shop Shopee độc lập. OpsOne đang phát triển adapter Shopee Partner API riêng (gọi thẳng Shopee, không qua Sapo). Tiến độ:
- Hiện tại: Beta nội bộ, hỗ trợ Pull đơn + Push trạng thái.
- Q3/2026: Ra mắt công khai, tích hợp Push thanh toán + cập nhật tồn kho 2 chiều.
- Đăng ký dùng sớm: liên hệ [email protected].
8. Cấu hình Webhook (real-time, advanced)
Nhịp 15 phút đã đủ cho hầu hết doanh nghiệp. Nhưng nếu anh/chị bán hàng cao điểm (Sale 12.12, Black Friday, livestream), độ trễ một nhịp có thể quá lâu. Lúc đó cấu hình Webhook để Sapo "gọi" OpsOne ngay khi có đơn.
URL endpoint của OpsOne
| Sự kiện | URL Webhook |
|---|---|
| Đơn được tạo | https://app.opsoneai.com/api/sapo-webhook/orders/create |
| Đơn cập nhật | https://app.opsoneai.com/api/sapo-webhook/orders/update |
| Đơn bị hủy | https://app.opsoneai.com/api/sapo-webhook/orders/cancel |
| Thanh toán cập nhật | https://app.opsoneai.com/api/sapo-webhook/transactions/update |
Cách cấu hình
- Vào admin Sapo → Cấu hình → Thông báo Webhook.
- Bấm Tạo Webhook, chọn sự kiện (ví dụ "Đơn hàng được tạo").
- Dán URL tương ứng từ bảng trên.
- Chọn format JSON.
- Lưu. Sapo sẽ gửi test request — OpsOne trả 200 OK.
X-Sapo-Hmac-Sha256. Anh/chị phải cấp webhook_secret trong tab Tích hợp để hệ thống xác thực. Không có secret → webhook bị từ chối 401.
9. Xử lý sự cố thường gặp
Đơn Sapo không sync về OpsOne sau 10 phút
Check theo thứ tự:
- sapo_enabled = 1? Vào Cài đặt → Tích hợp, đảm bảo toggle "Kích hoạt đồng bộ" đang bật.
- Scheduler còn chạy? Liên hệ support, họ check
pm2 logs tbbv-ops | grep sapotrên server. - API key còn hạn? Bấm "Test kết nối" — nếu 401, lấy lại token từ Sapo admin.
- IP server bị block? Sapo đôi khi rate-limit hoặc geo-block. Liên hệ Sapo support để whitelist IP
157.245.159.48. - Đơn có nằm trong khoảng filter? Đơn quá cũ (trước thời điểm bật sync) sẽ không tự kéo — phải backfill thủ công.
Push thanh toán lên Sapo bị lỗi 400 hoặc 404
Nguyên nhân thường gặp:
- 404: Đơn đã bị xóa trên Sapo (admin Sapo xóa thủ công) → OpsOne không tìm thấy đơn để gắn transaction.
- 400: Số tiền push lớn hơn số tiền đơn (vd đơn 100K, OpsOne push 120K) → Sapo từ chối.
- 422: Sai gateway code (vd OpsOne gửi "cash" nhưng Sapo cấu hình không có method này) → vào Cài đặt → mapping payment method.
Tồn kho lệch giữa Sapo và OpsOne
Sapo là nơi giữ tồn "kho POS / kho bán" thật. OpsOne giữ tồn "kho tổng" + "kho phân tích". Lệch nhẹ là bình thường khi:
- Có hàng chuyển kho nội bộ OpsOne mà Sapo không biết.
- Có nhập hàng vào OpsOne (PO) chưa đẩy lên Sapo.
Để 2 hệ khớp 100%, dùng tính năng Đồng bộ tồn 2 chiều trong tab Tích hợp (chạy 1 lần/ngày lúc đêm).
Webhook bị Sapo gọi nhưng OpsOne không tạo đơn
Check log webhook: vào Cài đặt → Webhook log. Thường do:
- Signature verify fail (secret sai).
- Payload format Sapo đổi mới (cần update adapter).
- Đơn đã tồn tại từ Scheduler trước đó (Webhook đến sau → skip duplicate).
10. Sapo Disabled — bật/tắt tích hợp
Có khi anh/chị muốn tạm tắt sync (đổi shop Sapo, đang test, hoặc Sapo bảo trì). Vào tab Tích hợp → tắt toggle "Kích hoạt đồng bộ Sapo". Cờ sapo_enabled chuyển về 0.
Khi sync đang tắt
- Scheduler bỏ qua project này, không Pull đơn mới.
- Push thanh toán cũng bị skip.
- Mọi thao tác liên quan Sapo (vd nút "Hủy đơn trên Sapo" trong CRM) sẽ trả lỗi
sapo_disabled(HTTP 403). - Dữ liệu cũ không bị mất — đơn đã sync vẫn nằm trong OpsOne, chỉ là không cập nhật mới.
11. Ba tình huống thực tế
Anh/chị bán hàng tại quầy bằng máy POS Sapo. OpsOne dùng để theo dõi công nợ NCC, dòng tiền, lãi lỗ.
Cấu hình tối thiểu: chỉ cần làm bước 3.1 → 3.4. Không cần Webhook. Push thanh toán cũng không cần vì khách trả ngay tại POS, Sapo đã ghi paid.
Anh/chị nhận đơn từ 3 nguồn: Shopee (qua Sapo Omnichannel), khách Sapo POS tại cửa hàng, Zalo OA (nhân viên tạo đơn tay trong OpsOne).
Cấu hình khuyến nghị: bật Webhook để Shopee về real-time (sale 12.12 không lo trễ), kích hoạt Push thanh toán để báo cáo doanh thu khớp với báo cáo Sapo. Channel filter trong OpsOne dùng để xem hiệu suất từng sàn.
Anh/chị có nhiều cửa hàng Sapo (mỗi chi nhánh 1 shop riêng) và nhiều kho. Cần OpsOne tổng hợp lại.
Cấu hình: tạo nhiều "Sapo Connection" trong tab Tích hợp, mỗi connection 1 cặp credentials + 1 store_id riêng. OpsOne map đơn từng store về warehouse tương ứng dựa trên rule mapping (vd sapo_store_hcm → warehouse_id 5). Báo cáo có filter theo cửa hàng + kho. Tính năng multi-store đang trong Beta — liên hệ support để bật.
12. Câu hỏi thường gặp
Sapo và OpsOne, đâu là "source of truth"?
Tùy loại dữ liệu:
- Đơn hàng: Sapo là gốc (đơn sinh ra ở Sapo trước, OpsOne Pull về).
- Thanh toán: OpsOne là gốc (phiếu nhận tiền approve ở OpsOne, push lên Sapo).
- Tồn kho: tùy module — Sapo giữ tồn POS, OpsOne giữ tồn tổng/phân tích. Đồng bộ 2 chiều 1 lần/ngày.
- Khách hàng + NCC + Sản phẩm: OpsOne là gốc. ⛔ Từ 13/08/2026 OpsOne KHÔNG còn đẩy SẢN PHẨM sang Sapo (CEO chốt: OpsOne là nơi quản lý sản phẩm). Sapo chỉ còn nhận ĐƠN từ sàn và đồng bộ TỒN KHO.
Đơn Sapo đã hủy có sync về OpsOne không?
Có. Khi đơn bị hủy trên Sapo, lần Pull tiếp theo (hoặc Webhook cancel) sẽ cập nhật đơn OpsOne sang trạng thái "Đã hủy" + ghi lý do. Đơn không bị xóa khỏi DB — vẫn giữ lại để audit. Nếu đơn đã có phiếu nhận tiền approved, hệ thống sẽ cảnh báo và yêu cầu hoàn tiền thủ công trước khi đánh dấu cancel.
Marketplace Shopee có thể tích hợp trực tiếp không qua Sapo không?
Hiện tại khuyến nghị qua Sapo. Adapter Shopee Partner API trực tiếp đang trong Beta nội bộ, dự kiến public Q3/2026. Lý do chậm: Shopee API phức tạp (cần đăng ký Shopee Partner, review app, OAuth dài), trong khi Sapo Omnichannel đã làm sẵn việc này.
Sapo đã nhận transaction từ OpsOne rồi mà vẫn show "Chưa thanh toán"?
Có 2 nguyên nhân:
- Sapo cache: Sapo admin caching 1-2 phút. Bấm F5 sau 2 phút.
- Transaction kind sai: OpsOne push
kind = "sale", nhưng Sapo expectkind = "capture"cho 1 số config gateway. Liên hệ support để kiểm tra adapter mapping.
Disable Sapo tạm thời có làm mất dữ liệu không?
Không. Tắt sapo_enabled chỉ tạm dừng Scheduler + Push. Đơn đã sync vẫn nằm nguyên trong OpsOne. Credentials cũng được giữ lại — bật lại là chạy tiếp. Lần Pull đầu tiên sau khi bật lại sẽ catch-up các đơn phát sinh trong khoảng thời gian tắt (giới hạn 30 ngày lùi về trước).
OpsOne có hỗ trợ multi-store Sapo không?
Có, trong Beta. 1 project OpsOne kết nối nhiều shop Sapo, mỗi shop 1 cặp credentials. Đơn từ mỗi shop được gắn sapo_store_id riêng để báo cáo phân tích. Cần liên hệ support bật tính năng. Sau khi bật, UI Cài đặt sẽ hiện nút "Thêm Sapo Connection".
Nếu tôi đổi shop Sapo mới (đổi tên miền) thì sao?
Vào Cài đặt → Tích hợp → cập nhật sapo_store + sapo_api_key + sapo_token mới. Đơn cũ từ shop cũ vẫn nằm trong OpsOne (không bị xóa), đơn mới sẽ kéo từ shop mới. Lưu ý: sapo_id của 2 shop có thể trùng số → tránh confusion bằng cách archive đơn cũ trước khi đổi shop.
Webhook Sapo có miễn phí không?
Webhook phía Sapo là tính năng miễn phí của Sapo (mọi gói đều có). Phía OpsOne cũng không tính thêm phí cho việc nhận Webhook. Chỉ Push tới các sàn TMĐT khác (ngoài Sapo) qua Partner API riêng có thể phát sinh phí theo gói.
Cần hỗ trợ tích hợp?
Đội kỹ thuật OpsOne giúp anh/chị cấu hình Sapo + Shopee miễn phí trong gói Pro trở lên. Khách Free có thể tự làm theo hướng dẫn này.
Xem các gói Liên hệ hỗ trợ