diff --git a/client.go b/client.go
index d1ee667..141703a 100644
--- a/client.go
+++ b/client.go
@@ -70,6 +70,27 @@ func (c *Client) Post(ctx context.Context, path string, param url.Values, body i
return
}
+// Put request for TikTok requests.
+// Note: Timestamp, appkey and signature will auto-management by action.
+func (c *Client) Put(ctx context.Context, path string, param url.Values, body interface{}, resp interface{}) (err error) {
+ param = c.prepareParam(path, param)
+ r := c.prepareBody(body)
+ err = c.request(ctx, http.MethodPut, APIBaseURL, path, param, r, resp)
+ return
+}
+
+// Delete request for TikTok requests. I don't known why there is body in delete request.
+// Note: Timestamp, appkey and signature will auto-management by action.
+func (c *Client) Delete(ctx context.Context, path string, param url.Values, body interface{}, resp interface{}) (err error) {
+ param = c.prepareParam(path, param)
+ var r io.Reader
+ if body != nil {
+ r = c.prepareBody(body)
+ }
+ err = c.request(ctx, http.MethodDelete, APIBaseURL, path, param, r, resp)
+ return
+}
+
func (c *Client) prepareParam(path string, param url.Values) url.Values {
ak := safeGet(param, "access_token")
if ak != "" {
diff --git a/helper_test.go b/helper_test.go
index da166e9..a3f5e05 100644
--- a/helper_test.go
+++ b/helper_test.go
@@ -48,12 +48,10 @@ func setupMock(t *testing.T, tt TestRecord, args, want interface{}) {
if tt.Request.Query != "" {
require.Equal(t, tt.Request.Query, r.URL.RawQuery)
}
- var b []byte
- if r.Method == http.MethodPost || r.Method == http.MethodPut {
- defer r.Body.Close()
- b, _ = io.ReadAll(r.Body)
- }
+
if len(tt.Request.Body) > 0 {
+ defer r.Body.Close()
+ b, _ := io.ReadAll(r.Body)
require.JSONEq(t, string(tt.Request.Body), string(b))
} else {
tt.Request.Body = []byte(`{}`)
@@ -85,3 +83,30 @@ func mockTime() func() {
}
}
}
+
+func mockTests(t *testing.T, fn string, args, want interface{}, exec func() (interface{}, error)) {
+ t.Helper()
+
+ restore := mockTime()
+ defer restore()
+
+ tests := loadTestData(t, fn)
+ for _, tt := range tests {
+ t.Run(tt.Name, func(t *testing.T) {
+ httpmock.Activate()
+ defer httpmock.DeactivateAndReset()
+ setupMock(t, tt, args, want)
+
+ got, err := exec()
+ if tt.WantErr {
+ require.Error(t, err)
+ return
+ } else {
+ require.NoError(t, err)
+ if want != nil {
+ require.Equal(t, want, got)
+ }
+ }
+ })
+ }
+}
diff --git a/product.go b/product.go
index d199e1e..1f226e0 100644
--- a/product.go
+++ b/product.go
@@ -83,42 +83,110 @@ func (c *Client) UploadFile(ctx context.Context, p Param, fn string, body []byte
return
}
-func (c *Client) CreateProduct(ctx context.Context, p Param) (err error) {
+func (c *Client) CreateProduct(ctx context.Context, p Param, req CreateProductRequest) (product Product, err error) {
+ var param url.Values
+ if param, err = c.params(p); err != nil {
+ return
+ }
+ if err = c.validate.Struct(&req); err != nil {
+ return
+ }
+ err = c.Post(ctx, "/api/products", param, req, &product)
return
}
-func (c *Client) EditProduct(ctx context.Context, p Param) (err error) {
+func (c *Client) EditProduct(ctx context.Context, p Param, req EditProductRequest) (product Product, err error) {
+ var param url.Values
+ if param, err = c.params(p); err != nil {
+ return
+ }
+
+ if err = c.validate.Struct(&req); err != nil {
+ return
+ }
+ err = c.Put(ctx, "/api/products", param, req, &product)
return
}
-func (c *Client) GetProductList(ctx context.Context, p Param) (err error) {
+func (c *Client) GetProductList(ctx context.Context, p Param, req ProductSearchRequest) (list ProductSearchList, err error) {
+ var param url.Values
+ if param, err = c.params(p); err != nil {
+ return
+ }
+ err = c.Post(ctx, "/api/products/search", param, req, &list)
return
}
-func (c *Client) GetProductDetail(ctx context.Context, p Param) (err error) {
+func (c *Client) GetProductDetail(ctx context.Context, p Param, productID string) (product Product, err error) {
+ var param url.Values
+ if param, err = c.params(p); err != nil {
+ return
+ }
+ param.Set("product_id", productID)
+ err = c.Get(ctx, "/api/products/details", param, &product)
return
}
-func (c *Client) UpdatePrice(ctx context.Context, p Param) (err error) {
+func (c *Client) UpdatePrice(ctx context.Context, p Param, req UpdatePriceRequest) (list UpdatePriceFailedSKU, err error) {
+ var param url.Values
+ if param, err = c.params(p); err != nil {
+ return
+ }
+ err = c.Put(ctx, "/api/products/prices", param, req, &list)
return
}
-func (c *Client) UpdateStock(ctx context.Context, p Param) (err error) {
+func (c *Client) UpdateStock(ctx context.Context, p Param, req UpdateStockRequest) (list UpdateStockFailedSKU, err error) {
+ var param url.Values
+ if param, err = c.params(p); err != nil {
+ return
+ }
+ err = c.Put(ctx, "/api/products/stocks", param, req, &list)
return
}
-func (c *Client) DeactivateProducts(ctx context.Context, p Param) (err error) {
+func (c *Client) DeactivateProducts(ctx context.Context, p Param, ids []string) (list FailedProductIDs, err error) {
+ var param url.Values
+ if param, err = c.params(p); err != nil {
+ return
+ }
+ req := map[string][]string{
+ "product_ids": ids,
+ }
+ err = c.Post(ctx, "/api/products/inactivated_products", param, req, &list)
return
}
-func (c *Client) DeleteProducts(ctx context.Context, p Param) (err error) {
+func (c *Client) DeleteProducts(ctx context.Context, p Param, ids []string) (list FailedProductIDs, err error) {
+ var param url.Values
+ if param, err = c.params(p); err != nil {
+ return
+ }
+ req := map[string][]string{
+ "product_ids": ids,
+ }
+ err = c.Delete(ctx, "/api/products", param, req, &list)
return
}
-func (c *Client) RecoverProduct(ctx context.Context, p Param) (err error) {
+func (c *Client) RecoverProduct(ctx context.Context, p Param, ids []string) (list FailedProductIDs, err error) {
+ param := url.Values{}
+ param.Set("access_token", p.AccessToken)
+ req := map[string][]string{
+ "product_ids": ids,
+ }
+ err = c.Post(ctx, "/api/products/recover", param, req, &list)
return
}
-func (c *Client) ActivateProduct(ctx context.Context, p Param) (err error) {
+func (c *Client) ActivateProduct(ctx context.Context, p Param, ids []string) (list FailedProductIDs, err error) {
+ var param url.Values
+ if param, err = c.params(p); err != nil {
+ return
+ }
+ req := map[string][]string{
+ "product_ids": ids,
+ }
+ err = c.Post(ctx, "/api/products/activate", param, req, &list)
return
}
diff --git a/product_test.go b/product_test.go
index 8446df7..dedf545 100644
--- a/product_test.go
+++ b/product_test.go
@@ -2,6 +2,7 @@ package tiktok_test
import (
"context"
+ "encoding/json"
"strings"
"testing"
@@ -235,3 +236,199 @@ func TestClient_UploadFile(t *testing.T) {
})
}
}
+
+// TODO: Fix those tests.
+// func TestClient_CreateProduct(t *testing.T) {
+// var args struct {
+// AppKey string `json:"app_key"`
+// AppSecret string `json:"app_secret"`
+// AccessToken string `json:"access_token"`
+// ShopID string `json:"shop_id"`
+// RequestJSON json.RawMessage `json:"request_json"`
+// }
+// var want tiktok.Product
+// mockTests(t, "testdata/product/create_product.json", &args, &want, func() (got interface{}, err error) {
+// c, err := tiktok.New(args.AppKey, args.AppSecret)
+// require.NoError(t, err)
+// var req tiktok.CreateProductRequest
+// err = json.Unmarshal(args.RequestJSON, &req)
+// require.NoError(t, err)
+// product, err := c.CreateProduct(context.TODO(), tiktok.Param{args.AccessToken, args.ShopID}, req)
+// got = &product
+// return
+// })
+// }
+
+// func TestClient_EditProduct(t *testing.T) {
+// var args struct {
+// AppKey string `json:"app_key"`
+// AppSecret string `json:"app_secret"`
+// AccessToken string `json:"access_token"`
+// ShopID string `json:"shop_id"`
+// RequestJSON json.RawMessage `json:"request_json"`
+// }
+// var want tiktok.Product
+// mockTests(t, "testdata/product/edit_product.json", &args, &want, func() (got interface{}, err error) {
+// c, err := tiktok.New(args.AppKey, args.AppSecret)
+// require.NoError(t, err)
+// var req tiktok.CreateProductRequest
+// err = json.Unmarshal(args.RequestJSON, &req)
+// require.NoError(t, err)
+// product, err := c.CreateProduct(context.TODO(), tiktok.Param{args.AccessToken, args.ShopID}, req)
+// got = &product
+// return
+// })
+// }
+
+func TestClient_GetProductList(t *testing.T) {
+ var args struct {
+ AppKey string `json:"app_key"`
+ AppSecret string `json:"app_secret"`
+ AccessToken string `json:"access_token"`
+ ShopID string `json:"shop_id"`
+ RequestJSON json.RawMessage `json:"request_json"`
+ }
+ var want tiktok.ProductSearchList
+ mockTests(t, "testdata/product/get_product_list.json", &args, &want, func() (got interface{}, err error) {
+ c, err := tiktok.New(args.AppKey, args.AppSecret)
+ require.NoError(t, err)
+ var req tiktok.ProductSearchRequest
+ err = json.Unmarshal(args.RequestJSON, &req)
+ require.NoError(t, err)
+ product, err := c.GetProductList(context.TODO(), tiktok.Param{args.AccessToken, args.ShopID}, req)
+ got = &product
+ return
+ })
+}
+
+func TestClient_GetProductDetail(t *testing.T) {
+ var args struct {
+ AppKey string `json:"app_key"`
+ AppSecret string `json:"app_secret"`
+ AccessToken string `json:"access_token"`
+ ShopID string `json:"shop_id"`
+ ProductID string `json:"product_id"`
+ }
+ var want tiktok.Product
+ mockTests(t, "testdata/product/get_product_detail.json", &args, &want, func() (got interface{}, err error) {
+ c, err := tiktok.New(args.AppKey, args.AppSecret)
+ require.NoError(t, err)
+ product, err := c.GetProductDetail(context.TODO(), tiktok.Param{args.AccessToken, args.ShopID}, args.ProductID)
+ got = &product
+ return
+ })
+}
+
+func TestClient_UpdatePrice(t *testing.T) {
+ var args struct {
+ AppKey string `json:"app_key"`
+ AppSecret string `json:"app_secret"`
+ AccessToken string `json:"access_token"`
+ ShopID string `json:"shop_id"`
+ RequestJSON json.RawMessage `json:"request_json"`
+ }
+ var want tiktok.UpdatePriceFailedSKU
+ mockTests(t, "testdata/product/update_price.json", &args, &want, func() (got interface{}, err error) {
+ c, err := tiktok.New(args.AppKey, args.AppSecret)
+ require.NoError(t, err)
+ var req tiktok.UpdatePriceRequest
+ err = json.Unmarshal(args.RequestJSON, &req)
+ require.NoError(t, err)
+ product, err := c.UpdatePrice(context.TODO(), tiktok.Param{args.AccessToken, args.ShopID}, req)
+ got = &product
+ return
+ })
+}
+
+func TestClient_UpdateStock(t *testing.T) {
+ var args struct {
+ AppKey string `json:"app_key"`
+ AppSecret string `json:"app_secret"`
+ AccessToken string `json:"access_token"`
+ ShopID string `json:"shop_id"`
+ RequestJSON json.RawMessage `json:"request_json"`
+ }
+ var want tiktok.UpdateStockFailedSKU
+ mockTests(t, "testdata/product/update_stock.json", &args, &want, func() (got interface{}, err error) {
+ c, err := tiktok.New(args.AppKey, args.AppSecret)
+ require.NoError(t, err)
+ var req tiktok.UpdateStockRequest
+ err = json.Unmarshal(args.RequestJSON, &req)
+ require.NoError(t, err)
+ product, err := c.UpdateStock(context.TODO(), tiktok.Param{args.AccessToken, args.ShopID}, req)
+ got = &product
+ return
+ })
+}
+
+func TestClient_DeactivateProducts(t *testing.T) {
+ var args struct {
+ AppKey string `json:"app_key"`
+ AppSecret string `json:"app_secret"`
+ AccessToken string `json:"access_token"`
+ ShopID string `json:"shop_id"`
+ ProductID []string `json:"product_id"`
+ }
+ var want tiktok.FailedProductIDs
+ mockTests(t, "testdata/product/deactivate_products.json", &args, &want, func() (got interface{}, err error) {
+ c, err := tiktok.New(args.AppKey, args.AppSecret)
+ require.NoError(t, err)
+ product, err := c.DeactivateProducts(context.TODO(), tiktok.Param{args.AccessToken, args.ShopID}, args.ProductID)
+ got = &product
+ return
+ })
+}
+
+func TestClient_DeleteProducts(t *testing.T) {
+ var args struct {
+ AppKey string `json:"app_key"`
+ AppSecret string `json:"app_secret"`
+ AccessToken string `json:"access_token"`
+ ShopID string `json:"shop_id"`
+ ProductID []string `json:"product_id"`
+ }
+ var want tiktok.FailedProductIDs
+ mockTests(t, "testdata/product/delete_products.json", &args, &want, func() (got interface{}, err error) {
+ c, err := tiktok.New(args.AppKey, args.AppSecret)
+ require.NoError(t, err)
+ product, err := c.DeleteProducts(context.TODO(), tiktok.Param{args.AccessToken, args.ShopID}, args.ProductID)
+ got = &product
+ return
+ })
+}
+
+func TestClient_RecoverProduct(t *testing.T) {
+ var args struct {
+ AppKey string `json:"app_key"`
+ AppSecret string `json:"app_secret"`
+ AccessToken string `json:"access_token"`
+ ShopID string `json:"shop_id"`
+ ProductID []string `json:"product_id"`
+ }
+ var want tiktok.FailedProductIDs
+ mockTests(t, "testdata/product/recover_product.json", &args, &want, func() (got interface{}, err error) {
+ c, err := tiktok.New(args.AppKey, args.AppSecret)
+ require.NoError(t, err)
+ product, err := c.RecoverProduct(context.TODO(), tiktok.Param{args.AccessToken, args.ShopID}, args.ProductID)
+ got = &product
+ return
+ })
+}
+
+func TestClient_ActivateProduct(t *testing.T) {
+ var args struct {
+ AppKey string `json:"app_key"`
+ AppSecret string `json:"app_secret"`
+ AccessToken string `json:"access_token"`
+ ShopID string `json:"shop_id"`
+ ProductID []string `json:"product_id"`
+ }
+ var want tiktok.FailedProductIDs
+ mockTests(t, "testdata/product/activate_product.json", &args, &want, func() (got interface{}, err error) {
+ c, err := tiktok.New(args.AppKey, args.AppSecret)
+ require.NoError(t, err)
+ product, err := c.ActivateProduct(context.TODO(), tiktok.Param{args.AccessToken, args.ShopID}, args.ProductID)
+ got = &product
+ return
+ })
+}
diff --git a/request.go b/request.go
index 3941a23..f3c2673 100644
--- a/request.go
+++ b/request.go
@@ -90,3 +90,84 @@ const (
ImgSceneCertificationImage
ImgSceneSizeChartImage
)
+
+type SKU struct {
+ ID string `json:"id,omitempty"`
+ SalesAttributes []SalesAttribute `json:"sales_attributes,omitempty"`
+ StockInfos []StockInfo `json:"stock_infos,omitempty"`
+ SellerSku string `json:"seller_sku,omitempty"`
+ OriginalPrice string `json:"original_price,omitempty"`
+}
+
+type File struct {
+ ID string `json:"id"`
+ Name string `json:"name"`
+ Type string `json:"type"`
+}
+
+type Image struct {
+ ID string `json:"id"`
+}
+
+type ProductCertificationRequest struct {
+ ID string `json:"id"`
+ Files []File `json:"files"`
+ Images []Image `json:"images"`
+}
+
+type CreateProductRequest struct {
+ ProductName string `json:"product_name"`
+ Description string `json:"description"`
+ CategoryID string `json:"category_id"`
+ BrandID string `json:"brand_id"`
+ Images []Image `json:"images"`
+ WarrantyPeriod int `json:"warranty_period"`
+ WarrantyPolicy string `json:"warranty_policy"`
+ PackageLength int `json:"package_length"`
+ PackageWidth int `json:"package_width"`
+ PackageHeight int `json:"package_height"`
+ PackageWeight string `json:"package_weight"`
+ ProductCertifications []ProductCertification `json:"product_certifications"`
+ IsCodOpen bool `json:"is_cod_open"`
+ Skus []SKU `json:"skus"`
+}
+
+type StockInfo struct {
+ WarehouseID string `json:"warehouse_id"`
+ AvailableStock int `json:"available_stock"`
+}
+
+type EditProductRequest struct {
+ ProductID string `json:"product_id"`
+ ProductName string `json:"product_name"`
+ Description string `json:"description"`
+ CategoryID string `json:"category_id"`
+ Images []Image `json:"images"`
+ PackageWeight string `json:"package_weight"`
+ IsCodOpen bool `json:"is_cod_open"`
+ Skus []SKU `json:"skus"`
+ BrandID string `json:"brand_id"`
+ WarrantyPeriod int `json:"warranty_period"`
+ WarrantyPolicy string `json:"warranty_policy"`
+ PackageLength int `json:"package_length"`
+ PackageWidth int `json:"package_width"`
+ PackageHeight int `json:"package_height"`
+ ProductCertifications []ProductCertificationRequest `json:"product_certifications"`
+}
+
+type ProductSearchRequest struct {
+ PageSize int `json:"page_size"`
+ PageNumber int `json:"page_number"`
+ SearchStatus int `json:"search_status"`
+ SellerSkuList []string `json:"seller_sku_list"`
+}
+
+type UpdatePriceRequest struct {
+ ProductID string `json:"product_id"`
+ Skus []SKU `json:"skus"`
+}
+
+type UpdateStockRequest struct {
+ ProductID string `json:"product_id"`
+ Skus []SKU `json:"skus"`
+}
diff --git a/response.go b/response.go
index 3256b04..3215206 100644
--- a/response.go
+++ b/response.go
@@ -269,3 +269,36 @@ type FileInfo struct {
FileName string `json:"file_name"`
FileType string `json:"file_type"`
}
+
+type SalesAttribute struct {
+ AttributeID string `json:"attribute_id"`
+ ValueID string `json:"value_id"`
+}
+
+type ProductSKU struct {
+ ID string `json:"id"`
+ SellerSku string `json:"seller_sku"`
+ SalesAttributes []SalesAttribute `json:"sales_attributes"`
+}
+
+type Product struct {
+ ProductID string `json:"product_id"`
+ Skus []ProductSKU `json:"skus"`
+}
+
+type ProductSearchList struct{}
+
+type UpdatePriceFailedSKU struct {
+ FailedSKUIDs []string `json:"failed_sku_ids"`
+}
+
+type FailedSKUStock struct {
+ ID string `json:"id"`
+ FailedWarehouseIDs []string `json:"failed_warehouse_ids"`
+}
+
+type UpdateStockFailedSKU struct {
+ FailedSKUs []FailedSKUStock `json:"failed_skus"`
+}
+
+type FailedProductIDs struct{}
diff --git a/testdata/product/activate_product.json b/testdata/product/activate_product.json
new file mode 100644
index 0000000..c9daf55
--- /dev/null
+++ b/testdata/product/activate_product.json
@@ -0,0 +1,52 @@
+[
+ {
+ "name": "Official example",
+ "args": {
+ "app_key": "12abcd",
+ "app_secret": "123",
+ "access_token": "abc1c123-3128-aa17-125e-2d2d51ab814fa",
+ "shop_id": "123456",
+ "product_id": [
+ "12312312123",
+ "123123123",
+ "123123123",
+ "1231231231"
+ ]
+ },
+ "request": {
+ "method": "POST",
+ "url": "https://open-api.tiktokglobalshop.com/api/products/activate",
+ "headers": {},
+ "query": "access_token=abc1c123-3128-aa17-125e-2d2d51ab814fa&app_key=12abcd&shop_id=123456&sign=d84e77bfa3701c3c3e4d37ad21b5b429478825b7e8af596cae74f2801859e2ea×tamp=1600000000",
+ "body": {
+ "product_ids": [
+ "12312312123",
+ "123123123",
+ "123123123",
+ "1231231231"
+ ]
+ }
+ },
+ "response": {
+ "status": 200,
+ "body": {
+ "code": 0,
+ "message": "Success",
+ "request_id": "123123123",
+ "data": {
+ "failed_product_ids": [
+ "123123123",
+ "123123123"
+ ]
+ }
+ }
+ },
+ "want": {
+ "failed_product_ids": [
+ "123123123",
+ "123123123"
+ ]
+ },
+ "want_err": false
+ }
+]
\ No newline at end of file
diff --git a/testdata/product/create_product.json b/testdata/product/create_product.json
new file mode 100644
index 0000000..65866be
--- /dev/null
+++ b/testdata/product/create_product.json
@@ -0,0 +1,249 @@
+[
+ {
+ "name": "Official example",
+ "args": {
+ "app_key": "12abcd",
+ "app_secret": "123",
+ "access_token": "abc1c123-3128-aa17-125e-2d2d51ab814fa",
+ "shop_id": "123456",
+ "request_json": {
+ "product_name": "aaaaaa",
+ "description": "
- It is recommended to avoid using Chinese because the copy will be displayed to local users
",
+ "category_id": "111111",
+ "brand_id": "2222222",
+ "images": [
+ {
+ "id": "fadfasdf"
+ },
+ {
+ "id": "asdfasdfasdf"
+ },
+ {
+ "id": "asdfasdf"
+ },
+ {
+ "id": "asdfasdf"
+ },
+ {
+ "id": "asdfasdf"
+ }
+ ],
+ "warranty_period": 1,
+ "warranty_policy": "N/A",
+ "package_length": 10,
+ "package_width": 10,
+ "package_height": 10,
+ "package_weight": "20",
+ "product_certifications": [
+ {
+ "id": "1111111",
+ "files": [
+ {
+ "id": "dfgsdfgsdfgsdfg",
+ "name": "aaaa.pdf",
+ "type": "PDF"
+ },
+ {
+ "id": "asdfasdfasdfasdf",
+ "name": "bbb.pdf",
+ "type": "PDF"
+ }
+ ],
+ "images": [
+ {
+ "id": "sdafasdfasdfasdfsd"
+ }
+ ]
+ }
+ ],
+ "is_cod_open": false,
+ "skus": [
+ {
+ "sales_attributes": [
+ {
+ "attribute_id": "11111",
+ "custom_value": "yellow"
+ },
+ {
+ "attribute_id": "222222",
+ "custom_value": "small",
+ "sku_img": {
+ "id": "asdfasdfasdfasdf"
+ }
+ }
+ ],
+ "stock_infos": [
+ {
+ "warehouse_id": "3333333",
+ "available_stock": 200
+ }
+ ],
+ "seller_sku": "test1",
+ "original_price": "100"
+ },
+ {
+ "sales_attributes": [
+ {
+ "attribute_id": "100000",
+ "custom_value": "red"
+ },
+ {
+ "attribute_id": "100007",
+ "custom_value": "big",
+ "sku_img": {
+ "id": "asdfasdfasdfasdfsda"
+ }
+ }
+ ],
+ "stock_infos": [
+ {
+ "warehouse_id": "4444444",
+ "available_stock": 0
+ }
+ ],
+ "seller_sku": "test2",
+ "original_price": "100"
+ }
+ ]
+ }
+ },
+ "request": {
+ "method": "POST",
+ "url": "https://open-api.tiktokglobalshop.com/api/products",
+ "headers": {},
+ "query": "access_token=abc1c123-3128-aa17-125e-2d2d51ab814fa&app_key=12abcd&shop_id=123456&sign=0bd65a4d92ab3328d0237c1714eb8664616efc55dfb41a7223f48517eb2e1358×tamp=1600000000",
+ "body": {
+ "product_name": "aaaaaa",
+ "description": "- It is recommended to avoid using Chinese because the copy will be displayed to local users
",
+ "category_id": "111111",
+ "brand_id": "2222222",
+ "images": [
+ {
+ "id": "fadfasdf"
+ },
+ {
+ "id": "asdfasdfasdf"
+ },
+ {
+ "id": "asdfasdf"
+ },
+ {
+ "id": "asdfasdf"
+ },
+ {
+ "id": "asdfasdf"
+ }
+ ],
+ "warranty_period": 1,
+ "warranty_policy": "N/A",
+ "package_length": 10,
+ "package_width": 10,
+ "package_height": 10,
+ "package_weight": "20",
+ "product_certifications": [
+ {
+ "id": "1111111",
+ "files": [
+ {
+ "id": "dfgsdfgsdfgsdfg",
+ "name": "aaaa.pdf",
+ "type": "PDF"
+ },
+ {
+ "id": "asdfasdfasdfasdf",
+ "name": "bbb.pdf",
+ "type": "PDF"
+ }
+ ],
+ "images": [
+ {
+ "id": "sdafasdfasdfasdfsd"
+ }
+ ]
+ }
+ ],
+ "is_cod_open": false,
+ "skus": [
+ {
+ "sales_attributes": [
+ {
+ "attribute_id": "11111",
+ "custom_value": "yellow"
+ },
+ {
+ "attribute_id": "222222",
+ "custom_value": "small",
+ "sku_img": {
+ "id": "asdfasdfasdfasdf"
+ }
+ }
+ ],
+ "stock_infos": [
+ {
+ "warehouse_id": "3333333",
+ "available_stock": 200
+ }
+ ],
+ "seller_sku": "test1",
+ "original_price": "100"
+ },
+ {
+ "sales_attributes": [
+ {
+ "attribute_id": "100000",
+ "custom_value": "red"
+ },
+ {
+ "attribute_id": "100007",
+ "custom_value": "big",
+ "sku_img": {
+ "id": "asdfasdfasdfasdfsda"
+ }
+ }
+ ],
+ "stock_infos": [
+ {
+ "warehouse_id": "4444444",
+ "available_stock": 0
+ }
+ ],
+ "seller_sku": "test2",
+ "original_price": "100"
+ }
+ ]
+ }
+ },
+ "response": {
+ "status": 200,
+ "body": {
+ "id": "2222332323",
+ "seller_sku": "bbbbb",
+ "sales_attributes": [
+ {
+ "attribute_id": "33333",
+ "value_id": "123124123123"
+ },
+ {
+ "attribute_id": "44444",
+ "value_id": "1312312312412"
+ }
+ ]
+ }
+ },
+ "want": {
+ "id": "2222332323",
+ "seller_sku": "bbbbb",
+ "sales_attributes": [
+ {
+ "attribute_id": "33333",
+ "value_id": "123124123123"
+ },
+ {
+ "attribute_id": "44444",
+ "value_id": "1312312312412"
+ }
+ ]
+ },
+ "want_err": false
+ }
+]
\ No newline at end of file
diff --git a/testdata/product/deactivate_products.json b/testdata/product/deactivate_products.json
new file mode 100644
index 0000000..2246111
--- /dev/null
+++ b/testdata/product/deactivate_products.json
@@ -0,0 +1,52 @@
+[
+ {
+ "name": "Official example",
+ "args": {
+ "app_key": "12abcd",
+ "app_secret": "123",
+ "access_token": "abc1c123-3128-aa17-125e-2d2d51ab814fa",
+ "shop_id": "123456",
+ "product_id": [
+ "123123123123",
+ "123123123",
+ "123123123",
+ "1231231231"
+ ]
+ },
+ "request": {
+ "method": "POST",
+ "url": "https://open-api.tiktokglobalshop.com/api/products/inactivated_products",
+ "headers": {},
+ "query": "access_token=abc1c123-3128-aa17-125e-2d2d51ab814fa&app_key=12abcd&shop_id=123456&sign=56d278da67aca36b5efe1bc7c4d164be0f9e2b8b6312d1af41911ab55b5c46f8×tamp=1600000000",
+ "body": {
+ "product_ids": [
+ "123123123123",
+ "123123123",
+ "123123123",
+ "1231231231"
+ ]
+ }
+ },
+ "response": {
+ "status": 200,
+ "body": {
+ "code": 0,
+ "message": "Success",
+ "request_id": "123123123",
+ "data": {
+ "failed_product_ids": [
+ "123123123",
+ "12312312312"
+ ]
+ }
+ }
+ },
+ "want": {
+ "failed_product_ids": [
+ "123123123",
+ "12312312312"
+ ]
+ },
+ "want_err": false
+ }
+]
\ No newline at end of file
diff --git a/testdata/product/delete_products.json b/testdata/product/delete_products.json
new file mode 100644
index 0000000..6b20c99
--- /dev/null
+++ b/testdata/product/delete_products.json
@@ -0,0 +1,52 @@
+[
+ {
+ "name": "Official example",
+ "args": {
+ "app_key": "12abcd",
+ "app_secret": "123",
+ "access_token": "abc1c123-3128-aa17-125e-2d2d51ab814fa",
+ "shop_id": "123456",
+ "product_id": [
+ "12312312123",
+ "123123123",
+ "123123123",
+ "1231231231"
+ ]
+ },
+ "request": {
+ "method": "DELETE",
+ "url": "https://open-api.tiktokglobalshop.com/api/products",
+ "headers": {},
+ "query": "access_token=abc1c123-3128-aa17-125e-2d2d51ab814fa&app_key=12abcd&shop_id=123456&sign=0bd65a4d92ab3328d0237c1714eb8664616efc55dfb41a7223f48517eb2e1358×tamp=1600000000",
+ "body": {
+ "product_ids": [
+ "12312312123",
+ "123123123",
+ "123123123",
+ "1231231231"
+ ]
+ }
+ },
+ "response": {
+ "status": 200,
+ "body": {
+ "code": 0,
+ "message": "Success",
+ "request_id": "123123123",
+ "data": {
+ "failed_product_ids": [
+ "123123123",
+ "123123123"
+ ]
+ }
+ }
+ },
+ "want": {
+ "failed_product_ids": [
+ "123123123",
+ "123123123"
+ ]
+ },
+ "want_err": false
+ }
+]
\ No newline at end of file
diff --git a/testdata/product/edit_product.json b/testdata/product/edit_product.json
new file mode 100644
index 0000000..e69de29
diff --git a/testdata/product/get_product_detail.json b/testdata/product/get_product_detail.json
new file mode 100644
index 0000000..f4b515f
--- /dev/null
+++ b/testdata/product/get_product_detail.json
@@ -0,0 +1,431 @@
+[
+ {
+ "name": "Official example",
+ "args": {
+ "app_key": "12abcd",
+ "app_secret": "123",
+ "access_token": "abc1c123-3128-aa17-125e-2d2d51ab814fa",
+ "shop_id": "123456",
+ "product_id": "123123123123123"
+ },
+ "request": {
+ "method": "GET",
+ "url": "https://open-api.tiktokglobalshop.com/api/products/details",
+ "headers": {},
+ "query": "access_token=abc1c123-3128-aa17-125e-2d2d51ab814fa&app_key=12abcd&product_id=123123123123123&shop_id=123456&sign=3c859ee8285fde6816f31aff7cca15dd75e6cc9061d6bb32c22ccf8429c931f8×tamp=1600000000"
+ },
+ "response": {
+ "status": 200,
+ "body": {
+ "code": 0,
+ "message": "Success",
+ "request_id": "123123123",
+ "data": {
+ "product_id": "123123123",
+ "product_status": 4,
+ "product_name": "aaaa",
+ "category_list": [
+ {
+ "id": "123123",
+ "parent_id": "0",
+ "local_display_name": "aaaa",
+ "is_leaf": false
+ },
+ {
+ "id": "123123123",
+ "parent_id": "1111",
+ "local_display_name": "bbb",
+ "is_leaf": false
+ },
+ {
+ "id": "123123123",
+ "parent_id": "2222",
+ "local_display_name": "cccc",
+ "is_leaf": false
+ },
+ {
+ "id": "123123123",
+ "parent_id": "3333",
+ "local_display_name": "ddddd",
+ "is_leaf": true
+ }
+ ],
+ "brand": {
+ "id": "123123123",
+ "name": "aaa",
+ "status": 1
+ },
+ "images": [
+ {
+ "height": 600,
+ "width": 600,
+ "thumb_url_list": [
+ "https://sadfasdfasdfasdfasdf",
+ "https://sadfasdfasdfadsf"
+ ],
+ "id": "asdfasdfasdf",
+ "url_list": [
+ "https://asdfasdfasdf",
+ "https://afsdfasdfasdf"
+ ]
+ },
+ {
+ "height": 720,
+ "width": 720,
+ "thumb_url_list": [
+ "https://asdfasdfasdfasdfas",
+ "https://asdfasfasdfasfasdf"
+ ],
+ "id": "afsdfasdfasdf",
+ "url_list": [
+ "https://asdfasdfasdfas",
+ "https://fasdfasdfasdf"
+ ]
+ },
+ {
+ "height": 1080,
+ "width": 1080,
+ "thumb_url_list": [
+ "https://asdfasdfasdf",
+ "https://asdfasdfasdf"
+ ],
+ "id": "asdfasdfasdf",
+ "url_list": [
+ "https://asdfasfdasdfasf",
+ "https://asdfasdfasdfsadf"
+ ]
+ }
+ ],
+ "description": "aaaa",
+ "warranty_period": {
+ "warranty_id": 2,
+ "warranty_description": "2 months"
+ },
+ "warranty_policy": "warranty policy test",
+ "package_length": 30,
+ "package_width": 20,
+ "package_height": 30,
+ "package_weight": "5",
+ "skus": [
+ {
+ "id": "123123123",
+ "seller_sku": "aaaa",
+ "price": {
+ "original_price": "1200",
+ "currency": "IDR"
+ },
+ "stock_infos": [
+ {
+ "warehouse_id": "123123123",
+ "available_stock": 160
+ }
+ ],
+ "sales_attributes": [
+ {
+ "id": "123123123",
+ "name": "aaa",
+ "value_id": "123123123",
+ "value_name": "red",
+ "sku_img": {
+ "height": 600,
+ "width": 600,
+ "thumb_url_list": [
+ "https://asdfasdfasdfasdfa",
+ "https://asdfasdfasdfasdf"
+ ],
+ "id": "asdfasdfasdfasdf",
+ "url_list": [
+ "https://asdfasdfasdfasf",
+ "https://asdfasdfasdfasdf"
+ ]
+ }
+ }
+ ]
+ },
+ {
+ "id": "123123123",
+ "seller_sku": "bbbb",
+ "price": {
+ "original_price": "1600",
+ "currency": "IDR"
+ },
+ "stock_infos": [
+ {
+ "warehouse_id": "123123123",
+ "available_stock": 3000
+ }
+ ],
+ "sales_attributes": [
+ {
+ "id": "123123123",
+ "name": "aaaa",
+ "value_id": "123123123",
+ "value_name": "black",
+ "sku_img": {
+ "height": 1080,
+ "width": 1080,
+ "thumb_url_list": [
+ "https://sadfasdfasdfasdfa",
+ "https://asdfasfasdfasdf"
+ ],
+ "id": "asdfasdfasdfasdf",
+ "url_list": [
+ "https://asdfasdfasdf?",
+ "https://asdfasdfasdfasdf"
+ ]
+ }
+ }
+ ]
+ }
+ ],
+ "product_certifications": [
+ {
+ "id": "123123123",
+ "title": "aaaaa",
+ "images": [
+ {
+ "height": 1080,
+ "width": 1080,
+ "thumb_url_list": [
+ "https://asdfasdfasdfasd",
+ "https://asdfasdfasdfasd"
+ ],
+ "id": "asdfasdfasdfasdfasdf",
+ "url_list": [
+ "https://afsdfasdfasdfasdf",
+ "https://asdfasdfasdfasdfsdaf"
+ ]
+ }
+ ],
+ "files": [
+ {
+ "id": "asdfadsfasdfasd",
+ "list": [
+ "http://asdfasdfasdf",
+ "http://asdfasdfasdfa"
+ ],
+ "name": "test1.pdf",
+ "type": "PDF"
+ },
+ {
+ "id": "asdfasdfasdfasdf",
+ "list": [
+ "http://asdfasdfasdfasdf",
+ "http://fasdfasdfasdfasdf"
+ ],
+ "name": "test2.pdf",
+ "type": "PDF"
+ }
+ ]
+ }
+ ],
+ "is_cod_open": false,
+ "update_time": 1111111111
+ }
+ }
+ },
+ "want": {
+ "product_id": "123123123",
+ "product_status": 4,
+ "product_name": "aaaa",
+ "category_list": [
+ {
+ "id": "123123",
+ "parent_id": "0",
+ "local_display_name": "aaaa",
+ "is_leaf": false
+ },
+ {
+ "id": "123123123",
+ "parent_id": "1111",
+ "local_display_name": "bbb",
+ "is_leaf": false
+ },
+ {
+ "id": "123123123",
+ "parent_id": "2222",
+ "local_display_name": "cccc",
+ "is_leaf": false
+ },
+ {
+ "id": "123123123",
+ "parent_id": "3333",
+ "local_display_name": "ddddd",
+ "is_leaf": true
+ }
+ ],
+ "brand": {
+ "id": "123123123",
+ "name": "aaa",
+ "status": 1
+ },
+ "images": [
+ {
+ "height": 600,
+ "width": 600,
+ "thumb_url_list": [
+ "https://sadfasdfasdfasdfasdf",
+ "https://sadfasdfasdfadsf"
+ ],
+ "id": "asdfasdfasdf",
+ "url_list": [
+ "https://asdfasdfasdf",
+ "https://afsdfasdfasdf"
+ ]
+ },
+ {
+ "height": 720,
+ "width": 720,
+ "thumb_url_list": [
+ "https://asdfasdfasdfasdfas",
+ "https://asdfasfasdfasfasdf"
+ ],
+ "id": "afsdfasdfasdf",
+ "url_list": [
+ "https://asdfasdfasdfas",
+ "https://fasdfasdfasdf"
+ ]
+ },
+ {
+ "height": 1080,
+ "width": 1080,
+ "thumb_url_list": [
+ "https://asdfasdfasdf",
+ "https://asdfasdfasdf"
+ ],
+ "id": "asdfasdfasdf",
+ "url_list": [
+ "https://asdfasfdasdfasf",
+ "https://asdfasdfasdfsadf"
+ ]
+ }
+ ],
+ "description": "aaaa",
+ "warranty_period": {
+ "warranty_id": 2,
+ "warranty_description": "2 months"
+ },
+ "warranty_policy": "warranty policy test",
+ "package_length": 30,
+ "package_width": 20,
+ "package_height": 30,
+ "package_weight": "5",
+ "skus": [
+ {
+ "id": "123123123",
+ "seller_sku": "aaaa",
+ "price": {
+ "original_price": "1200",
+ "currency": "IDR"
+ },
+ "stock_infos": [
+ {
+ "warehouse_id": "123123123",
+ "available_stock": 160
+ }
+ ],
+ "sales_attributes": [
+ {
+ "id": "123123123",
+ "name": "aaa",
+ "value_id": "123123123",
+ "value_name": "red",
+ "sku_img": {
+ "height": 600,
+ "width": 600,
+ "thumb_url_list": [
+ "https://asdfasdfasdfasdfa",
+ "https://asdfasdfasdfasdf"
+ ],
+ "id": "asdfasdfasdfasdf",
+ "url_list": [
+ "https://asdfasdfasdfasf",
+ "https://asdfasdfasdfasdf"
+ ]
+ }
+ }
+ ]
+ },
+ {
+ "id": "123123123",
+ "seller_sku": "bbbb",
+ "price": {
+ "original_price": "1600",
+ "currency": "IDR"
+ },
+ "stock_infos": [
+ {
+ "warehouse_id": "123123123",
+ "available_stock": 3000
+ }
+ ],
+ "sales_attributes": [
+ {
+ "id": "123123123",
+ "name": "aaaa",
+ "value_id": "123123123",
+ "value_name": "black",
+ "sku_img": {
+ "height": 1080,
+ "width": 1080,
+ "thumb_url_list": [
+ "https://sadfasdfasdfasdfa",
+ "https://asdfasfasdfasdf"
+ ],
+ "id": "asdfasdfasdfasdf",
+ "url_list": [
+ "https://asdfasdfasdf?",
+ "https://asdfasdfasdfasdf"
+ ]
+ }
+ }
+ ]
+ }
+ ],
+ "product_certifications": [
+ {
+ "id": "123123123",
+ "title": "aaaaa",
+ "images": [
+ {
+ "height": 1080,
+ "width": 1080,
+ "thumb_url_list": [
+ "https://asdfasdfasdfasd",
+ "https://asdfasdfasdfasd"
+ ],
+ "id": "asdfasdfasdfasdfasdf",
+ "url_list": [
+ "https://afsdfasdfasdfasdf",
+ "https://asdfasdfasdfasdfsdaf"
+ ]
+ }
+ ],
+ "files": [
+ {
+ "id": "asdfadsfasdfasd",
+ "list": [
+ "http://asdfasdfasdf",
+ "http://asdfasdfasdfa"
+ ],
+ "name": "test1.pdf",
+ "type": "PDF"
+ },
+ {
+ "id": "asdfasdfasdfasdf",
+ "list": [
+ "http://asdfasdfasdfasdf",
+ "http://fasdfasdfasdfasdf"
+ ],
+ "name": "test2.pdf",
+ "type": "PDF"
+ }
+ ]
+ }
+ ],
+ "is_cod_open": false,
+ "update_time": 1111111111
+ },
+ "want_err": false
+ }
+]
\ No newline at end of file
diff --git a/testdata/product/get_product_list.json b/testdata/product/get_product_list.json
new file mode 100644
index 0000000..1853a50
--- /dev/null
+++ b/testdata/product/get_product_list.json
@@ -0,0 +1,204 @@
+[
+ {
+ "name": "Official example",
+ "args": {
+ "app_key": "12abcd",
+ "app_secret": "123",
+ "access_token": "abc1c123-3128-aa17-125e-2d2d51ab814fa",
+ "shop_id": "123456",
+ "request_json": {
+ "page_size": 50,
+ "page_number": 1,
+ "search_status": 0,
+ "seller_sku_list": [
+ "aaaa"
+ ]
+ }
+ },
+ "request": {
+ "method": "POST",
+ "url": "https://open-api.tiktokglobalshop.com/api/products/search",
+ "headers": {},
+ "query": "access_token=abc1c123-3128-aa17-125e-2d2d51ab814fa&app_key=12abcd&shop_id=123456&sign=daefe57b6b9447f2beb621e7277632e9f14c06537a96fed5444f194e61b86e17×tamp=1600000000",
+ "body": {
+ "page_size": 50,
+ "page_number": 1,
+ "search_status": 0,
+ "seller_sku_list": [
+ "aaaa"
+ ]
+ }
+ },
+ "response": {
+ "status": 200,
+ "body": {
+ "code": 0,
+ "message": "Success",
+ "request_id": "123123123",
+ "data": {
+ "total": 15677,
+ "products": [
+ {
+ "id": "123123123",
+ "name": "aaa",
+ "status": 4,
+ "sale_regions": [
+ "ID"
+ ],
+ "skus": [
+ {
+ "id": "123123123123",
+ "seller_sku": "bbbb",
+ "price": {
+ "original_price": "1200",
+ "currency": "IDR"
+ },
+ "stock_infos": [
+ {
+ "warehouse_id": "123123123123123",
+ "available_stock": 161
+ }
+ ]
+ },
+ {
+ "id": "12312312312",
+ "seller_sku": "bbbb",
+ "price": {
+ "original_price": "1600",
+ "currency": "IDR"
+ },
+ "stock_infos": [
+ {
+ "warehouse_id": "123123123123",
+ "available_stock": 200
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "id": "1231231231",
+ "name": "aaa",
+ "status": 3,
+ "sale_regions": [
+ "ID"
+ ],
+ "skus": [
+ {
+ "id": "123123123123",
+ "seller_sku": "bbbb",
+ "price": {
+ "original_price": "1000",
+ "currency": "IDR"
+ },
+ "stock_infos": [
+ {
+ "warehouse_id": "123123123123",
+ "available_stock": 150
+ }
+ ]
+ },
+ {
+ "id": "12312312312312",
+ "seller_sku": "bbbb",
+ "price": {
+ "original_price": "1500",
+ "currency": "IDR"
+ },
+ "stock_infos": [
+ {
+ "warehouse_id": "123123123123",
+ "available_stock": 3000
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ }
+ },
+ "want": {
+ "total": 15677,
+ "products": [
+ {
+ "id": "123123123",
+ "name": "aaa",
+ "status": 4,
+ "sale_regions": [
+ "ID"
+ ],
+ "skus": [
+ {
+ "id": "123123123123",
+ "seller_sku": "bbbb",
+ "price": {
+ "original_price": "1200",
+ "currency": "IDR"
+ },
+ "stock_infos": [
+ {
+ "warehouse_id": "123123123123123",
+ "available_stock": 161
+ }
+ ]
+ },
+ {
+ "id": "12312312312",
+ "seller_sku": "bbbb",
+ "price": {
+ "original_price": "1600",
+ "currency": "IDR"
+ },
+ "stock_infos": [
+ {
+ "warehouse_id": "123123123123",
+ "available_stock": 200
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "id": "1231231231",
+ "name": "aaa",
+ "status": 3,
+ "sale_regions": [
+ "ID"
+ ],
+ "skus": [
+ {
+ "id": "123123123123",
+ "seller_sku": "bbbb",
+ "price": {
+ "original_price": "1000",
+ "currency": "IDR"
+ },
+ "stock_infos": [
+ {
+ "warehouse_id": "123123123123",
+ "available_stock": 150
+ }
+ ]
+ },
+ {
+ "id": "12312312312312",
+ "seller_sku": "bbbb",
+ "price": {
+ "original_price": "1500",
+ "currency": "IDR"
+ },
+ "stock_infos": [
+ {
+ "warehouse_id": "123123123123",
+ "available_stock": 3000
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ "want_err": false
+ }
+]
\ No newline at end of file
diff --git a/testdata/product/recover_product.json b/testdata/product/recover_product.json
new file mode 100644
index 0000000..b2f502d
--- /dev/null
+++ b/testdata/product/recover_product.json
@@ -0,0 +1,46 @@
+[
+ {
+ "name": "Official example",
+ "args": {
+ "app_key": "12abcd",
+ "app_secret": "123",
+ "access_token": "abc1c123-3128-aa17-125e-2d2d51ab814fa",
+ "shop_id": "123456",
+ "product_id": [
+ "1729385838007780943",
+ "1729382606675281112"
+ ]
+ },
+ "request": {
+ "method": "POST",
+ "url": "https://open-api.tiktokglobalshop.com/api/products/recover",
+ "headers": {},
+ "query": "access_token=abc1c123-3128-aa17-125e-2d2d51ab814fa&app_key=12abcd&sign=f8c08b8f3e42be12251435fa9d9e137000b957fbe8be3cb6e6b83bb2225e2bc9×tamp=1600000000",
+ "body": {
+ "product_ids": [
+ "1729385838007780943",
+ "1729382606675281112"
+ ]
+ }
+ },
+ "response": {
+ "status": 200,
+ "body": {
+ "code": 0,
+ "message": "Success",
+ "request_id": "123123123",
+ "data": {
+ "failed_product_ids": [
+ "1729385838007780943"
+ ]
+ }
+ }
+ },
+ "want": {
+ "failed_product_ids": [
+ "1729385838007780943"
+ ]
+ },
+ "want_err": false
+ }
+]
\ No newline at end of file
diff --git a/testdata/product/update_price.json b/testdata/product/update_price.json
new file mode 100644
index 0000000..17d9b7a
--- /dev/null
+++ b/testdata/product/update_price.json
@@ -0,0 +1,59 @@
+[
+ {
+ "name": "Official example",
+ "args": {
+ "app_key": "12abcd",
+ "app_secret": "123",
+ "access_token": "abc1c123-3128-aa17-125e-2d2d51ab814fa",
+ "shop_id": "123456",
+ "request_json": {
+ "product_id": "123123123",
+ "skus": [
+ {
+ "id": "123123123",
+ "original_price": "1200"
+ },
+ {
+ "id": "12312312312",
+ "original_price": "1600"
+ }
+ ]
+ }
+ },
+ "request": {
+ "method": "PUT",
+ "url": "https://open-api.tiktokglobalshop.com/api/products/prices",
+ "headers": {},
+ "query": "access_token=abc1c123-3128-aa17-125e-2d2d51ab814fa&app_key=12abcd&shop_id=123456&sign=2ef96993a61f2efd39ff041bd0be3a09bd669c271ccd2076445da196b0ced32c×tamp=1600000000",
+ "body": {
+ "product_id": "123123123",
+ "skus": [
+ {
+ "id": "123123123",
+ "original_price": "1200"
+ },
+ {
+ "id": "12312312312",
+ "original_price": "1600"
+ }
+ ]
+ }
+ },
+ "response": {
+ "status": 200,
+ "body": {
+ "code": 0,
+ "message": "Success",
+ "request_id": "123123123",
+ "data": {
+ "code": 0,
+ "message": "Success",
+ "request_id": "111123123123",
+ "data": []
+ }
+ }
+ },
+ "want": {},
+ "want_err": false
+ }
+]
\ No newline at end of file
diff --git a/testdata/product/update_stock.json b/testdata/product/update_stock.json
new file mode 100644
index 0000000..39149dc
--- /dev/null
+++ b/testdata/product/update_stock.json
@@ -0,0 +1,74 @@
+[
+ {
+ "name": "Official example",
+ "args": {
+ "app_key": "12abcd",
+ "app_secret": "123",
+ "access_token": "abc1c123-3128-aa17-125e-2d2d51ab814fa",
+ "shop_id": "123456",
+ "request_json": {
+ "product_id": "123123123",
+ "skus": [
+ {
+ "id": "123123123",
+ "stock_infos": [
+ {
+ "available_stock": 50,
+ "warehouse_id": "123123123123"
+ }
+ ]
+ },
+ {
+ "id": "123123123",
+ "stock_infos": [
+ {
+ "available_stock": 60,
+ "warehouse_id": "123123123"
+ }
+ ]
+ }
+ ]
+ }
+ },
+ "request": {
+ "method": "PUT",
+ "url": "https://open-api.tiktokglobalshop.com/api/products/stocks",
+ "headers": {},
+ "query": "access_token=abc1c123-3128-aa17-125e-2d2d51ab814fa&app_key=12abcd&shop_id=123456&sign=44673d90649d50f4eab6d76d2cc172e5f672e5d6ceb3cf088d8a1d1d2602cb82×tamp=1600000000",
+ "body": {
+ "product_id": "123123123",
+ "skus": [
+ {
+ "id": "123123123",
+ "stock_infos": [
+ {
+ "available_stock": 50,
+ "warehouse_id": "123123123123"
+ }
+ ]
+ },
+ {
+ "id": "123123123",
+ "stock_infos": [
+ {
+ "available_stock": 60,
+ "warehouse_id": "123123123"
+ }
+ ]
+ }
+ ]
+ }
+ },
+ "response": {
+ "status": 200,
+ "body": {
+ "code": 0,
+ "message": "Success",
+ "request_id": "12312312312",
+ "data": {}
+ }
+ },
+ "want": {},
+ "want_err": false
+ }
+]
\ No newline at end of file