從 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 和產品的管道欄位即將淘汰並移除。
  • 推出 Reporting 子 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 功能搭配使用。

舉例來說,你可以搭配現有的 Content API for Shopping 2.1 版products實作項目使用 Merchant Inventories API。你可以使用 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 網址要求需要更多參數。這包括資源、版本、名稱 (ID) 和方法 (非標準方法)。如要進一步瞭解這項功能,請參閱「帳戶和產品 ID」和範例

ID 的 AIP 原則

Content API for Shopping 使用 ID 來識別資源 (例如 merchantIdproductId),而 Merchant API 則使用 name 識別碼,以符合 AIP (請參閱 API 改良原則)。

{name} ID 包含資源 ID 和其父項 (或可能有多個父項),因此 {name} 等於 accounts/{account}/products/{product}

所有讀取和寫入呼叫都會傳回 name 欄位做為資源 ID。

{name} 也包含集合 ID accounts/products/

Merchant API 使用 {account} 代表 Merchant Center ID,並使用 {product} 代表產品 ID。

舉例來說,您可以實作 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}

詳情請參閱「ID 變更」。

再舉一例,使用 Merchant API 從 Merchant Center ID 4321 擷取 ID 為 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 中所有讀取和寫入呼叫的資源 ID。

在 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},不必傳遞整個父項資源。您也可以搭配 list 使用 parent 欄位

舉例來說,如要列出特定產品的店面庫存,請在 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 資源有兩個父項,包含在名稱 ID 中 (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 年年度動態饋給規格)。

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,請參閱商家 API 設計