对接amazon sp-spi-接口对接(二)

2,640 阅读10分钟

快速对接amazon sp-api步骤

sp-api官方的接口文档:references

第一步:在amazon官网在注册开发者,获取调用amazon sp-api所需的配置信息 SellingPartnerApiDeveloperGuide(中文).md developer-docs.amazon.com/sp-api/refe…

第二步:下载amazon sp-api对应的sdk,在amazon已经针对sp-api调用封装好了,提供对应的sdk

一、获取token

  1. 获取token对应的接口文档:

步骤 1。卖家从商城应用商店启动授权

  1. 卖家登录卖家平台并进入商城应用商店。
  2. 卖家转到应用程序的详情页面,然后单击立即授权按钮。此时将显示您的应用程序的同意页面。

步骤 2。卖家同意授权您的应用程序

  1. 卖家查看同意页面,查看并接受您的应用程序所请求的数据访问,然后单击立即登录 [您的应用程序名称] 按钮继续。卖家可以单击取消按钮,在不授权的情况下退出。
  2. 亚马逊将您的登录 URI(您在应用程序注册时提供)加载到浏览器中,并添加以下查询参数:
参数描述
amazon_callback_uri用于将浏览器重定向到亚马逊的 URI。
amazon_state亚马逊生成的状态值,用于防范跨站点请求伪造攻击。
selling_partner_id授权您的应用程序的卖家的卖家编号。

**注意:**如果这是测试工作流程(卖家通过导航到您的 OAuth 授权 URI 开始),亚马逊将包含 version=beta 参数。如果这是一个生产工作流程(卖家从商城应用商店开始),亚马逊不包含该参数。

例如:

https://d2yzyfnnpjylxu.cloudfront.net/index.html?amazon_callback_uri=https://amazon.com/apps/authorize/confirm/amzn1.sellerapps.app.2eca283f-9f5a-4d13-b16c-474EXAMPLE57&amazon_state=amazonstateexample&selling_partner_id=A3FHEXAMPLEYWS

将显示您网站的登录页面。

步骤 3。卖家登录您的网站

  1. 卖家登录您的网站。如果卖家还没有账户,他们要完成注册流程。
  2. 您的应用程序将亚马逊回调 URI(在上一步中由亚马逊传递)加载到浏览器中,并添加以下参数:
参数描述
redirect_uri用于将浏览器重定向到您的应用程序的 URI。
amazon_state亚马逊在上一步中传递的 amazon_state 值。
state您的应用程序生成的状态值。您的应用程序使用此值来维护此请求和响应之间的状态,从而帮助防范跨站点请求伪造攻击。重要说明: 由于 OAuth 信息是通过 URI 查询参数传递的,我们强烈建议您执行以下操作: (1) 确保状态令牌对您的用户属于短期令牌,并且具有可验证的唯一性,以及 (2) 设置 Referrer-Policy: no-referrer HTTP 标头,防止将敏感信息泄露到您的网站链接到的网站。有关跨站点请求伪造和计算 state 参数的更多信息,请参阅“使用亚马逊账户登录”文档中的跨站点请求伪造

**注意:**如果包含 Version=beta 参数,则工作流程将授权处于草稿状态的应用程序。如果您不包含该参数,则工作流程将授权在商城应用商店中发布的应用程序。

例如:

https://amazon.com/apps/authorize/confirm/amzn1.sellerapps.app.2eca283f-9f5a-4d13-b16c-474EXAMPLE57?redirect_uri=https://d2yzyfnnpjylxu.cloudfront.net/landing.html&amazon_state=amazonstateexample&state=-37131022&version=beta

或者

https://amazon.com/apps/authorize/confirm/amzn1.sellerapps.app.2eca283f-9f5a-4d13-b16c-474EXAMPLE57?redirect_uri=https://d2yzyfnnpjylxu.cloudfront.net/landing.html&amazon_state=amazonstateexample&state=-37131022

步骤 4。亚马逊向您发送授权信息

卖家平台会短时间显示一个页面,表明亚马逊正在授权您访问卖家数据。显示该页面时,将执行以下操作:

  1. 亚马逊将您的重定向 URI 加载到浏览器中,并添加以下查询参数:
参数描述
state您在上一步中传递的状态值。
selling_partner_id授权您的应用程序的卖家的卖家编号。
mws_auth_token您在为调用亚马逊商城网络服务创建查询字符串时使用的 MWSAuthToken 值。只有当卖家授权混合销售伙伴 API 应用程序时,才会传递 mws_auth_token 参数。有关更多信息,请参阅混合销售伙伴 API 应用程序
spapi_oauth_code您用来交换 LWA 刷新令牌的“使用亚马逊账户登录”(LWA) 授权码。有关更多信息,请参阅步骤 5:您的 应用程序用 LWA 授权码交换 LWA 刷新令牌

例如:

https://client-example.com?state=state-example&mws_auth_token=mwsauthtokenexample&selling_partner_id=sellingpartneridexample&spapi_oauth_code=spapioauthcodeexample
  1. 您的应用程序可以验证 state 值。
  2. 您的应用程序会保存 selling_partner_id、mws_auth_token(如果传递)和 spapi_oauth_code 值。
  3. 显示网站的登录页面。

步骤 5。您的应用程序用 LWA 授权码交换 LWA 刷新令牌

适用于 JavaScript 的“使用亚马逊账户登录”SDK 可以帮助您用 LWA 授权码交换 LWA 刷新令牌。有关更多信息,请参阅《使用亚马逊账户登录》文档。

用 LWA 授权码交换 LWA 刷新令牌

  1. 您的应用程序调用“使用亚马逊账户登录 (LWA)”授权服务器 (https://api.amazon.com/auth/o2/token),用 LWA 授权码交换 LWA 刷新令牌。调用必须包含以下查询参数。
参数描述
grant_type请求的访问授权类型。必须是 authorization_code
code您在步骤 4:亚马逊向您发送授权信息中收到的 LWA 授权码。
redirect_uri您的应用程序的重定向 URI。
client_id您的 LWA 凭证的一部分。要获得此值,请参阅查看您的开发者信息
client_secret您的 LWA 凭证的一部分。要获得此值,请参阅查看您的开发者信息

例如:

POST /auth/o2/token HTTP/l.l
Host: api.amazon.com
Content-Type: application/x-www-form-urlencoded;charset=UTF-8
grant_type=authorization_code&code=SplxlOexamplebYS6WxSbIA&client_id=foodev&client_secret=Y76SDl2F
  1. LWA 授权服务器返回 LWA 刷新令牌。响应采用 JSON 格式并包含以下元素。
参数描述
access_token授权您的应用程序代表卖家采取某些操作的令牌。请参阅连接到销售伙伴 API
token_type返回的令牌类型。应该是 bearer。
expires_in访问令牌失效之前的秒数。
refresh_token可以交换为新访问令牌的长期令牌。请参阅连接到销售伙伴 API
HTTP/l.l 200 OK
Content-Type: application/json;
charset UTF-8
Cache - Control: no-store
Pragma: no-cache
{
  "access_token": "Atza|IQEBLjAsAexampleHpi0U-Dme37rR6CuUpSR",
  "token_type": "bearer",
  "expires_in": 3600,
  "refresh_token": "Atzr|IQEBLzAtAhexamplewVz2Nn6f2y-tpJX2DeX"
}
  1. 您的应用程序保存 refresh_token 值。
  2. 浏览器向卖家显示一个页面,指示使用应用程序的后续步骤。

LWA 刷新令牌是您交换 LWA 访问令牌的长期令牌,它必须包含在销售伙伴 API 的每个请求中。发出访问令牌后,它的有效期为一小时。相同的访问令牌可用于多个 API 调用,直到它过期。请参阅连接到销售伙伴 API

您的应用程序现在已获得授权,可以代表卖家调用销售伙伴 API。

通过SDK调用接口:

amazon sp-api # 2021-06-30版本的SDK地址:

pan.baidu.com/s/1N_JHAXOH… 提取码: 2p4a

amazon sp-api # 2020-09-04版本的SDK地址:

pan.baidu.com/s/1ePc3MbwK… 提取码: w74s

二、同步订单接口

String accessKeyId = authorConfigDTO.getAccessKeyId();
String secretKey = authorConfigDTO.getSecretKey();
String region = authorConfigDTO.getRegion();
String roleArn = authorConfigDTO.getRoleArn();
String roleSessionName = authorConfigDTO.getRoleSessionName();
String clientId = authorConfigDTO.getClientId();
String clientSecret = authorConfigDTO.getClientSecret();
String refreshToken = authorConfigDTO.getRefreshToken();
String lwaEndpoint = authorConfigDTO.getLwaEndpoint();
String spEndPoint = authorConfigDTO.getSpEndPoint();

AWSAuthenticationCredentials awsAuthenticationCredentials;
AWSAuthenticationCredentialsProvider awsAuthenticationCredentialsProvider;
LWAAuthorizationCredentials lwaAuthorizationCredentials;

//注意这个地方的region分北美,欧洲,远东三个AWS区域
awsAuthenticationCredentials = AWSAuthenticationCredentials.builder()
        //注册成为开发者时生成的AWS访问密钥ID
        .accessKeyId(accessKeyId)
        //注册成为开发者时生成的AWS访问密钥
        .secretKey(secretKey)
        //注意,这里的region分北美(us-east-1),欧洲(eu-west-1),远东(us-west-2)
        .region(region)
        .build();

awsAuthenticationCredentialsProvider = AWSAuthenticationCredentialsProvider.builder()
        //创建IAM职权的时候会生成这个ARN
        .roleArn(roleArn)
        //唯一值,可以使用UUID
        .roleSessionName(roleSessionName)
        .build();

lwaAuthorizationCredentials = LWAAuthorizationCredentials.builder()
        //查看开发者信息的时候可看到LWA的客户端编码
        .clientId(clientId)
        //查看开发者信息的时候可看到LWA的客户端秘钥
        .clientSecret(clientSecret)
        //根据上面的客户端编码和客户端秘钥请求客户端令牌
        .refreshToken(refreshToken)
        //"https://api.amazon.com/auth/o2/token"
        .endpoint(lwaEndpoint)
        .build();

OrdersV0Api ordersV0Api = new OrdersV0Api.Builder()
        .awsAuthenticationCredentials(awsAuthenticationCredentials)
        .lwaAuthorizationCredentials(lwaAuthorizationCredentials)
        .awsAuthenticationCredentialsProvider(awsAuthenticationCredentialsProvider)
        //注意,这里的endpoint分北美,欧洲,远东三个地域,每个区域的链接是不一样的
        //北美,https://sellingpartnerapi-na.amazon.com
        //欧洲,https://sellingpartnerapi-eu.amazon.com
        //远东,https://sellingpartnerapi-fe.amazon.com
        .endpoint(spEndPoint)
        .build();
 OrdersV0Api ordersV0Api = commonSignHandler.buildOrdersV0Api(amazonAuthorConfigDTO);
            List<String> fulfillmentChannels = new ArrayList<>();
//            fulfillmentChannels.add("MFN");
            List<String> orderStatuses = new ArrayList<>();
            orderStatuses.add("Unshipped");
            orderStatuses.add("PartiallyShipped");
            List<String> marketplaceIds = this.buildMarketPlaceId(userAccountEcommerceModel);
            GetOrdersResponse orders = ordersV0Api.getOrders(marketplaceIds,null,null,
                    DateUtils.getISO8601Timestamp(startTime.getTime()),DateUtils.getISO8601Timestamp(endTime.getTime())
                    ,orderStatuses,fulfillmentChannels,null,null,null
                    ,10,null,null,null);

注意:同步下来的订单商品id:

//商品id
orderEcommerceDetailModel.setItemId(orderItem.getASIN());

三、同步商品

AWSAuthenticationCredentials awsAuthenticationCredentials;
AWSAuthenticationCredentialsProvider awsAuthenticationCredentialsProvider;
LWAAuthorizationCredentials lwaAuthorizationCredentials;

//注意这个地方的region分北美,欧洲,远东三个AWS区域
awsAuthenticationCredentials = AWSAuthenticationCredentials.builder()
        //注册成为开发者时生成的AWS访问密钥ID
        .accessKeyId(authorConfigDTO.getAccessKeyId())
        //注册成为开发者时生成的AWS访问密钥
        .secretKey(authorConfigDTO.getSecretKey())
        //注意,这里的region分北美(us-east-1),欧洲(eu-west-1),远东(us-west-2)
        .region(authorConfigDTO.getRegion())
        .build();

awsAuthenticationCredentialsProvider = AWSAuthenticationCredentialsProvider.builder()
        //创建IAM职权的时候会生成这个ARN
        .roleArn(authorConfigDTO.getRoleArn())
        //唯一值,可以使用UUID
        .roleSessionName(authorConfigDTO.getRoleSessionName())
        .build();

lwaAuthorizationCredentials = LWAAuthorizationCredentials.builder()
        //查看开发者信息的时候可看到LWA的客户端编码
        .clientId(authorConfigDTO.getClientId())
        //查看开发者信息的时候可看到LWA的客户端秘钥
        .clientSecret(authorConfigDTO.getClientSecret())
        //根据上面的客户端编码和客户端秘钥请求客户端令牌
        .refreshToken(authorConfigDTO.getRefreshToken())
        //"https://api.amazon.com/auth/o2/token"
        .endpoint(authorConfigDTO.getLwaEndpoint())
        .build();

ReportsApi reportsApi = new ReportsApi.Builder()
        .awsAuthenticationCredentials(awsAuthenticationCredentials)
        .lwaAuthorizationCredentials(lwaAuthorizationCredentials)
        .awsAuthenticationCredentialsProvider(awsAuthenticationCredentialsProvider)
        //注意,这里的endpoint分北美,欧洲,远东三个地域,每个区域的链接是不一样的
        //北美,https://sellingpartnerapi-na.amazon.com
        //欧洲,https://sellingpartnerapi-eu.amazon.com
        //远东,https://sellingpartnerapi-fe.amazon.com
        .endpoint(authorConfigDTO.getSpEndPoint())
        .build();
解析下载文档的xml
/**
 * 构建商品
 *
 * @param contents
 */
private List<GoodsModel> buildGoodsModel(List<String> contents,Long bPartnerId) {
    if (CollectionUtils.isEmpty(contents)) {
        return null;
    }
    List<GoodsModel> goodsModels = new ArrayList<>();
    for (int i = 0, size = contents.size(); i < size; i++) {
        try {
            if (i == 0) {
                continue;
            }

            if(null == contents.get(i)){
                continue;
            }
            String[] content = contents.get(i).split("\t");
            if(content.length > 16){
                GoodsModel resModel = new GoodsModel();
                resModel.setPlatformItemId(content[16]);
                resModel.setPlatformItemName(content[0]);
                //价格,目前不知道单位
                if (StringUtils.isNotEmpty(content[4]) && DataUtils.isPositiveDecimal(content[4])) {
                    resModel.setPlatformItemPrice(new BigDecimal(content[4]));
                }
                resModel.setPlatformItemSku(content[3]);
                resModel.setBpartnerId(bPartnerId.intValue());
                resModel.setPlatform(EcaConstants.Platform.AMAZON.toLowerCase());
                goodsModels.add(resModel);
                logger.info("buildGoodsModel content size is greater than 16 :{}", JSON.toJSONString(content));
            }else{
                logger.info("buildGoodsModel content size is less than or equal to 16 :{}", JSON.toJSONString(content));
            }
        } catch (Exception e){
            logger.error("buildGoodsModel构建商品详情出现异常Exception:{}",e);
        }
    }
    return goodsModels;
}

四、上传跟踪号

//Step 1. Create a feed document
Map<String, String> document = createFeedDocument(model);
//Step 2. Construct a feed
String xmlFeed = constructFeed(model);
//Step 3. Upload the feed data
uploadFeedData(document, xmlFeed);
//Step 4. Create a feed
createFeed(document, model, resModel);
//step 5. getFeed
getFeed(model,resModel);
 上传跟踪号上传的XML
 /**
     * Construct a feed
     *
     * @param model
     * @return
     */
    private String constructFeed(UploadTrackingEcommercePlatformModel model) {
        if(null == model){
            return null;
        }

        List<UploadTrackingEcommerceOrderModel> uploadTrackingEcommerceOrderModels = model
                .getUploadTrackingEcommerceOrderModels();
        if(CollectionUtils.isEmpty(uploadTrackingEcommerceOrderModels)){
            return null;
        }

        StringBuilder content = new StringBuilder();
        content.append("<?xml version="1.0" encoding="UTF-8"?>");
        content.append("<AmazonEnvelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="amzn-envelope.xsd">");
        content.append("<Header>");
        content.append("<DocumentVersion>1.02</DocumentVersion>");
        content.append("<MerchantIdentifier>" + model.getUserAccountEcommerceModel().getPlatformAccount() + "</MerchantIdentifier>");
        content.append("</Header>");
        content.append("<MessageType>OrderFulfillment</MessageType>");
        content.append("<PurgeAndReplace>true</PurgeAndReplace>");
        int cnt = 1;
        for(int i=0;i<uploadTrackingEcommerceOrderModels.size();i++) {
            content.append("<Message>");
            content.append("<MessageID>" + cnt + "</MessageID>");
            content.append("<OrderFulfillment>");
            content.append("<AmazonOrderID>" + uploadTrackingEcommerceOrderModels.get(i).getSiteId() + "</AmazonOrderID>");
            content.append("<FulfillmentDate>" + DateFormatUtils.format(DateUtils.addHours(new Date(), -9), "yyyy-MM-dd'T'HH:mm:ss") + ".304</FulfillmentDate>");
            content.append("<FulfillmentData>");
            content.append("<CarrierName>" + uploadTrackingEcommerceOrderModels.get(i).getCarrier()+ "</CarrierName>");
            content.append("<ShippingMethod>" + uploadTrackingEcommerceOrderModels.get(i).getCarrier() + "</ShippingMethod>");
            content.append("<ShipperTrackingNumber>" + uploadTrackingEcommerceOrderModels.get(i).getTrackingNo() + "</ShipperTrackingNumber>");
            content.append("</FulfillmentData>");
            content.append("</OrderFulfillment>");
            content.append("</Message>");
            cnt++;
        }
        content.append("</AmazonEnvelope>");
        return content.toString();

    }