从 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/v1/accounts/{ACCOUNT_ID}/developerRegistration:registerGcp

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

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

相对于 Content API for Shopping 的改进

Merchant API 可让您在 Merchant Center 中自动执行工作流并简化工作流,并且相对于 Content API for Shopping 提供了增强的功能。

关键应用场景:

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

主要改进领域:

具体变化:

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 来标识资源(例如 merchantId, productId),而 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/v1/{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/v1/{name}

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

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

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

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

在 Content API for Shopping 中,冒号 (:) 表示商品名称中的分隔符,而在 Merchant API 中,波浪号 (~) 执行此功能。Merchant API 标识符不包含 channel 部分。

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

channel:contentLanguage:feedLabel:offerId

在 Merchant API 中变为以下内容:

contentLanguage~feedLabel~offerId

子资源的父字段

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

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

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

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

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

父级为 accounts/{account}/products/{product}。请注意,在这种情况下 the 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.api.gax.grpc.InstantiatingGrpcChannelProvider;
import com.google.auth.oauth2.GoogleCredentials;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.shopping.merchant.products.v1.Availability;
import com.google.shopping.merchant.products.v1.Condition;
import com.google.shopping.merchant.products.v1.InsertProductInputRequest;
import com.google.shopping.merchant.products.v1.ProductAttributes;
import com.google.shopping.merchant.products.v1.ProductInput;
import com.google.shopping.merchant.products.v1.ProductInputsServiceClient;
import com.google.shopping.merchant.products.v1.ProductInputsServiceSettings;
import com.google.shopping.merchant.products.v1.Shipping;
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();

    ProductAttributes attributes =
        ProductAttributes.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(Availability.IN_STOCK)
            .setCondition(Condition.NEW)
            .setGoogleProductCategory("Media > Books")
            .addGtins("9780007350896")
            .addShipping(shipping)
            .addShipping(shipping2)
            .build();

    return ProductInput.newBuilder()
        .setContentLanguage("en")
        .setFeedLabel("CH")
        .setOfferId(generateRandomString())
        .setProductAttributes(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 a channel provider. This provider manages a pool of gRPC channels
    // to enhance throughput for bulk operations. Each individual channel in the pool
    // can handle up to approximately 100 concurrent requests.
    //
    // Channel: A single connection pathway to the service.
    // Pool: A collection of multiple channels managed by this provider.
    //   Requests are distributed across the channels in the pool.
    //
    // We recommend estimating the number of concurrent requests you'll make, divide by 50 (50%
    // utilization of channel capacity), and set the pool size to that number.
    InstantiatingGrpcChannelProvider channelProvider =
        InstantiatingGrpcChannelProvider.newBuilder().setPoolSize(30).build();

    // Creates service settings using the credentials retrieved above.
    ProductInputsServiceSettings productInputsServiceSettings =
        ProductInputsServiceSettings.newBuilder()
            .setCredentialsProvider(FixedCredentialsProvider.create(credential))
            .setTransportChannelProvider(channelProvider)
            .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);
  }
}

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

专有功能

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

Merchant API 专有的功能包括

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

价格

以下是 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 设计