腾讯珊瑚接入流程和开发指引

接入流程+开发工作量+开发接口


签名

<p>将请求参数和appSecret封装成Map集合,将Map集合的参数值(value)拼装成字符串进行MD5签名。排序顺序{requestBody}{appSecret}{timestamp},参数之间用分号;隔开。其中loginkey,appid不参与签名。</p> <p><strong>注意:</strong> 1、时间戳是毫秒单位;需要在15分钟内 2、requestBody按<strong>字典顺序</strong>排列。 3、如果参数中有对象,将对象的值转换成JSON字符串再进行签名</p> <h3>GET请求实例</h3> <p>请求地址:<code>https://gmall.m.qq.com/getDemo1</code> appid: <code>1</code> secret: <code>testsecret</code> 时间戳:<code>1678862436802</code> 请求参数:</p> <table> <thead> <tr> <th style="text-align: left;">key</th> <th style="text-align: left;">value</th> </tr> </thead> <tbody> <tr> <td style="text-align: left;">foo</td> <td style="text-align: left;">bar</td> </tr> <tr> <td style="text-align: left;">url</td> <td style="text-align: left;"><a href="https://www.qq.com">https://www.qq.com</a></td> </tr> <tr> <td style="text-align: left;">a</td> <td style="text-align: left;">b</td> </tr> </tbody> </table> <p>签名字符串为:<code>a=b;foo=bar;url=https%3A%2F%2Fwww.qq.com;testsecret1678862493257</code> 签名后sign为:<code>bfabdf358273ac9fbcb3383b927304c1</code> 实际请求地址为:<code>https://gmall.m.qq.com/getDemo1?app_id=1&amp;amp;sign=bfabdf358273ac9fbcb3383b927304c1&amp;amp;timestamp=1678862493257&amp;amp;foo=bar&amp;amp;url=https%3A%2F%2Fwww.qq.com&amp;amp;a=b</code></p> <h3>POST请求实例</h3> <p>请求地址:<code>https://gmall.m.qq.com/postDemo</code> appid: <code>1</code> secret: <code>testsecret</code> 时间戳:<code>1678863346070</code> 请求参数详情可见curl 实际请求的curl为</p> <pre><code class="language-shell">curl --location --request POST 'https://testgmall.m.qq.com/postDemo?appid=1&amp;amp;sign=9b6ad75f4cf0dfc42fb3e19e1dec9ecf&amp;amp;timestamp=1678863346070&amp;amp;' \ --header 'Content-Type: application/json' \ --data-raw '{ &amp;quot;url&amp;quot;: &amp;quot;https://www.qq.com&amp;quot;, &amp;quot;timestamp&amp;quot;: 1678863346070, &amp;quot;number&amp;quot;: 1, &amp;quot;foo&amp;quot;: &amp;quot;bar&amp;quot;, &amp;quot;object&amp;quot;: { &amp;quot;foo&amp;quot;: &amp;quot;bar&amp;quot;, &amp;quot;bar&amp;quot;: &amp;quot;foo&amp;quot;, &amp;quot;url&amp;quot;: &amp;quot;https://www.qq.com&amp;quot; } }'</code></pre> <p>签名字符串为:<code>foo=bar;number=1;object={&amp;quot;foo&amp;quot;:&amp;quot;bar&amp;quot;,&amp;quot;bar&amp;quot;:&amp;quot;foo&amp;quot;,&amp;quot;url&amp;quot;:&amp;quot;https://www.qq.com&amp;quot;};url=https%3A%2F%2Fwww.qq.com;testsecret1678863346070</code></p> <p>go语言实现</p> <pre><code class="language-javascript">package main import ( &amp;quot;bytes&amp;quot; &amp;quot;crypto/md5&amp;quot; &amp;quot;encoding/hex&amp;quot; &amp;quot;encoding/json&amp;quot; &amp;quot;fmt&amp;quot; &amp;quot;io/ioutil&amp;quot; &amp;quot;net/http&amp;quot; &amp;quot;net/url&amp;quot; &amp;quot;reflect&amp;quot; &amp;quot;sort&amp;quot; &amp;quot;strconv&amp;quot; &amp;quot;strings&amp;quot; &amp;quot;time&amp;quot; ) // 生成MD5签名字符串 // params: 请求参数map[string]string // appSecret: 开发者的appSecret // 返回值:string类型的签名字符串 func generateSignature(params map[string]string, appSecret string) string { // 将请求参数和appSecret封装成Map集合 params[&amp;quot;appSecret&amp;quot;] = appSecret // 按照字典序排序 keys := make([]string, 0, len(params)) for k := range params { keys = append(keys, k) } sort.Strings(keys) // 将Map集合的参数值(value)拼装成字符串进行MD5签名 var sign strings.Builder for _, k := range keys { if k == &amp;quot;loginkey&amp;quot; || k == &amp;quot;token&amp;quot; || k == &amp;quot;appid&amp;quot; { continue } sign.WriteString(k) sign.WriteByte('=') sign.WriteString(params[k]) sign.WriteByte(';') } sign.WriteString(appSecret) sign.WriteString(params[&amp;quot;timestamp&amp;quot;]) // 对签名字符串进行MD5加密 h := md5.New() h.Write([]byte(sign.String())) signature := hex.EncodeToString(h.Sum(nil)) return signature } // 发送GET请求 // url: 请求的URL地址 // params: 请求参数map[string]string // appID: 开发者的appID // appSecret: 开发者的appSecret // 返回值:[]byte类型的响应结果和error类型的错误 func sendGetRequest(urlString string, params map[string]string, appID string, appSecret string) ([]byte, error) { // 生成签名参数 params[&amp;quot;appId&amp;quot;] = appID params[&amp;quot;timestamp&amp;quot;] = strconv.FormatInt(time.Now().UnixNano()/1e6, 10) signature := generateSignature(params, appSecret) // 构建请求URL reqURL, err := url.ParseRequestURI(urlString) if err != nil { return nil, err } q := reqURL.Query() q.Set(&amp;quot;appid&amp;quot;, appID) q.Set(&amp;quot;sign&amp;quot;, signature) q.Set(&amp;quot;_timestamp&amp;quot;, params[&amp;quot;timestamp&amp;quot;]) for k, v := range params { q.Set(k, v) } reqURL.RawQuery = q.Encode() // 发送请求 resp, err := http.Get(reqURL.String()) if err != nil { return nil, err } defer resp.Body.Close() // 读取响应结果 body, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, err } return body, nil } // 发送POST请求 // url: 请求的URL地址 // params: 请求参数map[string]interface{} // appID: 开发者的appID // appSecret: 开发者的appSecret // 返回值:[]byte类型的响应结果和error类型的错误 func sendPostRequest(urlString string, params map[string]interface{}, appID string, appSecret string) ([]byte, error) { // 生成签名参数 timestamp := strconv.FormatInt(time.Now().UnixNano()/1e6, 10) signature := generateSignatureForPost(params, appSecret, timestamp) // 构建请求URL reqURL, err := url.ParseRequestURI(urlString) if err != nil { return nil, err } q := reqURL.Query() q.Set(&amp;quot;appid&amp;quot;, appID) q.Set(&amp;quot;sign&amp;quot;, signature) q.Set(&amp;quot;timestamp&amp;quot;, timestamp) reqURL.RawQuery = q.Encode() // 构建请求体 requestBody, err := json.Marshal(params) if err != nil { return nil, err } // 发送请求 resp, err := http.Post(reqURL.String(), &amp;quot;application/json&amp;quot;, bytes.NewReader(requestBody)) if err != nil { return nil, err } defer resp.Body.Close() // 读取响应结果 body, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, err } return body, nil } // 生成POST请求的MD5签名字符串 // params: 请求参数map[string]interface{} // appSecret: 开发者的appSecret // timestamp: 时间戳 // 返回值:string类型的签名字符串 func generateSignatureForPost(params map[string]interface{}, appSecret string, timestamp string) string { // 将请求参数和appSecret封装成Map集合 // 对参数中的对象进行JSON序列化 for k, v := range params { if reflect.TypeOf(v).Kind() == reflect.Map || reflect.TypeOf(v).Kind() == reflect.Slice { jsonBytes, _ := json.Marshal(v) params[k] = string(jsonBytes) } } // 按照字典序排序 keys := make([]string, 0, len(params)) for k := range params { keys = append(keys, k) } sort.Strings(keys) // 将Map集合的参数值(value)拼装成字符串进行MD5签名 var sign strings.Builder for _, k := range keys { if k == &amp;quot;loginkey&amp;quot; || k == &amp;quot;appid&amp;quot; { continue } sign.WriteString(k) sign.WriteByte('=') sign.WriteString(fmt.Sprintf(&amp;quot;%v&amp;quot;, params[k])) sign.WriteByte(';') } sign.WriteString(appSecret) sign.WriteString(timestamp) // 对签名字符串进行MD5加密 h := md5.New() h.Write([]byte(sign.String())) signature := hex.EncodeToString(h.Sum(nil)) return signature } func main() { appid := &amp;quot;appid&amp;quot; secret := &amp;quot;secret&amp;quot; body, err := sendPostRequest(&amp;quot;https://testgmall.m.qq.com/ext/stock2/consumeStock&amp;quot;, map[string]interface{}{ &amp;quot;account_id&amp;quot;: &amp;quot;testaccount&amp;quot;, &amp;quot;cid&amp;quot;: 5705, &amp;quot;order_id&amp;quot;: &amp;quot;test-12341&amp;quot;, &amp;quot;device_id&amp;quot;: &amp;quot;&amp;quot;, }, appid, secret) if err != nil { fmt.Printf(&amp;quot;Sign: %s\n&amp;quot;, err) } fmt.Printf(&amp;quot;Post Result: %s\n&amp;quot;, string(body)) getBody, err := sendGetRequest(&amp;quot;https://testgmall.m.qq.com/ext/mall/getMallItemData&amp;quot;, map[string]string{ &amp;quot;account_id&amp;quot;: &amp;quot;testaccount&amp;quot;, &amp;quot;mid&amp;quot;: &amp;quot;5705&amp;quot;, &amp;quot;device_id&amp;quot;: &amp;quot;&amp;quot;, }, appid, secret) if err != nil { fmt.Printf(&amp;quot;Sign: %s\n&amp;quot;, err) } fmt.Printf(&amp;quot;Get Result: %s\n&amp;quot;, string(getBody)) } </code></pre>

页面列表

ITEM_HTML