FA

مستندات API فروشنده

URL پایه: https://snapex.pro/api/v1/merchant

کلید API و اولین درخواست

  1. کلید را از پنل بگیرید

    پنل همکار → توسعه‌دهندگان → «دریافت کلید API». کلید به شکل ⁦smk_abcdefghij_plus48chars⁩ است و فقط یک بار نمایش داده می‌شود. اگر گمش کردید، کلید تازه صادر کنید: کلید قبلی بلافاصله از کار می‌افتد.

  2. یک هدر اضافه کنید

    Authorization: Bearer your_key.

  3. دسترسی را بررسی کنید

    موجودی را درخواست کنید. اگر پاسخ JSON با data برگشت، کلید پذیرفته شده است.

درخواست
curl -sS https://snapex.pro/api/v1/merchant/balances \
  -H "Authorization: Bearer smk_abcdefghij_yourkey" \
  -H "Accept: application/json"
پاسخ
{
  "data": [
    {
      "asset_id": "usdt",               // همیشه usdt — موجودی دیگری وجود ندارد
      "available_atomic": "15000000",   // ⁦15 USDT⁩ در واحد خرد (۶ رقم اعشار)
      "available": "15",                // همان مقدار به شکل اعشاری
      "held_atomic": "0",               // رزروشده برای برداشت‌های تمام‌نشده
      "held": "0"
    }
  ]
}

اگر درخواست ناموفق بود

پاسخ علت
401 unauthorized کلید فرستاده نشده، فاصله دارد یا ناقص است. بعد از Bearer باید شناسه کامل ⁦smk_…⁩ بیاید.
401 key_inactive کلید باطل شده است: کلید تازه‌ای صادر شده، اما درخواست هنوز با کلید قبلی فرستاده می‌شود.

پنج قاعده

بیشتر خطاهای یکپارچه‌سازی از رعایت‌نکردن یکی از این قاعده‌ها می‌آید.

۱. مبلغ‌ها را به‌صورت رشته بفرستید

یا amount را بفرستید — رشته اعشاری: ⁦"0.001"⁩، ⁦"15"⁩ — یا amount_atomic را — عدد صحیح در واحد خرد: ⁦"100000"⁩ برای ⁦0.001 BTC⁩. دقیقاً یکی از این دو فیلد را بفرستید. پاسخ‌ها همیشه هر دو شکل را دارند: amount در کنار amount_atomic می‌آید.

نمونه
"amount": "0.001"        // valid
"amount_atomic": "100000" // also valid
// both at once — 422

۲. asset_id را فقط از ⁦/assets⁩ بگیرید

کدهای مجاز: usdt، btc، xmr. مقدارهای دلخواه و شناسه‌های کاوشگرهای بلاکچین با خطای 422 رد می‌شوند.

نمونه
"asset_id": "btc"   // correct
"asset_id": "BTC"   // error
"asset_id": "bitcoin" // error

۳. Idempotency-Key در هر درخواستی که چیزی می‌سازد

شناسه یکتای هر تلاش. اگر اتصال قطع شد و نتیجه را نمی‌دانید، همان درخواست را با همان کلید تکرار کنید: نتیجه اصلی برمی‌گردد و عملیات دومی ساخته نمی‌شود. برای ⁦POST /invoices⁩، ⁦POST /payments⁩، ⁦POST /payouts/preview⁩ و ⁦POST /payouts⁩ الزامی است.

نمونه
Idempotency-Key: order-42-payment   // first attempt → 201, created
Idempotency-Key: order-42-payment   // retry → 200, same resource
Idempotency-Key: order-42-payment   // different amount → 409 conflict

۴. فیلدهای خالی فرستاده نمی‌شوند

مقدارهای null و رشته‌های خالی در پاسخ نمی‌آیند. پرداختی که هنوز انجام نشده، actual_amount، credited_amount و fee ندارد. وجود کلید را بررسی کنید، نه مقدار آن را.

نمونه
// unpaid payment:
{ "payment_id": "pay_…", "status": "awaiting_deposit" }
// credited_amount is simply absent

۵. سفارش را با expired نبندید

expired به این معنی نیست که انتقال هرگز نمی‌رسد. آدرس حدود 6 ساعت دیگر هم پایش می‌شود و انتقالی که دیر برسد به موجودی واریز می‌شود. وضعیت‌های نهایی همراه با واریز: confirmed، confirmed_underpaid، confirmed_overpaid.

سناریوها

هر سناریو چرخه کامل را نشان می‌دهد: ترتیب درخواست‌ها، ساختار پاسخ و معنای فیلدها. توضیح‌های داخل نمونه‌های JSON فقط برای فهم بهتر است و در پاسخ‌های واقعی وجود ندارد.

دریافت پرداخت

مشتری برای سفارش ⁦60 USD⁩ با BTC پرداخت می‌کند

سفارش order-42. حدود ⁦60 USDT⁩ لازم دارید؛ با قیمت حدود ⁦60,000⁩ برای BTC، مبلغی که از مشتری انتظار می‌رود ⁦0.001 BTC⁩ است.

1
فهرست دارایی‌های موجود را بگیرید

پاسخ کد دارایی و decimals را برمی‌گرداند — تعداد رقم‌های بعد از ممیز.

درخواست
curl -sS https://snapex.pro/api/v1/merchant/assets \
  -H "Authorization: Bearer smk_abcdefghij_yourkey" \
  -H "Accept: application/json"

نکات کلیدی

GET /assets
پارامتری لازم نیست.
Authorization
تنها هدر الزامی در API.
پاسخ
{
  "data": [
    {
      "asset_id": "btc",       // ← در درخواست‌های بعدی از این استفاده کنید
      "symbol": "BTC",         // برای نمایش به کاربر
      "network": "bitcoin",    // در برداشت‌ها استفاده می‌شود
      "decimals": 8,           // ⁦1 BTC = 100000000⁩ واحد خرد
      "price_usd": "60000"     // تقریبی، برای نرخ قطعی نیست
    },
    {
      "asset_id": "usdt",
      "symbol": "USDT",
      "network": "TRON",
      "decimals": 6,
      "price_usd": "1.00"
    }
  ]
}
2
نرخ را برای ۱۰ دقیقه قفل کنید

نرخ را تا وقتی مشتری در صفحه پرداخت است قفل می‌کند. در این مرحله نه پرداختی ساخته می‌شود نه آدرسی: فقط نرخ قطعی محاسبه می‌شود. پاسخ، مبلغی به USDT است که برای ⁦0.001 BTC⁩ به موجودی واریز می‌شود.

درخواست
curl -sS -X POST https://snapex.pro/api/v1/merchant/payments/credit-preview \
  -H "Authorization: Bearer smk_abcdefghij_yourkey" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{"asset_id":"btc","amount":"0.001"}'

نکات کلیدی

asset_id: "btc"
کد مرحله قبل.
amount: "0.001"
مبلغ BTC مورد انتظار از مشتری. به‌صورت رشته فرستاده می‌شود، نه عدد.
Idempotency-Key
اختیاری: عملیاتی ساخته نمی‌شود. می‌توانید آن را بفرستید تا همان نرخ قطعی دوباره برگردد.
پاسخ
{
  "data": {
    "credit_lock_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
                                       // ← برای مرحله بعد ذخیره کنید
    "asset_id": "btc",
    "expected_amount": "0.001",        // مبلغی که از مشتری انتظار می‌رود
    "expected_amount_atomic": "100000",
    "credit_usdt": "59.4",             // ← مبلغی که واریز می‌شود
    "credit_usdt_atomic": "59400000",
    "expires_at": "2026-08-11T17:20:00+00:00"
                                       // ← پس از این زمان credit-preview نامعتبر است
  }
}

expected_amount را به مشتری نشان دهید — یعنی چه مقدار BTC باید بفرستد.

3
پرداخت را بسازید و آدرس را بگیرید

این مرحله پرداخت را می‌سازد و آدرس واریز را برمی‌گرداند. مبلغ را در این درخواست نفرستید — در مرحله ۲ در credit-preview تعیین شده است.

درخواست
curl -sS -X POST https://snapex.pro/api/v1/merchant/payments \
  -H "Authorization: Bearer smk_abcdefghij_yourkey" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -H "Idempotency-Key: order-42-payment" \
  -d '{
    "credit_lock_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "external_id": "order-42"
  }'

نکات کلیدی

credit_lock_id
شناسه از پاسخ credit-preview. اگر مهلت قفل گذشته باشد، API خطای 422 برمی‌گرداند — یک credit-preview تازه بگیرید.
external_id: "order-42"
شماره سفارش شما. برای پیداکردن پرداخت در آینده و جلوگیری از دو پرداخت برای یک سفارش به کار می‌رود.
Idempotency-Key
الزامی. یک مقدار یکتا و ثابت بگذارید، مثلاً شماره سفارش با یک پسوند.
پاسخ
{
  "data": {
    "payment_id": "pay_01HZX...",      // ← شناسه برای بررسی وضعیت
    "external_id": "order-42",         // شماره سفارش از درخواست
    "asset_id": "btc",
    "expected_amount": "0.001",
    "credit_usdt": "59.4",
    "status": "awaiting_deposit",      // در انتظار انتقال
    "channel": "api",
    "deposit_address": "bc1qexampledepositaddress000000000",
                                       // ← آدرسی که به مشتری نشان می‌دهید
    "expires_at": "2026-08-11T18:00:00+00:00",
    "created_at": "2026-08-11T17:00:00+00:00"
  }
}

اگر شبکه memo لازم داشته باشد، پاسخ deposit_memo را هم دارد. انتقال بدون memo به موجودی واریز نمی‌شود — هر دو فیلد را نشان دهید.

4
منتظر پرداخت بمانید

هر ۱۰ تا ۳۰ ثانیه وضعیت پرداخت را استعلام کنید. ترتیب وضعیت‌ها: ⁦awaiting_deposit → detected → processing → confirmed⁩.

درخواست
curl -sS https://snapex.pro/api/v1/merchant/payments/pay_01HZX... \
  -H "Authorization: Bearer smk_abcdefghij_yourkey" \
  -H "Accept: application/json"

نکات کلیدی

GET /payments/{payment_id}
از payment_id پاسخ قبلی استفاده کنید.
چه زمانی
فاصله پیشنهادی ۱۰ تا ۳۰ ثانیه است. سقف، ۱۲۰ درخواست در دقیقه برای هر کلید است؛ سریع‌تر از این استعلام نکنید.
پاسخ
{
  "data": {
    "payment_id": "pay_01HZX...",
    "status": "detected",              // انتقال در شبکه دیده شد،
                                       // هنوز تأییدهای کافی ندارد
    "asset_id": "btc",
    "expected_amount": "0.001",
    "actual_amount": "0.001",          // ← مبلغی که واقعاً رسید
    "deposit_address": "bc1qexampledepositaddress000000000",
    "created_at": "2026-08-11T17:00:00+00:00"
  }
}
5
پرداخت تأیید شد

confirmed یعنی وجه به موجودی واریز شده است. سفارش را می‌توانید ببندید.

درخواست
curl -sS https://snapex.pro/api/v1/merchant/payments/pay_01HZX... \
  -H "Authorization: Bearer smk_abcdefghij_yourkey" \
  -H "Accept: application/json"

نکات کلیدی

status: confirmed
وضعیت نهایی؛ دیگر تغییر نمی‌کند.
credited_amount
مبلغی به USDT که واقعاً واریز شد. در پرداخت دقیق با credit_usdt برابر است.
پاسخ
{
  "data": {
    "payment_id": "pay_01HZX...",
    "external_id": "order-42",
    "status": "confirmed",             // ← واریز انجام شد
    "asset_id": "btc",
    "expected_amount": "0.001",
    "actual_amount": "0.001",          // مبلغ مورد انتظار رسید
    "credit_usdt": "59.4",
    "credited_amount": "59.4",         // ← مبلغ واریزشده به موجودی
    "fee": "0.6",                      // کارمزد، از قبل در credited_amount لحاظ شده
    "deposit_address": "bc1qexampledepositaddress000000000",
    "created_at": "2026-08-11T17:00:00+00:00"
  }
}

تطبیق را با credited_amount انجام دهید، نه expected_amount: در کم‌پرداخت این دو مقدار فرق دارند.

توجه

  • پرداخت را از قبل نسازید: قفل نرخ ۱۰ دقیقه اعتبار دارد و آدرس برای مبلغ مشخصی صادر می‌شود.
  • سفارش را با detected نبندید: وجه هنوز به موجودی واریز نشده است.

مشتری با USDT پرداخت می‌کند

روند همان سناریوی BTC است و فقط asset_id عوض می‌شود. نمونه کوتاه‌تر را پایین ببینید.

1
مبلغ را قفل کنید

تبدیلی در کار نیست، پس credit_usdt تقریباً برابر مبلغ منهای کارمزد است.

درخواست
curl -sS -X POST https://snapex.pro/api/v1/merchant/payments/credit-preview \
  -H "Authorization: Bearer smk_abcdefghij_yourkey" \
  -H "Content-Type: application/json" \
  -d '{"asset_id":"usdt","amount":"100"}'

نکات کلیدی

asset_id: "usdt"
تنها تفاوت با سناریوی BTC.
amount: "100"
مشتری ⁦100 USDT⁩ در شبکه TRON می‌فرستد.
پاسخ
{
  "data": {
    "credit_lock_id": "b2c3d4e5-...",
    "asset_id": "usdt",
    "expected_amount": "100",
    "credit_usdt": "99",         // ← کارمزد ⁦1 USDT⁩
    "expires_at": "2026-08-11T17:20:00+00:00"
  }
}
2
پرداخت را بسازید

همان مرحله قبلی. آدرس، آدرس TRON است (با T شروع می‌شود).

درخواست
curl -sS -X POST https://snapex.pro/api/v1/merchant/payments \
  -H "Authorization: Bearer smk_abcdefghij_yourkey" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-43-payment" \
  -d '{"credit_lock_id":"b2c3d4e5-...","external_id":"order-43"}'

نکات کلیدی

Idempotency-Key
برای سفارش جدید Idempotency-Key تازه بگذارید. کلید سفارش order-42 را دوباره به کار نبرید.
پاسخ
{
  "data": {
    "payment_id": "pay_01HZY...",
    "external_id": "order-43",
    "asset_id": "usdt",
    "expected_amount": "100",
    "credit_usdt": "99",
    "status": "awaiting_deposit",
    "deposit_address": "TXYZexampletronaddress000000000",
                                 // ← آدرس TRON، TRC20
    "expires_at": "2026-08-11T18:00:00+00:00"
  }
}

به مشتری بگویید شبکه TRC20 است. اگر USDT در شبکه دیگری به این آدرس فرستاده شود، به موجودی واریز نمی‌شود.

مشتری کمتر از مبلغ خواسته‌شده فرستاد

انتظار ⁦0.001 BTC⁩ بود و ⁦0.0009⁩ رسید — مثلاً کیف پول مشتری کارمزد شبکه را از خود مبلغ کم کرده است.

1
پرداخت را بررسی کنید

اگر کمتر از expected_amount رسیده ولی مبلغ هنوز برای واریز کافی است، وضعیت confirmed_underpaid می‌شود و credited_amount کمتر از مبلغ نرخ قطعی است. اگر مبلغ خیلی کم باشد، چیزی واریز نمی‌شود.

درخواست
curl -sS https://snapex.pro/api/v1/merchant/payments/pay_01HZX... \
  -H "Authorization: Bearer smk_abcdefghij_yourkey" \
  -H "Accept: application/json"

نکات کلیدی

معنی
credited_amount را مقایسه کنید، نه credit_usdt. اولی مبلغی است که واریز شد؛ دومی نرخ قطعی برای پرداخت دقیق است.
پاسخ
{
  "data": {
    "payment_id": "pay_01HZX...",
    "external_id": "order-42",
    "status": "confirmed_underpaid",   // ← کم‌پرداخت، اما وجه واریز شد
    "asset_id": "btc",
    "expected_amount": "0.001",        // مبلغ مورد انتظار
    "actual_amount": "0.0009",         // مبلغی که واقعاً رسید
    "credit_usdt": "59.4",             // نرخ قطعی برای پرداخت دقیق
    "credited_amount": "53.4",         // ← مبلغی که واقعاً واریز شد
    "fee": "0.6",
    "created_at": "2026-08-11T17:00:00+00:00"
  }
}

قدم بعدی با شماست: باقی‌مانده را از مشتری بخواهید، بخشی از سفارش را تحویل دهید یا آن را لغو کنید. API مبلغی را که واقعاً واریز شده برمی‌گرداند.

توجه

  • «پرداخت شده یا نه» را با مقایسه actual_amount و expected_amount تصمیم نگیرید. credited_amount را با مبلغی به USDT که لازم دارید مقایسه کنید.

مشتری به‌موقع پرداخت نکرد

زمان expires_at گذشته و انتقالی نرسیده است. پرداخت expired می‌شود، اما پایش آدرس ادامه دارد.

1
وضعیت expired

مهلت پرداخت تمام شده است. آدرس هنوز حدود 6 ساعت پایش می‌شود.

درخواست
curl -sS https://snapex.pro/api/v1/merchant/payments/pay_01HZX... \
  -H "Authorization: Bearer smk_abcdefghij_yourkey" \
  -H "Accept: application/json"

نکات کلیدی

status: expired
مهلت پرداخت تمام شده است.
credited_amount
هنوز وجهی نرسیده است.
پاسخ
{
  "data": {
    "payment_id": "pay_01HZX...",
    "status": "expired",               // مهلت تمام شد
    "asset_id": "btc",
    "expected_amount": "0.001",
    "deposit_address": "bc1qexampledepositaddress000000000",
    "expires_at": "2026-08-11T18:00:00+00:00"
    // actual_amount و credited_amount وجود ندارند — انتقالی نرسیده
  }
}
2
مشتری دیر پرداخت کرد

اگر انتقال در بازه پایش اضافه برسد، پرداخت باز هم به confirmed می‌رود. سفارش را با expired حذف نکنید: آن را «دیرکرد» علامت بزنید و استعلام را ادامه دهید.

درخواست
curl -sS https://snapex.pro/api/v1/merchant/payments/pay_01HZX... \
  -H "Authorization: Bearer smk_abcdefghij_yourkey" \
  -H "Accept: application/json"

نکات کلیدی

expired → confirmed
حالت عادی: انتقال بعد از expires_at رسید.
پاسخ
{
  "data": {
    "payment_id": "pay_01HZX...",
    "status": "confirmed",             // ← بعد از expires_at واریز شد
    "actual_amount": "0.001",
    "credited_amount": "59.4",
    "created_at": "2026-08-11T17:00:00+00:00"
  }
}

توجه

  • بلافاصله بعد از expired آدرس تازه صادر نکنید: ممکن است مشتری همین حالا به آدرس قبلی در حال پرداخت باشد. ما تا 6 ساعت دیگر هم منتظر انتقال می‌مانیم.

فاکتورها

فاکتور دلاری با صفحه پرداخت آماده. مشتری شبکه را انتخاب می‌کند و مبلغ به USDT به موجودی شما واریز می‌شود.

فاکتور ⁦15 USD⁩ که با USDT پرداخت می‌شود

سفارش order-15. فروشنده یک فاکتور دلاری می‌سازد و لینک صفحه پرداخت را برای مشتری می‌فرستد.

1
فاکتور را بسازید

فاکتور مبلغ دلاری و پرداخت‌کننده کارمزد را ثابت می‌کند و pay_url را برمی‌گرداند. هنوز آدرسی صادر نمی‌شود.

درخواست
curl -sS -X POST https://snapex.pro/api/v1/merchant/invoices \
  -H "Authorization: Bearer smk_abcdefghij_yourkey" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -H "Idempotency-Key: order-15-invoice" \
  -d '{
    "amount_usd": "15.00",
    "fee_on": "merchant",
    "external_id": "order-15"
  }'

نکات کلیدی

amount_usd
مبلغ سفارش به‌صورت رشته. این endpoint فقط USD می‌پذیرد — نه amount_atomic.
fee_on
merchant — کارمزد را شما می‌پردازید و مشتری ⁦15.00⁩ می‌بیند. customer — کارمزد را مشتری می‌پردازد.
Idempotency-Key
الزامی. تکرار با همان کلید همان فاکتور را برمی‌گرداند.
partner_ref
اختیاری. Partner ID یکپارچه‌سازی که اتصال شما را راه‌اندازی کرده است (برنامه همکاری SnapEX Pay). حساب شما را یک بار به آن همکار متصل می‌کند؛ اگر خودتان یکپارچه‌سازی می‌کنید، آن را نفرستید.
پاسخ
{
  "data": {
    "invoice_id": "INV-8K2M1Q0P",
    "amount_usd": "15.00",
    "fee_on": "merchant",
    "charge_usd": "15.00",
    "credit_usd": "13.85",
    "status": "open",
    "pay_url": "https://pay.snapex.pro/0123456789abcdef0123456789abcdef",
    "methods": [
      { "asset_id": "usdt.trc20", "symbol": "USDT", "network": "TRC20" },
      { "asset_id": "usdt.erc20", "symbol": "USDT", "network": "ERC20" },
      { "asset_id": "usdt.bep20", "symbol": "USDT", "network": "BEP20" }
    ]
  }
}

کلید باید دسترسی invoices.write یا payments.write داشته باشد. محیط تست: ⁦environment=test⁩.

2
مشتری شبکه را انتخاب می‌کند

این مرحله یک آدرس یکتا صادر می‌کند. تا پیش از رسیدن انتقال می‌توان روش پرداخت را عوض کرد.

درخواست
curl -sS -X POST https://snapex.pro/api/v1/merchant/invoices/INV-8K2M1Q0P/methods \
  -H "Authorization: Bearer smk_abcdefghij_yourkey" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-15-trc20" \
  -d '{"asset_id":"usdt.trc20"}'

نکات کلیدی

asset_id: "usdt.trc20"
یکی از کدهای methods: usdt.trc20، usdt.erc20 یا usdt.bep20. کوین‌های دیگر هم از همین فهرست می‌آیند.
payment.address
فقط همین آدرس و مبلغ را نشان دهید. memo و tag استفاده نمی‌شوند.
پاسخ
{
  "data": {
    "invoice_id": "INV-8K2M1Q0P",
    "status": "awaiting",
    "selected_asset_id": "usdt.trc20",
    "payment": {
      "asset_id": "usdt.trc20",
      "amount": "15 USDT",
      "address": "TExampleUniqueAddressForThisAttempt",
      "status": "awaiting"
    }
  }
}

می‌توانید این فراخوانی را انجام ندهید: مشتری شبکه را در pay_url انتخاب می‌کند.

3
فاکتور پرداخت شد

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

درخواست
curl -sS https://snapex.pro/api/v1/merchant/invoices/INV-8K2M1Q0P \
  -H "Authorization: Bearer smk_abcdefghij_yourkey" \
  -H "Accept: application/json"

نکات کلیدی

status: paid
وضعیت نهایی. می‌توانید سفارش را ببندید.
invoice.paid
وب‌هوک invoice.paid به URL تنظیم‌شده در پنل می‌رسد. بررسی امضا را در بخش «وب‌هوک‌ها» ببینید.
پاسخ
{
  "data": {
    "invoice_id": "INV-8K2M1Q0P",
    "status": "paid",
    "credit_usd": "13.85",
    "paid_at": "2026-09-25T12:00:10+00:00"
  }
}

کم‌پرداخت فاکتور را نمی‌بندد: وضعیت underpaid می‌شود و همان آدرس منتظر باقی‌مانده می‌ماند (USDT — ۷۲ ساعت). اگر باقی‌مانده به‌موقع نرسد، فاکتور با وضعیت partially_paid بسته می‌شود.

توجه

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

برداشت‌ها

انتقال‌های خروجی از موجودی USDT شما.

برای مشتری ⁦15 USDT⁩ را به‌صورت BTC بفرستید

amount مبلغی است که از موجودی شما کسر می‌شود، نه مبلغی که گیرنده می‌گیرد. مبلغ گیرنده را preview برمی‌گرداند.

1
موجودی قابل برداشت را بررسی کنید

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

درخواست
curl -sS https://snapex.pro/api/v1/merchant/balances \
  -H "Authorization: Bearer smk_abcdefghij_yourkey" \
  -H "Accept: application/json"

نکات کلیدی

available
موجودی قابل برداشت برای کسر.
held
رزروشده برای برداشت‌های در حال انجام.
پاسخ
{
  "data": [
    {
      "asset_id": "usdt",
      "available": "74.4",       // ← قابل خرج
      "available_atomic": "74400000",
      "held": "0",               // چیزی رزرو نشده
      "held_atomic": "0"
    }
  ]
}
2
نرخ قطعی برداشت را بگیرید

preview برمی‌گرداند که گیرنده در برابر ⁦15 USDT⁩ چه مقدار BTC می‌گیرد و کارمزد چقدر است. هنوز چیزی کسر نمی‌شود: فقط نرخ قطعی قفل می‌شود.

درخواست
curl -sS -X POST https://snapex.pro/api/v1/merchant/payouts/preview \
  -H "Authorization: Bearer smk_abcdefghij_yourkey" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -H "Idempotency-Key: wd-77-preview" \
  -d '{
    "destination_asset_id": "btc",
    "destination_network": "bitcoin",
    "destination_address": "bc1qexampledestinationaddress000000",
    "amount": "15"
  }'

نکات کلیدی

amount: "15"
مبلغی به USDT که از موجودی شما کسر می‌شود. این مبلغ گیرنده نیست.
destination_asset_id
دارایی گیرنده. کد از ⁦GET /assets⁩.
destination_network
شبکه گیرنده. برای BTC — bitcoin. آن را از network در ⁦GET /assets⁩ بگیرید.
destination_address
آدرس گیرنده. پیش از ارسال آن را بررسی کنید: انتقال قابل لغو نیست.
Idempotency-Key
الزامی. برای preview کلید جداگانه بگذارید.
پاسخ
{
  "data": {
    "preview_id": "c3d4e5f6-7890-abcd-ef12-34567890abcd",
                                     // ← شناسه برای مرحله بعد
    "from_asset_id": "usdt",
    "from_amount": "15",             // مبلغ کسر
    "to_asset_id": "btc",
    "to_amount": "0.000221",         // ← مبلغی که گیرنده می‌گیرد
    "fee_amount": "0.000019",        // کارمزد به BTC، از قبل لحاظ شده
    "expires_at": "2026-08-11T17:20:00+00:00"
                                     // ← پیش از این زمان اجرا کنید
  }
}

to_amount را به کاربر به‌عنوان مبلغی که دریافت می‌کند نشان دهید. اگر مبلغ قابل قبول نیست، preview را اجرا نکنید: در زمان expires_at منقضی می‌شود.

3
برداشت را اجرا کنید

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

درخواست
curl -sS -X POST https://snapex.pro/api/v1/merchant/payouts \
  -H "Authorization: Bearer smk_abcdefghij_yourkey" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -H "Idempotency-Key: wd-77-execute" \
  -d '{
    "preview_id": "c3d4e5f6-7890-abcd-ef12-34567890abcd",
    "destination_network": "bitcoin",
    "destination_address": "bc1qexampledestinationaddress000000",
    "external_id": "wd-77"
  }'

نکات کلیدی

Idempotency-Key: wd-77-execute
کلید تازه، متفاوت با کلید preview. استفاده دوباره از کلید preview خطای 409 برمی‌گرداند.
destination_network / address
باید دقیقاً با preview یکی باشد، وگرنه 422.
external_id
شناسه عملیات شما برای پیداکردن آن در آینده.
پاسخ
{
  "data": {
    "payout_id": "payout_01HZX...",  // ← شناسه برای پیگیری
    "external_id": "wd-77",
    "asset_id": "usdt",
    "debit_amount": "15",            // از موجودی کسر شد
    "destination_asset_id": "btc",
    "destination_network": "bitcoin",
    "destination_address": "bc1qexampledestinationaddress000000",
    "destination_amount": "0.000221",// مبلغی که گیرنده می‌گیرد
    "fee": "0.000019",
    "status": "reserved",            // وجه رزرو شد، ارسال آغاز شد
    "channel": "api",
    "created_at": "2026-08-11T17:10:00+00:00"
  }
}
4
منتظر تکمیل بمانید

ترتیب وضعیت‌ها: ⁦reserved → submitting → processing → completed⁩. هر ۱۵ تا ۳۰ ثانیه استعلام کنید.

درخواست
curl -sS https://snapex.pro/api/v1/merchant/payouts/payout_01HZX... \
  -H "Authorization: Bearer smk_abcdefghij_yourkey" \
  -H "Accept: application/json"

نکات کلیدی

status: completed
وجه به گیرنده رسید. وضعیت نهایی.
status: failed
انتقال ناموفق بود؛ مبلغ رزروشده معمولاً به موجودی برمی‌گردد.
status: manual_review
نتیجه مشخص نیست. منتظر بررسی بمانید؛ برداشت دیگری نسازید.
پاسخ
{
  "data": {
    "payout_id": "payout_01HZX...",
    "external_id": "wd-77",
    "debit_amount": "15",
    "destination_amount": "0.000221",
    "status": "completed",           // ← تحویل شد
    "created_at": "2026-08-11T17:10:00+00:00"
  }
}

توجه

  • amount مبلغ کسر است، نه مبلغ گیرنده.
  • برداشت‌ها سقف جداگانه دارند: ۵ درخواست در دقیقه و ۳۰ درخواست در ساعت. preview را در حلقه فراخوانی نکنید.
  • اگر مهلت preview گذشته باشد (expires_at)، ⁦POST /payouts⁩ خطای 422 برمی‌گرداند. یک preview تازه بگیرید.

ارسال USDT به کیف پول خارجی در TRON

تفاوت این است که destination_asset_id هم usdt است. در این حالت کارمزد حداقل است.

1
نرخ قطعی برداشت را بگیرید

دارایی کسر و دارایی مقصد یکی است، پس to_amount تقریباً برابر amount منهای کارمزد شبکه است.

درخواست
curl -sS -X POST https://snapex.pro/api/v1/merchant/payouts/preview \
  -H "Authorization: Bearer smk_abcdefghij_yourkey" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: wd-78-preview" \
  -d '{
    "destination_asset_id": "usdt",
    "destination_network": "TRON",
    "destination_address": "TXYZexampletronaddress000000000",
    "amount": "50"
  }'

نکات کلیدی

destination_network: "TRON"
مقدار را از network در ⁦GET /assets⁩ بگیرید؛ بزرگی و کوچکی حروف مهم است.
amount: "50"
⁦50 USDT⁩ کسر می‌شود.
پاسخ
{
  "data": {
    "preview_id": "d4e5f6a7-...",
    "from_asset_id": "usdt",
    "from_amount": "50",           // مبلغ کسر
    "to_asset_id": "usdt",
    "to_amount": "48.9",           // ← مبلغ دریافتی (پس از کسر کارمزد شبکه)
    "fee_amount": "1.1",
    "expires_at": "2026-08-11T17:25:00+00:00"
  }
}
2
برداشت را اجرا کنید

همان ترتیب سناریوی قبلی.

درخواست
curl -sS -X POST https://snapex.pro/api/v1/merchant/payouts \
  -H "Authorization: Bearer smk_abcdefghij_yourkey" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: wd-78-execute" \
  -d '{
    "preview_id": "d4e5f6a7-...",
    "destination_network": "TRON",
    "destination_address": "TXYZexampletronaddress000000000",
    "external_id": "wd-78"
  }'
پاسخ
{
  "data": {
    "payout_id": "payout_01HZZ...",
    "debit_amount": "50",
    "destination_asset_id": "usdt",
    "destination_amount": "48.9",
    "status": "reserved",
    "created_at": "2026-08-11T17:26:00+00:00"
  }
}

توجه

  • مطمئن شوید آدرس گیرنده در شبکه TRON است. انتقال به شبکه دیگر انجام نمی‌شود: یا درخواست رد می‌شود یا وجه نمی‌رسد.

موجودی برای برداشت کافی نیست

موجودی ⁦10 USDT⁩ است و برداشت ⁦15⁩ درخواست شده است.

1
preview درخواست را رد می‌کند

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

درخواست
curl -sS -X POST https://snapex.pro/api/v1/merchant/payouts/preview \
  -H "Authorization: Bearer smk_abcdefghij_yourkey" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: wd-79-preview" \
  -d '{
    "destination_asset_id": "btc",
    "destination_network": "bitcoin",
    "destination_address": "bc1q…",
    "amount": "15"
  }'

نکات کلیدی

HTTP 422
رد به دلیل منطق کسب‌وکار. ساختار پاسخ همان خطاهای اعتبارسنجی است.
details.amount
خطا به یک فیلد مربوط است و می‌توان آن را کنار همان فیلد در فرم نشان داد.
پاسخ
{
  "error": {
    "code": "validation_error",
    "message": "The request is invalid.",
    "details": {
      "amount": ["Insufficient balance."]
                              // ← علت رد به amount مربوط است
    }
  }
}

چیزی رزرو نمی‌شود. تکرار با همان Idempotency-Key بی‌خطر است؛ اول موجودی را شارژ کنید.

توجه

  • held را در نظر بگیرید: اگر بخشی از وجه برای برداشت دیگری رزرو شده باشد، available کمتر از انتظار شما خواهد بود.

عملیات

تاریخچه پرداخت‌ها

دفتر تراکنش‌ها (ledger) فهرست عملیات است: واریزها، کسرها و رزروها به‌صورت ردیف‌های جداگانه ثبت می‌شوند.

1
تاریخچه را صفحه به صفحه بگیرید

ردیف‌ها از جدیدترین به قدیمی‌ترین برمی‌گردند. در هر درخواست حداکثر ۱۰۰ ردیف؛ صفحه بعد با cursor گرفته می‌شود.

درخواست
curl -sS "https://snapex.pro/api/v1/merchant/transactions?limit=50" \
  -H "Authorization: Bearer smk_abcdefghij_yourkey" \
  -H "Accept: application/json"

نکات کلیدی

limit=50
تعداد ردیف در هر پاسخ: ۱ تا ۱۰۰، پیش‌فرض ۲۵.
status=payment
فیلتر اختیاری بر اساس نوع ردیف.
پاسخ
{
  "data": [
    {
      "id": "124",
      "direction": "credit",          // واریز
      "type": "payment",              // منبع — واریز
      "amount": "59.4",
      "asset_id": "usdt",
      "channel": "api",
      "occurred_at": "2026-08-11T17:05:00+00:00"
    },
    {
      "id": "125",
      "direction": "reserve",         // رزرو برای برداشت
      "type": "payout",
      "amount": "15",
      "asset_id": "usdt",
      "occurred_at": "2026-08-11T17:10:00+00:00"
    }
  ],
  "meta": {
    "next_cursor": "eyJpZCI6MTI1fQ",  // ← مقدار برای پارامتر cursor
    "per_page": 50
  }
}
2
صفحه بعد

وقتی next_cursor برابر null است، ردیف دیگری وجود ندارد.

درخواست
curl -sS "https://snapex.pro/api/v1/merchant/transactions?limit=50&cursor=eyJpZCI6MTI1fQ" \
  -H "Authorization: Bearer smk_abcdefghij_yourkey" \
  -H "Accept: application/json"

نکات کلیدی

cursor
مقدار meta.next_cursor از پاسخ قبلی. مقدار دلخواه پذیرفته نمی‌شود.
پاسخ
{
  "data": [ /* ردیف‌های بیشتر */ ],
  "meta": {
    "next_cursor": null,              // ← صفحه آخر
    "per_page": 50
  }
}

توجه

  • مقدارهای reserve و release در direction یعنی مسدودکردن و آزادکردن وجه، نه انتقال خروجی. آن‌ها را هزینه حساب نکنید.

پاسخی نیامد — همان درخواست را تکرار کنید

1
همان درخواست را تکرار کنید

همان Idempotency-Key و همان پارامترها را بفرستید. سرور آن را همان عملیات قبلی حساب می‌کند.

درخواست
curl -sS -X POST https://snapex.pro/api/v1/merchant/payouts \
  -H "Authorization: Bearer smk_abcdefghij_yourkey" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: wd-77-execute" \
  -d '{
    "preview_id": "c3d4e5f6-...",
    "destination_network": "bitcoin",
    "destination_address": "bc1qexampledestinationaddress000000",
    "external_id": "wd-77"
  }'

نکات کلیدی

Idempotency-Key
کلید تازه یعنی برداشت تازه.
HTTP 200 / 201
عملیات از قبل وجود داشت؛ شیء اصلی برمی‌گردد.
پاسخ
{
  "data": {
    "payout_id": "payout_01HZX...",   // ← همان شناسه پاسخ اول
    "external_id": "wd-77",
    "debit_amount": "15",
    "status": "processing",           // وضعیت ممکن است در این فاصله تغییر کرده باشد
    "created_at": "2026-08-11T17:10:00+00:00"
  }
}

HTTP 201 — عملیات در همین درخواست ساخته شد. HTTP 200 — از قبل وجود داشت. هر دو کد یعنی موفقیت.

2
اگر پارامترها عوض شده باشند

تکرار همان کلید با مبلغ یا آدرس دیگر رد می‌شود. این کار جلوی ناهمخوانی پارامترها را می‌گیرد.

درخواست
curl -sS -X POST https://snapex.pro/api/v1/merchant/payouts \
  -H "Authorization: Bearer smk_abcdefghij_yourkey" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: wd-77-execute" \
  -d '{
    "preview_id": "another-preview",
    "destination_network": "bitcoin",
    "destination_address": "bc1qanotheraddress"
  }'

نکات کلیدی

HTTP 409
تعارض: این کلید قبلاً به عملیات دیگری گره خورده است.
چه باید کرد
اگر برداشت تازه‌ای است، Idempotency-Key تازه بگذارید.
پاسخ
{
  "error": {
    "code": "conflict",
    "message": "Idempotency-Key was used with different parameters."
  }
}

توجه

  • کلید را یک بار بسازید و همراه سفارش ذخیره کنید. اگر پیش از هر ارسال کلید تازه بسازید، محافظت در برابر تکرار از کار می‌افتد.
  • با HTTP 429، چند ثانیه بعد با همان کلید دوباره تلاش کنید.

وب‌هوک‌ها

وقتی فاکتور، واریز، برداشت، تبدیل یا موجودی تغییر می‌کند، SnapEX یک درخواست POST به URL شما می‌فرستد. وب‌هوک فقط یک سیگنال است، نه مرجع نهایی: امضا را بررسی کنید و برای تصمیم‌های مهم، شیء را دوباره از طریق API بخوانید.

راه‌اندازی

  1. URL را در پنل تنظیم کنید

    پنل همکار → توسعه‌دهندگان → کارت «Webhook»: URL را وارد کنید و «ذخیره» را بزنید. برای هر حساب یک URL؛ همه انواع رویداد جدول زیر به آن فرستاده می‌شوند.

  2. رمز امضا را نگه دارید

    در اولین ذخیره، پنل رمز ⁦whsec_…⁩ را فقط یک بار نشان می‌دهد. آن را فقط روی سرور خودتان نگه دارید. اگر بعداً URL را عوض کنید، رمز همان می‌ماند.

  3. شرایط URL

    آدرس کامل ⁦https://⁩ با میزبان عمومی: بدون ⁦login:password⁩ در URL، بدون localhost، و میزبان نباید به IP خصوصی یا رزروشده اشاره کند. URL هنگام ذخیره و دوباره پیش از هر تلاش تحویل بررسی می‌شود.

چه چیزی می‌رسد

هدر معنی
Webhook-Id شناسه یکتای رویداد (UUID)، برابر با id در بدنه. در تلاش‌های دوباره و ارسال دستی تغییر نمی‌کند — تکراری‌ها را با آن کنار بگذارید.
Webhook-Timestamp زمان Unix به ثانیه، لحظه ارسال همین تلاش. هر تلاش دوباره مقدار تازه و امضای تازه دارد.
Webhook-Signature ⁦v1=⁩ و پس از آن HMAC-SHA256 به hex با حروف کوچک از رشته «⁦{Webhook-Timestamp}.{raw body}⁩». کلید، کل رمز است، همراه با پیشوند whsec_.
Content-Type همیشه ⁦application/json⁩. بدنه JSON با کدگذاری UTF-8 است.
درخواست
POST /hooks/snapex HTTP/1.1
Content-Type: application/json
Webhook-Id: 6f1c2a8e-3b4d-4f5a-9c7e-1a2b3c4d5e6f
Webhook-Timestamp: 1790337610
Webhook-Signature: v1=2d711642b726b04401627ca9fbac32f5c8530fb1903cc4db02258717921a4881
بدنه
{
  "id": "6f1c2a8e-3b4d-4f5a-9c7e-1a2b3c4d5e6f",   // = Webhook-Id
  "type": "invoice.paid",                          // نوع رویداد، جدول زیر را ببینید
  "created_at": "2026-09-25T12:00:10+00:00",       // زمان وقوع رویداد
  "data": {                                        // شیء در لحظه رویداد
    "invoice_id": "INV-8K2M1Q0P",
    "environment": "live",
    "amount_usd": "15.00",
    "charge_usd": "15.00",
    "credit_usd": "13.85",
    "status": "paid",
    "external_id": "order-15",
    "paid_at": "2026-09-25T12:00:10+00:00"
  }
}

محتوای data به نوع رویداد بستگی دارد: ⁦invoice.*⁩ — فاکتور با همان ساختار ⁦GET /invoices/{id}⁩ (attempts خالی است و تلاش فعلی در payment است)؛ ⁦payment.*⁩ — واریز (payment_id، external_id، status، مبلغ‌ها)؛ ⁦payout.*⁩ — برداشت (payout_id، external_id، status، مبلغ‌ها)؛ ⁦conversion.*⁩ — تبدیل (conversion_id، status، مبلغ‌ها)؛ balance.changed — asset_id، entry_type، available_atomic و held_atomic پس از تغییر.

انواع رویداد

رویداد معنی
invoice.created فاکتور ساخته شد؛ روش پرداخت هنوز انتخاب نشده است (open).
invoice.awaiting روش پرداخت انتخاب و آدرس صادر شد (awaiting).
invoice.underpaid بخشی از مبلغ رسید؛ همان آدرس منتظر باقی‌مانده است (underpaid).
invoice.paid کل مبلغ پرداخت و واریز شد (paid، نهایی). تنها سیگنال برای ارسال سفارش.
invoice.partially_paid مشتری فقط بخشی از مبلغ را پرداخت کرد، مهلت تکمیل تمام شد و بخش دریافت‌شده به موجودی شما واریز شد (partially_paid، نهایی).
invoice.expired مهلت فاکتور بدون پرداخت تمام شد (expired، نهایی).
invoice.extra_payment پس از پرداخت فاکتور، انتقال دیگری رسید؛ جداگانه واریز می‌شود.
payment.created یک واریز ساخته شد.
payment.detected انتقال در شبکه دیده شد اما هنوز به موجودی واریز نشده است.
payment.credited واریز به موجودی نشست (یکی از وضعیت‌های ⁦confirmed*⁩).
payment.expired مهلت واریز تمام شد.
payout.created برداشت ساخته و وجه آن رزرو شد.
payout.processing برداشت در حال ارسال است.
payout.completed وجه به گیرنده رسید.
payout.failed برداشت ناموفق بود.
conversion.completed تبدیل انجام شد.
conversion.failed تبدیل به‌طور کامل یا جزئی ناموفق بود.
balance.changed یک ردیف موجودی ثبت شد: واریز، رزرو یا کسر.

سفارش را فقط با invoice.paid ارسال کنید — این رویداد فقط وقتی فرستاده می‌شود که کل مبلغ پرداخت شده باشد. invoice.underpaid و invoice.partially_paid یعنی مبلغ کامل نیست.

فاکتورهایی که با ⁦environment=test⁩ ساخته می‌شوند شبیه‌سازی هستند: همان وضعیت‌ها را طی می‌کنند و همان وب‌هوک‌ها را می‌فرستند (⁦data.environment = "test"⁩)، اما هرگز موجودی واقعی را تغییر نمی‌دهند.

تحویل و تلاش دوباره

  • هر پاسخ 2xx موفق است. هر کد دیگر، پایان مهلت یا خطای شبکه ناموفق حساب می‌شود. بدنه پاسخ بررسی نمی‌شود.
  • مهلت‌ها: ۵ ثانیه برای اتصال، ۱۵ ثانیه برای کل درخواست.
  • ریدایرکت دنبال نمی‌شود: ⁦301/302⁩ ناموفق حساب می‌شود. URL نهایی را وارد کنید.
  • حداکثر ۵ تلاش برای هر رویداد: تلاش اول و سپس تلاش‌های دوباره حدود ۱۰ ثانیه، ۱ دقیقه، ۵ دقیقه و ۱۵ دقیقه پس از هر شکست. پس از پنجمین شکست، تحویل خودکار متوقف می‌شود.
  • کارت «ارسال‌های وب‌هوک» در همین صفحه رویداد، کد HTTP یا خطا، زمان و شماره تلاش را نشان می‌دهد. دکمه «ارسال دوباره» رویداد را دوباره با همان Webhook-Id می‌فرستد.
  • تحویل «دست‌کم یک بار» است: یک رویداد ممکن است چند بار برسد و رویدادها ممکن است بی‌ترتیب برسند (تلاش دوباره یک رویداد ممکن است بعد از رویداد جدیدتر برسد).

بررسی امضا

  1. بدنه خام درخواست را به‌صورت بایت و پیش از پارس‌کردن JSON بخوانید. JSONی که دوباره سریال‌سازی شود با امضا مطابقت نخواهد داشت.
  2. HMAC-SHA256 را روی «⁦{Webhook-Timestamp}.{raw body}⁩» با رمز ⁦whsec_…⁩ خود به‌عنوان کلید محاسبه و به hex تبدیل کنید.
  3. «⁦v1=⁩» + hex را با هدر Webhook-Signature با یک تابع زمان‌ثابت مقایسه کنید: hash_equals، crypto.timingSafeEqual، hmac.compare_digest.
  4. Webhook-Timestamp را که بیش از ۵ دقیقه با ساعت شما اختلاف دارد رد کنید (توصیه‌شده). تلاش‌های دوباره زمان تازه دارند، پس این کار آن‌ها را خراب نمی‌کند.
PHP
<?php
$secret = getenv('SNAPEX_WEBHOOK_SECRET'); // whsec_… از پنل
$body = file_get_contents('php://input');  // بدنه خام، پیش از پارس JSON
$timestamp = $_SERVER['HTTP_WEBHOOK_TIMESTAMP'] ?? '';
$signature = $_SERVER['HTTP_WEBHOOK_SIGNATURE'] ?? '';

$expected = 'v1='.hash_hmac('sha256', $timestamp.'.'.$body, $secret);
if (! ctype_digit($timestamp)
    || abs(time() - (int) $timestamp) > 300
    || ! hash_equals($expected, $signature)) {
    http_response_code(400);
    exit;
}

$event = json_decode($body, true);
// اگر این Webhook-Id قبلاً پردازش شده، از آن بگذرید؛ کار سنگین را در صف انجام دهید
http_response_code(200);

چک‌لیست هندلر

  • امضا را روی بدنه خام و با مقایسه زمان‌ثابت بررسی کنید؛ اگر مطابقت نداشت، 400 برگردانید.
  • درخواست‌هایی را که Webhook-Timestamp آن‌ها قدیمی‌تر از ۵ دقیقه است رد کنید.
  • مقدارهای Webhook-Id پردازش‌شده را ذخیره کنید و تکراری‌ها را نادیده بگیرید. به ترتیب رسیدن تکیه نکنید: وضعیت داخل رویداد را با آنچه از قبل دارید مقایسه کنید.
  • سریع 2xx برگردانید، خیلی زودتر از ۱۵ ثانیه، و کار سنگین را ناهمگام انجام دهید.
  • پس از وب‌هوک می‌توانید برای اطمینان، شیء را دوباره از طریق API بخوانید: ⁦GET /invoices/{id}⁩، ⁦GET /payments/{id}⁩، ⁦GET /payouts/{id}⁩.

وضعیت‌ها

نشان «نهایی» یعنی وضعیت دیگر تغییر نمی‌کند. تنها استثنا واریزهای منقضی‌شده است: انتقالی که دیر برسد هنوز ممکن است دریافت شود.

واریزها

وضعیت معنی چه باید کرد
awaiting_deposit آدرس صادر شد، در انتظار انتقال آدرس و مبلغ را به مشتری نشان دهید
detected انتقال در شبکه دیده شد، هنوز واریز نشده صبر کنید. سفارش را نبندید
processing در حال تبدیل به USDT صبر کنید
confirmed مبلغ مورد انتظار رسید، وجه واریز شد سفارش را ببندید نهایی
confirmed_underpaid کمتر از مبلغ مورد انتظار رسید، در محدوده مجاز credited_amount را بررسی کنید و تصمیم بگیرید که کافی است یا نه نهایی
confirmed_overpaid بیشتر از مبلغ مورد انتظار رسید، طبق credit-preview واریز شد سفارش را ببندید. درباره مبلغ اضافه با پشتیبانی تماس بگیرید نهایی
confirmed_mismatch برای سازگاری با نسخه‌های قبلی نگه داشته شده برای پرداخت‌های جدید استفاده نمی‌شود نهایی
manual_review نیاز به بررسی دستی منتظر بررسی بمانید؛ پرداخت دوم نسازید
expired مهلت پرداخت تمام شد، آدرس تا حدود 6 ساعت دیگر پایش می‌شود سفارش را حذف نکنید؛ استعلام را ادامه دهید
failed خطا در سمت ارائه‌دهنده پرداخت تازه بسازید نهایی
cancelled پرداخت لغو شد پرداخت تازه بسازید نهایی
refunded ارائه‌دهنده وجه را برگرداند موجودی را تطبیق دهید؛ اگر مطابقت نداشت، با پشتیبانی تماس بگیرید نهایی

فاکتورها

وضعیت معنی چه باید کرد
open فاکتور ساخته شد، روش پرداخت انتخاب نشده pay_url را به مشتری بدهید یا asset_id را خودتان انتخاب کنید
awaiting آدرس صادر شد، در انتظار انتقال آدرس و مبلغ را نشان دهید؛ وضعیت را استعلام کنید
underpaid بخشی از مبلغ رسید؛ همان آدرس از مشتری بخواهید باقی‌مانده را به همین آدرس بفرستد
paid اولین انتقال کامل واریز شد سفارش را ببندید نهایی
partially_paid مشتری فقط بخشی از مبلغ را پرداخت کرد، مهلت تکمیل تمام شد و بخش دریافت‌شده به موجودی شما واریز شد سفارش را به‌عنوان پرداخت‌شده ارسال نکنید؛ باقی‌مانده را با مشتری تسویه کنید نهایی
expired مهلت فاکتور تمام شد فاکتور تازه بسازید نهایی

برداشت‌ها

وضعیت معنی چه باید کرد
reserved وجه برای برداشت رزرو شد صبر کنید
submitting به ارائه‌دهنده فرستاده شد صبر کنید
processing ارائه‌دهنده در حال ارسال در بلاکچین است صبر کنید
completed وجه به گیرنده رسید انجام شد نهایی
manual_review نتیجه مشخص نیست برداشت دیگری نسازید؛ منتظر بررسی بمانید
failed انتقال ناموفق بود؛ مبلغ رزروشده معمولاً آزاد می‌شود موجودی را بررسی کنید؛ در صورت نیاز با Idempotency-Key تازه دوباره تلاش کنید نهایی

خطاها

پاسخ موفق همیشه در data است و خطا همیشه در error. قالب دیگری برای پاسخ وجود ندارد.

ساختار خطا
{
  "error": {
    "code": "validation_error",     // کد قابل‌پردازش برای برنامه، جدول زیر را ببینید
    "message": "The request is invalid.",
    "details": {                    // همیشه وجود ندارد
      "destination_address": ["The destination address field is required."]
                                    // کلید، نام فیلدی است که اعتبارسنجی آن رد شد
    }
  }
}
HTTP code چه زمانی چه باید کرد
401 unauthorized کلید فرستاده نشده یا نامعتبر است هدر ⁦Authorization: Bearer smk_…⁩ را کامل و بدون فاصله یا شکست خط بررسی کنید
401 key_inactive کلید باطل شده است کلید قبلی باطل شده است. کلید فعلی را از پنل بگیرید
401 key_expired کلید منقضی شده است کلید تازه صادر کنید
404 not_found شناسه پیدا نشد یا متعلق به فروشنده دیگری است از payment_id یا payout_id پاسخ‌های خودتان استفاده کنید
409 conflict Idempotency-Key به عملیات دیگری گره خورده است، یا external_id قبلاً استفاده شده است برای عملیات تازه کلید تازه بگذارید. برای تکرار، همان کلید و همان پارامترها را بفرستید
422 validation_error فیلدهای نامعتبر، credit-preview منقضی‌شده، موجودی ناکافی یا نبود Idempotency-Key فیلد و علت را در details ببینید
429 rate_limited از سقف تعداد درخواست بیشتر شد بعداً با همان Idempotency-Key دوباره تلاش کنید
500 internal_error خطای داخلی سرور بعداً با همان Idempotency-Key دوباره تلاش کنید. اگر ادامه داشت، با پشتیبانی تماس بگیرید و X-Request-Id را بفرستید