如何用PHP生成TikTok Partner API调用所需签名及问题排查
如何使用PHP生成TikTok Partner API调用所需的签名?
TikTok API的签名实现非常棘手,除了官方文档(示例采用Golang编写)外,几乎找不到相关资料。我已写出两行PHP加密代码,但对签名步骤5中「若请求头content_type不是multipart/form-data,则在末尾追加body」的描述感到困惑——我要发起的是GET请求,所谓的body指什么?这可能是我的签名始终被API判定为无效的原因。
后续我通过Postman中的API示例成功完成调用,现附上官方提供的可用Javascript、Golang签名代码,以及我的无效PHP代码,请求帮忙排查问题所在。
PHP基础加密代码
$hmac = hash_hmac('sha256', $input, $secretKey, true); $urlSafeEncoded = urlencode(rtrim(strtr(base64_encode($hmac), '+/', '-_'), '='));
可用Javascript签名代码
// Replace secret with your own var secret = "c4bb0b4d6ad7407cb53e6bc038a3074b15fc53b0" function objKeySort(obj) { var newKey = Object.keys(obj).sort() var newObj = {} for (var i = 0; i < newKey.length; i++) { newObj[newKey[i]] = obj[newKey[i]] } return newObj } function getEnvVar(k) { var v = pm.variables.get(k) if (v != null) { return v } v = pm.environment.get(k) if (v != null) { return v } v = pm.globals.get(k) if (v != null) { return v } return null } var ts = Date.parse(new Date()) / 1000 pm.variables.set("timestamp", ts) calSign = function(secret) { var ts = getEnvVar("timestamp") var queryParam = pm.request.url.query.members var param = {} for (var item in queryParam) { if (queryParam[item].key == "timestamp") { v = ts } else { var v = queryParam[item].value if (v == null || v == "{{" + queryParam[item].key + "}}") { v = getEnvVar(queryParam[item].key) } } param[queryParam[item].key] = v } delete param["sign"]; delete param["access_token"] var sortedObj = objKeySort(param) var signstring = secret + pm.request.url.getPath() for (var key in sortedObj) { signstring = signstring + key + sortedObj[key] } signstring = signstring + secret sign = CryptoJS.HmacSHA256(signstring, secret).toString() return sign } var sign = calSign(secret) pm.variables.set("sign", sign)
可用Golang签名代码
import ( "crypto/hmac" "crypto/sha256" "encoding/hex" "sort" ) /** ** path: API path, for example /api/orders ** queries: Extract all query param EXCEPT 'sign','access_token',query param,not body ** secret: App secter **/ func generateSHA256(path string, queries map[string]string, secret string) string{ //Reorder the params based on alphabetical order. keys := make([]string, len(queries)) idx := 0 for k, _ := range queries{ keys[idx] = k idx++ } sort.Slice(keys, func(i, j int) bool { return keys[i] < keys[j] }) //Concat all the param in the format of {key}{value} and append the request path to the beginning input := path for _, key := range keys{ input = input + key + queries[key] } //Wrap string generated in up with app_secret. input = secret + input + secret //Encode the digest byte stream in hexadecimal and use sha256 to generate sign with salt(secret) h := hmac.New(sha256.New, []byte(secret)) // error log if _, err := h.Write([]byte(input)); err != nil{ // todo: log error return "" } return hex.EncodeToString(h.Sum(nil)) }
无效PHP签名代码
public function encrypt_sign($vars) { $qstring = ''; ksort($vars['qvars']); if (count($vars['qvars']) > 0) { foreach ($vars['qvars'] as $k=>$v) { if ($k != 'sign') { $qstring .= $k.$v; } } } //add body of request here!?! what body, its a get request! //$qstring = $qstring.{request body}; $qstring = $this->tiktok_app_secret . $qstring . $this->tiktok_app_secret; $sign = $this->encryptForUrl($qstring, $this->tiktok_app_secret); return $sign; } public function encryptForUrl($input, $secretKey) { $hmac = hash_hmac('sha256', $input, $secretKey, true); $urlSafeEncoded = urlencode(rtrim(strtr(base64_encode($hmac), '+/', '-_'), '=')); return $urlSafeEncoded; }
问题排查与修复方案
核心问题点
- 遗漏API路径拼接:官方示例中签名字符串开头必须拼接API路径(如
/api/orders),你的PHP代码直接从查询参数开始拼接,缺少了这一关键部分。 - 未排除
access_token参数:官方规则明确要求排除sign和access_token两个参数,你的代码只排除了sign。 - 签名输出格式不匹配:官方最终输出的是十六进制编码的HMAC结果,你的代码做了Base64 URL安全编码,格式不符。
- GET请求的body疑问:GET请求无请求体,步骤5的规则对GET请求不生效,这部分你的理解正确,不是问题根源。
修复后的PHP签名代码
public function encrypt_sign($apiPath, $vars) { $qstring = ''; // 过滤掉sign和access_token参数 $filteredVars = array_filter($vars['qvars'], function($key) { return $key !== 'sign' && $key !== 'access_token'; }, ARRAY_FILTER_USE_KEY); ksort($filteredVars); if (count($filteredVars) > 0) { foreach ($filteredVars as $k => $v) { $qstring .= $k . $v; } } // 按照官方规则拼接签名原始字符串:secret + path + 排序后的参数 + secret $signRaw = $this->tiktok_app_secret . $apiPath . $qstring . $this->tiktok_app_secret; // 生成十六进制格式的HMAC-SHA256签名 $sign = hash_hmac('sha256', $signRaw, $this->tiktok_app_secret); return $sign; }
使用说明
- 调用时需传入完整的API路径(如
/api/orders),不能只传查询参数。 - 确保查询参数中的
timestamp为当前有效时间戳,且与请求中携带的timestamp完全一致。 - 生成的签名直接作为
sign参数传入请求即可,无需额外URL编码。
内容的提问来源于stack exchange,提问作者cguWEB
相关产品推荐
相关产品推荐

