پرش به مطلب اصلی

تراکنش‌ها و ایدمپوتنسی

هر جابه‌جایی موجودی در زربان سازمانی یک «تراکنش» است. سه نوع دارد:

نوعیعنی چهدسترسی لازم
TransactionTypeCoinDepositواریز روی زنجیره به آدرس حسابCoinDepositScope
TransactionTypeCoinWithdrawalبرداشت روی زنجیره به آدرس بیرونیCoinWithdrawalScope
TransactionTypeInternalTransferانتقال میان دو حساب همان سازمانInternalTransferScope

انتقال داخلی روی زنجیره نمی‌رود: کارمزد شبکه ندارد و آنی است.

فراخوانی‌های این صفحه روی محیط عملیاتی اجرا می‌شوند و برداشت پس از خروج از صف قابل بازگشت نیست. گردش کار پیشنهادی برای اولین بار: simulate، سپس یک اجرای واقعی با کوچک‌ترین مبلغ ممکن.

الگوی دو مرحله‌ای

هر تراکنش دو فراخوانی جدا دارد و این جداسازی عمدی است:

  1. POST /transactions/simulate — همه اعتبارسنجی‌ها را انجام می‌دهد و نتیجه را برمی‌گرداند، بدون آنکه چیزی جابه‌جا شود.

  2. POST /transactions/submit — همان تراکنش را واقعاً اجرا می‌کند.

simulate سازوکار خشک‌اجرای این مسیر است: همان اعتبارسنجی و برآورد، بدون ثبت تراکنش. جای استفاده از آن هم توسعه است و هم تولید — پیش از نمایش تأییدیه به کاربر.

شبیه‌سازی

curl -sS -X POST "https://hd.zarban.io/transactions/simulate" \
-H "Authorization: Bearer $ZARBAN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"accountId": 1234,
"type": "TransactionTypeCoinWithdrawal",
"input": {
"to": "0x1234567890abcdef1234567890abcdef12345678",
"amount": "25.5",
"symbol": "USDT",
"network": "arbitrum"
}
}'

اگر موجودی کافی نباشد، آدرس نامعتبر باشد یا مبلغ از حد مجاز شبکه کمتر باشد، همین‌جا خطا می‌گیرید — پیش از آنکه چیزی رخ داده باشد.

اجرا

curl -sS -X POST "https://hd.zarban.io/transactions/submit" \
-H "Authorization: Bearer $ZARBAN_TOKEN" \
-H "Content-Type: application/json" \
-H "x-transaction-uuid: 3f8a1c92-6b4d-4e77-9a03-1d2e5f7c8b90" \
-d '{
"accountId": 1234,
"type": "TransactionTypeCoinWithdrawal",
"input": {
"to": "0x1234567890abcdef1234567890abcdef12345678",
"amount": "25.5",
"symbol": "USDT",
"network": "arbitrum"
}
}'

تفاوت با شبیه‌سازی فقط یک چیز است: هدر x-transaction-uuid.

x-transaction-uuid — قاعده‌ای که نباید بشکنید

این هدر روی submit الزامی است و رفتارش دقیقاً این است:

  • ارسال دوباره با همان UUID و همان پارامترها، تراکنش اصلی را برمی‌گرداند و تراکنش دومی نمی‌سازد.
  • استفاده دوباره از همان UUID با پارامترهای متفاوت، با 409 رد می‌شود.

قاعده: UUID را پیش از اولین تلاش بسازید، آن را کنار رکورد قصد پرداخت در پایگاه داده خودتان ذخیره کنید، و در هر تلاش مجدد همان را بفرستید.

سرور از روی همین مقدار تشخیص می‌دهد که درخواست تکراری است. اگر UUID را داخل خودِ تابع تلاش مجدد بسازید، هر تلاش مقدار تازه‌ای می‌گیرد و سرور آن را تراکنشی جدید می‌شمارد. خطر این کار آنجا روشن می‌شود که شبکه پس از ارسال درخواست و پیش از رسیدن پاسخ قطع شود؛ آن وقت تلاش مجدد، برداشت دومی ثبت می‌کند.

الگوی درست:

  1. رکورد «قصد برداشت» را در پایگاه داده خود بسازید، با یک UUID تازه در ستونی کنارش.

  2. submit را با همان UUID صدا بزنید.

  3. اگر پاسخ نرسید یا خطای شبکه گرفتید، همان درخواست را با همان UUID دوباره بفرستید. اگر تراکنش قبلاً ساخته شده باشد، همان را پس می‌گیرید.

  4. اگر 409 گرفتید، یعنی همان UUID را با پارامتری متفاوت فرستاده‌اید. این باگ سمت شماست؛ دوباره تلاش نکنید و هشدار بدهید.

وضعیت تراکنش

curl -sS "https://hd.zarban.io/transactions/55012" \
-H "Authorization: Bearer $ZARBAN_TOKEN"
وضعیتیعنی چه
TransactionStatusPendingثبت شده، هنوز شروع نشده
TransactionStatusInProgressدر حال اجرا
TransactionStatusPendingManualApprovalمنتظر تأیید دستی است
TransactionStatusSuccessانجام شد
TransactionStatusFailedناموفق
TransactionStatusRejectedByAdminرد شد
TransactionStatusCancelledByUserلغو شد

TransactionStatusSuccess تنها وضعیت پایانیِ موفق است. Pending و InProgress و PendingManualApproval هنوز تمام نشده‌اند — موجودی را بر اساس آن‌ها به کاربر نشان ندهید و کالا یا خدمتی تحویل ندهید.

برای دنبال کردن وضعیت، به‌جای فراخوانی مکرر API، وب‌هوک بگیرید.

فهرست تراکنش‌ها

curl -sS "https://hd.zarban.io/transactions?accountId=1234" \
-H "Authorization: Bearer $ZARBAN_TOKEN"

بدون accountId، همه تراکنش‌های سازمان برمی‌گردد. با فیلترهای type و status هم می‌توانید محدودش کنید.

همین فهرست، با همین فیلترها، در داشبورد سازمانی هم هست — جای مناسبی برای بررسی یک تراکنش مشکوک بدون نوشتن کد:

تب تراکنش‌ها در داشبورد زربان سازمانی
هر سه نوع تراکنش در یک فهرست: واریز ارز، برداشت ارز و انتقال داخلی. برچسب وضعیت همان مقادیر جدول بالاست — «موفق» تنها وضعیت پایانیِ موفق است و «تأیید دستی» یعنی تراکنش منتظر تأیید یک اپراتور مانده است.

لغو برداشت

curl -sS -X POST "https://hd.zarban.io/transactions/55012/cancel" \
-H "Authorization: Bearer $ZARBAN_TOKEN"

لغو تنها تا وقتی ممکن است که برداشت هنوز در وضعیت ثبت‌شده در صف باشد و روی زنجیره ارسال نشده باشد. پس از ارسال، دیگر راه بازگشتی نیست.

اگر رابط کاربری شما دکمه «لغو» دارد، وضعیت را پیش از نمایش دکمه بخوانید و پاسخ فراخوانی لغو را هم بررسی کنید: تراکنش ممکن است در همان فاصله از صف خارج و ارسال شده باشد.

لغو موفق، موجودی رزروشده را آزاد می‌کند و درخواست را از صف برداشت بیرون می‌برد.

صف برداشت

curl -sS "https://hd.zarban.io/tenants/PLN/withdraw-queue" \
-H "Authorization: Bearer $ZARBAN_TOKEN"

برداشت‌هایی که ثبت شده‌اند اما هنوز روی زنجیره نرفته‌اند. برای نظارت عملیاتی به کار می‌آید: صفی که طولانی می‌شود، معمولاً یعنی موجودی کیف پول داغ سازمان رو به اتمام است.

انتقال داخلی

curl -sS -X POST "https://hd.zarban.io/transactions/submit" \
-H "Authorization: Bearer $ZARBAN_TOKEN" \
-H "Content-Type: application/json" \
-H "x-transaction-uuid: 7c1e4a05-9f3b-4d28-8e61-0a5b6c7d8e9f" \
-d '{
"accountId": 1234,
"type": "TransactionTypeInternalTransfer",
"input": {
"debitAccountId": 1234,
"creditAccountId": 5678,
"symbol": "USDT",
"amount": "10",
"memo": "invoice #42"
}
}'

هر دو حساب باید به سازمان شما تعلق داشته باشند. همان قاعده x-transaction-uuid اینجا هم برقرار است.