mcpField
M
marketplace-catalog-sync

marketplace-catalog-sync

github.com/vtex開発471.0k 回インストール2 週間前に更新
リポジトリ

marketplace-catalog-sync について

VTEX マーケットプレイスに接続する外部セラー向けのカタログ連携スキルです。SKU 通知や提案、価格・在庫更新に加え、レート制限や状態管理の注意点を扱います。

主な機能

  1. 01セラーSKUと市場SKUの経路を区別
  2. 02SKU提案と承認状態を管理
  3. 03価格と在庫の変更を通知
  4. 04429応答への再試行に対応
  5. 05フルフィルメントシミュレーションの応答時間を遵守

活用シーン

  1. 01外部セラー向けカタログコネクターの構築
  2. 02SKU登録と承認状況の追跡
  3. 03市場への価格・在庫変更通知

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/vtex/skills --skill marketplace-catalog-sync

インストール後、Claude Code で/marketplace-catalog-syncを入力して読み込まれたか確認します。

詳細

カタログと SKU の統合

このスキルを使用する場合

VTEX マーケットプレイスに商品カタログデータを送信し、SKU の承認ワークフローを処理する、または価格と在庫を同期するセラーコネクターを構築する場合に、このスキルを使用します。

  • SKU を登録・更新する Change Notification フローの構築
  • SKU 提案のライフサイクル(送信 → 保留 → 承認/拒否)の実装
  • 商品データを VTEX カタログスキーマにマッピング
  • 通知エンドポイントを介した価格と在庫の同期

以下の場合には、このスキルを使用しないでください。

  • マーケットプレイス側でのカタログ操作(Catalog API への直接書き込み)
  • 注文のフルフィルメントまたは請求書の処理(marketplace-fulfillment を参照)
  • レート制限パターンのみを扱う場合(marketplace-rate-limiting を参照)

判断ルール

VTEX では、api/catalog_system/pvt/skuseller/changenotification 配下に 2 つの POST ルートがあります。これらは相互に置き換え可能ではありません。パスの形式から、プラットフォームは送信された識別子を判別します。

ルートパスのパターン意味
マーケットプレイスの SKU ID を使用した変更通知.../changenotification/{skuId}{skuId} はマーケットプレイスカタログ内の SKU ID(VTEX)です。URL に sellerId はありません。
セラー ID とセラー SKU ID を使用した変更通知.../changenotification/{sellerId}/{skuId}{sellerId} はマーケットプレイス上のセラーアカウントです。{skuId} はセラー独自の SKU コード(PUT SKU Suggestion のパスで使用するものと同じ ID)です。

セラーコネクターのインテグレーションでは、必ず2 つ目のルートを使用してください。公式ドキュメントでは、この 2 つの説明が混同されている場合があります。URL の形式を基準にしてください。セラーをスコープとするフローでは、常に changenotification/ の後に2 つのパスセグメントを使用します。

SKU の提案(PUT/GET)は、ストアのホスト名ではなく https://api.vtex.com/{accountName}/suggestions/{sellerId}/{sellerSkuId} に送信してください。同じ App Key と App Token を使用します。{account}.vtexcommercestable.com.br/api/catalog_system/pvt/sku/seller/... を使って提案 URL を組み立てないでください。これは別の Catalog System のエンドポイントです。

  • セラー側のカタログインテグレーションには、POST /api/catalog_system/pvt/skuseller/changenotification/{sellerId}/{sellerSkuId} を使用してください(マーケットプレイスアカウント内のセラー ID + セラーの SKU ID)。200 OK は、そのセラーの SKU がマーケットプレイスにすでに存在することを意味します(更新のフロー)。404 Not Found は存在しないことを意味するため、SKU の提案を送信してください。セラーの SKU コードを指定して POST .../changenotification/{skuId} を使用しないでください。この単一セグメントのルートは、マーケットプレイスの SKU ID を想定しています。
  • VTEX マーケットプレイスの SKU ID が手元にある場合に限り、POST .../changenotification/{skuId} を使用してください(パスにセラーのセグメントはありません)。
  • 保留中の提案を登録または更新するには、/suggestions/{sellerId}/{sellerSkuId} に対して https://api.vtex.com/{accountName} 配下の PUT(Send SKU Suggestion)を使用してください。セラーはカタログを所有しません。新しい SKU はすべて、提案/承認ワークフローを通す必要があります。
  • 価格と在庫には、それぞれ別の通知エンドポイントを使用してください。POST /notificator/{sellerId}/changenotification/{sellerSkuId}/price と POST /notificator/{sellerId}/changenotification/{sellerSkuId}/inventory です。changenotification/ の後のパスセグメントはセラー SKU ID(セラー独自の SKU コード。提案およびセラーをスコープとするカタログフローで使用する識別子と同じ)であり、マーケットプレイスの VTEX SKU ID ではありません。リファレンスドキュメントでこのセグメントが skuId と記載されている場合でも、セラーコネクターのインテグレーションではsellerSkuIdとして扱ってください。
  • 価格/在庫の通知後、マーケットプレイスはセラーのフルフィルメントシミュレーションエンドポイント(POST /pvt/orderForms/simulation)を呼び出します。このエンドポイントは 2.5 秒以内に応答しなければなりません。応答できない場合、商品は在庫切れと見なされます。
  • 提案を更新できるのは「pending」状態の間だけです。承認または却下された後は、セラーは変更できません。

アーキテクチャ/データフロー:

Seller                          VTEX Marketplace
  │                                    │
  │─── POST changenotification ──────▶│
  │◀── 200 (exists) or 404 (new) ────│
  │                                    │
  │─── PUT Send SKU Suggestion ──────▶│  (if 404)
  │                                    │── Pending in Received SKUs
  │                                    │── Marketplace approves/denies
  │                                    │
  │─── POST price notification ──────▶│
  │◀── POST fulfillment simulation ───│  (marketplace fetches data)
  │─── Response with price/stock ────▶│

厳守事項

制約:直接 Catalog API を使用せず、SKU Integration API を使用してください

外部セラーは、SKU を連携する際、必ず Change Notification + SKU Suggestion フローを使用してください。直接 Catalog API に書き込む操作(POST /api/catalog/pvt/product または POST /api/catalog/pvt/stockkeepingunit)は、マーケットプレイス側の操作専用です。

重要な理由

セラーはカタログを所有していません。カタログへの直接書き込みは 403 Forbidden で失敗するか、承認ワークフローを迂回した孤立エントリーを作成します。提案の仕組みにより、マーケットプレイスの品質管理が確実に行われます。

検出方法

セラーの連携処理から商品/SKU 作成のための Catalog API への直接呼び出し(例:POST /api/catalog/pvt/product、POST /api/catalog/pvt/stockkeepingunit)が見つかった場合は、代わりに SKU Integration API を使用するよう警告してください。

正しい例

import axios, { AxiosInstance } from "axios";

interface SkuSuggestion {
  ProductName: string;
  SkuName: string;
  ImageUrl: string;
  ProductDescription: string;
  BrandName: string;
  CategoryFullPath: string;
  EAN: string;
  Height: number;
  Width: number;
  Length: number;
  WeightKg: number;
  SkuSpecifications: Array<{
    FieldName: string;
    FieldValues: string[];
  }>;
}

async function integrateSellerSku(
  client: AxiosInstance,
  marketplaceAccount: string,
  sellerId: string,
  sellerSkuId: string,
  skuData: SkuSuggestion
): Promise<void> {
  const storeBaseUrl = `https://${marketplaceAccount}.vtexcommercestable.com.br`;
  const suggestionUrl = `https://api.vtex.com/${marketplaceAccount}/suggestions/${sellerId}/${sellerSkuId}`;

  // Step 1: Seller-scoped change notification (Catalog API — store host)
  try {
    await client.post(
      `${storeBaseUrl}/api/catalog_system/pvt/skuseller/changenotification/${sellerId}/${sellerSkuId}`
    );
    // 200 OK — SKU exists, marketplace will fetch updates via fulfillment simulation
    console.log(`SKU ${sellerSkuId} exists in marketplace, update triggered`);
  } catch (error: unknown) {
    if (axios.isAxiosError(error) && error.response?.status === 404) {
      // 404 — SKU not found, send suggestion
      console.log(`SKU ${sellerSkuId} not found, sending suggestion`);
      await client.put(suggestionUrl, skuData);
      console.log(`SKU suggestion sent for ${sellerSkuId}`);
    } else {
      throw error;
    }
  }
}

誤った例

// WRONG: Marketplace-SKU-only path with the seller's SKU code (misroutes the notification).
// .../changenotification/{skuId} expects the VTEX marketplace SKU ID, not sellerSkuId.
await client.post(
  `https://${marketplaceAccount}.vtexcommercestable.com.br/api/catalog_system/pvt/skuseller/changenotification/${sellerSkuId}`
);

// WRONG: SKU Suggestion on the store host + Catalog path — public contract is api.vtex.com + /suggestions/...
await client.put(
  `https://${marketplaceAccount}.vtexcommercestable.com.br/api/catalog_system/pvt/sku/seller/${sellerId}/suggestion/${sellerSkuId}`,
  skuData
);

// WRONG: Seller writing directly to marketplace catalog — bypasses suggestion/approval; expect 403
async function createSkuDirectly(
  client: AxiosInstance,
  marketplaceAccount: string,
  productData: Record<string, unknown>
): Promise<void> {
  // Direct catalog write — sellers don't have permission for this
  await client.post(
    `https://${marketplaceAccount}.vtexcommercestable.com.br/api/catalog/pvt/product`,
    productData
  );
  // Will fail: 403 Forbidden — seller lacks catalog write permissions
  // Will fail: 403 Forbidden — seller lacks catalog write permissions
}

制約:カタログ通知のレート制限に対応してください

すべてのカタログ通知呼び出しでは、指数バックオフによる 429 対応を必ず実装してください。バッチ通知は、VTEX API のレート制限を守るためにスロットリングする必要があります。

重要な理由

Change Notification エンドポイントにはレート制限があります。スロットリングせずに大量の通知を送ると 429 レスポンスが発生し、セラーの API アクセスが一時的にブロックされ、連携全体が停止します。

検出方法

429 対応または再試行ロジックのないカタログ通知呼び出しが見つかった場合は、処理を停止してレート制限を追加してください。遅延なしで通知を送信するタイトなループが見つかった場合は、レート制限について警告してください。

正しい例

async function batchNotifySkus(
  client: AxiosInstance,
  baseUrl: string,
  sellerId: string,
  sellerSkuIds: string[],
  concurrency: number = 5,
  delayMs: number = 200
): Promise<void> {
  const results: Array<{ sellerSkuId: string; status: "exists" | "new" | "error" }> = [];

  for (let i = 0; i < sellerSkuIds.length; i += concurrency) {
    const batch = sellerSkuIds.slice(i, i + concurrency);

    const batchResults = await Promise.allSettled(
      batch.map(async (sellerSkuId) => {
        try {
          await client.post(
            `${baseUrl}/api/catalog_system/pvt/skuseller/changenotification/${sellerId}/${sellerSkuId}`
          );
          return { sellerSkuId, status: "exists" as const };
        } catch (error: unknown) {
          if (
            error instanceof Error &&
            "response" in error &&
            (error as { response?: { status?: number } }).response?.status === 404
          ) {
            return { sellerSkuId, status: "new" as const };
          }
          if (
            error instanceof Error &&
            "response" in error &&
            (error as { response?: { status?: number; headers?: Record<string, string> } })
              .response?.status === 429
          ) {
            const retryAfter = parseInt(
              (error as { response: { headers: Record<string, string> } }).response.headers[
                "retry-after"
              ] || "60",
              10
            );
            console.warn(`Rate limited. Waiting ${retryAfter}s before retry.`);
            await new Promise((resolve) => setTimeout(resolve, retryAfter * 1000));
            return { sellerSkuId, status: "error" as const };
          }
          throw error;
        }
      })
    );

    for (const result of batchResults) {
      if (result.status === "fulfilled") {
        results.push(result.value);
      }
    }

    // Throttle between batches
    if (i + concurrency < sellerSkuIds.length) {
      await new Promise((resolve) => setTimeout(resolve, delayMs));
    }
  }
}

誤った例

// WRONG: No rate limiting, no error handling, tight loop
async function notifyAllSkus(
  client: AxiosInstance,
  baseUrl: string,
  sellerId: string,
  sellerSkuIds: string[]
): Promise<void> {
  // Fires all requests simultaneously — will trigger 429 rate limits
  await Promise.all(
    sellerSkuIds.map((sellerSkuId) =>
      client.post(
        `${baseUrl}/api/catalog_system/pvt/skuseller/changenotification/${sellerId}/${sellerSkuId}`
      )
    )
  );
}

制約:提案のライフサイクル状態に対応してください

セラーは、更新を試みる前に必ず提案の状態を確認してください。提案を更新できるのは保留中の状態のみです。

重要な理由

承認済みまたは却下済みの提案を更新しようとすると、エラーが通知されずに失敗するか、重複したエントリーが作成されます。提案が承認されると、その SKU はマーケットプレイスの所有物になります。

検出方法

現在の提案ステータスを確認せずに SKU 提案を更新している箇所が見つかった場合は、提案のライフサイクルへの対応について警告してください。

正しい例

async function updateSkuSuggestion(
  client: AxiosInstance,
  marketplaceAccount: string,
  sellerId: string,
  sellerSkuId: string,
  updatedData: Record<string, unknown>
): Promise<boolean> {
  const suggestionUrl = `https://api.vtex.com/${marketplaceAccount}/suggestions/${sellerId}/${sellerSkuId}`;

  // Check current suggestion status before updating
  try {
    const response = await client.get(suggestionUrl);

    const suggestion = response.data;
    if (suggestion.Status === "Pending") {
      // Safe to update — suggestion hasn't been processed yet
      await client.put(suggestionUrl, updatedData);
      return true;
    }

    // Already approved or denied — cannot update
    console.warn(
      `SKU ${sellerSkuId} suggestion is ${suggestion.Status}, cannot update. ` +
        `Use changenotification to update existing SKUs.`
    );
    return false;
  } catch {
    // Suggestion may not exist — send as new
    await client.put(suggestionUrl, updatedData);
    return true;
  }
}

誤った例

// WRONG: Blindly sending suggestion update without checking state
async function blindUpdateSuggestion(
  client: AxiosInstance,
  marketplaceAccount: string,
  sellerId: string,
  sellerSkuId: string,
  data: Record<string, unknown>
): Promise<void> {
  // If the suggestion was already approved, this fails silently
  // or creates a duplicate that confuses the marketplace operator
  await client.put(
    `https://api.vtex.com/${marketplaceAccount}/suggestions/${sellerId}/${sellerSkuId}`,
    data
  );
}

推奨パターン

セラーコネクタークライアントをセットアップする

VTEX マーケットプレイスとの通信に使用する、認証済み HTTP クライアントを作成します。

import axios, { AxiosInstance } from "axios";

interface SellerConnectorConfig {
  marketplaceAccount: string;
  sellerId: string;
  appKey: string;
  appToken: string;
}

function createMarketplaceClient(config: SellerConnectorConfig): AxiosInstance {
  return axios.create({
    // Catalog System routes (e.g. changenotification) use the store host.
    baseURL: `https://${config.marketplaceAccount}.vtexcommercestable.com.br`,
    headers: {
      "Content-Type": "application/json",
      Accept: "application/json",
      "X-VTEX-API-AppKey": config.appKey,
      "X-VTEX-API-AppToken": config.appToken,
    },
    timeout: 10000,
  });
}

// Use the same headers for PUT/GET on https://api.vtex.com/{account}/suggestions/...
// (pass a full URL on the same axios instance, or set baseURL to https://api.vtex.com/{account} for suggestion-only calls).
Change Notification フローを実装する

changenotification エンドポイントからの「存在する」(200)場合と「新規」(404)場合の両方に対応します。

interface CatalogNotificationResult {
  skuId: string;
  action: "updated" | "suggestion_sent" | "error";
  error?: string;
}

async function notifyAndSync(
  client: AxiosInstance,
  marketplaceAccount: string,
  sellerId: string,
  sellerSkuId: string,
  skuData: SkuSuggestion
): Promise<CatalogNotificationResult> {
  const suggestionUrl = `https://api.vtex.com/${marketplaceAccount}/suggestions/${sellerId}/${sellerSkuId}`;

  try {
    await client.post(
      `/api/catalog_system/pvt/skuseller/changenotification/${sellerId}/${sellerSkuId}`
    );
    // SKU exists — marketplace will call fulfillment simulation to get updates
    return { skuId: sellerSkuId, action: "updated" };
  } catch (error: unknown) {
    if (axios.isAxiosError(error) && error.response?.status === 404) {
      try {
        await client.put(suggestionUrl, skuData);
        return { skuId: sellerSkuId, action: "suggestion_sent" };
      } catch (suggestionError: unknown) {
        const message = suggestionError instanceof Error ? suggestionError.message : "Unknown error";
        return { skuId: sellerSkuId, action: "error", error: message };
      }
    }

    const message = error instanceof Error ? error.message : "Unknown error";
    return { skuId: sellerSkuId, action: "error", error: message };
  }
}
Fulfillment Simulation エンドポイントを実装する

通知後に現在の価格と在庫データを取得するため、マーケットプレイスはセラー側のこのエンドポイントを呼び出します。

import { RequestHandler } from "express";

interface SimulationItem {
  id: string;
  quantity: number;
  seller: string;
}

interface SimulationRequest {
  items: SimulationItem[];
  postalCode?: string;
  country?: string;
}

interface SimulationResponseItem {
  id: string;
  requestIndex: number;
  quantity: number;
  seller: string;
  price: number;
  listPrice: number;
  sellingPrice: number;
  priceValidUntil: string;
  availability: string;
  merchantName: string;
}

const fulfillmentSimulationHandler: RequestHandler = async (req, res) => {
  const { items, postalCode, country }: SimulationRequest = req.body;

  const responseItems: SimulationResponseItem[] = await Promise.all(
    items.map(async (item, index) => {
      // Fetch current price and inventory from your system
      const skuInfo = await getSkuFromLocalCatalog(item.id);

      return {
        id: item.id,
        requestIndex: index,
        quantity: Math.min(item.quantity, skuInfo.availableQuantity),
        seller: item.seller,
        price: skuInfo.price,
        listPrice: skuInfo.listPrice,
        sellingPrice: skuInfo.sellingPrice,
        priceValidUntil: new Date(Date.now() + 3600000).toISOString(),
        availability: skuInfo.availableQuantity > 0 ? "available" : "unavailable",
        merchantName: "sellerAccountName",
      };
    })
  );

  // CRITICAL: Must respond within 2.5 seconds or products show as unavailable
  res.json({
    items: responseItems,
    postalCode: postalCode ?? "",
    country: country ?? "",
  });
};

async function getSkuFromLocalCatalog(skuId: string): Promise<{
  price: number;
  listPrice: number;
  sellingPrice: number;
  availableQuantity: number;
}> {
  // Replace with your actual catalog/inventory lookup
  return {
    price: 9990,
    listPrice: 12990,
    sellingPrice: 9990,
    availableQuantity: 15,
  };
}
価格と在庫の変更を通知する

価格の更新と在庫の更新について、それぞれ個別に通知を送信します。URL 内の {sellerSkuId} セグメントは、セラーの SKU 識別子です(カタログおよび changenotification/{sellerId}/{sellerSkuId}/提案で使用するものと同じコードです)。連携が明示的にその形式でキー設定されている場合を除き、マーケットプレイス内部の VTEX SKU ID を渡さないでください。

async function notifyPriceChange(
  client: AxiosInstance,
  sellerId: string,
  sellerSkuId: string
): Promise<void> {
  await client.post(
    `/notificator/${sellerId}/changenotification/${sellerSkuId}/price`
  );
}

async function notifyInventoryChange(
  client: AxiosInstance,
  sellerId: string,
  sellerSkuId: string
): Promise<void> {
  await client.post(
    `/notificator/${sellerId}/changenotification/${sellerSkuId}/inventory`
  );
}

async function syncPriceAndInventory(
  client: AxiosInstance,
  sellerId: string,
  sellerSkuIds: string[]
): Promise<void> {
  for (const sellerSkuId of sellerSkuIds) {
    await notifyPriceChange(client, sellerId, sellerSkuId);
    await notifyInventoryChange(client, sellerId, sellerSkuId);

    // Throttle to avoid rate limits
    await new Promise((resolve) => setTimeout(resolve, 200));
  }
}
完全な例
import axios from "axios";

async function runCatalogSync(): Promise<void> {
  const config: SellerConnectorConfig = {
    marketplaceAccount: "mymarketplace",
    sellerId: "externalseller01",
    appKey: process.env.VTEX_APP_KEY!,
    appToken: process.env.VTEX_APP_TOKEN!,
  };

  const client = createMarketplaceClient(config);

  // Fetch SKUs that need syncing from your system
  const skusToSync = await getLocalSkusNeedingSync();

  for (const sku of skusToSync) {
    const skuSuggestion: SkuSuggestion = {
      ProductName: sku.productName,
      SkuName: sku.skuName,
      ImageUrl: sku.imageUrl,
      ProductDescription: sku.description,
      BrandName: sku.brand,
      CategoryFullPath: sku.categoryPath,
      EAN: sku.ean,
      Height: sku.height,
      Width: sku.width,
      Length: sku.length,
      WeightKg: sku.weight,
      SkuSpecifications: sku.specifications,
    };

    const result = await notifyAndSync(
      client,
      config.marketplaceAccount,
      config.sellerId,
      sku.sellerSkuId,
      skuSuggestion
    );

    console.log(`SKU ${sku.sellerSkuId}: ${result.action}`);

    // Throttle between SKU operations
    await new Promise((resolve) => setTimeout(resolve, 200));
  }

  // Sync prices and inventory for all active SKUs (seller SKU IDs)
  const activeSellerSkuIds = skusToSync.map((s) => s.sellerSkuId);
  await syncPriceAndInventory(client, config.sellerId, activeSellerSkuIds);
}

async function getLocalSkusNeedingSync(): Promise<
  Array<{
    sellerSkuId: string;
    productName: string;
    skuName: string;
    imageUrl: string;
    description: string;
    brand: string;
    categoryPath: string;
    ean: string;
    height: number;
    width: number;
    length: number;
    weight: number;
    specifications: Array<{ FieldName: string; FieldValues: string[] }>;
  }>
> {
  // Replace with your actual data source
  return [];
}

よくある失敗パターン

  • セラーの SKU ID を使用して、単一セグメントの変更通知を呼び出す。 POST .../changenotification/{skuId} はマーケットプレイスの SKU ID を解決します。セラーのカタログコードを渡しても対象の SKU と一致せず、統合フローが正常に動作しません。セラーコネクターでは POST .../changenotification/{sellerId}/{sellerSkuId} を使用する必要があります。公開 API リファレンスの本文で sellerId の指定方法を説明しながら、URL が 1 セグメントになっている場合は、ドキュメントの不整合として扱い、上記のパス形式に従ってください。

  • 提案のステータスを短い間隔でポーリングする。 提案の承認は手動または半自動で行われるマーケットプレイスの処理で、数分から数日かかることがあります。短い間隔でポーリングすると API クォータを無駄に消費し、レート制限が発生して統合全体がブロックされる可能性があります。スケジュールされたジョブ(cron)を使用して提案のステータスを定期的に確認する(例:15〜30 分ごと)か、Webhook ベースの通知システムを実装してください。

  • フルフィルメントシミュレーションのタイムアウトを無視する。 セラーのフルフィルメントシミュレーションエンドポイントは、応答時間の制限を超える複雑なデータベースクエリや外部 API 呼び出しを実行します。VTEX マーケットプレイスがフルフィルメントシミュレーションの応答を待つ時間は、最大2.5 秒です。その後、商品は購入不可または非アクティブとみなされ、ストアフロントやチェックアウトに表示されなくなります。イベント駆動型の更新を使用し、価格と在庫データをインメモリーキャッシュまたは Redis キャッシュに事前にキャッシュして、シミュレーションエンドポイントが即座に応答できるようにしてください。

import { RequestHandler } from "express";

// Correct: Cache-first approach for fast fulfillment simulation
const cachedPriceInventory = new Map<string, {
  price: number;
  listPrice: number;
  sellingPrice: number;
  availableQuantity: number;
  updatedAt: number;
}>();

const fastFulfillmentSimulation: RequestHandler = async (req, res) => {
  const { items } = req.body;

  const responseItems = items.map((item: SimulationItem, index: number) => {
    const cached = cachedPriceInventory.get(item.id);

    if (!cached) {
      return {
        id: item.id,
        requestIndex: index,
        quantity: 0,
        availability: "unavailable",
      };
    }

    return {
      id: item.id,
      requestIndex: index,
      quantity: Math.min(item.quantity, cached.availableQuantity),
      price: cached.price,
      listPrice: cached.listPrice,
      sellingPrice: cached.sellingPrice,
      availability: cached.availableQuantity > 0 ? "available" : "unavailable",
    };
  });

  // Responds in < 50ms from cache
  res.json({ items: responseItems });
};

レビューチェックリスト

  • Change Notification + SKU Suggestion フローを使用していますか(Catalog API への直接書き込みではなく)?
  • カタログ変更通知で .../changenotification/{sellerId}/{sellerSkuId} を使用していますか(セラー SKU コードを指定した、単一セグメントのマーケットプレイス SKU 用ルートではなく)?
  • changenotification からの 200(存在する)と 404(新規)の両方の応答を統合で処理していますか?
  • SKU suggestion の更新はステータスチェックで保護されていますか(「Pending」の場合にのみ更新)?
  • suggestion の PUT/GET 呼び出しは、ストアのホスト名ではなく https://api.vtex.com/{account}/suggestions/{sellerId}/{sellerSkuId} に送信されていますか?
  • カタログ通知のバッチ送信は、429 の処理と指数バックオフによってスロットリングされていますか?
  • フルフィルメントシミュレーションエンドポイントは 2.5 秒以内に応答しますか?
  • 価格と在庫の通知は、正しい /notificator/{sellerId}/changenotification/{sellerSkuId}/price|inventory パス(パス内のセラー SKU ID)を介して送信されていますか?
  • プレースホルダー値(アカウント名、セラー ID、API キー)は実際の値に置き換えられていますか?

リファレンス

コメント