cURL
Best for verifying the auth chain quickly or running a CI healthcheck.
Dependencies
bash/zshopensslawk
Don't build production on cURL
Shell quoting gets ugly fast and error handling is weak. Use Python / Node / Go for production.
List Card Headers (simplest happy path)
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"Apply Virtual Card (with idempotency key)
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"Reusable Sign Script
Save the following as ~/sign.sh and chmod +x ~/sign.sh:
bash
#!/usr/bin/env bash
# Usage: sign.sh <method> <path> <body_json>
# Output: 4 lines (X-App-Id / X-Timestamp / X-Nonce / X-Signature)
# Prerequisite: export APP_ID and 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
EOFUsage:
bash
export APP_ID="cp_..."
export SECRET="f1e2d3c4..."
PATH_VAL="/api/v1/openapi/card_headers/list"
BODY='{"page":1,"page_size":20}'
# Get 4 header lines
HEADERS=$(~/sign.sh POST "$PATH_VAL" "$BODY")
# Convert to curl -H args
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'"Verifying a Webhook (debug only)
bash
# Suppose you captured a 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 verification is debug-only
Production webhook verification must be done in a programming language (constant-time compare + strict raw-body handling). Shell = is not constant time.