图片展示

成 都 狮 龙 书 廊 科 技 有 限 责 任 公 司

Chengdu Shilong Pearson Education technology  Limited  Liability  Technology Group Co., Ltd.

头条号网站验证文件

好啊,"搜 "

 

好啊,"搜 "

 

客服电话:13904310313   

 

公司总机:028-67876373              

 

钉钉客服:17684321066                   

 

备案电话:15680712313 

 

商标注册服务电话:15210354365  

 

抖音开发接入指南狮龙书廊实例

狮龙书廊 青云科技

抖音开发接入指南狮龙书廊实例

100

产品参数

  • 标准设计 1
  • 产品外观 1
  • 文本模块 1
  • 文件标准 1
展开全部 收起

产品详情

API调用指南

更新时间:2022-09-13 18:26:50访问次数:91228

前言

抖店开放平台的API是基于HTTP协议来调用的。

一、调用流程

组装参数 > 生成签名 > 拼装HTTP请求 > 发起HTTP请求 > 获得HTTP响应 > 解析json结果

二、调用地址

抖店开放平台目前只提供正式环境给开发者,API调用地址为如下,请访问时拼接对应方法URL:

https://openapi-fxg.jinritemai.com

三、调用方式

POST方式

注:由于历史原因,GET方式目前依旧支持,但后续可能会弃用

四、公共参数

调用任何一个API都必须传入的参数,目前支持的公共参数有:

参数名称参数类型是否必须示例值参数描述
methodstringproduct.getGoodsCategory调用的API接口名称
app_keystring3409409348479354011应用创建完成后被分配的key
access_tokenstringc6f957da-1239-4343-84a1-c84e68915ff7用于调用 API 的 access_token
param_jsonstring{"cid":"12","page":"1"}标准JSON类型
timestampstring2020-09-15 14:48:13时间戳,格式为yyyy-MM-dd HH:mm:ss,时区为GMT+8,例如:2016-01-01 12:00:00,和服务器时间相差超过10分钟会报错
vstring2API协议版本,当前版本为2
signstring签名算法参照下面的介绍输入参数签名结果
sign_methodStringhmac-sha256签名算法类型(不传默认为md5算法签名)
1. hmac-sha256,采用hmac_sha_256算法签名(推荐);
2. md5,采用md5算法签名。

param_json字段应当放在POST body中传输,形式为「Content-Type: application/json」。里面是业务参数,按照Key的字典序排序,嵌套JSON也需要按Key排序。
剩余字段(包括method、app_key、access_token、timestamp、v、sign、sign_method)依旧采用url query方式传递。

参考curl命令

curl --location --request POST 'https://openapi-fxg.jinritemai.com/product/getGoodsCategory?method=product.getGoodsCategory&app_key=123456780×tamp=2011-06-16%2013:23:30&v=2&sign=ab3387e5&access_token=xxxxxxxx%0A' \ 
--header 'Content-Type: application/json' \ 
--data-raw '{"cid":"12","page":"1"}

注:timestamp也支持「2006-01-02 15:04:05」这个格式,但不推荐;sign_method也支持md5,但后续可能被弃用。

五、业务参数

API调用除了必须包含公共参数外,如果API本身有业务级的参数也必须传入。
每个API的业务参数请参考 抖店开放平台API文档

六、签名算法

为了防止API调用过程中被黑客恶意篡改,调用任何一个API都需要携带签名;服务端会根据请求参数,对签名进行验证,签名不合法的请求将会被拒绝。
目前支持的签名算法为hmac-sha256(推荐)、md5两种。

下面以PHP为例,详细解析一下签名算法:
STEP1:序列化参数
将paramJson参数序列化成JSON格式,并满足下面几个要求:

  • 保证JSON所有层级上Key的有序性
// 序列化前的对象 
$a1 = array( 
"c" => 3, 
"b" => 2, 
"a" => array( 
 "c" => 3, 
 "b" => 2, 
 "a" => 1, 
), 
); 
// 序列化后的结果 
// {"a":{"a":1,"b":2,"c":3},"b":2,"c":3}
  • 保证JSON的所有数值不带多余的小数点
// 序列化前的对象
$a2 = array(
"a" => 1.0,
);
// 序列化后的结果
// {"a":1}
  • 禁用Html Escape,保证转义逻辑与示例代码一致
$a3 = array(
'a' => "&<>='/ô汉????",// 附加符号、中日韩、Emoji都不转义
);
// 序列化后的结果
// {"a":"&<>='/ô汉????"}

STEP2:计算签名

  • 拼接请求参数

以app_key、method、param_json、timestamp、v这个顺序,把以上参数的键值对依次拼接在一起

$paramPattern = 'app_key'.$appKey.'method'.$method.'param_json'.$paramJson.'timestamp'.$timestamp.'v'.$v; 
// 拼接后的格式差不多是这样: 
// app_key***method***param_json***timestamp***v*
  • 在上一步基础上,在头尾拼接app_secret
$signPattern = $appSecret.$paramPattern.$appSecret;
  • 使用hmac-sha256算法,计算签名串
$sign = hash_hmac("sha256", $signPattern, $appSecret);

七、返回错误码列表

平台基础错误码汇总

八、注意事项

所有的请求和响应数据编码皆为utf-8格式

九、示例代码

Java调用说明
PHP调用说明
C#调用说明
Golang调用说明

JAVA调用说明

更新时间:2022-09-13 15:11:53访问次数:28238

概述

本文提供Java调用抖店开放平台API的示例代码给开发者参考。 另外,Java SDK当前已经在内测中,如有需要参与内测可提交工单联系抖店开放平台的工作人员。

环境说明

Java:openjdk 16.0.2
Gson:2.8.7
以上为示例代码的测试环境,供参考。

序列化参数

// 序列化参数// 这一步看上去冗余,实际很重要。如果要自己实现,则必须保证这三点:// 1、保证JSON所有层级上Key的有序性// 2、保证JSON的所有数值不带多余的小数点// 3、保证转义逻辑与这段代码一致public static String marshal(Object o) {
String raw = CustomGson.toJson(o);
Map m = CustomGson.fromJson(raw, LinkedTreeMap.class); // 执行反序列化,把所有JSON对象转换成LinkedTreeMap
return CustomGson.toJson(m); // 重新序列化,保证JSON所有层级上Key的有序性}private static final Gson CustomGson = new GsonBuilder()
.registerTypeAdapter(LinkedTreeMap.class, newMapSerializer()) // 定制LinkedTreeMap序列化,确保所有key按字典序排序
.registerTypeAdapter(Integer.class, newNumberSerializer()) // 定制数值类型的序列化,确保整数输出不带小数点
.registerTypeAdapter(Long.class, newNumberSerializer()) // 同上
.registerTypeAdapter(Double.class, newNumberSerializer()) // 同上
.disableHtmlEscaping() // 禁用Html Escape,确保符号不转义:&<>='
.create();
// 为LinkedTreeMap定制的序列化器public static JsonSerializer> newMapSerializer() {return new JsonSerializer<>() {@Override
public JsonElement serialize(LinkedTreeMap src, Type typeOfSrc, JsonSerializationContext context) {
List keys = src.keySet().stream().map(Object::toString).sorted().toList();
JsonObject o = new JsonObject();for (String k : keys) {
o.add(k, context.serialize(src.get(k)));
}return o;
}
};
}// 为Number定制化的序列化器private static  JsonSerializer newNumberSerializer() {return new JsonSerializer() {@Override
public JsonElement serialize(T number, Type type, JsonSerializationContext context) {if (number instanceof Integer) {return new JsonPrimitive(number.intValue());
}if (number instanceof Long) {return new JsonPrimitive(number.longValue());
}if (number instanceof Double) {long longValue = number.longValue();double doubleValue = number.doubleValue();if (longValue == doubleValue) {return new JsonPrimitive(longValue);
}
}return new JsonPrimitive(number);
}
};
}

计算签名

// 计算签名public static String sign(String appKey, String appSecret, String method, long timestamp, String paramJson) {// 按给定规则拼接参数
String paramPattern = "app_key" + appKey + "method" + method + "param_json" + paramJson + "timestamp" + timestamp + "v2";
String signPattern = appSecret + paramPattern + appSecret;
System.out.println("sign_pattern:" + signPattern);return hmac(signPattern, appSecret);
}// 计算hmacpublic static String hmac(String plainText, String appSecret) {
Mac mac;try {byte[] secret = appSecret.getBytes(StandardCharsets.UTF_8);
SecretKeySpec keySpec = new SecretKeySpec(secret, "HmacSHA256");

mac = Mac.getInstance("HmacSHA256");
mac.init(keySpec);
} catch (NoSuchAlgorithmException | InvalidKeyException e) {return "";
}byte[] plainBytes = plainText.getBytes(StandardCharsets.UTF_8);byte[] digest = mac.doFinal(plainBytes);
StringBuilder sb = new StringBuilder();for (byte b: digest) {
sb.append(String.format("%02x", b));
}return sb.toString();
}

发起请求

这里采用标准库的HttpURLConnection作为例子,建议开发者选用自己熟悉的库。

// 调用Open Api,取回数据public static String fetch(String appKey, String host, String method, long timestamp, String paramJson, String accessToken, String sign) throws IOException {
String methodPath = method.replace('.', '/');

String u = host + "/" + methodPath +"?method=" + URLEncoder.encode(method, StandardCharsets.UTF_8) +"&app_key=" + URLEncoder.encode(appKey, StandardCharsets.UTF_8) +"&access_token=" + URLEncoder.encode(accessToken, StandardCharsets.UTF_8) +"×tamp=" + URLEncoder.encode(Long.toString(timestamp), StandardCharsets.UTF_8) +"&v=" + URLEncoder.encode("2", StandardCharsets.UTF_8) +"&sign=" + URLEncoder.encode(sign, StandardCharsets.UTF_8) +"&sign_method=" + URLEncoder.encode("hmac-sha256", StandardCharsets.UTF_8);
						
URL url = new URL(u);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setUseCaches(false);
conn.setRequestMethod("POST");
conn.setRequestProperty("Accept", "*/*");
conn.setRequestProperty("Content-Type", "application/json;charset=UTF-8");

OutputStream os = conn.getOutputStream();
os.write(paramJson.getBytes(StandardCharsets.UTF_8));
os.flush();

InputStream is = conn.getInputStream();byte[] all = is.readAllBytes();return new String(all, StandardCharsets.UTF_8);
}

完整调用

// 下面是一个批量加密接口的示例public static void main(String[] args) throws IOException {// 收集参数
String appKey = "*"; //替换成你的app_key
String appSecret = "*"; // 替换成你的app_secret
String accessToken = "*"; // 替换成你的access_token
String host = "https://openapi-fxg.jinritemai.com";
String method = "order.batchEncrypt";long timestamp = System.currentTimeMillis() / 1000;

Map m2 = new HashMap<>();
m2.put("plain_text", "&<>='/ô汉????");//附加符号、中日韩、Emoji都不转义
m2.put("auth_id", "12345");
m2.put("is_support_index", false);
m2.put("sensitive_type", 2);

Map m = new HashMap<>();
m.put("batch_encrypt_list", new Object[]{m2});// 序列化参数
String paramJson = marshal(m);
System.out.println("param_json:" + paramJson);// 计算签名
String signVal = sign(appKey, appSecret, method, timestamp, paramJson);
System.out.println("sign_val:" + signVal);// 发起请求
String responseVal = fetch(appKey, host, method, timestamp, paramJson, accessToken, signVal);
System.out.println("response_val:" + responseVal);
}

Golang调用说明

更新时间:2022-09-13 16:51:11访问次数:5618

概述

本文提供Golang调用抖店开放平台API的示例代码给开发者作为参考。

环境说明

Go 1.16.5
以上为示例代码的测试环境,供参考。

序列化参数

// Marshal 序列化参数func Marshal(o interface{}) string {// 序列化一次
raw, _ := json.Marshal(o)// 反序列化为map
m := make(map[string]interface{})
reader := bytes.NewReader(raw)
decode := json.NewDecoder(reader)
decode.UseNumber()
_ = decode.Decode(&m)// 重新做一次序列化,并禁用Html Escape
buffer := bytes.NewBufferString("")
encoder := json.NewEncoder(buffer)
encoder.SetEscapeHTML(false)
_ = encoder.Encode(m)

marshal := strings.TrimSpace(buffer.String()) // Trim掉末尾的换行符
return marshal
}

计算签名

// Sign 计算签名func Sign(appKey, appSecret, method string, timestamp int64, paramJson string) string {// 按给定规则拼接参数
paramPattern := "app_key" + appKey + "method" + method + "param_json" + paramJson + "timestamp" + strconv.FormatInt(timestamp, 10) + "v2"
signPattern := appSecret + paramPattern + appSecret
fmt.Println("sign_pattern:" + signPattern)return Hmac(signPattern, appSecret)
}// Hmac 计算hmacfunc Hmac(s string, appSecret string) string {
h := hmac.New(sha256.New, []byte(appSecret))
_, _ = h.Write([]byte(s))return hex.EncodeToString(h.Sum(nil))
}

发起请求

// Fetch 调用Open Api,取回数据func Fetch(appKey, host, method string, timestamp int64, paramJson, accessToken, sign string) (string, error) {
methodPath := strings.Replace(method, ".", "/", -1)

params := url.Values{}
params.Add("method", method)
params.Add("app_key", appKey)
params.Add("access_token", accessToken)
params.Add("timestamp", strconv.FormatInt(timestamp, 10))
params.Add("v", "2")
params.Add("sign", sign)
params.Add("sign_method", "hmac-sha256")

u := host + "/" + methodPath + "?" + params.Encode()

reader := strings.NewReader(paramJson)
req, err := http.NewRequest(http.MethodPost, u, reader)if err != nil {return "", nil
}

req.Header.Set("Accept", "*/*")
req.Header.Set("Content-Type", "application/json;charset=UTF-8")

do, err := http.DefaultClient.Do(req)if err != nil {return "", err
}

all, err := ioutil.ReadAll(do.Body)if err != nil {return "", err
}return string(all), err
}

完整调用

func main() {// 收集参数
appKey := "*"// 替换成你的app_key
appSecret := "*" // 替换成你的app_secret
accessToken := "*" // 替换成你的access_token
host := "https://openapi-fxg.jinritemai.com"
method := "order.batchEncrypt"

timestamp := time.Now().Unix()

param := map[string]interface{}{"batch_encrypt_list": map[string]interface{}{"plain_text": "&<>='/ô汉????",//附加符号、中日韩、Emoji都不转义
"auth_id":"12345","is_support_index": false,"sensitive_type": 2,
},
}// 序列化参数
paramJson := Marshal(param)
fmt.Println("param_json:" + paramJson)// 计算签名
signVal := Sign(appKey, appSecret, method, timestamp, paramJson)
fmt.Println("sign_val:" + signVal)// 发起请求
responseVal, err := Fetch(appKey, host, method, timestamp, paramJson, accessToken, signVal)if err != nil {
fmt.Println("err:" + err.Error())
}
fmt.Println("response_val:" + responseVal)
}

C#调用说明

更新时间:2022-09-13 16:46:13访问次数:7792

概述

本文提供C#调用抖店开放平台API的示例代码给开发者作为参考。

环境说明

.NET 5.0.301
Newtonsoft.Json 13.0.1
以上为示例代码的测试环境,供参考。

序列化参数

// 序列化参数
// 这一步看上去冗余,实际很重要。如果要自己实现,则必须保证这三点:
// 1、保证JSON所有层级上Key的有序性
// 2、保证JSON的所有数值不带多余的小数点
// 3、保证转义逻辑与这段代码一致
public static string Marshal(object o)
{
var raw = JsonConvert.SerializeObject(o);

// 反序列化为JObject
var dict = JsonConvert.DeserializeObject(raw);

// 重新序列化
var settings = new JsonSerializerSettings();
settings.Converters = new List {new JObjectConverter(), new JValueConverter()};
return JsonConvert.SerializeObject(dict, Formatting.None, settings);
}

// 自定义JObject的序列化方法,确保对象的Key按字典序输出
class JObjectConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
{
var val = value! as JObject;
var props = val!.Properties().OrderBy(i => i.Name).ToList();
writer.WriteStartObject();
foreach (var p in props)
{
writer.WritePropertyName(p.Name);
serializer.Serialize(writer, p.Value);
}

writer.WriteEndObject();
}

public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue,
JsonSerializer serializer)
{
throw new NotImplementedException();
}

public override bool CanConvert(Type objectType)
{
return objectType == typeof(JObject);
}
}

// 自定义JValue的序列化方法,确保浮点数输出时移除小数点后多余的零
class JValueConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
{
var val = value! as JValue;
if (val!.Type == JTokenType.Float)
{
var d = Convert.ToDouble(val.Value);
var i = (long) d;
if (Math.Abs(i - d) == 0) // 针对float,如果小数点后的零是多余的,那么按整数方式输出
{
writer.WriteValue(i);
return;
}
}
writer.WriteValue(value); // 否则按原逻辑
}

public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}

public override bool CanConvert(Type objectType)
{
return objectType == typeof(JValue);
}
}

计算签名

// 计算签名
public static string Sign(string appKey, string appSecret, string method, long timestamp, string paramJson)
{
// 按给定规则拼接参数
var paramPattern = "app_key" + appKey + "method" + method + "param_json" + paramJson + "timestamp" +
 timestamp + "v2";
var signPattern = appSecret + paramPattern + appSecret;
Console.WriteLine("sign_pattern:" + signPattern);

return Hmac(signPattern, appSecret);
}

// 计算hmac
public static string Hmac(string plainText, string appSecret)
{
var h = new HMACSHA256(Encoding.UTF8.GetBytes(appSecret));
var sum = h.ComputeHash(Encoding.UTF8.GetBytes(plainText));

var sb = new StringBuilder();
foreach (byte b in sum)
{
sb.Append(b.ToString("x2"));
}
return sb.ToString();
}

发起请求

// 调用Open Api,取回数据
public static string Fetch(string appKey, string host, string method, long timestamp, string paramJson,
string accessToken, string sign)
{
var methodPath = method.Replace('.', '/');
var u = host + "/" + methodPath +
"?method=" + HttpUtility.UrlEncode(method, Encoding.UTF8) +
"&app_key=" + HttpUtility.UrlEncode(appKey, Encoding.UTF8) +
"&access_token=" + HttpUtility.UrlEncode(accessToken, Encoding.UTF8) +
"×tamp=" + HttpUtility.UrlEncode(timestamp.ToString(), Encoding.UTF8) +
"&v=" + HttpUtility.UrlEncode("2", Encoding.UTF8) +
"&sign=" + HttpUtility.UrlEncode(sign, Encoding.UTF8) +
"&sign_method=" + HttpUtility.UrlEncode("hmac-sha256", Encoding.UTF8);

var client = new HttpClient();

var req = new HttpRequestMessage(HttpMethod.Post, u);
req.Headers.Add("Accept", "*/*");

var content = new ByteArrayContent(Encoding.UTF8.GetBytes(paramJson));
content.Headers.Add("Content-Type", "application/json;charset=UTF-8");
req.Content = content;

var resp = client.Send(req);
using var reader = new StreamReader(resp.Content.ReadAsStream(), Encoding.UTF8);
return reader.ReadToEnd();
}

完整调用

static void Main(string[] args)
{
TestCase();
return;
// 收集参数
var appKey = "*"; //替换成你的app_key
var appSecret = "*"; // 替换成你的app_secret
var accessToken = "*"; // 替换成你的access_token
var host = "https://openapi-fxg.jinritemai.com";
var method = "order.batchEncrypt";

var timestamp = DateTimeOffset.Now.ToUnixTimeSeconds();

var m = new Dictionary
{
{
"batch_encrypt_list", new Dictionary
{
{"plain_text", "&<>='/ô汉????"},//附加符号、中日韩、Emoji都不转义
{"auth_id", "12345"},
{"is_support_index", false},
{"sensitive_type", 2.0},
}
}
};

// 序列化参数
var paramJson = Marshal(m);
Console.WriteLine("param_json:" + paramJson);

// 计算签名
var signVal = Sign(appKey, appSecret, method, timestamp, paramJson);
Console.WriteLine("sign_val:" + signVal);

// 发起请求
var responseVal = Fetch(appKey, host, method, timestamp, paramJson, accessToken, signVal);
Console.WriteLine("response_val:" + responseVal);
}

PHP调用说明

更新时间:2022-09-13 15:10:27访问次数:12337

概述

本文提供PHP调用抖店开放平台API的示例代码给开发者作为参考。

环境说明

PHP:8.0.8
以上为示例代码的测试环境,供参考。

序列化参数

// 序列化参数,入参必须为关联数组
function marshal(array $param): string {
rec_ksort($param); // 对关联数组中的kv,执行排序,需要递归
$s = json_encode($param, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); // 重新序列化,确保所有key按字典序排序
// 加入flag,确保斜杠不被escape,汉字不被escape
return $s;
}

// 关联数组排序,递归
function rec_ksort(array &$arr) {
$kstring = true;
foreach ($arr as $k => &$v) {
if (!is_string($k)) {
$kstring = false;
}
if (is_array($v)) {
rec_ksort($v);
}
}
if ($kstring) {
ksort($arr);
}
}

计算签名

// 计算签名
function sign(string $appKey, string $appSecret, string $method, int $timestamp, string $paramJson): string {
$paramPattern = 'app_key' . $appKey . 'method' . $method . 'param_json' . $paramJson . 'timestamp' . $timestamp . 'v2';
$signPattern = $appSecret . $paramPattern . $appSecret;

print('sign_pattern:' . $signPattern . "\n");
return hash_hmac("sha256", $signPattern, $appSecret);
}

发起请求

// 调用Open Api,取回数据
function fetch(string $appKey, string $host, string $method, int $timestamp, string $paramJson, string $accessToken, string $sign): string {
$methodPath = str_replace('.', '/', $method);
$url = $host . '/' . $methodPath .
'?method=' . urlencode($method) .
'&app_key=' . urlencode($appKey) .
'&access_token=' .urlencode($accessToken) .
'×tamp=' . urlencode(strval($timestamp)) .
'&v=' . urlencode('2') .
'&sign=' . urlencode($sign) .
'&sign_method=' . urlencode('hmac-sha256');
$opts = array('http' =>
array(
'method' => 'POST',
'header' => "Accept: */*\r\n" .
"Content-type: application/json;charset=UTF-8\r\n",
'content' => $paramJson
)
);
$context = stream_context_create($opts);
$result = file_get_contents($url, false, $context);
return $result;
}

完整调用

// 收集参数
$appKey = '*'; //替换成你的app_key
$appSecret = '*'; // 替换成你的app_secret
$accessToken = '*'; // 替换成你的access_token
$host = 'https://openapi-fxg.jinritemai.com';
$method = 'order.batchEncrypt';

$timestamp = time();

$m = array(
'batch_encrypt_list' => array(
0 => array(
'plain_text' => "&<>='/ô汉????",//附加符号、中日韩、Emoji都不转义
'auth_id' => '12345',
'is_support_index' => false,
'sensitive_type' => 2,
)
)
);

// 序列化参数
$paramJson = marshal($m);
print('param_json:' . $paramJson . "\n");

// 计算签名
$signVal = sign($appKey, $appSecret, $method, $timestamp, $paramJson);
print('sign_val:' . $signVal . "\n");

// 发起请求
$responseVal = fetch($appKey, $host, $method, $timestamp, $paramJson, $accessToken, $signVal);
print('response_val:' . $responseVal . "\n");

SPI网关服务接入指南

更新时间:2022-09-13 16:24:21访问次数:2949

一、查看场景和接口

对于ISV或者自研商家来说,只有官方同学添加相关场景的授权之后,开发者才可以在自己的控制台看到相关的场景及需要实现的接口及接口文档。

!

二、根据接口文档进行编码

2.1.接口实现大致流程

2.2.调用方式

GET/POST

2.3.通讯协议

HTTPS

2.4.验签(官方SDK已支持SPI验签-> 参考文档中心

为了防止API调用过程中被黑客恶意篡改,抖店开放平台在发起调用任何一个API都会携带签名。ISV服务端在接收到请求后需要对请求做验签,确保请求是来自抖店开放平台的而不是其他的非法请求。目前从抖店开放平台发出的请求均采用MD5进行加密。
说明:验签只是为了验证参数有没有被篡改,所以服务商在接收到抖店开放平台的请求后**一定要把验签放在第一步、一定要把验签放在第一步、一定要把验签放在第一步。**验签测试阶段,我们构造的测试参数并不一定完全符合业务逻辑,但是这不会影响验签。一定要拿着我们原始的请求去做签名计算,而不是先反序列化或者解析成你们自己定义的结构体后用你们自己的结构体去做签名计算,这样有可能导致签名失败。即便现在不失败,后面我们新增参数的话,你们的接口可能会出兼容问题。

获得sign的过程如下:(和API不一样哦)

1、将param_json中参数按照key字母先后顺序排序,组成json

  • 例如: param_json={"product_id":123,"code":"HHK"} 需要调整为param_json={"code":"HHK","product_id":"123"}
  • 特殊字符需要转义:"&"转成"\u0026";"<"转成"\u003c";">"转成"\u003e"
  • 使用gson的同学注意要进行disableHtmlEscaping
  • 使用python json.dumps的同学注意seperator不要让json字段间的逗号带空格

说明:如果是GET请求 param_json请从url中获取,如果是post请求,param_json的value为post请求的body
2、请求参数按照字母先后顺序排列

  • 将param_json,app_key,timestamp排序为app_key,param_json,timestamp 没有method和v哦 没有method和v哦 没有method和v哦

3、把参数名和参数值进行拼装

  • 例如:app_keyxxxparam_jsonxxxtimestampxxx

4、把app_secret分别拼接在上面得到的字符串的两端,假定app_secret的值为AppSecret

  • 例如:AppSecretXXXXAppSecret

5、上述步骤获得的待加密字符串,使用 MD5签名算法后得到sign,与请求中的sign进行比较,如果不一致则验签失败。
注:计算签名逻辑请使用下面的示例代码!!!不要自己实现

2.5.代码示例

SPISDK已经上线,推荐使用SDK对接,SDK说明文档:抖店开放平台SPI&消息网关SDK,使用SDK对接可跳过下面示例代码部分

Java示例代码

以接口“demo/spi”为例

public Object spiDemo() { 
DemoSpiRequest request =new DemoSpiRequest(); 
//param中的参数从服务端调用你们发布的地址上取 
//sign timestamp sign_method app_key sign_v2从URL参数取 
//paramJson分为两种情况,如果是get请求,从URL参数取,如果post请求从请求体里取 
//举个例子,发布的地址是http://www.abc.com 
//服务端实际调用时的请求地址是:http://www.abc.com?app_key=xxx&sign=xxx&sign_v2=xxx&sign_method=xxx×tamp=xxx¶m_json=xxx 
request.getParam().setSign( xxxx ); 
request.getParam().setTimestamp( xxxx ); 
request.getParam().setSignMethod( xxxx ); 
request.getParam().setAppKey( xxxx ); 
request.getParam().setParamJson( xxxx ); 
request.getParam().setSignV2( xxxx ); 
request.registerHandler(bizHandler); 
 
DemoSpiResponse response = request.execute(); 
//将response序列化成json返回 
return response; 
} 
 
private DoudianOpSpiBizHandler bizHandler = new DoudianOpSpiBizHandler() { 
@Override 
public void handle(DoudianOpSpiContext context) { 
 
// 获取入参对象 
DemoSpiParam param = context.getParam(); 
// 业务处理逻辑 
// ... 
// 设置data数据 
DemoSpiData data = context.getData(); 
// data.setXXX() 
// 返回成功 
context.wrapSuccess(); 
// 返回失败 
context.wrapError(100002L,系统错误 ); 
} 
};

Golang示例代码

import ( 
 "code.byted.org/middleware/hertz" 
 "crypto/md5" 
 "encoding/hex" 
 "encoding/json" 
 "io" 
 "sort" ) 
 
// TestSpi . // @router /api/spi/test [POST] func TestSpi(c *hertz.RequestContext) { 
 //1.从请求中解析入参 
 appKey := c.Query("app_key") 
 paramJson := c.Query("param_json") 
 timestamp := c.Query("timestamp") 
 sign := c.Query("sign") 
 
 //2.参数校验,此处省略 
 
 //3.验签 
 //3.1.解析业务参数 
 paramJsonInterfaceMap := make(map[string]interface{}) 
 err := json.Unmarshal([]byte(paramJson), ¶mJsonInterfaceMap) 
 if err != nil { 
c.JSON(200, &DemoResult{ 
 Code:100002, 
 Message: "参数解析失败", 
}) 
return 
 } 
 
 //3.2.把param_json的key按照自然顺序排序 
 mm := getJsonString(paramJsonInterfaceMap) 
 sortedParamJson, _ := json.Marshal(mm) 
 
 sortedString := "app_key" + appKey + "param_json" + string(sortedParamJson) + "timestamp" + timestamp 
 
 //3.3.拼接 appSecret 在头尾 
 appSecret := "63415a7a-de83-43ea-a522-cb616c47a4ef" 
 preSignString := appSecret + sortedString + appSecret 
 
 //3.4使用md5对参数加签 
 newSign := Md5(preSignString) 
 
 //3.5.将入参中的sign和加签后的结果作比较,如果不一致则验签失败。 
 if newSign != sign { 
c.JSON(200, &DemoResult{ 
 Code:100001, 
 Message: "验签失败", 
}) 
return 
 } 
 
 //4.业务处理,此处省略 
 
 //5.组装响应结果 
 c.JSON(200, &DemoResult{ 
Code:0, 
Message: "success", 
Data: &TestResult{ 
 Total: 100, 
 Test:false, 
 List: []*RefundOrder{ 
&RefundOrder{ 
 RefundId: "11111", 
 RefundReason: "七天无理由", 
}, 
 }, 
}, 
 }) 
} 
 
func Md5(s string) string { 
 h := md5.New() 
 _, _ = io.WriteString(h, s) 
 return hex.EncodeToString(h.Sum(nil)) 
} 
 
func getJsonString(param map[string]interface{}) interface{} { 
 sortedKeys := make([]string, 0) 
 for k := range param { 
sortedKeys = append(sortedKeys, k) 
 } 
 sort.Strings(sortedKeys) 
 
 sortedParamJsonMap := make(map[string]interface{}) 
 for _, sk := range sortedKeys { 
switch value := param[sk].(type) { 
case string: 
 sortedParamJsonMap[sk] = value 
case map[string]interface{}: 
 sortedParamJsonMap[sk] = getJsonString(value) 
case []interface{}: 
 if len(value) > 0 { 
if _, ok := value[0].(map[string]interface{}); ok { 
 var sliceResult = make([]interface{}, 0) 
 for _, interfaceValue := range value { 
switch mapInterfaceValue := interfaceValue.(type) { 
case map[string]interface{}: 
 sliceResult = append(sliceResult, getJsonString(mapInterfaceValue)) 
} 
 } 
 sortedParamJsonMap[sk] = sliceResult 
} else { 
 sortedParamJsonMap[sk] = value 
} 
 } else { 
sortedParamJsonMap[sk] = make([]interface{}, 0) 
 } 
default: 
 sortedParamJsonMap[sk] = value 
} 
 } 
 
 return sortedParamJsonMap 
} 
 
type DemoResult struct { 
 Codeint64 `json:"code"` 
 Message string`json:"message"` 
 Data*TestResult `json:"data"` 
} 
 
type TestResult struct { 
 Total int64`json:"total"` 
 Testbool `json:"test"` 
 List[]*RefundOrder `json:"list"` 
} 
 
type RefundOrder struct { 
 RefundId string `json:"refund_id"` 
 RefundReason string `json:"refund_reason"` 
} import ( 
 "code.byted.org/middleware/hertz" 
 "crypto/md5" 
 "encoding/hex" 
 "encoding/json" 
 "io" 
 "sort" ) 
 
// TestSpi . // @router /api/spi/test [POST] func TestSpi(c *hertz.RequestContext) { 
 //1.从请求中解析入参 
 appKey := c.Query("app_key") 
 paramJson := c.Query("param_json") 
 timestamp := c.Query("timestamp") 
 sign := c.Query("sign") 
 
 //2.参数校验,此处省略 
 
 //3.验签 
 //3.1.解析业务参数 
 paramJsonInterfaceMap := make(map[string]interface{}) 
 err := json.Unmarshal([]byte(paramJson), ¶mJsonInterfaceMap) 
 if err != nil { 
c.JSON(200, &DemoResult{ 
 Code:100002, 
 Message: "参数解析失败", 
}) 
return 
 } 
 
 //3.2.把param_json的key按照自然顺序排序 
 mm := getJsonString(paramJsonInterfaceMap) 
 sortedParamJson, _ := json.Marshal(mm) 
 
 sortedString := "app_key" + appKey + "param_json" + string(sortedParamJson) + "timestamp" + timestamp 
 
 //3.3.拼接 appSecret 在头尾 
 appSecret := "63415a7a-de83-43ea-a522-cb616c47a4ef" 
 preSignString := appSecret + sortedString + appSecret 
 
 //3.4使用md5对参数加签 
 newSign := Md5(preSignString) 
 
 //3.5.将入参中的sign和加签后的结果作比较,如果不一致则验签失败。 
 if newSign != sign { 
c.JSON(200, &DemoResult{ 
 Code:100001, 
 Message: "验签失败", 
}) 
return 
 } 
 
 //4.业务处理,此处省略 
 
 //5.组装响应结果 
 c.JSON(200, &DemoResult{ 
Code:0, 
Message: "success", 
Data: &TestResult{ 
 Total: 100, 
 Test:false, 
 List: []*RefundOrder{ 
&RefundOrder{ 
 RefundId: "11111", 
 RefundReason: "七天无理由", 
}, 
 }, 
}, 
 }) 
} 
 
func Md5(s string) string { 
 h := md5.New() 
 _, _ = io.WriteString(h, s) 
 return hex.EncodeToString(h.Sum(nil)) 
} 
 
func getJsonString(param map[string]interface{}) interface{} { 
 sortedKeys := make([]string, 0) 
 for k := range param { 
sortedKeys = append(sortedKeys, k) 
 } 
 sort.Strings(sortedKeys) 
 
 sortedParamJsonMap := make(map[string]interface{}) 
 for _, sk := range sortedKeys { 
switch value := param[sk].(type) { 
case string: 
 sortedParamJsonMap[sk] = value 
case map[string]interface{}: 
 sortedParamJsonMap[sk] = getJsonString(value) 
case []interface{}: 
 if len(value) > 0 { 
if _, ok := value[0].(map[string]interface{}); ok { 
 var sliceResult = make([]interface{}, 0) 
 for _, interfaceValue := range value { 
switch mapInterfaceValue := interfaceValue.(type) { 
case map[string]interface{}: 
 sliceResult = append(sliceResult, getJsonString(mapInterfaceValue)) 
} 
 } 
 sortedParamJsonMap[sk] = sliceResult 
} else { 
 sortedParamJsonMap[sk] = value 
} 
 } else { 
sortedParamJsonMap[sk] = make([]interface{}, 0) 
 } 
default: 
 sortedParamJsonMap[sk] = value 
} 
 } 
 
 return sortedParamJsonMap 
} 
 
type DemoResult struct { 
 Codeint64 `json:"code"` 
 Message string`json:"message"` 
 Data*TestResult `json:"data"` 
} 
 
type TestResult struct { 
 Total int64`json:"total"` 
 Testbool `json:"test"` 
 List[]*RefundOrder `json:"list"` 
} 
 
type RefundOrder struct { 
 RefundId string `json:"refund_id"` 
 RefundReason string `json:"refund_reason"` 
}

2.6.调用示例

127.0.0.1:6789/shop/user/register?app_key=6900812651828348424¶m_json=%7B%22order_id%22%3A%221234%22%2C%22page%22%3A10%2C%22size%22%3A11%7D&sign=6c4447b0bf1898d38f78ab80f7d86e46×tamp=2021-06-01+21%3A49%3A17

2.7.响应示例

2.7.1.成功响应示例

{ 
"code": 0, 
"message": "success", 
"data": { 
"total": 100, 
"test": false, 
"list": [ 
{ 
"refund_id": "11111", 
"refund_reason": "七天无理由" 
} 
]

2.7.2.失败响应示例

{ 
"code": 100001, 
"message": "验签失败", 
"data": null }

2.8.错误码

错误码说明
0业务处理成功
100001验签失败
100002参数错误
100003系统错误

更多业务错误码请参考各个SPI接口的接口文档说明。

三、接口自测

ISV在完成代码研发后,可以在抖店开发者控制台发起接口自测,自测通过之后可以将服务端url去发布。

四、服务端地址发布

接口自测通过后可以将服务端地址发布到对应的接口上。接口发布成功之后字节内部的业务就可以发起相关的调用啦。


五、修改服务端url

六、补充说明

为了方便排查问题,抖店开放平台在向服务商的地址发起请求时会把平台的logId放到请求的hedaer中,建议开发者在接受到请求后,除了打印请求入参外把logId也打印或者记录下来。



抖音开发接入指南狮龙书廊实例
抖音开发接入指南狮龙书廊实例
长按识别二维码查看详情
长按图片保存/分享
询盘

在线询盘 更多+
  • 联系人 *

  • 手机 *

  • 描述

  • 提交

  • 验证码
    看不清?换一张
    取消
    确定

咨询内容:


你还没有添加任何产品

图片展示
图片展示

———

 

合作企业

“狮龙书廊,很多家企业合作的选择。〞

成都狮龙书廊科技有限公司于2016年12月08日成立。法定代表人毛凌国,公司经营范围包括:网络工程开发;计算机领域的技术开发、转让、咨询、推广服务;网页设计制作;

图片展示

———

 

伙伴合作企业

“商中在线,很多家企业的选择。〞

云指网络科技有限公司于2017年08月04日成立。法定代表人朱芹芹,公司经营范围包括:网络工程开发;计算机领域的技术开发、转让、咨询、推广服务;网页设计制作;

图片展示
文件标题
文件大小
下载次数
更新时间

成都狮龙书廊科技有限责任公司,是一家互联网高科技技术研究开发企业,本公司不以销售产品为目的,以展示公司文化为主。登记注册在四川成都高新区,企业纳税人识别号:91510100MA62N9D027,公司经营主业以计算机软件代码和硬件技术设计,电子产品和提供技术服务;软件开发;网页设计;图文设计;美术图案设计;网络技术服务;和商务咨询,代理制作发布各类广告推广业务,企业简称狮龙书廊科技,艺画斋----国际商业美术师,毛凌国.中国 互联网应用展示平台,是当代科技前沿领域开展艺术引领创新的云空间,在这里,本着帮助现有科技企业,开发设计领域,学术指导领域,新的创新,发明实用新型,和外观设计的展示平台,有机会聚集青年一代设计师,艺术设计者,学者和文化传承人的学术应用展示,普及传统美学思想与中外文化白花映演,实现中国梦,凝聚中国民族团结的社会价值。

狮龍畫廊---品牌是邦企科技认证的品牌项目,(狮龙书廊)企业实名认证的信用,科技驱动人文艺术和文明,推动中国文化传承事业在一带一路建设中的积极作用,我们不仅仅是企业,也是国际商业美术事业发展的推动者,高科技应用的转化和开发前沿领跑者,是未来职业发展的标准贯彻执行推广实体,是追求目标的技术型推广基石。成都狮龙书廊科技有限责任公司,服务社会的类型是以企业模式经营,独立的法人负责制体系,对社会组织和个体包括公司,开展各类的服务项目,如网络技术服务,建站设计,代理域名,空间结构分布云技术,微周微信开发和技术产品推广,特别是国际高科技人才服务和认证服务领域的经验的分享服务。成都狮龙书廊科技有限责任公司入围中国数字产业企业诚信企业,狮龙书廊通过中国电子人证服务产业联盟认证,具有较高服务和任用等级。艺画斋企业服务的一个项目。 

国际商业美术设计师(International Commercial Art Designer英文缩写ICAD)职业资格认证是国际商业美术设计师协会(International Commercial Art Designer Association,英文缩写ICADA)在全球范围内推行的四级商业美术设计专业资质认证体系的总称。它是以国际职业标准为导向,以实际工作能力、经验、知识和艺术素养为考评依据的一种新型的职业资格证书制度。  

 

 
CONTACT US

点击链接加入我的企业“成都狮龙廊科技有限责任公司长春分公司”,一起开启全新办公体验吧。 https://work.weixin.qq.com/join/w-A6Tt1b7iLuLRUM_ChDDg/hb_share_qymng_mjoin?is=203

 

电话总机:028-67873673

分机:

 

 

地址:成都高新区府城大道399号7栋2单元14层1404号

成都狮龙书廊科技有限责任公司 毛凌国 业务电话:17684321066神马搜索推广品牌竞价排名

<--------阿里地图开发代码-将代码嵌入到论坛、博客、网站中:------>

 

公共邮箱:public@maolg.com

企业管理员:admin@maolg.com

网站管理:webmaster@maolg.com

企业邮箱:mail.maolg.com

 

 

 

中国区官网:  www.maolg.com

成都总公司:   www.maolg.net

狮龙书廊长春分公司:www.maolg.cn

毛凌国我爱你: www.毛凌国.我爱你

 

添加微信好友,详细了解产品
使用企业微信
“扫一扫”加入群聊
复制成功
添加微信好友,详细了解产品
我知道了