cURL
适合快速验证鉴权链路或在 CI 里跑健康检查。
依赖
bash/zshopensslawk
列卡头(最简单的成功路径)
bash
APP_ID="cp_a1b2c3d4..."
SECRET="f1e2d3c4..."
BASE="https://api.coinepay.net"
PATH_VAL="/api/v1/openapi/card_headers/list"
BODY='{"page":1,"page_size":20}'
TS=$(date +%s)
NONCE=$(openssl rand -hex 16)
BODY_HASH=$(echo -n "$BODY" | openssl dgst -sha256 -hex | awk '{print $2}')
SIGN_INPUT=$(printf 'POST\n%s\n\n%s\n%s\n%s' "$PATH_VAL" "$TS" "$NONCE" "$BODY_HASH")
SIG=$(echo -n "$SIGN_INPUT" | openssl dgst -sha256 -hmac "$SECRET" -hex | awk '{print $2}')
curl -X POST "$BASE$PATH_VAL" \
-H "X-App-Id: $APP_ID" \
-H "X-Timestamp: $TS" \
-H "X-Nonce: $NONCE" \
-H "X-Signature: $SIG" \
-H "Content-Type: application/json" \
-d "$BODY"申请虚拟卡(带幂等键)
bash
PATH_VAL="/api/v1/openapi/card/apply"
BODY='{"header_id":"hdr_5","package_id":"pkg_12","first_name":"John","last_name":"Doe"}'
IDEMPOTENCY_KEY=$(uuidgen) # macOS/Linux 通用
TS=$(date +%s)
NONCE=$(openssl rand -hex 16)
BODY_HASH=$(echo -n "$BODY" | openssl dgst -sha256 -hex | awk '{print $2}')
SIGN_INPUT=$(printf 'POST\n%s\n\n%s\n%s\n%s' "$PATH_VAL" "$TS" "$NONCE" "$BODY_HASH")
SIG=$(echo -n "$SIGN_INPUT" | openssl dgst -sha256 -hmac "$SECRET" -hex | awk '{print $2}')
curl -X POST "$BASE$PATH_VAL" \
-H "X-App-Id: $APP_ID" \
-H "X-Timestamp: $TS" \
-H "X-Nonce: $NONCE" \
-H "X-Signature: $SIG" \
-H "Idempotency-Key: $IDEMPOTENCY_KEY" \
-H "Content-Type: application/json" \
-d "$BODY"通用签名脚本
把以下保存为 ~/sign.sh 后 chmod +x ~/sign.sh:
bash
#!/usr/bin/env bash
# 用法:sign.sh <method> <path> <body_json>
# 输出:4 行 header(X-App-Id / X-Timestamp / X-Nonce / X-Signature)
# 调用前需 export APP_ID 和 SECRET
set -euo pipefail
METHOD="${1:-POST}"
PATH_VAL="$2"
BODY="${3:-{}}"
: "${APP_ID:?APP_ID env var required}"
: "${SECRET:?SECRET env var required}"
TS=$(date +%s)
NONCE=$(openssl rand -hex 16)
BODY_HASH=$(echo -n "$BODY" | openssl dgst -sha256 -hex 2>/dev/null | awk '{print $2}')
SIGN_INPUT=$(printf '%s\n%s\n\n%s\n%s\n%s' "$METHOD" "$PATH_VAL" "$TS" "$NONCE" "$BODY_HASH")
SIG=$(echo -n "$SIGN_INPUT" | openssl dgst -sha256 -hmac "$SECRET" -hex 2>/dev/null | awk '{print $2}')
cat <<EOF
X-App-Id: $APP_ID
X-Timestamp: $TS
X-Nonce: $NONCE
X-Signature: $SIG
EOF使用:
bash
export APP_ID="cp_..."
export SECRET="f1e2d3c4..."
PATH_VAL="/api/v1/openapi/card_headers/list"
BODY='{"page":1,"page_size":20}'
# 拿到 4 行 header
HEADERS=$(~/sign.sh POST "$PATH_VAL" "$BODY")
# 转成 curl -H 参数(每行一个 -H)
CURL_HEADERS=$(echo "$HEADERS" | awk '{print "-H \"" $0 "\""}' | tr '\n' ' ')
eval curl -X POST "https://api.coinepay.net$PATH_VAL" \
$CURL_HEADERS \
-H \"Content-Type: application/json\" \
-d "'$BODY'"验签 webhook
bash
# 假设你抓到一个 webhook:
RAW_BODY='{"card_id":"card_12345","status":"opened","opened_at":"2026-04-29T11:00:12Z"}'
SIG_HEADER='v1,9b8e7f6d5c4b3a2918273645d4e3c2b1a0f9e8d7c6b5a4938271605f4e3d2c1b'
TS_HEADER='1714377612'
WEBHOOK_SECRET='your_webhook_secret_64_hex'
SIG="${SIG_HEADER#v1,}"
EXPECTED=$(printf '%s.%s' "$TS_HEADER" "$RAW_BODY" | \
openssl dgst -sha256 -hmac "$WEBHOOK_SECRET" -hex | awk '{print $2}')
if [ "$SIG" = "$EXPECTED" ]; then
echo "OK"
else
echo "FAIL"
fishell 验签仅供调试
生产环境的 webhook 验签必须用编程语言实现(恒定时间比较 + 严格的 raw body 处理)。shell 比较不是恒定时间。