已连接账户

使用 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。

TypeScript
const link = await client.connectGoogleAccount('https://app.example.com/onboarding/google/done','https://app.example.com/onboarding/google/failed',); console.log(link.url);

参数

参数类型说明
successUrlstring所有者授权后返回的绝对 URL。
errorUrlstring所有者取消授权或授权失败时返回的绝对 URL。

解析为 bulkConnectLinkForGoogle 下的数据:url、success 和 errors。请检查 url,不要只检查 success,因为重定向不可用时,也可能返回成功响应,但 url 为 null。链接的有效期较短,因此每次尝试都应创建新链接。

connectFacebookAccount

这是 connectGoogleAccount 对应的 Facebook Pages 方法,接受相同的 2 个重定向参数。

TypeScript
const link = await client.connectFacebookAccount('https://app.example.com/onboarding/facebook/done','https://app.example.com/onboarding/facebook/failed',); console.log(link.url);

参数

参数类型说明
successUrlstring所有者授权后返回的绝对 URL。
errorUrlstring所有者取消授权或授权失败时返回的绝对 URL。

解析为 bulkConnectLinkForFacebook 下的数据:url、success 和 errors。

getOauthConnectUrl

创建一个仅适用于单个地点而非整个账户的 OAuth 链接。如果您已知道所有者要连接哪个地点,并且希望所有者返回您产品中该地点的页面,请使用此方法。

TypeScript
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);

参数

参数类型说明
locationIdstring | number此连接关联的地点。数字 ID 会自动编码。
sitestring要连接的发布平台:'GOOGLE'、'FACEBOOK' 或 'APPLE'。
successUrlstring所有者授权后返回的绝对 URL。
errorUrlstring授权失败时所有者返回的绝对 URL。

解析为 createConnectUrl 下的数据:url、success 和 errors。2 个重定向 URL 均为必填项,且必须为绝对 URL。链接只能使用 1 次,并绑定到您指定的地点。

fetchConnectedAccounts

列出在您的账户中获得授权的 Google 和 Facebook 账户,以及连接健康状态和已关联地点的数量。后续每个方法所需的 connectedAccountId 都从这里获取。

TypeScript
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);}

选项

参数类型说明
publisherstring账户类型,例如 'GoogleAccount' 或 'FacebookAccount'。
statusstring连接状态筛选条件:'Connected'、'NotConnected' 或 'ConnectionIssue'。
pagenumber页码(从 1 开始)。
perPagenumber每页记录数。

解析为 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,使用此方法比对整个列表逐页查询的开销更小;匹配任务运行期间也应使用此方法轮询。

TypeScript
const details = await client.fetchConnectedAccountDetails('4f712c17-4f95-42dd-90f4-97171a2e67b5',); console.log(details.details.requestMatchesStatus); // MATCH_IN_PROGRESS, then MATCH_COMPLETED

参数

参数类型说明
connectedAccountIdstring已连接账户的 ID,从 fetchConnectedAccounts 获取。

解析为 connectedAccountDetails 下的数据,其中记录嵌套在 details 下,字段与列表返回的字段相同。请监测 requestMatchesStatus 以了解匹配建议何时就绪,并读取 status 和 connectivityIssue,以判断是否需要提示所有者重新授权。

fetchConnectedAccountFolders

列出已连接账户可访问的 Google 账户群组(文件夹)。如果所有者希望将新商家信息放入特定群组,请在调用 createGmbListing 之前调用此方法。

TypeScript
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' },);

选项

参数类型说明
folderNamestring将结果限定为与此名称匹配的文件夹。

解析为 getFoldersUnderGoogleAccount 数组,默认为空数组。每个文件夹都包含 folderId(一个 accounts/<id> 字符串,传递时请保持原样)、folderName 和 locationCount;当 Google 未提供数量时,最后一个字段可以为 null。此路径仅适用于 Google 类型的账户。

fetchConnectedAccountListings

列出已连接账户有权访问的发布平台商家信息。每条记录的 id 就是 connectListing 接受的 connectedAccountListingId,因此您可以据此构建商家信息选择器。

TypeScript
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);}

选项

参数类型说明
locationInfostring对商家名称、地址、所在城市、邮政编码、门店代码和电话号码进行自由文本搜索。
pagenumber页码(从 1 开始)。
perPagenumber每页记录数。默认为 500,上限也是 500。

解析为 connectedAccountListings 下的数据:records 和 pageInfo。每条记录都包含 id、locationName、address、phone、liveLink 和 accountTypeName。虽然底层请求采用写入请求的形式,但此操作是读取操作:SDK 会按照端点要求,自动将账户 ID 和分页参数放入请求体。

triggerConnectedAccountMatches

将任务加入队列,以获取指定账户可访问的所有资料,并将其与您的地点进行匹配。所有者完成授权页面上的操作后,请立即调用此方法;批量创建地点后,请再次调用。

TypeScript
const result = await client.triggerConnectedAccountMatches(['4f712c17-4f95-42dd-90f4-97171a2e67b5',]); console.log(result.success, result.failedIds);

参数

参数类型说明
connectedAccountIdsstring[]要执行匹配的账户。

解析为 connectedAccountsTriggerMatches 下的数据:success 和 failedIds。success 为 true 表示任务已加入队列,并不表示匹配已完成。请轮询 fetchConnectedAccountDetails,直到 requestMatchesStatus 的值为 MATCH_COMPLETED,然后读取匹配建议。

fetchConnectionSuggestions

读取匹配产生的配对结果,以便操作人员在建立任何关联之前,将每个地点与发布平台上的商家信息进行比较。

TypeScript
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);}

选项

参数类型说明
pagenumber页码(从 1 开始)。
perPagenumber每页记录数。

解析为 connectionSuggestionsForAccount 下的数据:records 和 pageInfo。匹配建议记录没有 id 字段。其标识符为 matchedDataDatabaseId,而 accountType(GMB、FB 或 APPLE)表示该记录属于哪种匹配记录类型。请将两者一起保留,因为 confirmConnectedAccountMatches 需要使用两者来构建其接受的 ID。尚无匹配结果的账户会返回空的 records 数组。

confirmConnectedAccountMatches

接受已检查的配对结果。确认后,每个地点都会关联到其发布平台商家信息,以便后续地点更新同步到该商家信息。

根据匹配建议记录构建每个 ID:用冒号连接匹配记录类型名称与记录的 matchedDataDatabaseId,然后进行 base64 编码。GMB 对应 GmbLocationMatchedData,FB 对应 FbLocationMatchedData,APPLE 对应 AppleLocationMatchedData。

TypeScript
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);

参数

参数类型说明
matchRecordIdsstring[]要确认的已编码匹配记录 ID。至少需要 1 个。

解析为 confirmConnectMatches 下的数据:success 和 failedIds。每次调用都请检查 success:如果某个 ID 解码后不属于这 3 种类型中的任何一种,仍会返回成功格式的响应,但 success 为 false,且没有 failedIds。如果部分 ID 处理失败,failedIds 包含的是解码后的数据库 ID,而不是您发送的编码值,因此请将其与 matchedDataDatabaseId 对照匹配。

将单条商家信息关联到单个地点

以下 3 个方法用于完成单条已匹配商家信息与单个地点之间的关联操作,其文档与其他商家信息端点一并提供,此处不再重复:

在 confirmConnectedAccountMatches 已处理某个账户中的大部分关联后,如果仍有一家门店需要手动配对,可使用这些方法。


oauthDisconnect

这是相同的单地点断开连接操作,通过按地点划分的 OAuth 路径调用。它接受相同的参数,并解析为相同的有效负载,因此您可以选择在代码中更易读的方法名称。

TypeScript
await client.oauthDisconnect(1800289, 'GOOGLE');

参数

参数类型说明
locationIdstring | number要断开连接的地点。
sitestring要断开连接的发布平台:'GOOGLE'、'FACEBOOK' 或 'APPLE'。

解析为 disconnectConnectedAccountsLocations 有效负载,其键与 disconnectListing 返回的键相同。

disconnectGoogleAccount

断开整个 Google 账户的连接:通过该账户关联的所有地点都将停止接收更新。在客户停止使用服务或转移所有权时使用。

TypeScript
await client.disconnectGoogleAccount('4f712c17-4f95-42dd-90f4-97171a2e67b5');

参数

参数类型说明
connectedAccountIdstring要断开连接的 Google 账户。

解析为 gmbBulkDisconnect 有效负载,其中包含 success。此操作会解除全部关联,无法只解除部分关联:如果您要解除单个地点的关联并保留账户,请使用 disconnectListing。即使 ID 未知,返回的 success 也为 true,因此如果您需要确认结果,请随后读取 fetchConnectedAccounts。

disconnectFacebookAccount

这是对应的 Facebook 方法,参数相同,同样只能解除全部关联,无法只解除部分关联。

TypeScript
await client.disconnectFacebookAccount('9d2b5a41-0c7e-4f83-bb16-2ac43e70d915');

参数

参数类型说明
connectedAccountIdstring要断开连接的 Facebook 账户。

解析为 fbBulkDisconnect 有效负载,其中包含 success。同样需要注意:即使 ID 未知,也仍会报告成功,因此请使用 fetchConnectedAccounts 验证。

典型接入流程

connect_google.ts
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');