已連接的帳戶
使用 listingsapi-js SDK 連結 Google、Facebook 和 Apple Business Connect 帳戶,將其商家資訊與地點配對,以及中斷這些帳戶的連結。
已連結帳戶是指 Google Business Profile、Facebook Pages 或 Apple Business Connect 擁有者授予的 OAuth 授權。取得授權後,平台即可讀取該帳戶擁有的商家資訊,並將每筆商家資訊連結至地點。所有已連結帳戶的方法都位於 ListingsAPI 用戶端實例上。
一般流程為:建立連結帳戶用的連結,等待擁有者授權,列出帳戶以取得 connectedAccountId,啟動配對,檢查建議,然後確認建議或手動連結商家資訊。每個方法完成後,都會傳回各自回應鍵下的資料,以下各節會列出對應的回應鍵。
connectGoogleAccount
建立適用於整個帳戶的 Google Business Profile OAuth 連結。請引導擁有者前往傳回的 url;同意畫面關閉後,擁有者會返回您的 successUrl 或 errorUrl。
const link = await client.connectGoogleAccount('https://app.example.com/onboarding/google/done','https://app.example.com/onboarding/google/failed',); console.log(link.url);參數
完成後傳回 bulkConnectLinkForGoogle 下的資料:url、success 和 errors。請檢查 url,不要只檢查 success,因為重新導向無法使用時,仍可能傳回成功回應,但 url 為 null。連結的有效時間很短,因此每次嘗試都應建立新連結。
connectFacebookAccount
這是 connectGoogleAccount 對應的 Facebook Pages 方法,使用相同的 2 個重新導向引數。
const link = await client.connectFacebookAccount('https://app.example.com/onboarding/facebook/done','https://app.example.com/onboarding/facebook/failed',); console.log(link.url);參數
完成後傳回 bulkConnectLinkForFacebook 下的資料:url、success 和 errors。
getOauthConnectUrl
建立範圍限定為單一地點,而非整個帳戶的 OAuth 連結。若您已知道擁有者要連結哪個地點,且希望擁有者返回您產品中該地點的畫面,請使用此方法。
const link = await client.getOauthConnectUrl(1800289,'GOOGLE','https://app.example.com/locations/1800289/connected','https://app.example.com/locations/1800289/connect-failed',); console.log(link.url);參數
完成後傳回 createConnectUrl 下的資料:url、success 和 errors。2 個重新導向 URL 都是必填,且必須為絕對 URL。連結只能使用 1 次,並綁定至您指定的地點。
fetchConnectedAccounts
列出您的帳戶中已獲授權的 Google 和 Facebook 帳戶,以及連線健康狀態和已連結地點數。您可在此取得後續每個方法所需的 connectedAccountId。
const page1 = await client.fetchConnectedAccounts({publisher: 'GoogleAccount',status: 'Connected',page: 1,perPage: 20,}); for (const record of page1.records ?? []) {console.log(record.email, record.connectedAccountId, record.status);}選項
完成後傳回 connectedAccountsInfo 下的資料:records 陣列,以及包含 hasNextPage、hasPreviousPage、totalPages 和 totalRecords 的 pageInfo。每筆記錄包含 connectedAccountId、email、connectedAccountType、connectedLocationsCount、connectivityIssue、status 和 requestMatchesStatus。
篩選條件與欄位使用的值不同。status 選項接受 'Connected'、'NotConnected' 或 'ConnectionIssue',而記錄本身的 status 值則為 CONNECTED、MATCH_IN_PROGRESS 或 CONNECTIVITY_ISSUE。若將記錄的值作為篩選條件傳入,請求會遭到拒絕。
fetchConnectedAccountDetails
依 ID 讀取單一帳戶。若您已有 ID,使用此方法比逐頁讀取完整清單所需的資源更少,也可在配對執行期間用來輪詢。
const details = await client.fetchConnectedAccountDetails('4f712c17-4f95-42dd-90f4-97171a2e67b5',); console.log(details.details.requestMatchesStatus); // MATCH_IN_PROGRESS, then MATCH_COMPLETED參數
完成後傳回 connectedAccountDetails 下的資料,其中記錄位於 details 下,欄位與清單傳回的欄位相同。請觀察 requestMatchesStatus,以確認建議何時準備完成,並讀取 status 和 connectivityIssue,以判斷是否需要提示使用者重新授權。
fetchConnectedAccountFolders
列出已連結帳戶可存取的 Google 帳戶群組(資料夾)。若擁有者希望將新的商家資訊放入特定群組,請在呼叫 createGmbListing 之前呼叫此方法。
const folders = await client.fetchConnectedAccountFolders('4f712c17-4f95-42dd-90f4-97171a2e67b5',); for (const folder of folders) {console.log(folder.folderId, folder.folderName, folder.locationCount);} // Narrow by nameconst matching = await client.fetchConnectedAccountFolders('4f712c17-4f95-42dd-90f4-97171a2e67b5',{ folderName: 'West Region' },);選項
完成後傳回 getFoldersUnderGoogleAccount 陣列,預設為空陣列。每個資料夾包含 folderId(必須原樣傳入的 accounts/<id> 字串)、folderName 和 locationCount;當 Google 未提供數量時,最後一個欄位可能為 null。此路徑僅適用於 Google 類型的帳戶。
fetchConnectedAccountListings
列出已連結帳戶可存取的發布平台商家資訊。每筆記錄的 id 就是 connectListing 接受的 connectedAccountListingId,因此您可使用此方法建立商家資訊選擇器。
const listings = await client.fetchConnectedAccountListings('4f712c17-4f95-42dd-90f4-97171a2e67b5',{ locationInfo: 'William St', page: 1, perPage: 50 },); for (const record of listings.records ?? []) {console.log(record.id, record.locationName, record.address);}選項
完成後傳回 connectedAccountListings 下的資料:records 和 pageInfo。每筆記錄包含 id、locationName、address、phone、liveLink 和 accountTypeName。雖然底層請求採用寫入請求的形式,這仍是讀取操作:SDK 會依端點要求,為您將帳戶 ID 和分頁資訊放入請求主體。
triggerConnectedAccountMatches
將工作加入佇列,以擷取指定帳戶可存取的所有商家檔案,並與您的地點進行配對。請在擁有者完成同意畫面後立即呼叫此方法,並在大量建立地點後再次呼叫。
const result = await client.triggerConnectedAccountMatches(['4f712c17-4f95-42dd-90f4-97171a2e67b5',]); console.log(result.success, result.failedIds);參數
完成後傳回 connectedAccountsTriggerMatches 下的資料:success 和 failedIds。success 為 true 表示工作已加入佇列,不代表配對已完成。請輪詢 fetchConnectedAccountDetails,直到 requestMatchesStatus 的值為 MATCH_COMPLETED,再讀取建議。
fetchConnectionSuggestions
讀取配對產生的結果,讓操作人員在建立任何連結之前,逐一比對每個地點與發布平台上的商家資訊。
const suggestions = await client.fetchConnectionSuggestions('4f712c17-4f95-42dd-90f4-97171a2e67b5',{ page: 1, perPage: 50 },); for (const record of suggestions.records ?? []) {console.log(record.accountType, record.matchedDataDatabaseId, record.locationInfo.name);}選項
完成後傳回 connectionSuggestionsForAccount 下的資料:records 和 pageInfo。建議記錄沒有 id 欄位。其識別碼為 matchedDataDatabaseId,而 accountType(GMB、FB 或 APPLE)則表示配對記錄的類型。請一併保留這 2 個值,因為 confirmConnectedAccountMatches 需要使用這 2 個值來建構其接受的 ID。尚無配對結果的帳戶會傳回空的 records 陣列。
confirmConnectedAccountMatches
接受已檢查的配對結果。確認後,每個地點都會連結至其發布平台上的商家資訊,讓後續的地點更新能同步至該商家資訊。
根據建議記錄建構每個 ID:以冒號連接配對記錄的類型名稱與記錄的 matchedDataDatabaseId,再以 base64 編碼。GMB 對應至 GmbLocationMatchedData,FB 對應至 FbLocationMatchedData,APPLE 對應至 AppleLocationMatchedData。
const TYPES: Record<string, string> = {GMB: 'GmbLocationMatchedData',FB: 'FbLocationMatchedData',APPLE: 'AppleLocationMatchedData',}; const matchRecordId = (record: any) =>Buffer.from(TYPES[record.accountType] + ':' + record.matchedDataDatabaseId).toString('base64'); const approved = (suggestions.records ?? []).map(matchRecordId);const result = await client.confirmConnectedAccountMatches(approved); console.log(result.success, result.failedIds);參數
完成後傳回 confirmConnectMatches 下的資料:success 和 failedIds。每次呼叫都請檢查 success:若 ID 解碼後不屬於這 3 種類型之一,仍會傳回成功格式的回應,但 success 為 false,且沒有 failedIds。若部分 ID 處理失敗,failedIds 會包含解碼後的資料庫 ID,而非您傳送的編碼值,因此請將這些 ID 與 matchedDataDatabaseId 比對。
將單筆商家資訊連結至單一地點
有 3 個方法可完成單筆已配對商家資訊與單一地點之間的連結,這些方法與其他商家資訊端點一併記載,不在此重複說明:
- connectListing 將已配對的商家資訊連結至地點。
- disconnectListing 解除該連結。
- createGmbListing 為尚無 Google Business Profile 的地點建立新的 Google Business Profile,並將其放在 fetchConnectedAccountFolders 提供的資料夾中。
當 confirmConnectedAccountMatches 已處理帳號中的大部分項目,但仍有一家門市需要手動配對時,請使用這些方法。
oauthDisconnect
這是相同的單一地點中斷連線操作,可透過個別地點的 OAuth 路徑呼叫。它接受的引數與完成後傳回的酬載完全相同,因此請選擇在您的程式碼中較易讀的名稱。
await client.oauthDisconnect(1800289, 'GOOGLE');參數
完成後傳回 disconnectConnectedAccountsLocations 酬載,其鍵與 disconnectListing 傳回的相同。
disconnectGoogleAccount
中斷整個 Google 帳號的連線:透過該帳號連結的所有地點都會停止接收更新。當客戶停止使用服務或轉移擁有權時,請使用此方法。
await client.disconnectGoogleAccount('4f712c17-4f95-42dd-90f4-97171a2e67b5');參數
完成後傳回 gmbBulkDisconnect 酬載,其中包含 success。此操作只能全部解除連結,無法只解除部分連結:若要解除單一地點的連結並保留帳號,請使用 disconnectListing。即使 ID 不存在,傳回的 success 仍為 true,因此若您需要確認結果,請在操作後讀取 fetchConnectedAccounts。
disconnectFacebookAccount
這是對應的 Facebook 方法,引數相同,也同樣只能全部解除連結,無法只解除部分連結。
await client.disconnectFacebookAccount('9d2b5a41-0c7e-4f83-bb16-2ac43e70d915');參數
完成後傳回 fbBulkDisconnect 酬載,其中包含 success。同樣需要注意:即使 ID 不存在,仍會回報成功,因此請使用 fetchConnectedAccounts 驗證。
典型的初始設定流程
import { ListingsAPI } from 'listingsapi-js'; const client = new ListingsAPI(); // reads LISTINGSAPI_KEY from env // 1. Send the owner through the consent screenconst link = await client.connectGoogleAccount('https://app.example.com/onboarding/google/done','https://app.example.com/onboarding/google/failed',);console.log('open:', link.url); // 2. After they return, pick up the account IDconst accounts = await client.fetchConnectedAccounts({ publisher: 'GoogleAccount' });const accountId = accounts.records[0].connectedAccountId; // 3. Match its listings against your locationsawait client.triggerConnectedAccountMatches([accountId]); // 4. Poll until matching finisheslet details = await client.fetchConnectedAccountDetails(accountId);while (details.details.requestMatchesStatus !== 'MATCH_COMPLETED') {await new Promise((resolve) => setTimeout(resolve, 5000));details = await client.fetchConnectedAccountDetails(accountId);} // 5. Read the suggestions and confirm the approved onesconst suggestions = await client.fetchConnectionSuggestions(accountId, { perPage: 100 });console.log('found ' + (suggestions.records ?? []).length + ' suggested matches');