【问题标题】:Etsy API updateInventory responds with 200 OK, but doesn't update inventory dataEtsy API updateInventory 响应 200 OK,但不更新库存数据
【发布时间】:2020-10-03 16:03:39
【问题描述】:

我有一个 Etsy 列表,其中有 1 个产品处于 draft 状态。当我尝试通过updateInventory API 端点(文档:https://www.etsy.com/developers/documentation/reference/listing#method_updatelisting)更新此列表的清单时,我收到了错误。

我按照“使用一种产品更新列表”部分中的示例尝试更新价格、数量和 SKU,但没有成功:https://www.etsy.com/developers/documentation/getting_started/inventory

我收到一个 oauth signature_invalid 错误或 expected products property to be present 错误。

最后,我能够发出一个返回 200 OK 的请求。但列表中的数据没有更新。这是该请求(为了便于阅读,我在正文中添加了换行符,原始请求正文中没有空格或换行符):

POST https://openapi.etsy.com/v2/listings/808984070/inventory?method=PUT HTTP/1.1
Host: openapi.etsy.com
Authorization: OAuth oauth_consumer_key="xxxxxxxxx",oauth_nonce="xxxxxxxxx",oauth_signature="xxxxxxxxx",oauth_signature_method="HMAC-SHA1",oauth_timestamp="1591886701",oauth_token="xxxxxxxxx",oauth_version="1.0"
Accept: application/json, text/json, text/x-json, text/javascript, application/xml, text/xml
User-Agent: RestSharp/106.11.4.0
Connection: Keep-Alive
Request-Id: |38197ee2-416cf452906ba6a9.4.
Accept-Encoding: gzip, deflate
Content-Type: application/json
Content-Length: 192

{
  "products": [{
    "product_id": 4415387567,
    "sku": "TEST1",
    "property_values": [],
    "offerings":[{
      "offering_id": 4627746384,
      "price": "50.00",
      "quantity":100,
      "is_enabled":1,
      "is_deleted":0
    }],
    "is_deleted":0
  }]
}

为什么 Etsy 不更新库存,即使它返回 200 OK?更具体地说,这个请求应该改变什么来让 Etsy 更新库存数据?

附言 在达到上述要求之前我尝试过的一些事情:

  • 尝试在 URL 中发送不带“?method=PUT”的 PUT 请求。收到 signature_invalid 错误。
  • 尝试通过将Content-Type 标头设置为该值,并将正文设置为products=<url-encoded string of JSON representation of products array>,对正文使用x-www-form-urlencoded 格式。收到 signature_invalid 错误。
  • 尝试使用和不使用 SKU - 我返回 200 OK,但未设置 SKU。

【问题讨论】:

  • 您好!是什么导致了signature_invalid,您是如何解决的?我在 403 Forbidden 中有一个 X-Error-Detail: oauth_problem=signature_invalid。我可以对受保护的 oAuth 端点进行其他 GET 调用,但不能对 PUT 或 POST 进行调用。附言我正在为 oAuth 1.0 使用 Java 和这个库:com.github.seratch:signedrequest4j:2.14
  • 你好。我在我的解决方案中添加了答案,希望对您有所帮助。

标签: etsy


【解决方案1】:

问题源于我对如何使用我正在使用的 C# 库 (RestSharp) 的误解。经过一些调整,我能够生成一个有效的请求。这是它的样子:

PUT https://openapi.etsy.com/v2/listings/834145978/inventory HTTP/1.1
Host: openapi.etsy.com
Authorization: OAuth oauth_consumer_key="xxxxx",oauth_nonce="xxxxx",oauth_signature="xxxxx",oauth_signature_method="HMAC-SHA1",oauth_timestamp="xxxxx",oauth_token="xxxxx",oauth_version="1.0"
Accept: application/json, text/json, text/x-json, text/javascript, application/xml, text/xml
User-Agent: RestSharp/106.11.4.0
Connection: Keep-Alive
Request-Id: |feb37503-4e0cc9e4a34b242a.4.
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 654

products=%5B%0D%0A%20%20%7B%0D%0A%20%20%20%20%22product_id%22%3A%204638831553%2C%0D%0A%20%20%20%20%22sku%22%3A%20%22N27%22%2C%0D%0A%20%20%20%20%22property_values%22%3A%20%5B%5D%2C%0D%0A%20%20%20%20%22offerings%22%3A%20%5B%0D%0A%20%20%20%20%20%20%7B%0D%0A%20%20%20%20%20%20%20%20%22offering_id%22%3A%204740581353%2C%0D%0A%20%20%20%20%20%20%20%20%22price%22%3A%20%2265.00%22%2C%0D%0A%20%20%20%20%20%20%20%20%22quantity%22%3A%201%2C%0D%0A%20%20%20%20%20%20%20%20%22is_enabled%22%3A%201%2C%0D%0A%20%20%20%20%20%20%20%20%22is_deleted%22%3A%200%0D%0A%20%20%20%20%20%20%7D%0D%0A%20%20%20%20%5D%2C%0D%0A%20%20%20%20%22is_deleted%22%3A%200%0D%0A%20%20%7D%0D%0A%5D

需要注意的部分有:

  • 请求方法为PUT
  • Content-Type 标头是application/x-www-form-urlencoded
  • 这是请求正文在为请求进行 url 编码之前的样子:
products=[
  {
    "product_id": 4638831553,
    "sku": "N27",
    "property_values": [],
    "offerings": [
      {
        "offering_id": 4740581353,
        "price": "65.00",
        "quantity": 1,
        "is_enabled": 1,
        "is_deleted": 0
      }
    ],
    "is_deleted": 0
  }
]

不完全相关,但这是我使用 C# 和 RestSharp 在 Etsy 中创建新列表的代码。这是一项正在进行的工作,需要清理,但它可以工作:

public async Task<ListingModel> PostListing(ListingCreateModel createModel)
{
    // Create authenticator for the URL we will need to use.
    _restClient.Authenticator = MakeAuthenticatorForProtectedResource();

    // Update the create model, making sure we set the ShippingProfileId - this way
    // you won't need to set it manually in every listing afterwards.
    createModel.Quantity = createModel.Quantity;
    createModel.ShippingProfileId = ShippingProfileId;

    // Create a basic listing first, with minimal properties.
    var basicListing = ExecuteAndDeserialize<ListingListModel>(
        CreatePostRequest("listings", createModel)).Results.First();

    // Get the current products for this listing (we'll need to modify that object
    // and send it back to Etsy.)
    var productList = ExecuteAndDeserialize<dynamic>(
        CreateGetRequest($"listings/{basicListing.ListingId}/inventory"));
    
    // set SKU, price, and quantity (just experimenting, you can set any
    // supported properties you need here)
    productList.results.products[0].sku = createModel.Sku;
    productList.results.products[0].offerings[0].price =
        createModel.Price.ToString("0.00");
    productList.results.products[0].offerings[0].quantity =
        createModel.Quantity;

    // Now that we have an updated products list, call the PUT endpoint to
    // update the listing.
    // The CreatePutRequest method sets the 'Content-Type' header to
    // 'application/x-www-form-urlencoded'.
    // RestSharp will take care of serializing the `new { productList.results.products }`
    // object to the form-urlencoded format.
    var updatedListing = ExecuteAndDeserialize<dynamic>(
        await CreatePutRequest($"listings/{basicListing.ListingId}/inventory",
        new
        {
            productList.results.products
        }));
    
    var newListingList = ExecuteAndDeserialize<ListingListModel>(
        CreateGetRequest($"listings/{basicListing.ListingId}"));
    return newListingList.Results.First();
}

private async Task<IRestRequest> CreatePutRequest<TBody>(string resource, TBody body)
{
    var request = new RestRequest(resource)
    {
        Method = Method.PUT
    };

    request.AddHeader("Content-Type", "application/x-www-form-urlencoded");
    request.AddObject(body);
    return request;
}

//-------- NOT SO RELEVANT TO THE QUESTION ----------

private IRestRequest CreateGetRequest(string resource)
{
    var request = new RestRequest(resource)
    {
        Method = Method.GET
    };

    return request;
}

private OAuth1Authenticator MakeAuthenticatorForProtectedResource()
{
    var auth = OAuth1Authenticator
        .ForProtectedResource(_authRepo.AuthInfo.AppKey, _authRepo.AuthInfo.SharedSecret,
        _authRepo.AuthInfo.AccessToken, _authRepo.AuthInfo.AccessTokenSecret);
    return auth;
}

private T ExecuteAndDeserialize<T>(IRestRequest req)
{
    var response = _restClient.Execute(req);
    if (response.IsSuccessful)
    {
        return JsonConvert.DeserializeObject<T>(response.Content);
    }

    if ((int) response.StatusCode >= 400 && (int) response.StatusCode < 500)
    {
        throw new ValidationException(response.Content);
    }
    throw new InvalidOperationException(response.Content);
}

【讨论】:

  • 感谢您的回答!我试图检查你的建议,似乎我也在做同样的事情。但我仍然得到 403 和 X-Error-Detail: oauth_problem=signature_invalid。也许这与我发送到updateInventory PUT 的有效负载有关。文档不是很清楚,也许body不想要一些像ID这样的字段(product_id,value_ids,...),但是对于那种错误很难理解是什么问题:(
  • 考虑使用此答案中的请求正文作为已知的工作示例进行调试,只需更改 product_idoffering_id 以匹配您自己的。但根据我的经验,signature_invalid 发生在身份验证实际上不正确或请求正文格式不正确时。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-05-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-15
  • 1970-01-01
  • 2017-01-17
相关资源
最近更新 更多