连接Google Business Profile并导入地点

创建用于连接 Google 的链接,将所有者在 Google 上的商家信息与您的地点匹配,确认匹配结果,并导入尚无对应地点的商家信息。

连接 Google 账号后,即可在 Google 上为地点发布内容并获取报告。在连接账号之前,地点的高级商家信息中的 Google Maps 行会保持 REQUIRING_ACTION,Google 评价不会被拉取,帖子无法发布到 GOOGLE,Google 分析数据会返回 null。连接账号即可解决这 4 个问题。

批量流程(步骤 1 至 6)一次连接整个 Google 账号,并将该账号拥有的每条商家信息与您的地点进行匹配。单地点流程(步骤 7)每次连接 1 个地点:链接与该地点绑定,因此后续无需匹配。

链接的作用范围限于拥有该 API 密钥的账号,因此请求体只包含您的两个重定向 URL。引导所有者打开返回的 url;完成操作后,Google 会将其重定向到 successUrl 或 errorUrl。

Connect link
curl -X POST https://listingsapi.com/api/v4/connected-accounts/connect-google \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{  "input": {    "successUrl": "https://app.example.com/connect/google/success",    "errorUrl": "https://app.example.com/connect/google/error"  }}'
JSON
{  "data": {    "bulkConnectLinkForGoogle": {      "success": true,      "url": "https://connect.example.com/oauth?token=8f3c1d92-...",      "errors": null    }  }}

上面的 url 是占位符。请引导所有者打开您自己的响应中返回的 url。

链接的有效期为 24 小时。请检查 url 是否非空,不要仅依赖 success。如果所有者的凭据之后过期,请用相同方式创建新链接:现有连接会被刷新,不会重复创建连接。

2.OAuth 返回后列出已连接的账号

当所有者被重定向到您的 successUrl 时,列出 Google 账号。此接口采用偏移量分页:records[] 加上 pageInfo.totalRecords,使用 page 和 perPage 分页。

List accounts
curl "https://listingsapi.com/api/v4/connected-accounts?publisher=GoogleAccount&page=1&perPage=20" \-H "Authorization: API $LISTINGSAPI_KEY"
JSON
{  "data": {    "connectedAccountsInfo": {      "pageInfo": { "hasNextPage": false, "totalRecords": 1 },      "records": [        {          "connectedAccountId": "4f712c17-4f95-42dd-90f4-97171a2e67b5",          "email": "owner@brightsmile-dental.com",          "status": "CONNECTED",          "requestMatchesStatus": "MATCH_COMPLETED",          "connectedLocationsCount": 0        }      ]    }  }}

选择 email 对应刚刚同意授权的所有者的记录,并保存其 connectedAccountId。后续每次调用都需要传入此 UUID。

status含义
CONNECTED凭据有效。可以安全地触发匹配。
MATCH_IN_PROGRESS匹配任务已排队或正在运行。
CONNECTIVITY_ISSUE授权已过期。connectivityIssue 说明了原因;请重新生成连接链接。

可选的 status 查询筛选条件使用不同的取值(Connected、NotConnected、ConnectionIssue);将记录自身的 status 作为筛选值传回,会返回 400。

3.触发匹配

匹配程序会获取该账号可访问的所有商家资料,并将每份资料与您的一个地点配对。此时只会生成建议,尚未建立任何关联。

Trigger matches
curl -X POST https://listingsapi.com/api/v4/connected-accounts/trigger-matches \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{ "input": { "connectedAccountIds": ["4f712c17-4f95-42dd-90f4-97171a2e67b5"] } }'

响应为 data.connectedAccountsTriggerMatches,包含 success 和 failedIds。success: true 表示任务已加入队列;无法加入队列的账号会列在 failedIds 中。

4.轮询,直到状态为 MATCH_COMPLETED

使用设有终止限制的循环,每 15 秒轮询一次账号详情。这个间隔也能确保 Launch 套餐的请求频率不超过每分钟 10 次。

Poll details
curl https://listingsapi.com/api/v4/connected-accounts/4f712c17-4f95-42dd-90f4-97171a2e67b5/details \-H "Authorization: API $LISTINGSAPI_KEY"

记录在 data.connectedAccountDetails.details 中返回,字段与列表中的记录相同。任务运行时,requestMatchesStatus 为 MATCH_IN_PROGRESS;建议准备就绪时,则为 MATCH_COMPLETED。Completed 表示匹配程序已完成,并不表示已建立任何关联:在您确认之前,connectedLocationsCount 会保持为 0。

5.审核并确认建议

每条建议将您的一个地点(locationInfo,其 databaseId 为 locationId)与一条 Google 商家信息(suggestedLocationInfo)配对。请向操作人员展示双方信息,或在名称和城市一致时自动接受建议。

Suggestions
curl "https://listingsapi.com/api/v4/connected-accounts/4f712c17-4f95-42dd-90f4-97171a2e67b5/connection-suggestions?page=1&perPage=20" \-H "Authorization: API $LISTINGSAPI_KEY"
JSON
{  "data": {    "connectionSuggestionsForAccount": {      "pageInfo": { "hasNextPage": false, "totalRecords": 2 },      "records": [        {          "accountType": "GMB",          "matchedDataDatabaseId": "0d5e306f-0da0-46e3-929f-fd3c67ccd8cc",          "locationId": "73415",          "locationInfo": { "name": "My Business", "city": "Lincoln", "stateIso": "NE" },          "suggestedLocationInfo": { "locationInfo": { "name": "My Business", "city": "Lincoln" } }        }      ]    }  }}

建议没有 id。要确认建议,您需要构造匹配记录的 id:从 accountType 中选择类型,然后对 <MatchRecordType>:<matchedDataDatabaseId> 进行 base64 编码。对于 Google(GMB),类型为 GmbLocationMatchedData,因此上面的记录编码后为 R21iTG9jYXRpb25NYXRjaGVkRGF0YTowZDVlMzA2Zi0wZGEwLTQ2ZTMtOTI5Zi1mZDNjNjdjY2Q4Y2M=。Facebook(FB)使用 FbLocationMatchedData,Apple(APPLE)使用 AppleLocationMatchedData。

Confirm matches
# base64 of GmbLocationMatchedData:0d5e306f-0da0-46e3-929f-fd3c67ccd8cccurl -X POST https://listingsapi.com/api/v4/connected-accounts/confirm-matches \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{ "input": { "matchRecordIds": ["R21iTG9jYXRpb25NYXRjaGVkRGF0YTowZDVlMzA2Zi0wZGEwLTQ2ZTMtOTI5Zi1mZDNjNjdjY2Q4Y2M="] } }'

响应为 data.confirmConnectMatches,包含 success 和 failedIds。failedIds 包含无法确认的记录所对应的已解码 matchedDataDatabaseId 值,例如记录已被使用,或地点在匹配后已归档。使用错误类型构造的 id 不会导致返回 400:该调用会返回 200,包含 success: false,但不包含 failedIds,因此请每次都检查 success。未提交的建议保持不变。

6. 导入尚未关联地点的商家信息

匹配只会将商家信息与您已有的地点配对。要导入其余商家信息,请先列出该账号可见的所有商家信息,再根据您需要的每条商家信息创建地点。

Account listings
curl -X POST https://listingsapi.com/api/v4/connected-accounts/connected-account-listings \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{ "connectedAccountId": "4f712c17-4f95-42dd-90f4-97171a2e67b5", "perPage": 50 }'
JSON
{  "data": {    "connectedAccountListings": {      "pageInfo": { "hasNextPage": false, "totalRecords": 60 },      "records": [        {          "id": "R21iQnVsa0RhdGFMYWtlOjA2MDQ4NmExLTA0NzQtNDc4NS1iMjNhLTBkOGM4MzhmNGQ5OA==",          "locationName": "Trial",          "address": "123 William St, NY 10038, Manhattan, US",          "liveLink": null        }      ]    }  }}

传入 locationInfo 并指定搜索词,即可按名称、地址、电话、邮政编码或门店代码缩小大型账号的搜索范围。每条记录中的 id 就是后续 2 次调用接受的 connectedAccountListingId。这 2 个 SDK 都尚未封装 create-location-from-listing,因此 Python 和 Node 示例使用相同的请求头直接调用 REST。

Create from listing
curl -X POST https://listingsapi.com/api/v4/connected-accounts/create-location-from-listing \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{  "input": {    "connectedAccountId": "4f712c17-4f95-42dd-90f4-97171a2e67b5",    "connectedAccountListingId": "R21iQnVsa0RhdGFMYWtlOjA2MDQ4NmExLTA0NzQtNDc4NS1iMjNhLTBkOGM4MzhmNGQ5OA=="  }}'
JSON
{  "data": {    "createLocationFromConnectedAccountListing": { "success": true, "locationId": 88277, "errors": null }  }}

地点根据 Google 数据(名称、地址、营业时间、类别、电话、属性)创建,并立即关联到该商家信息。Google 商家信息会保持 IN_PROGRESS 状态,直到您首次更新地点并将更新发布到该商家信息。locationId 就是普通的 databaseId。失败时,success 为 false,errors 中会包含 SY81067 等代码(商家信息 id 无法解析)。字段名为 connectedAccountListingId;listingId 会被模式校验拒绝。

如果商家信息属于您已有的地点,请将其关联到该地点,避免创建重复地点。

Link a listing
curl -X POST https://listingsapi.com/api/v4/connected-accounts/connect-listing \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{  "input": {    "locationId": "TG9jYXRpb246MTgwMDI4OQ==",    "connectedAccountId": "4f712c17-4f95-42dd-90f4-97171a2e67b5",    "connectedAccountListingId": "R21iQnVsa0RhdGFMYWtlOjA2MDQ4NmExLTA0NzQtNDc4NS1iMjNhLTBkOGM4MzhmNGQ5OA=="  }}'

关联失败时会返回 success: false,其中包含 message,例如 Listing record not found,或者一条说明该商家信息已连接到其他地点的提示。

7. 改为连接单个地点

如果您已经知道所有者要连接哪个地点,可以跳过匹配,创建绑定到该地点的链接。成功后,所有者会返回您的 successUrl,凭据仅关联到该地点。

Single-location link
curl -X POST https://listingsapi.com/api/v4/locations/oauth_connect_url \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{  "input": {    "locationId": "TG9jYXRpb246MTgwMDI4OQ==",    "site": "GOOGLE",    "successUrl": "https://app.example.com/locations/1800289/connected",    "errorUrl": "https://app.example.com/locations/1800289/connect-failed"  }}'

响应为 data.createConnectUrl,其中包含 success、url 和 errors。该链接只能使用 1 次,因此每次尝试都需要创建新链接。如果重定向 URL 是相对地址或无法解析,则会返回 success: true,其中 null 为 url,因此请检查 url。无权访问此地点的密钥会收到 403,其中包含 SY90003。

8. 后续处理

商家信息关联后,地点的高级商家信息列表中的 Google Maps 行会从 REQUIRING_ACTION 变为 IN_PROGRESS,最终变为 SYNCED,并包含 listingUrl。Google 会自动添加为评价来源,因此评价会开始出现在互动列表中,您对地点所做的更新也会同步到商家资料。

在控制台中设置 webhook URL 后,您就无需轮询。每个载荷都包含 location_id 和 data.connected_account_id。

事件触发条件处理方式
connection.location_connected商家信息关联到地点;data.platform 为 google。在您的界面中将该地点标记为已连接。
connection.reauth_required该账号的凭据已失效。重新授权之前,不会同步任何内容。创建新链接(第 1 步),并请所有者重新登录。
connection.listing_inaccessible凭据正常,但对该条商家信息的访问权限已被移除。请所有者在 Google 上恢复访问权限,或关联其他商家信息。

后续步骤