Etsy API updateInventory 响应 200 OK,但不更新库存数据
Etsy API updateInventory responds with 200 OK, but doesn't update inventory data
我有一个 Etsy 列表,其中有 1 个产品处于 draft
状态。当我尝试通过 updateInventory
API 端点(文档:https://www.etsy.com/developers/documentation/reference/listing#method_updatelisting)更新此清单的清单时,出现错误。
我按照 'Updating a Listing with one product' 部分中的示例尝试更新价格、数量和 SKU,但没有成功:https://www.etsy.com/developers/documentation/getting_started/inventory
我收到 oauth signature_invalid
错误或 expected products property to be present
错误。
最后,我能够发出返回 200 OK 的请求。但列表中的数据没有更新。这是那个请求(为了便于阅读,我在 body 中添加了 new-lines,原始请求在 body 中没有空格或 new-lines):
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 不更新库存,即使它 returns 200 OK?更具体地说,此请求中应更改什么以使 Etsy 更新库存数据?
P.S.
在达到上述要求之前我尝试了一些事情:
- 尝试在 URL 中发送没有“?method=PUT”的 PUT 请求。出现
signature_invalid
错误。
- 通过将
Content-Type
header 设置为该值,并将 body 设置为 [=19=,尝试对 body 使用 x-www-form-urlencoded
格式].出现 signature_invalid
错误。
- 尝试使用和不使用 SKU - 我得到 200 OK,但 SKU 没有设置。
这个问题源于我对如何使用我正在使用的 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
header 是 application/x-www-form-urlencoded
- 这是请求 body 在成为请求 url-encoded 之前的样子:
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 中创建新列表的代码。这是一个 work-in-progress 并且需要清理,但它有效:
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);
}
我有一个 Etsy 列表,其中有 1 个产品处于 draft
状态。当我尝试通过 updateInventory
API 端点(文档:https://www.etsy.com/developers/documentation/reference/listing#method_updatelisting)更新此清单的清单时,出现错误。
我按照 'Updating a Listing with one product' 部分中的示例尝试更新价格、数量和 SKU,但没有成功:https://www.etsy.com/developers/documentation/getting_started/inventory
我收到 oauth signature_invalid
错误或 expected products property to be present
错误。
最后,我能够发出返回 200 OK 的请求。但列表中的数据没有更新。这是那个请求(为了便于阅读,我在 body 中添加了 new-lines,原始请求在 body 中没有空格或 new-lines):
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 不更新库存,即使它 returns 200 OK?更具体地说,此请求中应更改什么以使 Etsy 更新库存数据?
P.S. 在达到上述要求之前我尝试了一些事情:
- 尝试在 URL 中发送没有“?method=PUT”的 PUT 请求。出现
signature_invalid
错误。 - 通过将
Content-Type
header 设置为该值,并将 body 设置为 [=19=,尝试对 body 使用x-www-form-urlencoded
格式].出现signature_invalid
错误。 - 尝试使用和不使用 SKU - 我得到 200 OK,但 SKU 没有设置。
这个问题源于我对如何使用我正在使用的 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
header 是application/x-www-form-urlencoded
- 这是请求 body 在成为请求 url-encoded 之前的样子:
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 中创建新列表的代码。这是一个 work-in-progress 并且需要清理,但它有效:
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);
}