| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194 |
- import requests
- import json
- import datetime
- import hashlib
- import hmac
- import time
- from urllib.parse import quote
- # ==================== 配置区域 ====================
- CLIENT_ID = "amzn1.application-oa2-client.9f775d35077a4c7598116e5a89b60222"
- CLIENT_SECRET = "amzn1.oa2-cs.v1.6909777baa82b796555a431cd23709342e8e01434abcad1f8b59cadf048b51c3"
- REFRESH_TOKEN = "Atzr|IwEBICh_fojh7qAfrePzSjOZhQxTnHRZCYOuWNvSAHGaBs1WfkjR4qjC1Dmv0uK3oyq_L-R9SRKK4cYmPwF1MQXQdqKlpzOAGItWeXnNzh-SyhHHISv0_vCt3D6hluVmIEmF2kA7ro_XF-PslD41g3Eq6bv0Qq4y4pQbkDj-9dudDuz7nku8UL-nc4EWgJCrHCJTAs8TK48Yvrfi3E35uv97tYhs6qb8D9d4dR9C7daaC49eoDqP88qyUHThQV1Kqon8xukx4AT2_wLqWNcabpADzuK-5w7D-vvFFtvzfkbmtfzoPeEdHPulG6scc59BPFTyBlGU7a3d1JSFg2ROHIlrljRm"
- AWS_ACCESS_KEY = "AKIA24CLQSVTW2K4S6RP" # ← 填你的
- AWS_SECRET_KEY = "CwBJqlsYATNC920A+U6iv80WnP6Eso+ECfIalQ1W" # ← 填你的
- AWS_REGION = "us-east-1"
- SELLER_ID = "A1H50Z0WYYLEGQ"
- SKU = "bar table pink"
- MARKETPLACE_ID = "ATVPDKIKX0DER"
- NEW_IMAGE_URL = "https://cunchu.site/temp/8c7a90e1-a5a1-41af-a119-41b1ca74b40d.png"
- # ==================== 工具函数 ====================
- def sha256_hash(data):
- return hashlib.sha256(data.encode("utf-8")).hexdigest()
- def hmac_sha256(key, msg):
- return hmac.new(key, msg.encode("utf-8"), hashlib.sha256).digest()
- def get_signature_key(secret_key, date_stamp, region, service):
- k_date = hmac_sha256(("AWS4" + secret_key).encode("utf-8"), date_stamp)
- k_region = hmac_sha256(k_date, region)
- k_service = hmac_sha256(k_region, service)
- return hmac_sha256(k_service, "aws4_request")
- def get_access_token():
- url = "https://api.amazon.com/auth/o2/token"
- resp = requests.post(url, data={
- "grant_type": "refresh_token",
- "refresh_token": REFRESH_TOKEN,
- "client_id": CLIENT_ID,
- "client_secret": CLIENT_SECRET,
- })
- resp.raise_for_status()
- return resp.json()["access_token"]
- def spapi_request(method, path, query, body=None, access_token=None):
- service = "execute-api"
- host = "sellingpartnerapi-na.amazon.com"
- payload = json.dumps(body, separators=(",", ":")) if body else ""
-
- t = datetime.datetime.now(datetime.timezone.utc)
- amz_date = t.strftime("%Y%m%dT%H%M%SZ")
- date_stamp = t.strftime("%Y%m%d")
-
- canonical_headers = (
- f"host:{host}\n"
- f"x-amz-access-token:{access_token}\n"
- f"x-amz-date:{amz_date}\n"
- )
- signed_headers = "host;x-amz-access-token;x-amz-date"
- payload_hash = sha256_hash(payload)
-
- canonical_request = (
- f"{method}\n{path}\n{query}\n"
- f"{canonical_headers}\n{signed_headers}\n{payload_hash}"
- )
-
- algorithm = "AWS4-HMAC-SHA256"
- credential_scope = f"{date_stamp}/{AWS_REGION}/{service}/aws4_request"
- string_to_sign = (
- f"{algorithm}\n{amz_date}\n{credential_scope}\n"
- f"{sha256_hash(canonical_request)}"
- )
-
- signing_key = get_signature_key(AWS_SECRET_KEY, date_stamp, AWS_REGION, service)
- signature = hmac.new(signing_key, string_to_sign.encode("utf-8"), hashlib.sha256).hexdigest()
-
- auth_header = (
- f"{algorithm} Credential={AWS_ACCESS_KEY}/{credential_scope}, "
- f"SignedHeaders={signed_headers}, Signature={signature}"
- )
-
- headers = {
- "Host": host,
- "x-amz-access-token": access_token,
- "x-amz-date": amz_date,
- "Authorization": auth_header,
- }
- if body:
- headers["Content-Type"] = "application/json"
-
- url = f"https://{host}{path}?{query}"
- return requests.request(method, url, headers=headers, data=payload or None)
- # ==================== 通用图片修改函数 ====================
- def patch_image(access_token, image_type, image_url):
- """
- image_type 可选值:
- "main" → 主图
- "other_1" ~ "other_8" → 副图1~8
- "swatch" → 变体图
- """
- encoded_sku = quote(SKU, safe="")
- path = f"/listings/2021-08-01/items/{SELLER_ID}/{encoded_sku}"
- query = f"marketplaceIds={MARKETPLACE_ID}"
-
- # 根据类型生成 path
- if image_type == "main":
- attr_path = "/attributes/main_product_image_locator"
- elif image_type.startswith("other_") and 1 <= int(image_type.split("_")[1]) <= 8:
- n = image_type.split("_")[1]
- attr_path = f"/attributes/other_product_image_locator_{n}"
- elif image_type == "swatch":
- attr_path = "/attributes/swatch_product_image_locator"
- else:
- raise ValueError(f"不支持的 image_type: {image_type}")
-
- body = {
- "productType": "TABLE",
- "patches": [
- {
- "op": "replace",
- "path": attr_path,
- "value": [
- {
- "marketplace_id": MARKETPLACE_ID,
- "media_location": image_url
- }
- ]
- }
- ]
- }
- return spapi_request("PATCH", path, query, body=body, access_token=access_token)
- def check_submission_result(access_token):
- encoded_sku = quote(SKU, safe="")
- path = f"/listings/2021-08-01/items/{SELLER_ID}/{encoded_sku}"
- # 关键:只带 issues,减少返回数据量
- query = f"marketplaceIds={MARKETPLACE_ID}&includedData=issues"
-
- resp = spapi_request("GET", path, query, access_token=access_token)
- data = resp.json()
-
- issues = data.get("issues", [])
- if not issues:
- print("✅ 没有 issues,图片应该已生效")
- return True
-
- print(f"⚠️ 发现 {len(issues)} 个问题:\n")
- for issue in issues:
- print(f" 错误码: {issue.get('code')}")
- print(f" 级别: {issue.get('severity')}")
- print(f" 信息: {issue.get('message')}")
-
- enforcements = issue.get("enforcements", {})
- actions = enforcements.get("actions", [])
- for action in actions:
- print(f" 执行动作: {action.get('action')}") # 如 ATTRIBUTE_SUPPRESSED
-
- # 看是否跟图片相关
- attrs = issue.get("attributeNames", [])
- if any("image" in a or "media" in a for a in attrs):
- print(f" 涉及属性: {attrs} → 图片审核未通过")
- print()
-
- return False
- # ==================== 主程序 ====================
- if __name__ == "__main__":
- print("🔑 获取 Access Token...")
- token = get_access_token()
- print("✅ 令牌获取成功")
-
- # 示例:增加/修改副图第3张
- new_images = {
- "other_1": NEW_IMAGE_URL
- }
-
- for img_type, img_url in new_images.items():
- print(f"\n🖼️ 正在设置 {img_type}: {img_url}")
- resp = patch_image(token, img_type, img_url)
- print(f"📤 状态码: {resp.status_code}")
- try:
- timestamp = int(time.time())
- print(timestamp)
- print(json.dumps(resp.json(), indent=2, ensure_ascii=False))
- except Exception:
- print(resp.text)
-
- if resp.status_code == 200:
- print(f"✅ {img_type} 提交成功")
- else:
- print(f"⚠️ {img_type} 提交失败")
|