从 Content API for Shopping 迁移到 Merchant API

本指南介绍了从 Content API for Shopping 迁移到 Merchant API 以进行商家数据管理的过程。

您可以按照本指南将现有的 Content API for Shopping 实现迁移到 Merchant API。如需详细了解 Merchant API 及其子 API 的详细信息,请参阅 Merchant API 设计

开始使用

如需开始使用 Merchant API,请将请求网址更改为以下格式:

https://merchantapi.googleapis.com/{SUB_API}/{VERSION}/{RESOURCE_NAME}:{METHOD}

如需使用 Merchant API,您必须使用开发者注册方法关联您的 Merchant Center 账号和 Google Cloud 项目,具体操作如下:

POST https://merchantapi.googleapis.com/accounts/v1beta/accounts/{ACCOUNT_ID}/developerRegistration:registerGcp

{
  developer_email:"example-email@example.com"
}

如需了解详情,请参阅快速入门指南和 Merchant API 参考文档

请参阅 Merchant API(Beta 版)中的最新更新

相对于 Content API for Shopping 的改进

借助 Merchant API,您可以自动执行并简化 Merchant Center 中的工作流程,并且与 Content API for Shopping 相比,它还提供增强的功能。

关键应用场景:

  • 自动账号管理
  • 自动化产品管理
  • 自动化的产品目录管理系统
  • 自定义报告

主要改进方面:

具体变化:

  • 每次 API 调用的 pageSize 上限从 250 行增加到 1,000 行。
  • 修复了在创建 DataSources 后,商品插入、促销活动、商品评价和商家评价方面存在的延迟问题。

即将推出的功能:

  • 弃用并将在未来移除 DataSources 和产品的渠道字段。
  • 报告子 API 下的 productView 表中,针对 clickPotentialRank 推出了更新后的定义: * 基于 clickPotential 的商品排名已归一化为介于 1 到 1000 之间的值。
    • clickPotentialRank 较低的商品在满足搜索查询条件的商家商品中仍具有最高的点击潜力。这是一项非破坏性变更,可能会于 2025 年 7 月 1 日推出。
  • AccountRelationship资源中的 AccountIdAlias 可用于更好地管理复杂的账号结构。例如,购物平台会使用用户定义的别名,而不是商家的内部 ID(例如账号 ID)。

gRPC 支持

Merchant API 支持 gRPC 和 REST。您可以同时使用 gRPC(用于 Merchant API)和 REST(用于 Content API for Shopping)。

Merchant API 客户端库需要 gRPC。

如需了解详情,请参阅 gRPC 概览

兼容性

本指南介绍了适用于整个 Merchant API 的一般性变更。

Merchant API 旨在与现有的 Content API for Shopping 功能协同运作。

例如,您可以将 Merchant Inventories API 与现有的 Content API for Shopping v2.1 products 实现搭配使用。您可以使用 Content API for Shopping 上传新的本地商品(您在实体店中销售的商品),然后使用 Merchant Inventories API LocalInventory 资源来管理该商品的实体店信息。

相对于 Content API 的改进

Merchant API 在以下方面优于 Content API:

我们来更详细地了解一下这些变化。

版本控制和子 API

Merchant API 引入了版本控制子 API 的概念。其模块化设计可让您专注于所需子 API,并更轻松地将未来迁移到新版本,从而提高易用性。版本控制将应用于您的请求网址。此策略与 Google Ads API 体验类似。

更强大的请求

Merchant API 网址请求需要更多参数才能调用 Merchant API。这包括资源、版本、名称(标识符)和方法(非标准方法)。如需详细了解此方面的信息,请参阅账号和产品标识符示例

标识符的 AIP 原则

虽然 Content API for Shopping 使用 ID 来标识资源(例如 merchantIdproductId),但 Merchant API 使用 name 标识符来与 AIP 保持一致(请参阅 API 改进原则)。

{name} 标识符包含资源标识符及其父级(或可能包含多个父级),因此 {name} 等于 accounts/{account}/products/{product}

所有读取和写入调用都会返回 name 字段作为资源标识符。

{name} 还包括核销标识符 accounts/products/

Merchant API 使用 {account} 来指代 Merchant Center ID,并使用 {product} 来指代商品标识码。

例如,实现一种 getName() 方法,用于从资源中检索 name,并将输出存储为变量,而不是自行从商家和资源 ID 构建 name

以下示例展示了如何在调用中使用 name 字段:

   POST https://merchantapi.googleapis.com/inventories/v1beta/{PARENT}/regionalInventories:insert

下表显示了 Content API for Shopping products.get 请求的变化:

Content API for Shopping Merchant API
GET https://shoppingcontent.googleapis.com/content/v2.1/{merchantId}/products/{productId} GET https://merchantapi.googleapis.com/products/v1beta/{name}

如需了解详情,请参阅标识符变更

再举一个例子,使用 Merchant API 从 Merchant Center ID 4321 中检索标识符为 online~en~US~1234 的商品,如下所示:

    GET
    https://merchantapi.googleapis.com/products/v1beta/accounts/4321/products/online~en~US~1234

其中 {name} 等于 accounts/4321/products/online~en~US~1234。此新名称字段会作为 Merchant API 中所有读取和写入调用的资源标识符返回。

在 Content API for Shopping 中,英文冒号 (:) 表示商品名称中的分隔符,而在 Merchant API 中,英文波浪号 (~) 执行此功能。

例如,Content API for Shopping 中的商品 ID:

channel:contentLanguage:feedLabel:offerId

在 Merchant Center API 中,以下内容:

channel~contentLanguage~feedLabel~offerId

子资源的父字段

在 Merchant API 中,所有子资源都具有 parent 字段。您可以使用 parent 字段指定要将子资源插入到的资源的 {name},而不是传递整个父资源。您还可以将 parent 字段与 list 搭配使用

例如,如需列出指定商品的实体店商品目录,请在 list 方法的 parent 字段中指定商品的 name。在这种情况下,指定的 product 是返回的 LocalInventory 资源的 parent

    GET
    https://merchantapi.googleapis.com/inventories/v1beta/{parent}/localInventories

如需检索商品 online~en~US~1234' 和账号 4321 的所有本地商品目录,请求应如下所示

    GET
    https://merchantapi.googleapis.com/inventories/v1beta/accounts/4321/products/online~en~US~1234/localInventories</code>

父项为 accounts/{account}/products/{product}。请注意,在这种情况下,localInventories 资源在名称标识符中包含两个父级(accounts/products/),因为账号是商品资源的父级。

常见枚举

使用通用枚举可提高一致性。

Destination.DestinationEnum 字段用于指定要显示资源的途径。 DestinationEnum 列出了目的地定位的所有可用值,并在各个子 API(例如促销信息属性)中保持统一。

ReportingContext.ReportingContextEnum 字段表示账号和商品问题所适用的情境。 此字段可用于各种报告方法(例如 IssueSeverityPerReportingContext)。

向后兼容性

开始使用 Merchant API 后,您现有的 Content API for Shopping 集成功能将继续正常运行,不会中断。如需了解详情,请参阅兼容性

将子 API 迁移到 Merchant API 后,我们建议您仅使用 Merchant API 来处理已迁移的子 API。

远程过程调用 (gRPC) 的可用性

gRPC 是目前推荐的与 Merchant API 集成的方式。

其优势包括:

自定义批处理变为内置批处理

使用异步调用时,批处理的执行效率更高。详细了解如何在 Merchant API 中使用并行调用来实现批处理,以及如何重构代码以实现并发请求

为帮助您加快迁移速度,我们建议您使用客户端库

Merchant API 不支持 Content API for Shopping 中提供的 customBatch 方法。请改为参阅一次性发送多个请求或以异步方式执行调用。

以下 Java 示例演示了如何插入商品输入。

   import com.google.api.core.ApiFuture;
import com.google.api.core.ApiFutureCallback;
import com.google.api.core.ApiFutures;
import com.google.api.gax.core.FixedCredentialsProvider;
import com.google.auth.oauth2.GoogleCredentials;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.shopping.merchant.products.v1beta.Attributes;
import com.google.shopping.merchant.products.v1beta.InsertProductInputRequest;
import com.google.shopping.merchant.products.v1beta.ProductInput;
import com.google.shopping.merchant.products.v1beta.ProductInputsServiceClient;
import com.google.shopping.merchant.products.v1beta.ProductInputsServiceSettings;
import com.google.shopping.merchant.products.v1beta.Shipping;
import com.google.shopping.type.Channel.ChannelEnum;
import com.google.shopping.type.Price;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;
import shopping.merchant.samples.utils.Authenticator;
import shopping.merchant.samples.utils.Config;

/** This class demonstrates how to insert a product input */
public class InsertProductInputAsyncSample {

  private static String getParent(String accountId) {
    return String.format("accounts/%s", accountId);
  }

  private static String generateRandomString() {
    String characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
    Random random = new Random();
    StringBuilder sb = new StringBuilder(8);
    for (int i = 0; i < 8; i++) {
      sb.append(characters.charAt(random.nextInt(characters.length())));
    }
    return sb.toString();
  }

  private static ProductInput createRandomProduct() {
    Price price = Price.newBuilder().setAmountMicros(33_450_000).setCurrencyCode("USD").build();

    Shipping shipping =
        Shipping.newBuilder().setPrice(price).setCountry("GB").setService("1st class post").build();

    Shipping shipping2 =
        Shipping.newBuilder().setPrice(price).setCountry("FR").setService("1st class post").build();

    Attributes attributes =
        Attributes.newBuilder()
            .setTitle("A Tale of Two Cities")
            .setDescription("A classic novel about the French Revolution")
            .setLink("https://exampleWebsite.com/tale-of-two-cities.html")
            .setImageLink("https://exampleWebsite.com/tale-of-two-cities.jpg")
            .setAvailability("in stock")
            .setCondition("new")
            .setGoogleProductCategory("Media > Books")
            .addGtin("9780007350896")
            .addShipping(shipping)
            .addShipping(shipping2)
            .build();

    return ProductInput.newBuilder()
        .setChannel(ChannelEnum.ONLINE)
        .setContentLanguage("en")
        .setFeedLabel("CH")
        .setOfferId(generateRandomString())
        .setAttributes(attributes)
        .build();
  }

  public static void asyncInsertProductInput(Config config, String dataSource) throws Exception {

    // Obtains OAuth token based on the user's configuration.
    GoogleCredentials credential = new Authenticator().authenticate();

    // Creates service settings using the credentials retrieved above.
    ProductInputsServiceSettings productInputsServiceSettings =
        ProductInputsServiceSettings.newBuilder()
            .setCredentialsProvider(FixedCredentialsProvider.create(credential))
            .build();

    // Creates parent to identify where to insert the product.
    String parent = getParent(config.getAccountId().toString());

    // Calls the API and catches and prints any network failures/errors.
    try (ProductInputsServiceClient productInputsServiceClient =
        ProductInputsServiceClient.create(productInputsServiceSettings)) {

      // Creates five insert product input requests with random product IDs.
      List<InsertProductInputRequest> requests = new ArrayList<>(5);
      for (int i = 0; i < 5; i++) {
        InsertProductInputRequest request =
            InsertProductInputRequest.newBuilder()
                .setParent(parent)
                // You can only insert products into datasource types of Input "API", and of Type
                // "Primary" or "Supplemental."
                // This field takes the `name` field of the datasource.
                .setDataSource(dataSource)
                // If this product is already owned by another datasource, when re-inserting, the
                // new datasource will take ownership of the product.
                .setProductInput(createRandomProduct())
                .build();

        requests.add(request);
      }

      System.out.println("Sending insert product input requests");
      List<ApiFuture<ProductInput>> futures =
          requests.stream()
              .map(
                  request ->
                      productInputsServiceClient.insertProductInputCallable().futureCall(request))
              .collect(Collectors.toList());

      // Creates callback to handle the responses when all are ready.
      ApiFuture<List<ProductInput>> responses = ApiFutures.allAsList(futures);
      ApiFutures.addCallback(
          responses,
          new ApiFutureCallback<List<ProductInput>>() {
            @Override
            public void onSuccess(List<ProductInput> results) {
              System.out.println("Inserted products below");
              System.out.println(results);
            }

            @Override
            public void onFailure(Throwable throwable) {
              System.out.println(throwable);
            }
          },
          MoreExecutors.directExecutor());

    } catch (Exception e) {
      System.out.println(e);
    }
  }

  public static void main(String[] args) throws Exception {
    Config config = Config.load();
    // Identifies the data source that will own the product input.
    String dataSource = "accounts/" + config.getAccountId() + "/dataSources/{datasourceId}";

    asyncInsertProductInput(config, dataSource);
  }
}

如果您在 Content API 中使用 customBatch,并且需要 Merchant API 提供此功能,请在反馈中告知我们原因。

专有功能

未来的功能将仅在 Merchant API 中提供。(但也有一些例外情况,例如 2025 年的 Feed 规范。)

Merchant API 独有的功能包括

  • Reviews API。使用“评价”功能来实现和管理商品评分和商店评分。如需了解详情,请参阅卖家评价商品评价
  • 通知:订阅以接收有关账号商品数据变更的推送通知。

价格

以下是 Merchant Common 软件包中针对 Price 所做的更改:

Content API for Shopping Merchant API
金额字段 value:string amountMicros:int64
币种字段 currency:string currencyCode:string

Price 金额现在以微单位记录,100 万微单位相当于您所用币种的标准单位。

在 Content API for Shopping 中,Price 是字符串形式的十进制数。

金额字段名称已从 value 更改为 amountMicros

币种字段名称已从 currency 更改为 currencyCode。格式仍为 ISO 4217

最新动态和公告

如需了解更精细的更新,请参阅每个子 API 的特定版本说明。如需了解更常规的 Merchant API 汇总更新,请查看我们的最新更新

如需了解更具体的信息,并详细了解 Merchant API,请访问我们的开发者网站,查看概览和总体迁移指南,了解更多详情。

如需详细了解 Merchant API 及其子 API,请参阅 Merchant API 设计