Salekit Docs

API tích hợp Partner

https://api.salekit.com:3036

Luồng tổng thể (đọc trước khi tích hợp)#

  1. Tạo page (POST /api/v2/landingpage/create) hoặc Clone page (POST /api/v2/landingpage/clone/:id) → nhận page_id.
  2. Partner tự redirect user sang editor — API chỉ trả về page_id, không trả về URL editor. Partner tự ghép URL https://builder.<domain>/{page_id}?token=... và điều hướng user sang đó.
  3. Page vừa tạo/clone chưa có HTML (trừ khi clone từ page nguồn đã publish). Sau khi vào editor, user bấm Xuất bản (Publish) — chỉ sau bước này page mới có HTML và tự động đồng bộ sang domain riêng của partner.

Editor URL — chỉ còn 1 domain duy nhất#

Partner không cần lưu / không cần quan tâm page thuộc builder cũ hay mới. Cứ luôn điều hướng về:

https://builder.<DOMAIN_PARTNER>/{page_id}?token=<TOKEN>

Builder tự nhận diện loại page và tự redirect sang builder cũ (/v1/{page_id}, nginx proxy ngược về builder PHP) nếu page đó không thuộc builder React. Toàn bộ việc phân luồng do builder xử lý.

Ví dụ với unica.vn: dù page cũ hay mới, partner đều mở https://builder.unica.vn/{page_id}?token=....

Lưu ý migrate: nếu hệ thống partner đang hardcode builder1.<domain>, bỏ đi — domain đó không dùng cho tích hợp mới. Mọi thứ đi qua builder.<domain>.

1. Tạo Page#

Thay thế cho luồng Clone page cũ (https://salekit.page/api/page/clone). Luồng cũ thực chất luôn clone từ 1 page mẫu cố định (copy_id), bản chất là tạo page mới — nên chuyển sang API tạo page của builder mới, không dùng clone.

Sử dụng mã hóa Json Web Token chuỗi Token:

DATA: Mảng giá trị

Field Type Desc
shop_id Int hoặc String ID Shop trên hệ thống DOMAIN_PARTNER
creator_id Int hoặc String ID User trên hệ thống DOMAIN_PARTNER

TOKEN = jwt(DATA, KEY)

Key tích hợp: KEY = XTBHTCTBWCPPGX0U

Url: https://api.salekit.com:3036/api/v2/landingpage/create Method: POST

Header

Key Value
Content-Type application/json
Token TOKEN

Body

Field Value Desc
domain_site DOMAIN_PARTNER Tên miền tích hợp
pageType INT (default 0) Loại page: 2 = Sale page (tương ứng Landingpage bên hệ cũ)

Response

Field Type Desc
error Boolean true/false
data String Id page vừa tạo trả về từ hệ thống Builder

Ví dụ response thành công (HTTP 201):

{
  "error": false,
  "data": "68f1c0a9d4b2e30012ab34cd"
}

API này CHỈ trả về page_id — không trả về object page, không trả về URL editor, không trả về HTML. Sau khi nhận data, partner phải tự redirect user sang editor builder mới:

https://builder.<DOMAIN_PARTNER>/{data}?token=<TOKEN>

Ví dụ với unica.vn và data = 68f1c0a9d4b2e30012ab34cd:

https://builder.unica.vn/68f1c0a9d4b2e30012ab34cd?token=...

Response lỗi

HTTP Body Nguyên nhân
401 { "error": true, "msg": "Token không được cung cấp" } Thiếu header Token
401 { "error": true, "msg": "Token không hợp lệ" } JWT ký sai KEY hoặc sai domain_site
401 { "error": true, "msg": "Token đã hết hạn" } JWT hết hạn (exp)
400 { "error": true, "msg": "domain_site là bắt buộc" } Body thiếu domain_site
500 { "error": true, "msg": "..." } Lỗi hệ thống Builder

Lưu ý quan trọng: Page vừa tạo qua API này chưa publish và chưa có HTML — API tạo page chỉ khởi tạo cấu trúc block rỗng theo pageType, không build HTML. Muốn có HTML thật (để hiển thị/clone/lấy nội dung), partner bắt buộc phải đưa user vào editor builder mới (https://builder.<domain>/{page_id}?token=...) để bấm Xuất bản (Publish). Chỉ sau khi publish, hệ thống mới:

  • Ghi HTML vào page (react_publish.html).
  • Tự động đồng bộ HTML sang domain riêng của partner (webhook apiv1/landingpage/update) — không cần partner tự gọi thêm API nào để đồng bộ, việc này tự chạy ngay khi publish.

2. Clone Page#

Dùng khi partner muốn clone 1 page đã tạo qua builder mới (page_id lấy từ response mục tạo page ở trên) — không dùng cho clone từ page mẫu builder cũ.

Sử dụng mã hóa Json Web Token chuỗi Token:

DATA: Mảng giá trị

Field Type Desc
shop_id Int hoặc String ID Shop trên hệ thống DOMAIN_PARTNER
creator_id Int hoặc String ID User trên hệ thống DOMAIN_PARTNER
copy_id String page_id nguồn cần clone (page đã tạo qua builder mới)
parent_id String (optional) Gắn page mới là con của parent_id — dùng cho A/B test variant
name String (optional) Tên page mới

TOKEN = jwt(DATA, KEY)

Key tích hợp: KEY = XTBHTCTBWCPPGX0U

Url: https://api.salekit.com:3036/api/v2/landingpage/clone/:id Method: POST

:id trên URL chính là page_id của page muốn clone (page nguồn) — cùng ý nghĩa với copy_id trong DATA. Chỉ cần truyền 1 trong 2: nếu DATA có copy_id thì copy_id được ưu tiên dùng, :id trên URL sẽ bị bỏ qua; nếu DATA không có copy_id thì hệ thống mới lấy :id trên URL làm page nguồn.

Header

Key Value
Content-Type application/json
Token TOKEN

Body

Field Value Desc
domain_site DOMAIN_PARTNER Tên miền tích hợp

Response

Field Type Desc
error Boolean true/false
data String Id page mới trả về từ hệ thống Builder
html String HTML đã publish của page gốc (copy_id), base64-encode

Redirect sang editor: giống API tạo page, response clone chỉ trả về page_id mới — partner tự ghép URL và điều hướng user sang https://builder.<DOMAIN_PARTNER>/{data}?token=<TOKEN>.

Lưu ý: html chỉ có giá trị khi page gốc (copy_id) đã từng publish. Nếu partner clone 1 page vừa tạo qua API POST /api/v2/landingpage/create ở trên (page chưa publish lần nào), field html sẽ luôn trả về rỗng "" — không dùng field này để lấy nội dung page trong luồng tạo mới → clone.

3. Export Page#

Lấy toàn bộ nội dung page của builder mới ra dạng JSON, để lưu thành file .lp hoặc chuyển sang shop/hệ thống khác. Export không trả HTML — chỉ trả cấu trúc block.

Sử dụng mã hóa Json Web Token chuỗi Token:

DATA: Mảng giá trị

Field Type Desc
shop_id Int hoặc String ID Shop trên hệ thống DOMAIN_PARTNER
creator_id Int hoặc String ID User trên hệ thống DOMAIN_PARTNER

TOKEN = jwt(DATA, KEY)

Key tích hợp: KEY = XTBHTCTBWCPPGX0U

API export không đọc nội dung DATA — chỉ cần JWT ký đúng KEY của domain_site. Page được xác định hoàn toàn bằng :id trên URL.

Url: https://api.salekit.com:3036/api/v2/landingpage/export/:id Method: POST

:id là page_id của page cần export — phải là chuỗi 24 ký tự hex (_id MongoDB). Truyền sai định dạng sẽ trả lỗi 400.

Header

Key Value
Content-Type application/json
Token TOKEN

Body

Field Value Desc
domain_site DOMAIN_PARTNER Tên miền tích hợp (bắt buộc — dùng để tra KEY xác thực token)

Response

Field Type Desc
error Boolean true/false
data Object Nội dung page đã export

Các field bên trong data:

Field Type Desc
name String Tên page
shop_id Int hoặc String Shop sở hữu page gốc
creator_id Int hoặc String User tạo page gốc
type Int Loại bản ghi nội bộ
blocks Object Toàn bộ block của page — dữ liệu chính khi import
hierarchy Object Cây phân cấp block (block nào nằm trong block nào)
page_settings Object Cấu hình page: config, customCode, seo, tracking
page_type Int Loại page (0, 2, 8, 9, 10)
builder_type String Luôn là "react"
body Object (optional) Chỉ xuất hiện khi page có dữ liệu builder cũ
export Object (optional) Chỉ xuất hiện khi page có dữ liệu builder cũ

Ví dụ response thành công (HTTP 200):

{
  "error": false,
  "data": {
    "name": "Landing page khóa học",
    "shop_id": 1234,
    "creator_id": 5678,
    "type": 0,
    "blocks": {
      "page": { "type": "page", "label": "Page", "cname": "page", "configs": {}, "bpConfigs": {} },
      "SECTION1": { "type": "section", "label": "Section", "cname": "section", "configs": {}, "bpConfigs": {} }
    },
    "hierarchy": { "page": ["SECTION1"], "SECTION1": [] },
    "page_settings": { "config": { "pageType": 2 }, "customCode": {}, "seo": {}, "tracking": {} },
    "page_type": 2,
    "builder_type": "react"
  }
}

Response lỗi

HTTP Body Nguyên nhân
401 { "error": true, "msg": "Token không được cung cấp" } Thiếu header Token
401 { "error": true, "msg": "Token không hợp lệ" } JWT ký sai KEY hoặc sai domain_site
401 { "error": true, "msg": "Token đã hết hạn" } JWT hết hạn (exp)
400 { "error": true, "msg": "domain_site là bắt buộc" } Body thiếu domain_site
400 { "error": true, "msg": "page_id không hợp lệ" } :id không phải 24 ký tự hex
404 { "error": true, "msg": "Page không tồn tại" } Không tìm thấy page với :id đó
500 { "error": true, "msg": "Export failed" } Lỗi hệ thống Builder

4. Import Page#

Không có API import riêng. Import chính là API tạo page ở mục 1 (POST /api/v2/landingpage/create), nhưng body được gắn thêm blocks / hierarchy / page_settings lấy từ response export. Nếu body không có các field này, hệ thống tạo page rỗng theo pageType như bình thường.

Sử dụng mã hóa Json Web Token chuỗi Token:

DATA: Mảng giá trị

Field Type Desc
shop_id Int hoặc String ID Shop đích trên hệ thống DOMAIN_PARTNER
creator_id Int hoặc String ID User đích trên hệ thống DOMAIN_PARTNER
name String (optional) Tên page mới. Nếu có, ưu tiên hơn name trong Body

TOKEN = jwt(DATA, KEY)

Key tích hợp: KEY = XTBHTCTBWCPPGX0U

Url: https://api.salekit.com:3036/api/v2/landingpage/create Method: POST

Header

Key Value
Content-Type application/json
Token TOKEN

Body

Field Value Desc
domain_site DOMAIN_PARTNER Tên miền tích hợp (bắt buộc)
page_type INT Lấy từ data.page_type của response export. Chấp nhận cả tên pageType
blocks Object data.blocks từ response export
hierarchy Object data.hierarchy từ response export
page_settings Object data.page_settings từ response export
name String (optional) Tên page mới — chỉ dùng khi JWT không có name
body Object (optional) data.body từ export, nếu có
export Object (optional) data.export từ export, nếu có

Response

Giống hệt mục 1 — chỉ trả page_id mới:

{
  "error": false,
  "data": "68f1c0a9d4b2e30012ab34cd"
}

Sau đó partner tự redirect user sang https://builder.<DOMAIN_PARTNER>/{data}?token=<TOKEN>. Page vừa import chưa có HTML — user phải vào editor bấm Xuất bản (Publish).

Response lỗi: giống bảng lỗi ở mục 1 (401 token, 400 thiếu domain_site, 500 lỗi hệ thống).

4.1. Ba điểm bắt buộc lưu ý khi import#

a) page_type không tự đi theo blocks

Response export trả page_type ở cấp data, nhưng API import đọc loại page từ Body (page_type / pageType), không đọc từ page_settings.config.pageType. Partner phải tự map:

body.page_type = exported.page_type;

Nếu bỏ qua bước này, hệ thống fallback về sales_page trong JWT, và nếu cũng không có thì page được tạo với page_type = 0 — sai loại page. Giá trị hợp lệ: 0, 2, 8, 9, 10; giá trị ngoài danh sách bị quy về 0.

Ngoài ra page_settings.config.pageType luôn bị hệ thống ghi đè bằng page_type đã resolve từ Body — không cần chỉnh tay field này.

b) blocks phải có nhiều hơn 1 phần tử

Hệ thống chỉ chấp nhận blocks từ Body khi object đó có từ 2 key trở lên. Page chỉ có đúng 1 block gốc (page) sẽ bị âm thầm thay bằng cấu trúc mặc định của page_type, không báo lỗi — response vẫn trả 201 kèm page_id bình thường.

Partner nên kiểm tra trước khi gọi:

if (!exported.blocks || Object.keys(exported.blocks).length <= 1) {
  // page rỗng — import sẽ không giữ nội dung, cảnh báo user
}

c) hierarchy chỉ được nhận kèm blocks

hierarchy chỉ có tác dụng khi blocks hợp lệ theo điều kiện (b). Gửi hierarchy một mình sẽ bị bỏ qua. Luôn gửi cả cặp.

4.2. Giới hạn dữ liệu của import/export (hiện tại)#

Cặp API này chỉ mang theo bố cục và cấu hình trang. Những thứ sau không nằm trong data của export và không được khôi phục khi import:

Dữ liệu Trạng thái
blocks, hierarchy ✅ Được mang theo
page_settings (config, customCode, seo, tracking) ✅ Được mang theo
page_type ⚠️ Được export, nhưng partner phải tự map sang Body khi import (xem 4.1a)
Popup ❌ Không export, không import — page đích sẽ không có popup nào
Tiện ích dùng chung (global_components) ❌ Không export, không import
HTML đã publish ❌ Không export — page đích phải publish lại từ editor
Ảnh thumbnail page ❌ Không export
Trạng thái publish, domain, slug, lượt xem ❌ Không export — page đích luôn ở trạng thái chưa publish

Nếu page nguồn dùng popup hoặc tiện ích dùng chung, hãy báo trước cho user rằng các phần này cần dựng lại thủ công sau khi import. Đây là giới hạn hiện tại của hệ thống, không phải lỗi tích hợp.

4.3. Luồng tham khảo: export → file .lp → import#

Đây là luồng đang chạy thật trên hệ thống partner:

┌─ EXPORT ────────────────────────────────────────────────┐
│ 1. POST /api/v2/landingpage/export/{page_id}            │
│    → data                                                │
│ 2. Bỏ các field không cần: shop_id, creator_id           │
│ 3. Gắn cờ version = 1  (đánh dấu page builder mới)       │
│ 4. jwt(data) → ghi ra file <tên page>.lp                 │
└──────────────────────────────────────────────────────────┘
                          ↓
┌─ IMPORT ────────────────────────────────────────────────┐
│ 1. User upload file .lp                                  │
│ 2. decode jwt(file) → file_content                       │
│ 3. Rẽ nhánh theo file_content.version:                   │
│      version == 1 → POST /api/v2/landingpage/create      │
│      còn lại      → API builder cũ                       │
│    body = { domain_site, ...file_content }               │
│ 4. Nhận page_id mới → lưu vào DB partner (version = 1)   │
│ 5. Redirect: builder.<domain>/{page_id}?token=...        │
└──────────────────────────────────────────────────────────┘

Cờ version do partner tự gắn vào file .lp, không phải field của API. Nó giúp phân biệt file export từ builder mới (version = 1) và builder cũ (version = 0) để gọi đúng endpoint lúc import.

Ví dụ gọi import bằng Node.js:

const exported = fileContent;           // đã decode từ file .lp
const token = jwt.sign({ shop_id, creator_id, name }, KEY);

const body = {
  domain_site: "unica.vn",
  page_type: exported.page_type,        // bắt buộc — xem 4.1a
  blocks: exported.blocks,
  hierarchy: exported.hierarchy,
  page_settings: exported.page_settings,
};

const res = await fetch("https://api.salekit.com:3036/api/v2/landingpage/create", {
  method: "POST",
  headers: { "Content-Type": "application/json", Token: token },
  body: JSON.stringify(body),
});
const { error, data: newPageId } = await res.json();