رفتن به محتوا

امضای درخواست

کتابخانه‌ی رومو این کار را خودش انجام می‌دهد. این صفحه برای وقتی است که می‌خواهید خودتان امضا بسازید یا خطای 401 را پیدا کنید.

هر درخواست با کلید خصوصی خودتان امضا می‌شود.

هدر مقدار
X-Custody-Key-Id شناسه‌ی کلید، UUID با حروف کوچک
X-Custody-Timestamp زمان فعلی به ثانیه‌ی یونیکس، عدد دهدهی بی صفر اول
X-Custody-Nonce رشته‌ی تصادفی یک‌بارمصرف، 16 تا 64 نویسه از A-Z a-z 0-9 _ -
X-Custody-Signature امضای Ed25519 با base64url بی =

رشته‌ی امضا

بخش «رشته‌ی امضا»

نه خط، به این ترتیب، با \n به هم وصل می‌شوند. پس از خط نهم \n نمی‌آید.

custody.api.request.v1
<key_id>
<timestamp>
<nonce>
<METHOD>
<path>
<query>
<sha256 of body, lower-case hex>
<Idempotency-Key, or empty>
خط قانون
METHOD با حروف بزرگ، مثل GET یا POST
path مسیر، دقیقاً همان که در درخواست می‌رود، مثل /v1/transactions
query هر چه بعد از ? می‌آید، بایت به بایت همان که می‌فرستید. اگر query ندارید، خط خالی
بدنه sha256 همان بایت‌هایی که می‌فرستید. بدنه‌ی خالی هم هش دارد: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
Idempotency-Key اگر نیست، خط آخر خالی است و رشته به \n ختم می‌شود
  • پنجره‌ی زمانی: زمان درخواست باید در 5 دقیقه‌ی زمان سرور باشد، پیش یا پس. ساعت سرور را با NTP هماهنگ نگه دارید.
  • nonce: برای هر درخواست تازه و تصادفی باشد، مثلاً 18 بایت تصادفی به base64url. nonce تکراری یک کلید رد می‌شود.
  • همه‌ی خطاهای احراز هویت یک پاسخ دارند: 401 با {"error":"unauthenticated"}⁠. این شامل امضای غلط، کلید ناشناخته یا باطل یا منقضی، IP غیرمجاز، زمان بیرون از پنجره و nonce تکراری است. دو استثنا: بدنه‌ی بزرگ‌تر از حد، پس از درست بودن امضا، 413 می‌گیرد؛ و اگر رومو موقتاً نتواند کلید یا nonce را بسنجد 503 می‌گیرید و دوباره می‌فرستید.
  • بدنه: فقط JSON، حداکثر 16 KiB. فیلد ناشناخته 400 می‌گیرد.
  • TLS: فقط TLS 1.3.

بردار آزمون

پیش از وصل شدن، پیاده‌سازی خودتان را با این بردارها بسنجید. کد این سایت با همین بردارها آزموده می‌شود. کلید، کلید آزمون RFC 8032 (بردار 1) است و هرگز به رومو کار نمی‌کند.

private seed (hex) 9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60
public key (hex) d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a
key_id 01926e60-0000-7000-8000-000000000001
timestamp 1790726400
nonce n0nce-AAAAAAAAAAAAAA_z

GET /v1/whoami⁠، بی query و بی بدنه

بخش «GET /v1/whoami⁠، بی query و بی بدنه»

خط هفتم (query) خالی است. خط نهم هم خالی است، پس رشته پس از هش یک \n دارد.

custody.api.request.v1
01926e60-0000-7000-8000-000000000001
1790726400
n0nce-AAAAAAAAAAAAAA_z
GET
/v1/whoami
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
signature CL-8PYt7avP809S0E91_W16zCkf7aQ2kAnSCogANpyDePJPw67Td5EPe_Nam83cAEi6DwGfM531-iVQk_B2LDQ

POST /v1/transactions با Idempotency-Key

بخش «POST /v1/transactions با Idempotency-Key»

بدنه دقیقاً {"amount":"1"} است و کلید withdrawal-1⁠.

custody.api.request.v1
01926e60-0000-7000-8000-000000000001
1790726400
n0nce-AAAAAAAAAAAAAA_z
POST
/v1/transactions
cdb8de78f10f4776de95629e660ff8a5aba9ab22166d96ce73e50d0e3acfc6dc
withdrawal-1
signature 8N9H7CbO38qFuBimj2bNCpNo2jb9DhfnA24TswZGO03eq6tXfghS9kQEQa6vv88lZnCrI0yW2K5iTg25uG_6Aw

کلاینت آماده

این کدها با بردارهای بالا آزموده شده‌اند و نمونه‌های مرجع API از همین‌ها استفاده می‌کنند. هر چهار تا این متغیرها را می‌خوانند: ROMU_KEY_ID⁠، ROMU_KEY_FILE (پیش‌فرض romu-api-key.pem⁠) و ROMU_BASE_URL (پیش‌فرض نشانی sandbox است؛ برای تولید https://api.romu.io را بگذارید). فایل‌ها: romu.ts، romu.go، romu.py و romu-curl.sh.

romu-curl.sh
#!/usr/bin/env bash
# One signed request to Romu with curl and OpenSSL 3.
# Usage: ./romu-curl.sh METHOD PATH[?QUERY] [BODY] [IDEMPOTENCY_KEY]
set -euo pipefail
: "${ROMU_KEY_ID:?set ROMU_KEY_ID to your key_id}"
key_file=${ROMU_KEY_FILE:-romu-api-key.pem}
base_url=${ROMU_BASE_URL:-"https://api.romu.ir"}
method=$1
target=$2
body=${3:-}
idempotency_key=${4:-}
path=${target%%\?*}
query=""
if [[ $target == *\?* ]]; then query=${target#*\?}; fi
timestamp=$(date +%s)
nonce=$(openssl rand -base64 18 | tr '+/' '-_' | tr -d '=')
body_hash=$(printf '%s' "$body" | openssl dgst -sha256 -r | cut -d' ' -f1)
# Nine lines joined with \n, no newline after the last one.
message=$(mktemp)
trap 'rm -f "$message"' EXIT
printf 'custody.api.request.v1\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s' \
"$ROMU_KEY_ID" "$timestamp" "$nonce" "$method" "$path" "$query" "$body_hash" "$idempotency_key" >"$message"
signature=$(openssl pkeyutl -sign -inkey "$key_file" -rawin -in "$message" |
openssl base64 -A | tr '+/' '-_' | tr -d '=')
args=(-sS -X "$method" "$base_url$target"
-H "X-Custody-Key-Id: $ROMU_KEY_ID"
-H "X-Custody-Timestamp: $timestamp"
-H "X-Custody-Nonce: $nonce"
-H "X-Custody-Signature: $signature")
if [[ -n $idempotency_key ]]; then args+=(-H "Idempotency-Key: $idempotency_key"); fi
if [[ -n $body ]]; then args+=(-H "Content-Type: application/json" --data-raw "$body"); fi
curl "${args[@]}"

در Go کلاینت را یک بار می‌سازید:

client, err := romu.NewClient("https://api.romu.ir", os.Getenv("ROMU_KEY_ID"), "romu-api-key.pem")

اگر کلید لو رفت

بخش «اگر کلید لو رفت»

کلید را در کنسول باطل کنید: «باطل کردن» و بعد «بله، باطل کنید». این کار فوری و برای همیشه است و تأیید نمی‌خواهد. بعد کلید تازه بسازید.