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 比較不是恆定時間。