Skip to content

cURL

适合快速验证鉴权链路或在 CI 里跑健康检查。

依赖

  • bash / zsh
  • openssl
  • awk

不要在 cURL 命令上构建生产业务

shell 转义复杂、错误处理弱。生产请用 Python / Node / Go 等。

列卡头(最简单的成功路径)

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.shchmod +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"
fi

shell 验签仅供调试

生产环境的 webhook 验签必须用编程语言实现(恒定时间比较 + 严格的 raw body 处理)。shell 比较不是恒定时间。

采用 MIT 等价条款发布