VERSION 1.1 · HMAC-SHA256

API dành cho Đại lý

Kết nối sản phẩm LUONGMMO vào bot bán hàng đang có. Khách trả tiền cho Đại lý; hệ thống nguồn chỉ trừ ví nhập hàng theo giá sỉ và giao sản phẩm trực tiếp bằng bot shop đã liên kết.

Base URLhttps://www.luongmmo.io.vn/api/v1/index.php
Kích hoạtLấy key trong bot nguồn theo hướng dẫn bên dưới
Xác thực4 header HMAC + IP binding

Kích hoạt từ A đến Z

API Key lấy tại bot nguồn được dùng để kích hoạt connector một lần. Sau khi đăng ký thành công, key tự thu hồi; file TXT hướng dẫn không chứa key.

Mở bot nguồn

Vào /start → API → Tạo API Key. Mọi người đều tự tạo được, không cần Admin duyệt hoặc tạo hồ sơ trước.

Tự tạo một key

Bấm Tạo key kết nối. Sao chép key và giữ riêng, không đưa vào file TXT.

Đăng ký từ VPS bot Đại lý

Gửi POST HTTPS từ đúng máy production. IP gọi lần đầu sẽ được ràng buộc.

Lưu credentials

Lưu api_url, api_key, api_secret ngoài web root với quyền 0600.

Đồng bộ và đặt giá

Gọi products, lưu namespace riêng và đặt giá bán không thấp hơn giá sỉ.

Thử đơn nhỏ

Chỉ gọi create_order sau khi bot Đại lý xác nhận đã nhận tiền khách.

Request kích hoạt

Endpoint đăng ký nằm sẵn trong file TXT tạo ở cuối trang. Đại lý chỉ cần lấy key riêng trong bot.

POST https://www.luongmmo.io.vn/reseller/register.phpContent-Type: application/json

{
  "registration_key": "APK_KEY_VUA_TAO_TRONG_BOT",
  "client_id": "bot-dai-ly-id-on-dinh-32-ky-tu",
  "version": "connector-1.0",
  "delivery_bot_token": "BOT_TOKEN_DANG_CHAY_CUA_DAI_LY",
  "shop_name": "Tên shop Đại lý",
  "owner_telegram_id": "123456789",
  "owner_username": "username_khong_co_dau_at",
  "product_events_callback_url": "https://partner.example.com/webhooks/mloder",
  "product_events_enabled": true,
  "product_events_events": ["product.created", "product.restocked", "product.updated"]
}
Không gửi Bot Token qua chat công khai. Token chỉ đi trong request HTTPS kích hoạt, được kiểm tra với Telegram rồi mã hóa trước khi lưu.
Tự cấu hình đồng bộ sản phẩm: nếu gửi product_events_callback_url, hệ thống tự tạo webhook, bật 3 sự kiện và trả product_events.shared_secret đúng một lần trong response. Bot đối tác chỉ cần lưu secret đó ngoài web root rồi nhận callback; không cần Admin tạo webhook thủ công.

Ký mỗi request bằng HMAC

raw_json  = chuỗi JSON chính xác sẽ gửi
timestamp = Unix time hiện tại
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: X-ML612-Key, X-ML612-Timestamp, X-ML612-Nonce, X-ML612-Signature và tùy chọn X-ML612-Client-Version.

Timestamp được lệch tối đa 5 phút. Mỗi nonce chỉ dùng một lần. Chuỗi được ký phải giống byte-for-byte với body gửi đi.

Các action được phép

ActionMục đíchField chính
productsĐọc sản phẩm được Admin cấp, giá sỉ/giá bán.Không cần field thêm.
balanceĐọc số dư ví nhập hàng.Không cần field thêm.
shop_configĐọc tên shop, hỗ trợ và trạng thái nhận đơn.Không cần field thêm.
set_retail_priceĐặt giá bán của Đại lý.product_id, retail_price
create_orderTrừ ví giá sỉ và giao qua bot shop.ID đơn, idempotency, sản phẩm, số lượng, khách.
order_statusTra đơn khi timeout hoặc theo dõi giao hàng.external_order_id hoặc order_code
commission_summaryĐối soát doanh thu, giá vốn và lãi.from, to
Ví dụ: products / balance
{"action":"products"}
{"action":"balance"}
Ví dụ: đặt giá bán
{
  "action": "set_retail_price",
  "product_id": 12,
  "retail_price": 150000
}
Ví dụ: báo cáo lãi
{
  "action": "commission_summary",
  "from": "2026-08-01",
  "to": "2026-08-29"
}
Dashboard ví: response có wallet.balance, wallet.recent_transactions, commission.month_earned, commission.month_credited, commission.lifetime_credited và commission.lifetime_payable. auto_credit_to_wallet=true cho biết hoa hồng được tự động cộng sau khi giao thành công.

Luồng tạo đơn đúng

  1. Bot Đại lý tạo đơn local và nhận tiền khách theo giá bán.
  2. Lưu cố định external_order_id và idempotency_key trước lần gọi đầu.
  3. Gọi create_order. Retry phải dùng lại đúng hai giá trị cũ và body cũ.
  4. Nếu timeout, gọi order_status; không sinh một đơn mới.
  5. Hệ thống nguồn giao trực tiếp cho khách bằng bot Đại lý đã liên kết.
{
  "action": "create_order",
  "external_order_id": "SHOP-ORDER-20260829-0001",
  "idempotency_key": "order-20260829-0001-product-12-qty-1",
  "product_id": 12,
  "quantity": 1,
  "buyer_telegram_id": 123456789,
  "buyer_username": "khachhang"
}
Retry cùng nội dung trả lại đơn cũ, không trừ ví, lấy kho hoặc giao lần hai.

Hoa hồng và ví nhập hàng

Hoa hồng được tính từ đúng snapshot của đơn, không bị thay đổi khi Admin cập nhật giá sản phẩm.

Công thức: hoa hồng = giá bán snapshot − giá sỉ snapshot.
Tự động: chỉ khi giao hàng thành công, phần chênh lệch mới được cộng vào ví nhập hàng. Mỗi đơn chỉ cộng một lần, kể cả khi worker retry hoặc callback lặp.
  • Đơn đang chờ, giao lỗi hoặc bị hủy không được cộng hoa hồng.
  • Số dư ví, hoa hồng tháng này, tổng đã tự động cộng và phần còn chờ đối soát có trong dashboard/commission_summary.
  • Lịch sử cộng/trừ được lưu trong sổ giao dịch ví; chốt thủ công chỉ áp dụng cho phần cũ chưa được cộng, không thể thanh toán trùng.

Thông báo cho cả hai Admin

Sau mỗi mốc đơn hàng, cùng một bộ metadata an toàn được gửi đồng thời cho chủ Đại lý qua bot Đại lý và Admin nguồn qua bot chính.

  • Có đơn mới, giao thành công hoặc giao thất bại.
  • Mã đơn, mã phía Đại lý, sản phẩm, SKU và số lượng.
  • Giá sỉ, giá bán, hoa hồng dự kiến/đã ghi nhận.
  • Hoa hồng đang chờ đối soát, số dư còn lại và trạng thái.
  • Thiếu số dư: cả hai bên nhận số dư hiện tại, số cần và số còn thiếu; không trừ ví, không lấy kho, không giao hàng.
Bảo mật: thông báo không chứa Bot Token, API Secret hoặc nội dung hàng hóa. Chủ Đại lý phải bấm /start bot đối tác; Admin nguồn nhận qua các Admin notification targets đã cấu hình.

Code PHP hoàn chỉnh

Trong gói tích hợp có sẵn examples/register_connector.php và examples/reseller_client.php. Ví dụ ký request tối thiểu:

<?php
$body = ['action' => 'balance'];
$raw = json_encode($body, JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES);
$timestamp = (string)time();
$nonce = bin2hex(random_bytes(16));
$canonical = $timestamp."\n".$nonce."\n".hash('sha256', $raw);
$signature = hash_hmac('sha256', $canonical, getenv('ML612_API_SECRET'));

// POST $raw tới https://www.luongmmo.io.vn/api/v1/index.php với 4 header HMAC.

Hai file mẫu đầy đủ nằm trong gói bàn giao tại examples/reseller_client.php và examples/register_connector.php. Không đặt source chứa secret trong thư mục web công khai.

Xử lý lỗi

HTTP / codeCách xử lý
401 AUTH_REQUIRED / INVALID_SIGNATUREKiểm tra key, secret, timestamp, nonce và raw JSON đã ký.
403 IP_NOT_ALLOWEDConnector đang gọi từ IP khác. Tạo key mới và kích hoạt lại từ VPS đúng.
402 INSUFFICIENT_BALANCENạp thêm ví nhập hàng; không tạo mã đơn mới khi retry.
409 IDEMPOTENCY_CONFLICTID/key cũ đang gắn với body khác. Dừng và đối chiếu đơn local.
409 RETAIL_PRICE_OUTDATEDĐồng bộ sản phẩm và đặt lại giá bán ≥ giá sỉ mới.
429 RATE_LIMITEDBackoff có giới hạn, không spam retry.
5xx / timeoutGiữ nguyên idempotency và gọi order_status.

Quy tắc bảo mật bắt buộc

  • Không commit Registration Key, API key, API secret hoặc Bot Token.
  • Secret file nằm ngoài web root, quyền 0600.
  • Không log raw body chứa credential, chữ ký hoặc nội dung giao hàng.
  • Luôn xác minh TLS; không tự đi theo redirect với request đã ký.
  • Đồng bộ giờ VPS bằng NTP. Timeout đề xuất: connect 5 giây, tổng 20 giây.
  • Sản phẩm nguồn nằm trong namespace riêng; không ghi đè sản phẩm cũ của bot Đại lý.

Checklist nghiệm thu

☐ Domain/HTTPS và health endpoint hoạt động · ☐ Kích hoạt từ VPS production thành công · ☐ Credentials lưu riêng · ☐ Sản phẩm cũ không bị đổi · ☐ Đồng bộ sản phẩm nguồn · ☐ Giá bán không dưới giá sỉ · ☐ Đọc đúng số dư · ☐ Đơn nhỏ giao đúng bot · ☐ Retry không trừ/giao lặp · ☐ Báo cáo lãi đúng.

📄 Tạo file TXT hướng dẫn tích hợp

File được tạo ngay trên trình duyệt và chứa toàn bộ thông tin kỹ thuật liên kết bot. File không chứa Registration Key, API Secret, Bot Token hoặc dữ liệu khách.

1. Tạo file hướng dẫn

Bấm nút dưới đây để xuất file TXT hoàn chỉnh dành cho AI hoặc người tích hợp.

Không có dữ liệu nào được gửi lên server khi tạo file.

2. Cách liên kết bot

Chuẩn bị đúng ba phần sau rồi gửi cho AI/người code:

  1. Full source bot của bạn.
  2. Key lấy riêng tại /start → API → Tạo API Key trong bot nguồn.
  3. File TXT hướng dẫn vừa tạo tại đây.
Full source bot + Key lấy ở bot + File TXT hướng dẫn → ném cho AI làm, hoặc tự làm thủ công nếu có kinh nghiệm.
Không dán key vào trang web hoặc file TXT. Chỉ cung cấp key riêng cho AI/người tích hợp tại thời điểm kích hoạt; xóa key khỏi prompt, log và cấu hình sau khi đổi key thành credentials thành công.

Đồng bộ sản phẩm tự động

Đây là callback riêng cho catalog, không thay thế Order API. Khi đăng ký connector, bot đối tác gửi callback HTTPS để hệ thống tự lưu webhook, secret và 3 loại sự kiện. Catalog sản phẩm đang bán cũng được đồng bộ tự động; dự án thông báo vẫn có công tắc bật/tắt riêng để Admin kiểm soát nơi gửi.

Không cần SSH, Bot Token Telegram hay database của bot nguồn. Bot đối tác chỉ cần endpoint HTTPS công khai. Request sản phẩm được đưa vào outbox và worker gửi nền, không chặn luồng mua hàng/thanh toán.

Response sau khi đăng ký

{
  "product_events": {
    "enabled": true,
    "callback_url": "https://partner.example.com/webhooks/mloder",
    "events": ["product.created", "product.restocked", "product.updated"],
    "shared_secret": "mlevt_SECRET_CHI_TRA_MOT_LAN",
    "secret_once": true
  }
}

Lưu shared_secret vào secret/env của bot đối tác. Nếu đăng ký lại có callback URL, secret mới được cấp lại; cập nhật secret mới trước khi bật connector.

Header và chữ ký bắt buộc

Content-Type: application/json
Accept: application/json
User-Agent: MLoder-Product-Events/1.0
X-MLoder-Event-Id: evt_xxx
X-MLoder-Timestamp: 1790000000
X-MLoder-Nonce: nonce-moi-moi-lan
X-MLoder-Signature: hex-hmac-sha256
X-MLoder-Version: product-events-v1

body_hash = SHA256(raw_json_body)
canonical = timestamp + "\n" + nonce + "\n" + body_hash
signature = HMAC_SHA256(canonical, shared_secret)

Payload mẫu

{
  "version": "product-events-v1",
  "event_id": "evt_xxx_partner_12",
  "event_type": "product.restocked",
  "occurred_at": 1790000000,
  "source": {"name": "ML Oder", "bot_username": "mloder_bot"},
  "product": {
    "external_id": "12",
    "code": "SP-001",
    "name": "Ten san pham",
    "price": 275000,
    "currency": "VND",
    "stock": 8,
    "added_stock": 5,
    "active": true,
    "description": "Mo ta",
    "image_url": "https://...",
    "product_url": "https://..."
  }
}

PHP receiver tối thiểu

<?php
$raw = file_get_contents('php://input');
$timestamp = $_SERVER['HTTP_X_MLODER_TIMESTAMP'] ?? '';
$nonce = $_SERVER['HTTP_X_MLODER_NONCE'] ?? '';
$signature = $_SERVER['HTTP_X_MLODER_SIGNATURE'] ?? '';
if (!ctype_digit($timestamp) || abs(time() - (int)$timestamp) > 300) {
    http_response_code(401); exit;
}
$canonical = $timestamp . "\n" . $nonce . "\n" . hash('sha256', $raw);
$expected = hash_hmac('sha256', $canonical, getenv('MLODER_EVENT_SECRET'));
if (!hash_equals($expected, $signature)) { http_response_code(401); exit; }
$event = json_decode($raw, true, 512, JSON_THROW_ON_ERROR);
// Luu nonce/event_id de chong replay, sau do dua vao queue noi bo.
header('Content-Type: application/json');
echo json_encode(['ok' => true]);

Sự kiện gồm product.created, product.restocked, product.updated; system.test chỉ để kiểm tra callback. Lưu event_id và nonce để chống xử lý trùng/replay. Trả 2xx là đã nhận; 408/425/429/5xx/timeout sẽ retry có backoff, lỗi 400/401/403/404/410 chuyển failed để Admin retry.

Phạm vi gửi: webhook chỉ phát sự kiện ở các Dự án thông báo đã bật “gửi cho đối tác” và đã tích đúng đối tác. Việc này không làm mất đồng bộ catalog tự động khi link.