Files
order_site/docs/发货平台-2/1-鉴权.md
T
2026-07-07 08:51:23 +08:00

232 lines
7.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
鉴权
每个请求在 Header 携带:
Header 说明
X-App-Key App Key
X-Timestamp Unix 秒级时间戳(字符串),与服务器相差不超过 5 分钟
X-Sign 签名(见下)
#签名算法
code
待签名字符串 = app_key + timestamp + raw_json_body
sign = HMAC_SHA256(待签名字符串, app_secret) // 小写十六进制
要点 说明
raw_json_body 与 HTTP Body 完全一致 的原始字符串;签名后再原样发送
是否排序 不需要对 JSON 字段排序;勿解析后再 json_encode 一次
Header app_key、timestamp 只放在 Header,不参与 Body JSON
中文 PHP 使用 JSON_UNESCAPED_UNICODEPython 使用 ensure_ascii=False
商户后台 开放 API → 调用调试 需先通过密码或动态口令验证,方可加载密钥并查看待签名字符串、签名结果与最终发包参数。
#PHP
php
<?php
$appKey = 'your_app_key';
$appSecret = 'your_app_secret';
$apiBase = 'http://skin-exchange.yiquyou.icu';
$payload = [
'platform_order_no' => 'YOUR-ORDER-001',
'product_code' => '10000001',
'platform_buy_num' => 1,
];
// 生成 body 后不要再改,签名与 curl 必须用同一字符串
$body = json_encode($payload, JSON_UNESCAPED_UNICODE);
$timestamp = (string) time();
$preSign = $appKey . $timestamp . $body;
$sign = hash_hmac('sha256', $preSign, $appSecret);
$ch = curl_init($apiBase . '/api/open/v1/orders/store');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $body,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'X-App-Key: ' . $appKey,
'X-Timestamp: ' . $timestamp,
'X-Sign: ' . $sign,
],
CURLOPT_RETURNTRANSFER => true,
]);
$response = curl_exec($ch);
curl_close($ch);
#Java
依赖 JDK 标准库(javax.crypto),无需第三方包。
java
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
public class OpenApiSignExample {
private static String hmacSha256Hex(String secret, String message) throws Exception {
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
byte[] hash = mac.doFinal(message.getBytes(StandardCharsets.UTF_8));
StringBuilder hex = new StringBuilder(hash.length * 2);
for (byte b : hash) {
hex.append(String.format("%02x", b));
}
return hex.toString();
}
public static void main(String[] args) throws Exception {
String appKey = "your_app_key";
String appSecret = "your_app_secret";
String apiBase = "http://skin-exchange.yiquyou.icu";
// 与最终请求体一致;可用 Gson/Jackson 生成,但不要签名后再格式化
String body = "{\"platform_order_no\":\"YOUR-ORDER-001\",\"product_code\":\"10000001\",\"platform_buy_num\":1}";
String timestamp = String.valueOf(Instant.now().getEpochSecond());
String preSign = appKey + timestamp + body;
String sign = hmacSha256Hex(appSecret, preSign);
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(apiBase + "/api/open/v1/orders/store"))
.header("Content-Type", "application/json")
.header("X-App-Key", appKey)
.header("X-Timestamp", timestamp)
.header("X-Sign", sign)
.POST(HttpRequest.BodyPublishers.ofString(body))
.build();
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.statusCode());
System.out.println(response.body());
}
}
#Python
Python 3hmac + hashlib 标准库。
python
import hashlib
import hmac
import json
import time
import urllib.request
APP_KEY = "your_app_key"
APP_SECRET = "your_app_secret"
API_BASE = "http://skin-exchange.yiquyou.icu"
payload = {
"platform_order_no": "YOUR-ORDER-001",
"product_code": "10000001",
"platform_buy_num": 1,
}
# separators 生成紧凑 JSON,与常见 PHP json_encode 一致;签名后勿再 dumps
body = json.dumps(payload, ensure_ascii=False, separators=(",", ":"))
timestamp = str(int(time.time()))
pre_sign = APP_KEY + timestamp + body
sign = hmac.new(
APP_SECRET.encode("utf-8"),
pre_sign.encode("utf-8"),
hashlib.sha256,
).hexdigest()
req = urllib.request.Request(
API_BASE + "/api/open/v1/orders/store",
data=body.encode("utf-8"),
method="POST",
headers={
"Content-Type": "application/json",
"X-App-Key": APP_KEY,
"X-Timestamp": timestamp,
"X-Sign": sign,
},
)
with urllib.request.urlopen(req) as resp:
print(resp.status)
print(resp.read().decode())
使用 requests 时:
python
import requests
resp = requests.post(
API_BASE + "/api/open/v1/orders/store",
data=body.encode("utf-8"), # 传 bytes,避免库再次序列化
headers={
"Content-Type": "application/json",
"X-App-Key": APP_KEY,
"X-Timestamp": timestamp,
"X-Sign": sign,
},
timeout=30,
)
print(resp.status_code, resp.text)
#Go
go
package main
import (
"bytes"
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"net/http"
"time"
)
func sign(appKey, appSecret, body string) (timestamp, sign string) {
timestamp = fmt.Sprintf("%d", time.Now().Unix())
mac := hmac.New(sha256.New, []byte(appSecret))
mac.Write([]byte(appKey + timestamp + body))
sign = hex.EncodeToString(mac.Sum(nil))
return
}
func main() {
appKey := "your_app_key"
appSecret := "your_app_secret"
apiBase := "http://skin-exchange.yiquyou.icu"
// 建议用 struct + json.Marshal;保证签名用的 body 与 Post 发送的字节一致
type orderReq struct {
PlatformOrderNo string `json:"platform_order_no"`
ProductCode string `json:"product_code"`
PlatformBuyNum int `json:"platform_buy_num"`
}
bodyBytes, _ := json.Marshal(orderReq{
PlatformOrderNo: "YOUR-ORDER-001",
ProductCode: "10000001",
PlatformBuyNum: 1,
})
body := string(bodyBytes)
timestamp, sig := sign(appKey, appSecret, body)
req, _ := http.NewRequest(http.MethodPost, apiBase+"/api/open/v1/orders/store", bytes.NewReader(bodyBytes))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-App-Key", appKey)
req.Header.Set("X-Timestamp", timestamp)
req.Header.Set("X-Sign", sig)
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(resp.Status, string(out))
}
#常见错误
现象 原因
签名校验失败 Body 与签名时不一致(多空格、字段顺序、Unicode 转义不同)
timestamp 无效或已过期 服务器时间差超过 5 分钟
缺少 app_key、timestamp 或 sign Header 名称或大小写错误(应为 X-App-Key 等)
排查:用商户后台 调用调试 对比「待签名字符串」与己方代码输出是否一致。