整合
在你的應用程式中嵌入編輯器
了解如何把 Bannerify 編輯器放進你的產品,讓使用者用你的範本進行設計。
當你的使用者需要在你的產品內製作橫幅、貼文、標籤或文件時,請使用嵌入功能。你的頁面會在 iframe 中顯示 Bannerify 編輯器。每位使用者都有自己的設計,也可以從你工作區的範本開始。
運作方式
- 你的伺服器用嵌入功能的簽署密鑰,為使用者簽署一個短效的權杖。
- 你的頁面在 iframe 中開啟
https://embed.bannerify.co/<embed id>?token=<token>。 - Bannerify 檢查權杖和頁面的網域,然後為該使用者開啟編輯器。
- 使用者的設計是你工作區中的範本。你可以像其他範本一樣,用 API 繪製它們。
先決條件
- 一個 Scale 或 Enterprise 方案的工作區。
- 一個能簽署 JWT 的伺服器。簽署密鑰必須留在你的伺服器上。
| Scale | Enterprise | |
|---|---|---|
| 允許的網域 | 3 | 8 |
| 編輯器使用者 | 100 | 500 |
| 下載、權限、名稱、標誌和強調色 | 有 | 有 |
| SDK 事件與指令 | 有 | 有 |
| Webhook | 有 | 有 |
| 隱藏 Bannerify 品牌 | 無 | 有 |
| 來自你應用程式的素材選擇器 | 無 | 有 |
| 分析 | 無 | 有 |
設定嵌入功能
- 開啟儀表板側邊欄中的 Embed。
- 點選 Set up the embed,然後開啟 Turn on the embed。
- 在 Allowed domains 底下,加入每個會顯示編輯器的頁面的來源,例如
https://app.example.com。所有子網域請用https://*.example.com,開發期間則用http://localhost:3000。 - 複製 Embed id 和 Signing secret。
在你的伺服器上簽署權杖
權杖是 HS256 的 JWT。請把 sub 設為使用者在你應用程式中的 ID。相同的 sub 一律會開啟相同的設計。
import { SignJWT } from "jose"
const token = await new SignJWT({ sub: user.id, name: user.name, email: user.email })
.setProtectedHeader({ alg: "HS256" })
.setExpirationTime("1h")
.sign(new TextEncoder().encode(process.env.BANNERIFY_EMBED_SECRET))
| 宣告 | 必填 | 說明 |
|---|---|---|
sub |
否 | 使用者在你應用程式中的 ID。可使用英文字母、數字、_、-、@ 和 .,最多 256 個字元。沒有它時,編輯器會編輯你工作區的範本。 |
name |
否 | 使用者的名稱。 |
email |
否 | 使用者的電子郵件。 |
exp |
是 | 權杖的過期時間。最多從現在起 24 小時。 |
顯示編輯器
<iframe
src="https://embed.bannerify.co/emb_xxx?token=TOKEN"
style="width: 100%; height: 800px; border: 0"
allow="clipboard-write"
></iframe>
在網址後面加上 &templateId=tpl_xxx,就能直接開啟某一份設計。
使用 SDK
SDK 會替你加入 iframe、告訴你的頁面使用者做了什麼,並讓你的頁面控制編輯器。
npm install @bannerify/embed
import { createBannerifyEmbed } from "@bannerify/embed"
const embed = createBannerifyEmbed({
container: document.getElementById("editor"),
embedId: "emb_xxx",
token, // signed by your server
})
embed.on("template:update", ({ template }) => {
console.log("Saved", template.id)
})
在 React 中,請安裝 @bannerify/embed-react 並使用元件。ref 可以控制編輯器。
npm install @bannerify/embed-react
import { BannerifyEmbed, type BannerifyEmbedHandle } from "@bannerify/embed-react"
const editor = useRef<BannerifyEmbedHandle>(null)
<BannerifyEmbed
ref={editor}
embedId="emb_xxx"
token={token}
style={{ height: 800 }}
onDownload={({ fileName, content }) => upload(fileName, content)}
/>
事件
| 事件 | React 屬性 | 資料 |
|---|---|---|
ready |
onReady |
{ user }。從這時起,編輯器會接受指令。 |
template:open |
onTemplateOpen |
{ template, canEdit }。對於你工作區的範本,canEdit 為 false。 |
template:close |
onTemplateClose |
{ templateId } |
template:create |
onTemplateCreate |
{ template },用於新的設計或副本。 |
template:update |
onTemplateUpdate |
{ template },在儲存或重新命名之後。 |
template:delete |
onTemplateDelete |
{ templateId } |
download |
onDownload |
{ templateId, format, fileName, content }。content 是檔案的資料網址。 |
error |
onError |
{ message },在編輯器無法開啟時。 |
asset:request |
onAssetRequest |
{ requestId, type }。請見素材選擇器。 |
template 有 id、name、width、height 和 updatedAt。
指令
每個指令都會等待 ready,並回傳 Promise。
| 指令 | 結果 |
|---|---|
openTemplate(templateId) |
{ template }。開啟使用者的設計,或你工作區的範本。 |
closeTemplate() |
回到設計清單。 |
setModifications([{ name, text, color, src, qrcode, barcode, visible }]) |
{ changed }。像 API 的 modification 一樣,依名稱變更已開啟設計中的圖層。 |
save() |
{ template }。儲存已開啟的設計。 |
requestContent(format) |
{ templateId, content }。把已開啟的設計繪製成 png、jpeg、webp 或 pdf。 |
你工作區的範本是唯讀的,所以 setModifications 和 save 會以 “Make a copy of this template to change it” 失敗。
每次 requestContent 都會計為你工作區的一次生成,就像在編輯器中下載,或用 API 繪製一樣。當工作區用完所有生成次數時,下載會在編輯器中顯示錯誤,而 requestContent 也會因該錯誤而失敗。
素材選擇器
在 Enterprise 方案中,當使用者新增圖片或挑選顏色時,你的頁面可以顯示自己的素材庫。請在 Asset picker 底下開啟 Images from your app 或 Colors from your app。這樣 Media 面板就會顯示帶有你應用程式名稱的 Choose from,顏色選擇器也會多出一個色票按鈕。使用者仍然可以上傳自己的檔案。
const embed = createBannerifyEmbed({
container,
embedId: "emb_xxx",
token,
onAssetRequest: async ({ type }) => {
if (type === "image") {
const photo = await openMyMediaLibrary() // your own UI
return photo ? { url: photo.url, fileName: photo.name } : null
}
const hex = await openMyBrandColors()
return hex ? { value: hex } : null
},
})
當使用者關閉你的選擇器時,請回傳 null。Bannerify 會從 https 網址下載圖片,並複製到使用者的上傳項目中,所以之後網址就算失效也沒關係。圖片可以是 PNG、JPEG、WebP 或 SVG,最大 5 MB。在 React 中,請把相同的函式傳給 onAssetRequest。
不使用 SDK
編輯器透過 postMessage 與你的頁面溝通。它會把 { source: "bannerify-embed", type, data } 傳送給包住它的頁面,並且只接受來自該頁面的 { source: "bannerify-host", type, requestId, data }。它會用 { source: "bannerify-embed", type: "response", requestId, data } 回應每個指令;發生錯誤時,則以 error 取代 data。指令類型有 open-template、close-template、set-modifications、save、request-content 和 asset-response。對每個 asset:request 事件,請用 asset-response 和資料 { requestId, asset } 回應,其中 asset 是 { url, fileName }、{ value } 或 null。
你的使用者可以做什麼
- 以常見尺寸建立設計,或複製你工作區的範本。
- 編輯文字、圖片、形狀、QR 碼,以及編輯器中的其他圖層。
- 上傳自己的圖片。
- 把設計下載為 PNG、JPEG、WebP 或 PDF。每次下載都計為你工作區的一次生成。
你可以在 Permissions 底下分別開啟或關閉這些功能。在 Appearance 底下,可以設定編輯器頂端的名稱和標誌,以及主要按鈕的強調色。
繪製使用者的設計
用範本清單的 embedUserId 參數列出某位使用者的設計,然後像任何範本一樣繪製它們。
curl "https://api.bannerify.co/v1/templates?apiKey=bnfy_xxx&embedUserId=user_123"
你的使用者的範本不會顯示在儀表板的範本清單中。
Webhook
在 Webhook 底下新增一個網址,當使用者建立、變更或刪除設計時,你的伺服器就會收到 POST。請用它來維護你自己的紀錄,因為 SDK 的事件只會傳到已開啟的頁面。
{
"id": "eev_01J9X...",
"type": "template.create",
"createdAt": "2026-10-19T08:00:00.000Z",
"embedId": "emb_xxx",
"user": { "id": "user_123" },
"template": { "id": "tpl_xxx", "name": "Summer sale", "width": 1080, "height": 1080 }
}
type |
時機 |
|---|---|
template.create |
使用者開始新的設計,或複製範本。 |
template.update |
使用者儲存或重新命名設計。 |
template.delete |
使用者刪除設計。 |
test |
你點選 Send test event。user 和 template 為 null。 |
對於沒有 sub 的權杖,user 為 null。請在 10 秒內以 2xx 狀態碼回應。失敗的傳送會在 1、2、4、8、16 和 32 分鐘後重試。相同的事件一律有相同的 id,所以請略過已經處理過的 id。
檢查簽章
每個請求都有 Bannerify-Signature 標頭:t=<unix seconds>,v1=<signature>。簽章是用 webhook 密鑰,對 <t>.<raw body> 計算 HMAC-SHA256 後的十六進位值。
import { createHmac, timingSafeEqual } from "node:crypto"
const verify = (rawBody: string, header: string, secret: string) => {
const { t, v1 } = Object.fromEntries(header.split(",").map((part) => part.split("=")))
if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false
const expected = createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex")
return v1.length === expected.length && timingSafeEqual(Buffer.from(v1), Buffer.from(expected))
}
請在解析之前先讀取原始本文:解析後再序列化的本文,位元組可能會不一樣。
分析
在 Enterprise 方案中,Embed 頁面的 Analytics 區段會顯示你的使用者在過去 7、30 或 90 天內如何使用編輯器:使用者、工作階段、建立的設計和下載次數、每日的工作階段數、開啟編輯器的網站,以及最活躍的使用者。工作階段是指編輯器的一次載入。日期以 UTC 計算。
輪替簽署密鑰
當密鑰外洩時,請點選 Rotate secret。用舊密鑰簽署的權杖會立刻失效,所以請先更新你的伺服器。
疑難排解
| 訊息 | 處理方式 |
|---|---|
| The embed token is not valid | 請用目前的簽署密鑰簽署權杖,alg 使用 HS256,並加入 exp 宣告。 |
| This embed is turned off | 請在儀表板中開啟嵌入功能。 |
| This app reached its limit of editor users | 每個 sub 計為一位使用者。請升級到 Enterprise 以取得更多使用者名額。 |
| This app used all its generations for this month | 下載會計為你工作區的生成次數。請等到每月 1 日重設,或變更你的方案。 |
| 頁面沒有顯示編輯器 | 請在 Allowed domains 底下加入該頁面的來源。 |