7.0 KiB
7.0 KiB
鉴权 每个请求在 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_UNICODE;Python 使用 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 response = HttpClient.newHttpClient() .send(request, HttpResponse.BodyHandlers.ofString()); System.out.println(response.statusCode()); System.out.println(response.body()); } } #Python Python 3,hmac + 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 等) 排查:用商户后台 调用调试 对比「待签名字符串」与己方代码输出是否一致。