# 需要 jq 和 openssl
API_HOST="https://<api-host>" # 接入地址,见商户后台“API 配置”
API_SECRET="<your API secret>" # API 密钥,只保存在服务端
API_PATH="/api/v1/payins/query"
BODY='{"merchant_id":"10001","merchant_order_id":"ORDER-10001"}'
# 去掉 sign,按键名排序,生成紧凑 JSON
CANONICAL=$(printf '%s' "$BODY" | jq -cS 'del(.sign)')
SIGN=$(printf 'POST\n%s\n%s' "$API_PATH" "$CANONICAL" \
| openssl dgst -sha256 -hmac "$API_SECRET" | awk '{print $NF}')
curl -X POST "$API_HOST$API_PATH" \
-H "Content-Type: application/json" \
-d "$(printf '%s' "$CANONICAL" | jq -c --arg sign "$SIGN" '. + {sign: $sign}')"
import hashlib, hmac, json
from urllib.request import Request, urlopen
API_HOST = "https://<api-host>" # 接入地址,见商户后台“API 配置”
MERCHANT_ID = "10001" # 商户编号
API_SECRET = "<your API secret>" # API 密钥,只保存在服务端
def canonical(payload):
# 去掉 sign,按键名排序,生成紧凑 JSON
unsigned = {k: v for k, v in payload.items() if k != "sign"}
return json.dumps(unsigned, sort_keys=True, ensure_ascii=False,
separators=(",", ":"), allow_nan=False)
def call(path, fields):
payload = {"merchant_id": MERCHANT_ID, **fields}
message = "POST\n" + path + "\n" + canonical(payload)
payload["sign"] = hmac.new(API_SECRET.encode(), message.encode("utf-8"),
hashlib.sha256).hexdigest()
request = Request(API_HOST + path, method="POST",
data=json.dumps(payload, ensure_ascii=False).encode("utf-8"),
headers={"Content-Type": "application/json"})
with urlopen(request, timeout=15) as response:
return json.load(response)
print(call("/api/v1/payins/query", {"merchant_order_id": "ORDER-10001"}))
<?php
const API_HOST = 'https://<api-host>'; // 接入地址,见商户后台“API 配置”
const MERCHANT_ID = '10001'; // 商户编号
const API_SECRET = '<your API secret>'; // API 密钥,只保存在服务端
const JSON_FLAGS = JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES;
// 去掉 sign,按键名排序,生成紧凑 JSON
// 空对象请用 new stdClass(),不要用 []
function canonical($value): string {
if (is_array($value) && array_is_list($value)) {
return '[' . implode(',', array_map('canonical', $value)) . ']';
}
if (is_array($value) || is_object($value)) {
$fields = (array) $value;
ksort($fields, SORT_STRING);
$parts = [];
foreach ($fields as $key => $item) {
$parts[] = json_encode((string) $key, JSON_FLAGS) . ':' . canonical($item);
}
return '{' . implode(',', $parts) . '}';
}
return json_encode($value, JSON_FLAGS);
}
function call(string $path, array $fields): array {
$payload = ['merchant_id' => MERCHANT_ID] + $fields;
unset($payload['sign']);
$message = "POST\n" . $path . "\n" . canonical($payload);
$payload['sign'] = hash_hmac('sha256', $message, API_SECRET);
$ch = curl_init(API_HOST . $path);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
CURLOPT_POSTFIELDS => json_encode($payload, JSON_FLAGS),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 15,
]);
$response = curl_exec($ch);
curl_close($ch);
return json_decode($response, true);
}
print_r(call('/api/v1/payins/query', ['merchant_order_id' => 'ORDER-10001']));
// Java 17+,无第三方依赖
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.Duration;
import java.util.*;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
public class WinniePay {
static final String API_HOST = "https://<api-host>"; // 接入地址,见商户后台“API 配置”
static final String MERCHANT_ID = "10001"; // 商户编号
static final String API_SECRET = "<your API secret>"; // API 密钥,只保存在服务端
// 去掉 sign,按键名排序,生成紧凑 JSON
static String canonical(Object value) {
if (value instanceof Map<?, ?> map) {
StringJoiner out = new StringJoiner(",", "{", "}");
new TreeMap<>(map).forEach((k, v) -> out.add(quote((String) k) + ":" + canonical(v)));
return out.toString();
}
if (value instanceof List<?> list) {
StringJoiner out = new StringJoiner(",", "[", "]");
list.forEach(v -> out.add(canonical(v)));
return out.toString();
}
if (value instanceof String s) return quote(s);
return String.valueOf(value); // null, true/false, whole numbers
}
static String quote(String s) {
StringBuilder b = new StringBuilder("\"");
for (char c : s.toCharArray()) {
switch (c) {
case '"' -> b.append("\\\"");
case '\\' -> b.append("\\\\");
case '\n' -> b.append("\\n");
case '\r' -> b.append("\\r");
case '\t' -> b.append("\\t");
case '\b' -> b.append("\\b");
case '\f' -> b.append("\\f");
default -> b.append(c < 0x20 ? String.format("\\u%04x", (int) c) : String.valueOf(c));
}
}
return b.append('"').toString();
}
static String hmac(String secret, String message) throws Exception {
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
return HexFormat.of().formatHex(mac.doFinal(message.getBytes(StandardCharsets.UTF_8)));
}
static String call(String path, Map<String, Object> fields) throws Exception {
Map<String, Object> payload = new TreeMap<>(fields);
payload.put("merchant_id", MERCHANT_ID);
payload.remove("sign");
String body = canonical(payload);
String sign = hmac(API_SECRET, "POST\n" + path + "\n" + body);
String signed = body.substring(0, body.length() - 1) + ",\"sign\":" + quote(sign) + "}";
HttpRequest request = HttpRequest.newBuilder(URI.create(API_HOST + path))
.header("Content-Type", "application/json")
.timeout(Duration.ofSeconds(15))
.POST(HttpRequest.BodyPublishers.ofString(signed, StandardCharsets.UTF_8))
.build();
return HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString()).body();
}
public static void main(String[] args) throws Exception {
System.out.println(call("/api/v1/payins/query", Map.of("merchant_order_id", "ORDER-10001")));
}
}
// Node.js 18+
const crypto = require("crypto");
const API_HOST = "https://<api-host>"; // 接入地址,见商户后台“API 配置”
const MERCHANT_ID = "10001"; // 商户编号
const API_SECRET = "<your API secret>"; // API 密钥,只保存在服务端
// 去掉 sign,按键名排序,生成紧凑 JSON
function canonicalJson(value) {
if (Array.isArray(value)) return "[" + value.map(canonicalJson).join(",") + "]";
if (value !== null && typeof value === "object") {
return "{" + Object.keys(value).sort()
.map((k) => JSON.stringify(k) + ":" + canonicalJson(value[k])).join(",") + "}";
}
return JSON.stringify(value);
}
async function call(path, fields) {
const payload = { merchant_id: MERCHANT_ID, ...fields };
delete payload.sign;
const message = `POST\n${path}\n${canonicalJson(payload)}`;
payload.sign = crypto.createHmac("sha256", API_SECRET).update(message, "utf8").digest("hex");
const response = await fetch(API_HOST + path, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload),
});
return response.json();
}
call("/api/v1/payins/query", { merchant_order_id: "ORDER-10001" }).then(console.log);