【亚马逊 SP-API 实战】Java 批量创建变体 Listing(父商品 + 子变体 + 独立图片)完整教程(亲测可用)
前言
【亚马逊 SP-API 实战】Java 实现单体商品 Listing 创建 + 图片上传完整教程(亲测可用)-CSDN博客
在上一篇教程中,我们实现了单个亚马逊 Listing 的创建与图片上传。但在实际运营中,变体商品(如不同颜色、尺寸)才是主流形态。
本文基于亚马逊 SP-API v2021-08-01,使用 Java 语言实现变体商品全自动上架: ✅ 创建父商品(虚拟关联体) ✅ 批量创建子变体(颜色 / 尺寸) ✅ 每个变体独立上传图片 ✅ 完整错误处理 + 防限流机制
代码完全可直接运行,敏感信息已脱敏,适配北美站点(美国站)。
一、变体 Listing 核心原理
亚马逊变体商品必须遵循父子结构:
- 父商品(Parent):虚拟商品,无库存、无价格、不可购买,仅用于关联子变体
- 子商品(Child):真实可售商品,包含价格、库存、变体属性(颜色 / 尺寸)
- 变体主题(Variation Theme):如
COLOR、SIZE,决定变体维度 - 关联关系:子商品通过
parent_sku绑定父商品
二、核心技术栈
- 语言:Java 8+
- API:亚马逊 SP-API Listings Items v2021-08-01
- 授权:LWA Refresh Token 授权
- 接口:
putListingsItem:创建父子商品patchListingsItem:上传变体独立图片
三、完整代码解读(脱敏版)
1. 依赖(Maven)
<dependency> <groupId>software.amazon.spapi</groupId> <artifactId>spapi-sdk</artifactId> <version>1.5.0</version> </dependency>2. 脱敏配置信息(必须替换)
所有密钥已脱敏加密,使用时替换为自己的真实信息:
java
运行
// ==================== 脱敏配置参数 ==================== private static final String CLIENT_ID = "amzn1.application-oa2-client.**************************"; private static final String CLIENT_SECRET = "amzn1.oa2-cs.v1.**************************************************"; private static final String REFRESH_TOKEN = "Atzr|IwEBI********************************************************************"; private static final String SELLER_ID = "A1AZHTIRR4Q7WJAY"; private static final String MARKETPLACE_ID = "ATVPDKIKX0DER"; // 美国站3. 变体数据结构(模拟 Excel)
使用VariantData类封装每个变体的独立信息,可直接对接 Excel 导入:
private static class VariantData { final String sku; // 变体SKU final String color; // 颜色 final double price; // 价格 final String gtin; // UPC/EAN final List<String> imageUrls; // 独立图片 }批量变体数据配置(可扩展 N 个变体):
private static final List<VariantData> VARIANTS = Arrays.asList( new VariantData("QB3V8GFA63-CHROME", "Polished Chrome", 99.99, "01234567890123", 图片列表), new VariantData(...) );4. 主执行流程(三步曲)
public static void main(String[] args) { // 1. LWA授权初始化 // 2. 创建父商品 createParentListing(...); // 3. 批量创建子变体 for (VariantData v : VARIANTS) createChildVariant(...); // 4. 每个变体上传独立图片 for (VariantData v : VARIANTS) uploadVariantImages(...); }执行顺序绝对不能乱!必须:父商品 → 子变体 → 图片上传
5. 核心模块详解
(1)创建父商品(虚拟商品)
父商品关键属性:
// 标记为父商品 attributes.put("parentage_level", List.of(Map.of("value", "parent"))); // 变体主题:颜色 attributes.put("variation_theme", List.of(Map.of("name", "COLOR")));⚠️ 父商品不能包含:价格、库存、GTIN、颜色、图片
(2)创建子变体(核心)
子商品必须关联父商品:
// 标记为子商品 attributes.put("parentage_level", List.of(Map.of("value", "child"))); // 绑定父SKU attributes.put("child_parent_sku_relationship", List.of( Map.of("parent_sku", PARENT_SKU, "variation_theme", "COLOR") )); // 变体属性:颜色 attributes.put("color", List.of(Map.of("value", variant.color)));每个变体拥有:
- 独立 SKU
- 独立价格
- 独立 GTIN
- 独立图片
(3)变体独立图片上传
使用PATCH接口为每个变体单独上传图片:
PatchOperation op = new PatchOperation(); op.setOp(REPLACE); op.setPath("/attributes/main_product_image_locator"); op.setValue(图片地址);✅ 支持:主图 + N 张附图 ✅ 每个变体图片互不干扰
(4)错误日志统一处理
private static void printIssues(响应结果) { // 打印 ERROR/WARNING 信息 // 自动判断是否创建失败 }四、代码运行效果(控制台输出)
>>> 步骤1:创建父商品... ✅ 父商品创建请求已提交! >>> 步骤2:创建子变体商品... --- 正在创建变体: Polished Chrome --- ✅ 变体创建成功 >>> 步骤3:上传各变体图片... ✅ 主图上传成功 ✅ 附图上传成功 🎉 全部完成!五、必看避坑指南(非常重要)
- 父商品不能有价格 / 库存 / Gtin,否则创建失败
- 变体主题必须一致(父 & 子都要写)
- 子商品必须明确关联 parent_sku
- 图片必须是 HTTPS 公开地址,亚马逊可直接下载
- 请求必须加延时,避免触发限流(代码已内置)
- 每个变体必须有唯一 GTIN,不能重复
- 类目必须支持变体(如厨房水龙头支持 COLOR 变体)
六、适用扩展场景
- 批量上架变体 Listing
- ERP 系统对接亚马逊变体商品
- 一键铺货工具核心模块
- 按 Excel 模板自动创建变体
七、总结
这是一套工业级可用的亚马逊变体 Listing 自动化创建方案: ✅ 父子商品结构标准 ✅ 变体独立图片 ✅ 批量处理 ✅ 错误完善 ✅ 可直接对接 Excel / 数据库
如果你做亚马逊 ERP、铺货工具、自动化运营,这份代码可以直接作为核心模块使用!
💡 原创不易,觉得有用欢迎点赞 + 收藏 + 关注,后续继续更新 SP-API 库存、订单、定价接口实战!
附:全部代码
import com.amazon.SellingPartnerAPIAA.LWAAuthorizationCredentials; import com.amazon.SellingPartnerAPIAA.LWAException; import software.amazon.spapi.ApiException; import software.amazon.spapi.api.listings.items.v2021_08_01.ListingsApi; import software.amazon.spapi.models.listings.items.v2021_08_01.ListingsItemPatchRequest; import software.amazon.spapi.models.listings.items.v2021_08_01.ListingsItemPutRequest; import software.amazon.spapi.models.listings.items.v2021_08_01.ListingsItemSubmissionResponse; import software.amazon.spapi.models.listings.items.v2021_08_01.PatchOperation; import java.util.*; public class CreateListingVariants { // ==================== 配置参数(使用你已验证过的凭证)==================== private static final String CLIENT_ID = "amzn1.application-oa2-client.d4099ee7a42f480d8a4ee2fa63c1597d26"; private static final String CLIENT_SECRET = "amzn1.oa2-cs.v1.a940db0aafee4c23d81fa1f1f933f6647d6adb5f2aafe1dsd992cf4a6829972992"; private static final String REFRESH_TOKEN = "Atzr|IwEBIJ6fz00AKqA5eU166lsnFAMuzcGnYRJN18Q0BpQ4Rx85VwViDVgWsdJO35FFhU7wkD_hjrM7PCugYbsXZobnpCYCmYnUGLs4C-UVFVSnvLP2wG028l8vn4EKyjxm2faK8vrCszwCPY8FU0V4X7ZdxWf-ugDSvvwoQiO-PFi81Q1OAiSSw6doluNuw8logWW4qMMQdy5Dc2aNBnFKLHRyLlG8503_LYqefpsbUpmJC8jYq0KBk9Q6IEFT0DhUaZQ_VQhVCwh51tIFPCVU5zR9-uoVDG3xpCu3ny-sVRzQkBz2I35FoHH7EtZksG7LUoZTl6Zi0"; private static final String SELLER_ID = "A1AZHTI4Q7WeJAY"; // 你的卖家ID private static final String MARKETPLACE_ID = "ATVPDKIKX0DER"; // 美国站 // ==================== 变体配置(模拟Excel数据,写死)==================== // 父SKU(虚拟SKU,不单独售卖,仅用于关联子变体) private static final String PARENT_SKU = "QB3V8GFA63-PARENT"; // 变体主题(颜色) private static final String VARIATION_THEME = "COLOR"; // 变体数据列表(模拟Excel行数据) private static final List<VariantData> VARIANTS = Arrays.asList( new VariantData( "QB3V8GFA63-CHROME", // SKU "Polished Chrome", // 颜色 99.99, // 价格 "01234567890123", // GTIN Arrays.asList( // 图片URLs "https://m.media-amazon.com/images/I/51f0Hh4toYL._SL1500_.jpg", "https://m.media-amazon.com/images/I/81WUIDw2vwL._SL1500_.jpg", "https://m.media-amazon.com/images/I/71+VPo-TOuL._SL1500_.jpg" ) ), new VariantData( "QB3V8GFA63-MATTE-BLACK", // SKU "Matte Black", // 颜色 109.99, // 价格 "01234567890124", // GTIN Arrays.asList( // 图片URLs "https://m.media-amazon.com/images/I/61aB+CDEFGH._SL1500_.jpg", "https://m.media-amazon.com/images/I/82XYZ123456._SL1500_.jpg", "https://m.media-amazon.com/images/I/73ABC789012._SL1500_.jpg" ) ), new VariantData( "QB3V8GFA63-BRUSHED-NICKEL", // SKU "Brushed Nickel", // 颜色 119.99, // 价格 "01234567890125", // GTIN Arrays.asList( // 图片URLs "https://m.media-amazon.com/images/I/62DEF+GHIJK._SL1500_.jpg", "https://m.media-amazon.com/images/I/83LMN456789._SL1500_.jpg", "https://m.media-amazon.com/images/I/74OPQ345678._SL1500_.jpg" ) ), new VariantData( "QB3V8GFA63-OIL-RUBBED", // SKU "Oil Rubbed Bronze", // 颜色 129.99, // 价格 "01234567890126", // GTIN Arrays.asList( // 图片URLs "https://m.media-amazon.com/images/I/63GHI+JKLMN._SL1500_.jpg", "https://m.media-amazon.com/images/I/84RST567890._SL1500_.jpg", "https://m.media-amazon.com/images/I/75UVW456789._SL1500_.jpg" ) ) ); public static void main(String[] args) { try { // 1. 配置 LWA 凭证 LWAAuthorizationCredentials credentials = LWAAuthorizationCredentials.builder() .clientId(CLIENT_ID) .clientSecret(CLIENT_SECRET) .refreshToken(REFRESH_TOKEN) .endpoint("https://api.amazon.com/auth/o2/token") .build(); // 2. 创建 ListingsApi 实例 ListingsApi listingsApi = new ListingsApi.Builder() .lwaAuthorizationCredentials(credentials) .endpoint("https://sellingpartnerapi-na.amazon.com") .build(); List<String> marketplaceIds = Collections.singletonList(MARKETPLACE_ID); // ========== 第1步:创建父商品(虚拟商品,不可购买)========== System.out.println(">>> 步骤1:创建父商品(虚拟商品,用于关联变体)..."); createParentListing(listingsApi, marketplaceIds); // 等待父商品创建完成 Thread.sleep(1000); // ========== 第2步:创建所有子变体商品 ========== System.out.println("\n>>> 步骤2:创建子变体商品..."); for (VariantData variant : VARIANTS) { System.out.println("\n--- 正在创建变体: " + variant.color + " (SKU: " + variant.sku + ") ---"); createChildVariant(listingsApi, marketplaceIds, variant); Thread.sleep(800); // 避免请求过快 } // ========== 第3步:为每个变体上传图片 ========== System.out.println("\n>>> 步骤3:上传各变体图片..."); for (VariantData variant : VARIANTS) { System.out.println("\n--- 正在上传 " + variant.color + " 的图片 ---"); uploadVariantImages(listingsApi, marketplaceIds, variant); Thread.sleep(800); } System.out.println("\n🎉 全部完成!父商品和所有变体已创建,图片已上传。"); } catch (ApiException e) { System.err.println("❌ API 调用异常"); System.err.println("状态码: " + e.getCode()); System.err.println("响应体: " + e.getResponseBody()); e.printStackTrace(); } catch (Exception e) { System.err.println("❌ 其他错误: " + e.getMessage()); e.printStackTrace(); } } /** * 创建父商品(虚拟商品) * 父商品不包含具体库存和价格,仅用于关联子变体 */ private static void createParentListing(ListingsApi listingsApi, List<String> marketplaceIds) throws ApiException, LWAException { Map<String, Object> attributes = buildParentAttributes(); ListingsItemPutRequest requestBody = new ListingsItemPutRequest(); requestBody.setProductType("KITCHEN"); requestBody.setRequirements(ListingsItemPutRequest.RequirementsEnum.LISTING); requestBody.setAttributes(attributes); ListingsItemSubmissionResponse response = listingsApi.putListingsItem( requestBody, SELLER_ID, PARENT_SKU, marketplaceIds, null, null, null, null ); System.out.println("✅ 父商品创建请求已提交!"); System.out.println("SubmissionId: " + response.getSubmissionId()); System.out.println("Status: " + response.getStatus()); printIssues(response); } /** * 创建子变体商品 */ private static void createChildVariant(ListingsApi listingsApi, List<String> marketplaceIds, VariantData variant) throws ApiException, LWAException { Map<String, Object> attributes = buildChildAttributes(variant); ListingsItemPutRequest requestBody = new ListingsItemPutRequest(); requestBody.setProductType("KITCHEN"); requestBody.setRequirements(ListingsItemPutRequest.RequirementsEnum.LISTING); requestBody.setAttributes(attributes); ListingsItemSubmissionResponse response = listingsApi.putListingsItem( requestBody, SELLER_ID, variant.sku, marketplaceIds, null, null, null, null ); System.out.println("✅ 变体 [" + variant.color + "] 创建请求已提交!"); System.out.println("SubmissionId: " + response.getSubmissionId()); System.out.println("Status: " + response.getStatus()); printIssues(response); } /** * 为变体上传图片(使用 PATCH) */ private static void uploadVariantImages(ListingsApi listingsApi, List<String> marketplaceIds, VariantData variant) throws ApiException { // 定义图片属性名 String[] imageAttributeNames = { "main_product_image_locator", "other_product_image_locator_1", "other_product_image_locator_2" }; for (int i = 0; i < variant.imageUrls.size(); i++) { String imageUrl = variant.imageUrls.get(i); String attributeName = imageAttributeNames[Math.min(i, imageAttributeNames.length - 1)]; boolean isMain = (i == 0); try { ListingsItemPatchRequest patchRequest = new ListingsItemPatchRequest(); patchRequest.setProductType("KITCHEN"); PatchOperation patchOp = new PatchOperation(); patchOp.setOp(PatchOperation.OpEnum.REPLACE); patchOp.setPath("/attributes/" + attributeName); List<Map<String, Object>> imageValues = Collections.singletonList(Map.of( "marketplace_id", MARKETPLACE_ID, "media_location", imageUrl )); patchOp.setValue(imageValues); patchRequest.setPatches(Collections.singletonList(patchOp)); ListingsItemSubmissionResponse patchResponse = listingsApi.patchListingsItem( patchRequest, SELLER_ID, variant.sku, marketplaceIds, null, null, null, null ); System.out.println(" ✅ " + (isMain ? "主图" : "附图" + i) + " 上传成功"); System.out.println(" URL: " + imageUrl); System.out.println(" Status: " + patchResponse.getStatus()); printIssues(patchResponse); Thread.sleep(500); } catch (ApiException e) { System.err.println(" ❌ " + (isMain ? "主图" : "附图" + i) + " 上传失败"); System.err.println(" 状态码: " + e.getCode()); System.err.println(" 响应: " + e.getResponseBody()); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } catch (LWAException e) { throw new RuntimeException(e); } } } /** * 构建父商品属性(虚拟商品) */ private static Map<String, Object> buildParentAttributes() { Map<String, Object> attributes = new HashMap<>(); String language = "en_US"; String marketplace = MARKETPLACE_ID; // 父商品标识:parentage_level = "parent" attributes.put("parentage_level", List.of(Map.of("value", "parent"))); // 变体主题 attributes.put("variation_theme", List.of(Map.of("name", VARIATION_THEME))); // 基础信息(父商品也需要,但不包含库存和价格) attributes.put("item_name", List.of( Map.of("value", "Stylish 22 Inch High-Arc Pull-Down Faucet with Spring for Commercial Kitchen Sinks", "language_tag", language, "marketplace_id", marketplace) )); attributes.put("product_description", List.of( Map.of("value", "This stylish high-arc pull-down faucet features a commercial spring design...", "language_tag", language) )); attributes.put("brand", List.of(Map.of("value", "TGM", "language_tag", language))); attributes.put("manufacturer", List.of(Map.of("value", "TGM", "language_tag", language))); attributes.put("condition_type", List.of(Map.of("value", "new_new"))); attributes.put("item_type_keyword", List.of(Map.of("value", "kitchen-faucet"))); attributes.put("model_name", List.of(Map.of("value", "Kitchen Faucet Spring"))); // 父商品不需要:price, inventory, color, gtin, images等具体变体属性 // 1. 五点描述(Bullet Point)- 父商品必填 attributes.put("bullet_point", List.of( Map.of("value", "Elegant and Functional Design: This 22-inch high-arc kitchen faucet features a sleek, single-handle design..."), Map.of("value", "Premium Quality Construction: Crafted from 100% lead-free brass for all water-contact parts..."), Map.of("value", "Enhanced Flow Rate: Experience the efficiency of a 1.8 GPM flow rate..."), Map.of("value", "Long-Lasting Seal Valve: Engineered to international brand standards..."), Map.of("value", "Versatile 3-Function Spray Head: Our pull-down sprayer offers three modes...") )); // 2. 原产国(Country of Origin)- 父商品必填 attributes.put("country_of_origin", List.of(Map.of("value", "CN"))); // 3. 是否需要电池(Are Batteries Required?)- 父商品必填,水龙头不需要电池 attributes.put("batteries_required", List.of( Map.of("value", false, "marketplace_id", marketplace) )); // 危险品信息 attributes.put("supplier_declared_dg_hz_regulation", List.of(Map.of("value", "other"))); return attributes; } /** * 构建子变体属性 */ private static Map<String, Object> buildChildAttributes(VariantData variant) { Map<String, Object> attributes = new HashMap<>(); String language = "en_US"; String marketplace = MARKETPLACE_ID; // ========== 变体关系 ========== // 子商品标识 attributes.put("parentage_level", List.of(Map.of("value", "child"))); // 关联到父SKU attributes.put("child_parent_sku_relationship", List.of( Map.of("parent_sku", PARENT_SKU, "variation_theme", VARIATION_THEME) )); // ========== 变体主题 ========== attributes.put("variation_theme", List.of(Map.of("name", VARIATION_THEME))); // ========== 标识符 ========== attributes.put("merchant_suggested_asin", List.of(Map.of("value", "QB3V8GFA63"))); attributes.put("externally_assigned_product_identifier", List.of( Map.of("value", variant.gtin, "type", "GTIN") )); // ========== 危险品信息 ========== attributes.put("supplier_declared_dg_hz_regulation", List.of(Map.of("value", "other"))); // ========== 基础信息(通用部分) ========== attributes.put("item_name", List.of( Map.of("value", "Stylish 22 Inch High-Arc Pull-Down Faucet with Spring for Commercial Kitchen Sinks, " + variant.color, "language_tag", language, "marketplace_id", marketplace) )); attributes.put("product_description", List.of( Map.of("value", "This stylish high-arc pull-down faucet features a commercial spring design...", "language_tag", language) )); attributes.put("brand", List.of(Map.of("value", "TGM", "language_tag", language))); attributes.put("manufacturer", List.of(Map.of("value", "TGM", "language_tag", language))); attributes.put("part_number", List.of(Map.of("value", variant.sku))); attributes.put("condition_type", List.of(Map.of("value", "new_new"))); attributes.put("material", List.of(Map.of("value", "Brass"))); attributes.put("size", List.of(Map.of("value", "One Size"))); attributes.put("country_of_origin", List.of(Map.of("value", "CN"))); attributes.put("item_type_keyword", List.of(Map.of("value", "kitchen-faucet"))); attributes.put("model_name", List.of(Map.of("value", "Kitchen Faucet Spring"))); attributes.put("model_number", List.of(Map.of("value", variant.sku))); attributes.put("care_instructions", List.of(Map.of("value", "Clean with soft cloth"))); attributes.put("included_components", List.of(Map.of("value", "Faucet, Installation Kit, Instructions"))); // ========== 变体特定属性:颜色 ========== attributes.put("color", List.of(Map.of("value", variant.color))); // ========== 布尔 / 数字 ========== attributes.put("is_refurbished", List.of(Map.of("value", false))); attributes.put("number_of_boxes", List.of(Map.of("value", 1))); attributes.put("contains_liquid_contents", List.of(Map.of("value", false))); attributes.put("number_of_items", List.of(Map.of("value", 1))); // ========== 五点描述(通用) ========== attributes.put("bullet_point", List.of( Map.of("value", "Elegant and Functional Design: This 22-inch high-arc kitchen faucet features a sleek, single-handle design..."), Map.of("value", "Premium Quality Construction: Crafted from 100% lead-free brass for all water-contact parts..."), Map.of("value", "Enhanced Flow Rate: Experience the efficiency of a 1.8 GPM flow rate..."), Map.of("value", "Long-Lasting Seal Valve: Engineered to international brand standards..."), Map.of("value", "Versatile 3-Function Spray Head: Our pull-down sprayer offers three modes...") )); // ========== 搜索关键词(通用) ========== attributes.put("generic_keyword", List.of( Map.of("value", "Farmhouse; Kitchen Faucet; 22 Inch; High Arc; Commercial; Spring Faucet; Pull Down Sprayer; Sink; " + variant.color + ";") )); // ========== 变体特定:价格与库存 ========== attributes.put("list_price", List.of(Map.of("value", variant.price, "currency_code", "USD"))); attributes.put("fulfillment_availability", List.of( Map.of("fulfillment_channel_code", "DEFAULT", "quantity", 1) )); // ========== 重量 ========== attributes.put("item_weight", List.of(Map.of("value", 5.9, "unit", "pounds"))); return attributes; } /** * 打印响应中的问题 */ private static void printIssues(ListingsItemSubmissionResponse response) { if (response.getIssues() != null && !response.getIssues().isEmpty()) { boolean hasError = false; for (var issue : response.getIssues()) { if ("ERROR".equalsIgnoreCase(String.valueOf(issue.getSeverity()))) { hasError = true; } System.out.println(" - [" + issue.getSeverity() + "] " + issue.getCode() + ": " + issue.getMessage()); } if (hasError) { System.out.println("❌ 请求包含错误!"); } } } /** * 变体数据内部类(模拟Excel行) */ private static class VariantData { final String sku; // 变体SKU final String color; // 颜色 final double price; // 价格 final String gtin; // GTIN/UPC final List<String> imageUrls; // 图片URL列表 VariantData(String sku, String color, double price, String gtin, List<String> imageUrls) { this.sku = sku; this.color = color; this.price = price; this.gtin = gtin; this.imageUrls = imageUrls; } } }