====================================================================== HƯỚNG DẪN KẾT NỐI SẢN PHẨM ĐẠI LÝ ====================================================================== Tài liệu này có hai phần: PHẦN A — Chủ Đại lý làm theo 5 bước ngắn. PHẦN B — Người tích hợp dùng để gắn connector vào bot đang hoạt động. ###################################################################### # PHẦN A — DÀNH CHO CHỦ ĐẠI LÝ ###################################################################### 🟦 BƯỚC 1 — NHẬN KEY TỪ ADMIN 1. Admin mở bot nguồn hàng và bấm /start. 2. Chọn API / Kết nối nguồn hàng. 3. Bấm “TẠO KEY KẾT NỐI”. Bot tạo key ngay, không cần tạo hồ sơ trước. 4. Gửi CONNECTION_URL, REGISTRATION_KEY và file hướng dẫn này cho chủ Đại lý hoặc người đang quản lý bot Đại lý. 🟨 BƯỚC 2 — TẠO KEY 1. Vào /start → API / Kết nối nguồn hàng. 2. Bấm “TẠO KEY KẾT NỐI”. 3. Bot sẽ gửi ngay: - CONNECTION_URL - REGISTRATION_KEY - File ML612_CONNECTOR_SETUP.txt này Key có hiệu lực 10 phút và chỉ dùng một lần. Nếu hết hạn, tạo key mới. 🟩 BƯỚC 3 — GỬI CHO NGƯỜI TÍCH HỢP Gửi trong một lần: 1. CONNECTION_URL 2. REGISTRATION_KEY 3. File ML612_CONNECTOR_SETUP.txt 4. Source hiện tại của bot Đại lý Yêu cầu họ làm đúng PHẦN B và giữ nguyên toàn bộ chức năng đang có của bot. 🟪 BƯỚC 4 — KIỂM TRA SAU KHI TÍCH HỢP 1. Sản phẩm cũ của bot Đại lý vẫn hiển thị và mua bình thường. 2. Sản phẩm được Admin cấp từ nguồn mới đã xuất hiện. 3. Giá bán của Đại lý không thấp hơn giá sỉ. 4. Số dư nhập hàng đọc đúng. 5. Thử một đơn nhỏ sau khi xác nhận đã nhận tiền khách. 6. Kiểm tra hàng được giao, ví bị trừ đúng giá sỉ và đơn có trong báo cáo. 🟥 BƯỚC 5 — NẾU CÓ LỖI - Không tạo được key: kiểm tra tài khoản đang bấm có nằm trong danh sách Telegram nhận báo Admin hay không. - Đăng ký báo thiếu Bot Token: người tích hợp cần lấy đúng Bot Token đang chạy bot Đại lý và gửi trong request đăng ký HTTPS theo Mục 3. - Key hết hạn/đã dùng: tạo key mới. - Không thấy sản phẩm: nhờ Admin kiểm tra danh sách sản phẩm được cấp. - Giá bán bị chặn: tăng giá bán bằng hoặc cao hơn giá sỉ hiện tại. - Request bị từ chối sau khi đổi VPS/IP: nhờ Admin cấp key mới và đăng ký lại từ đúng máy đang chạy bot. ###################################################################### # PHẦN B — DÀNH CHO NGƯỜI TÍCH HỢP BOT ###################################################################### MỤC TIÊU Gắn thêm một nguồn sản phẩm vào bot Đại lý đang hoạt động. Bot, VPS, tên miền, database, menu, thanh toán và sản phẩm sẵn có của Đại lý phải tiếp tục hoạt động như trước. 1. NGUYÊN TẮC KHÔNG ĐƯỢC VI PHẠM 1.1. Không thay bot hiện tại bằng bot mẫu. 1.2. Không xóa, đổi mã hoặc ghi đè sản phẩm sẵn có của Đại lý. 1.3. Không dùng lệnh đồng bộ kiểu xóa toàn bộ danh mục rồi tạo lại. 1.4. Không đổi luồng mua hàng của sản phẩm sẵn có. 1.5. Chỉ gọi connector khi khách chọn sản phẩm thuộc nguồn ML612. 1.6. Sản phẩm cũ tiếp tục chạy handler cũ; sản phẩm ML612 chạy handler mới. 1.7. Không lưu key/secret trong source public, log, tin nhắn hoặc database không mã hóa. 1.8. Không tạo đơn nguồn trước khi bot Đại lý xác nhận khách đã thanh toán. 1.9. Retry phải dùng lại cùng external_order_id và idempotency_key. 2. SAO LƯU TRƯỚC KHI SỬA Trước khi tích hợp, sao lưu tối thiểu: - Toàn bộ source bot hiện tại. - Database hiện tại. - File cấu hình và biến môi trường. - Cấu hình webhook/worker/cron/service đang chạy. Ghi lại checksum hoặc thời điểm backup. Chỉ triển khai khi có thể quay về bản cũ nếu kiểm thử không đạt. 3. ĐĂNG KÝ CONNECTOR MỘT LẦN Nhận từ chủ Đại lý: CONNECTION_URL= REGISTRATION_KEY= Từ đúng VPS production của bot Đại lý, gửi POST JSON tới CONNECTION_URL: { "registration_key": "REGISTRATION_KEY", "client_id": "ID_ON_DINH_RIENG_CUA_BOT", "version": "connector-1.0", "delivery_bot_token": "BOT_TOKEN_DANG_CHAY_BOT_DAI_LY", "shop_name": "TEN_DAI_LY", "owner_telegram_id": "TELEGRAM_ID_CHU_DAI_LY", "owner_username": "USERNAME_KHONG_CO_DAU_AT" } delivery_bot_token phải lấy từ cấu hình hiện tại của chính bot Đại lý. Gửi duy nhất trong request HTTPS đăng ký; hệ thống nguồn xác minh với Telegram, mã hóa trước khi lưu và không trả token trong response. Token này dùng để giao hàng trực tiếp cho khách bằng đúng bot Đại lý. shop_name, owner_telegram_id và owner_username dùng để tự tạo hồ sơ Đại lý trong Admin. owner_username không có dấu @. Nếu chủ Đại lý chưa cung cấp Telegram ID/username thì có thể để hai trường owner_* là chuỗi rỗng và Admin bổ sung sau. Yêu cầu client_id: - Dài 16–128 ký tự. - Chỉ dùng chữ, số, dấu chấm, gạch dưới hoặc gạch ngang. - Sinh ngẫu nhiên một lần rồi giữ ổn định cho bot này. Kết quả thành công trả về một lần: - api_url - api_key - api_secret - auth = hmac-sha256-v1 Lưu ba giá trị api_url, api_key, api_secret vào secret hoặc biến môi trường ngoài web root, quyền đọc chỉ dành cho tiến trình bot. Xóa REGISTRATION_KEY khỏi môi trường ngay sau khi đăng ký thành công. 4. KÝ MỖI REQUEST Mỗi request nghiệp vụ là POST JSON tới api_url. Tạo: raw_json = chuỗi JSON chính xác sẽ gửi timestamp = Unix time hiện tại tính bằng giây nonce = chuỗi ngẫu nhiên mới cho từng request body_hash = SHA256(raw_json) canonical = timestamp + "\n" + nonce + "\n" + body_hash signature = HMAC_SHA256(canonical, api_secret) Gửi các header: Content-Type: application/json Accept: application/json X-ML612-Key: api_key X-ML612-Timestamp: timestamp X-ML612-Nonce: nonce X-ML612-Signature: signature X-ML612-Client-Version: phiên_bản_connector Lưu ý: - Ký đúng raw_json, không ký object trước rồi encode thành chuỗi khác. - Mỗi lần gọi phải dùng nonce mới. - Đồng bộ giờ hệ thống VPS. - Bật xác minh chứng chỉ HTTPS. - Không tự đi theo redirect khi gửi request có chữ ký. - Timeout hợp lý: kết nối 5 giây, toàn request khoảng 20 giây. 5. TÁCH SẢN PHẨM NGUỒN MỚI KHỎI SẢN PHẨM CŨ Khuyến nghị tạo bảng riêng, ví dụ supplier_products, hoặc bổ sung các cột: source_type = ml612 supplier_product_id supplier_sku wholesale_price retail_price supplier_active supplier_synced_at Khóa duy nhất nên là: (source_type, supplier_product_id) Không dùng product_id từ nguồn làm ID chính của sản phẩm cũ. Không đối chiếu chỉ bằng tên vì hai nguồn có thể trùng tên. Khi hiển thị menu: danh_mục_đại_lý = sản_phẩm_cũ_đang_bán + sản_phẩm_ML612_được_cấp Khi khách chọn mua: if source_type == "ml612": chạy luồng connector else: chạy nguyên luồng mua hàng cũ 6. ĐỒNG BỘ SẢN PHẨM Request: {"action":"products"} Với từng sản phẩm trả về: - Upsert theo supplier_product_id trong namespace ml612. - Cập nhật tên, mã, trạng thái, giá sỉ và giá bán hiện tại. - Chỉ cập nhật dòng thuộc source_type=ml612. - Sản phẩm không còn trong lần đồng bộ được đánh dấu tạm ẩn; không hard delete. - Tuyệt đối không sửa sản phẩm local của Đại lý. Nên đồng bộ: - Khi khởi động worker. - Theo lịch nền định kỳ. - Khi Admin/Đại lý bấm “Đồng bộ ngay”. - Trước trang thanh toán có thể kiểm tra lại giá của đúng sản phẩm. Nếu đồng bộ lỗi, giữ danh mục cũ và báo “tạm chưa cập nhật”, không xóa dữ liệu. 7. GIÁ BÁN VÀ LỢI NHUẬN Đại lý được tăng giá bán nhưng: retail_price >= wholesale_price Gọi đặt giá: { "action": "set_retail_price", "product_id": PRODUCT_ID_NGUON, "retail_price": GIA_BAN_VND } Quy tắc tiền: - Khách thanh toán cho Đại lý theo retail_price. - Nguồn trừ ví nhập hàng theo wholesale_price × quantity. - Lãi Đại lý = retail_price − wholesale_price. - Giá được chụp tại thời điểm tạo đơn để đối soát không đổi về sau. Nếu giá sỉ mới cao hơn giá bán cũ, dừng bán riêng sản phẩm đó và yêu cầu Đại lý đặt lại giá. Không tự hạ giá sỉ và không tạo đơn khi giá bán chưa hợp lệ. 8. TẠO ĐƠN ĐÚNG LUỒNG Chỉ gọi create_order sau khi hệ thống hiện tại đã xác nhận khách thanh toán. Request mẫu: { "action": "create_order", "external_order_id": "MA_DON_ON_DINH_CUA_BOT_DAI_LY", "idempotency_key": "KHOA_ON_DINH_CUA_DON", "product_id": PRODUCT_ID_NGUON, "quantity": SO_LUONG, "buyer_telegram_id": TELEGRAM_ID_KHACH, "buyer_username": "username_khach" } external_order_id: - Là mã đơn đã lưu trong database bot Đại lý. - Không đổi khi retry. idempotency_key: - Dài 8–100 ký tự ASCII an toàn. - Sinh ổn định từ mã đơn + sản phẩm + số lượng + khách. - Lưu vào database cùng đơn trước lần gọi đầu tiên. - Mọi retry của cùng đơn phải dùng đúng key cũ. Nếu request timeout hoặc mất mạng, không tạo mã đơn mới. Chuyển sang tra trạng thái bằng external_order_id. 9. TRA TRẠNG THÁI ĐƠN Theo mã đơn phía Đại lý: { "action": "order_status", "external_order_id": "MA_DON_ON_DINH_CUA_BOT_DAI_LY" } Hoặc theo order_code nguồn đã trả về: { "action": "order_status", "order_code": "MA_DON_NGUON" } Các trạng thái công khai cần xử lý: - delivery_pending: đang chờ giao - delivering: đang giao - delivered: đã giao thành công - delivery_failed: giao chưa thành công Worker nên tiếp tục kiểm tra các đơn chưa kết thúc. Không tạo lại đơn chỉ vì chưa nhận phản hồi ngay. 10. SỐ DƯ, CẤU HÌNH SHOP VÀ BÁO CÁO Đọc số dư nhập hàng: {"action":"balance"} Đọc cấu hình hiển thị của shop: {"action":"shop_config"} Đọc báo cáo lãi theo kỳ: { "action": "commission_summary", "from": "YYYY-MM-DD", "to": "YYYY-MM-DD" } Hiển thị rõ cho Đại lý: - Số dư nhập hàng. - Giá sỉ, giá bán và lãi từng sản phẩm. - Đơn đang xử lý, đã giao và giao lỗi. - Doanh thu, giá vốn và lãi đã chốt. 11. XỬ LÝ LỖI Quy tắc chung: - 401/403: dừng gọi, báo cần kiểm tra connector/quyền/IP. - 409 IDEMPOTENCY_CONFLICT: không retry với nội dung khác; kiểm tra đơn cũ. - 422 giá không hợp lệ: yêu cầu Đại lý cập nhật giá bán. - 429: tôn trọng thời gian chờ rồi retry có giới hạn. - 5xx/mất mạng: backoff, sau đó gọi order_status nếu liên quan đơn hàng. Log chỉ nên lưu: - request_id - action - mã đơn nội bộ - HTTP status - error.code - thời gian xử lý Không ghi api_secret, chữ ký, raw request chứa credential hoặc nội dung nhạy cảm. 12. CHECKLIST NGHIỆM THU BẮT BUỘC [ ] Đã backup source và database bot Đại lý. [ ] Đăng ký key từ đúng VPS production thành công. [ ] Credentials được lưu riêng và Registration Key đã xóa. [ ] Sản phẩm cũ vẫn hiển thị và mua được như trước. [ ] Sản phẩm ML612 nằm trong namespace riêng, không đè sản phẩm cũ. [ ] Đồng bộ lỗi không làm mất danh mục đang có. [ ] Không thể đặt giá bán thấp hơn giá sỉ. [ ] Đọc số dư thành công. [ ] Tạo một đơn nhỏ sau khi xác nhận thanh toán. [ ] Retry cùng đơn không bị trừ ví hoặc giao hai lần. [ ] Tra trạng thái đơn được khi request tạo đơn timeout. [ ] Ví Đại lý bị trừ đúng giá sỉ × số lượng. [ ] Báo cáo lãi bằng giá bán trừ giá sỉ. [ ] Kiểm tra tốt trên cả máy tính và điện thoại. ====================================================================== KẾT QUẢ ĐẠT: BOT CŨ VẪN HOẠT ĐỘNG + SẢN PHẨM NGUỒN ĐƯỢC BỔ SUNG AN TOÀN ======================================================================