文件预览

post_item_offers_batch.py

查看 Amazon Store Pricing 技能包中的文件内容。

文件内容

scripts/post_item_offers_batch.py

#!/usr/bin/env python3
"""
Amazon Store — getItemOffersBatch (SP-API Product Pricing v0)
=============================================================

POST 批量查询多个 ASIN 的 getItemOffers(每批 1~20 条子请求)。

官方参考: https://developer-docs.amazon.com/sp-api/reference/getitemoffersbatch

JSON 入参(简化):
  sellerId, region,
  requests: [ { "asin", "marketplaceId", "itemCondition", "customerType"?, "headers"? }, ... ]

脚本将每条展开为 Amazon 所需的 uri(/products/pricing/v0/items/{Asin}/offers)、method GET、
MarketplaceId、ItemCondition 等。若需完全自定义子请求体,可传 useAmazonRequestShape: true,
此时 requests 须为 Amazon 原始对象数组(仍须 1~20 条)。
"""

from __future__ import annotations

import json
import sys
from urllib.parse import quote

from _spapi_pricing_common import (
    developer_proxy_post_json,
    ensure_auth_skill_available,
    get_store_tokens,
)

PATH_BATCH = "batches/products/pricing/v0/itemOffers"
MAX_REQUESTS = 20


def _expand_simple(req: dict) -> dict:
    asin = str(req["asin"]).strip()
    if not asin:
        raise ValueError("each request needs non-empty asin")
    mid = str(req.get("marketplaceId") or "").strip()
    if not mid:
        raise ValueError("each request needs marketplaceId")
    ic = str(req.get("itemCondition") or "").strip()
    if not ic:
        raise ValueError("each request needs itemCondition")
    uri = f"/products/pricing/v0/items/{quote(asin, safe='')}/offers"
    out: dict = {
        "uri": uri,
        "method": "GET",
        "MarketplaceId": mid,
        "ItemCondition": ic,
    }
    if req.get("customerType"):
        out["CustomerType"] = str(req["customerType"]).strip()
    if req.get("headers") is not None:
        out["headers"] = req["headers"]
    return out


def main() -> None:
    if len(sys.argv) < 2:
        print(
            "Usage: post_item_offers_batch.py '<JSON>'\n"
            f"Required: sellerId, region, requests (1..{MAX_REQUESTS} items).\n"
            "Each item: asin, marketplaceId, itemCondition [, customerType, headers].",
            file=sys.stderr,
        )
        sys.exit(1)
    try:
        params = json.loads(sys.argv[1])
    except json.JSONDecodeError as e:
        print(f"Invalid JSON: {e}", file=sys.stderr)
        sys.exit(1)

    if not params.get("skipDepCheck"):
        ensure_auth_skill_available("post_item_offers_batch.py")

    for f in ("sellerId", "region", "requests"):
        if f not in params:
            print(f"Missing required field: {f}", file=sys.stderr)
            sys.exit(1)

    raw = params["requests"]
    if not isinstance(raw, list) or not raw:
        print("requests must be a non-empty array", file=sys.stderr)
        sys.exit(1)
    if len(raw) > MAX_REQUESTS:
        print(f"At most {MAX_REQUESTS} batch sub-requests.", file=sys.stderr)
        sys.exit(1)

    use_amazon = bool(params.get("useAmazonRequestShape"))
    try:
        if use_amazon:
            requests_out = raw
        else:
            requests_out = [_expand_simple(r) for r in raw]
    except (KeyError, ValueError) as e:
        print(str(e), file=sys.stderr)
        sys.exit(1)

    body_obj = {"requests": requests_out}
    seller_id = str(params["sellerId"])
    region = str(params["region"])

    tokens = get_store_tokens(seller_id, region)
    if "error" in tokens or "accessToken" not in tokens:
        print(json.dumps(tokens, indent=2, ensure_ascii=False))
        sys.exit(1)

    proxy = developer_proxy_post_json(region, PATH_BATCH, tokens["accessToken"], body_obj)
    out: dict = {
        "developerProxy": proxy,
        "resolvedPath": PATH_BATCH,
        "requestBody": body_obj,
    }
    if proxy.get("errcode") == 200 and proxy.get("httpStatus") == 200:
        br = proxy.get("body") or "{}"
        try:
            out["itemOffersBatch"] = json.loads(br)
        except json.JSONDecodeError:
            out["itemOffersBatch"] = None
            out["itemOffersBatchRaw"] = br
    print(json.dumps(out, indent=2, ensure_ascii=False))


if __name__ == "__main__":
    main()