API نمایندگی
API نمایندگی راهی است که یک شریک تجاری با آن مشتریان خودش را روی نسین از سامانههای خودش اداره میکند — وبسایتش، پنل صورتحسابش، یا یک نصب WHMCS — بدون اینکه کسی وارد داشبورد شود.
- آدرس پایه:
https://api.nsin.cloud - پیشوند: هر مسیر در این API با
/reseller/v1شروع میشود - احراز هویت: یک توکن ماشین که خودتان میسازید
- مرجع کامل نقاط پایانی: Reseller API Reference
- Swagger UI: آزمودن نقاط پایانی — درخواست واقعی میفرستد
- مرورگر تکصفحهای: مرجع یکصفحهای
- سند ماشینخوان:
/docs/reseller-v1.yaml
این محصولی جدا از REST API مشتری است. مخاطب متفاوت،
اعتبارنامهی متفاوت، پیشوند متفاوت. یک کلید API پنل (nsin_…) نمیتواند آن را صدا
بزند، و توکن نماینده هم نمیتواند API مشتری را صدا بزند.
نسین کیست
Section titled “نسین کیست”نسین ارائهدهنده است، نه نماینده. «نمایندهی شماره ۱» وجود ندارد. مشتری مستقیم
نسین reseller_id = null دارد؛ مشتریان شما شناسهی شما را دارند.
احراز هویت
Section titled “احراز هویت”یک توکن بسازید و آن را بهعنوان اعتبارنامهی bearer بفرستید:
curl -H "Authorization: Bearer nsin_live_xxxxxxxxxxxx_yyyy…" \ https://api.nsin.cloud/reseller/v1/pingGET /reseller/v1/ping بررسی ارزان زندهبودن و درستی اعتبارنامه است. TestConnection
در WHMCS به همین نگاشت میشود.
ساختار توکن ماشین چنین است:
nsin_live_<key_id>_<secret> └ عمومی ┘ └ محرمانه ┘key_id بهصورت متن ساده ذخیره و ایندکس میشود و نمایش و ثبت آن در لاگ بیخطر است —
نسین با همان توکن را پیدا میکند. اما بخش محرمانه فقط بهصورت هش bcrypt ذخیره
میشود و دقیقاً یک بار، هنگام ساخت، نمایش داده میشود. هیچ چیزی در سامانه نمیتواند
بعداً آن را بازیابی کند؛ اگر گمش کردید، توکن را باطل کنید و یکی دیگر بسازید.
دو نوع اعتبارنامه در همان هدر Authorization: Bearer میآیند و میانافزار با پیشوند
آنها را از هم تشخیص میدهد:
| اعتبارنامه | استفادهکننده | نحوهی بررسی |
|---|---|---|
nsin_live_… | کلاینتهای ماشینی — WHMCS، سایت خودتان | key_id جستوجو و بخش محرمانه با هش bcrypt آن بررسی میشود |
| هر چیز دیگر | داشبورد نمایندگی | مثل JWT نشست تفسیر میشود |
در هر دو حالت هندلر به یک هویت واحد میرسد، و هر هندلر پیش از دستزدن به هر ردیفی بررسی میکند که مشتری یا دامنهای که نام بردهاید مال شماست.
شما کیف پول خودتان را به ارزش اسمی از طریق درگاه زرینپال شارژ میکنید — این بخش جزو این API نیست. هر چیزی که مشتریانتان میخرند در نهایت از همان موجودی و با قیمت رسمی نسین پرداخت میشود. هیچوقت تخفیف نمایندگی در لحظهی خرید وجود ندارد.
کیف پول مشتری شما واقعی است، اما او هرگز نمیتواند آن را ببیند یا به آن دست بزند:
هر نقطهی پایانی کیف پول، فاکتور، قیمت و تیکت برای کاربری که reseller_id دارد
403 برمیگرداند.
هر فیلد مالی عدد صحیح و بر حسب ریال است (*_rials). برای نمایش به تومان بر ۱۰
تقسیم کنید. هرگز عدد اعشاری نفرستید.
خرید پلن، کیف پول مشتری را خودکار شارژ میکند
Section titled “خرید پلن، کیف پول مشتری را خودکار شارژ میکند”لازم نیست پیش از خرید پلن، کیف پول مشتری را شارژ کنید. ساخت یا تغییر سرویس
حساب میکند که کیف پول مشتری چقدر کم دارد، دقیقاً همان مقدار را از کیف پول شما به
او منتقل میکند و بعد از او کسر میکند — همه در یک تراکنش پایگاه داده. این انتقال
در هر دو دفتر با عنوان reseller_autofund ثبت میشود.
POST /reseller/v1/clients/{clientId}/credit همچنان وجود دارد و همچنان مفید است —
برای گذاشتن موجودی اولیه روی یک حساب، یا شارژ خارج از فرآیند خرید. فقط پیشنیاز
نیست؛ شارژ قبلی صرفاً باعث میشود انتقال خودکار کاری نکند.
برخلاف خرید، credit هرگز شما را به منفی نمیبرد: نمیتوانید بیش از موجودی فعلی
خود منتقل کنید.
آن 402 که واقعاً میبینید
Section titled “آن 402 که واقعاً میبینید”وقتی خریدی بهخاطر پول شکست میخورد، کیف پولی که کم آورده مال شماست، نه مشتری:
{ "error": "This purchase would take your wallet past your credit limit. Top up, or ask NSIN to raise the limit.", "code": "credit_limit_reached" }کیف پول خودتان را از طریق زرینپال شارژ کنید، یا از نسین بخواهید سقف اعتبارتان را
بالا ببرد. شارژکردن مشتری کمکی نمیکند — و خودش هم 402 میگیرد.
نسین میتواند به شما سقف اعتبار منفی بدهد. این مقدار بهصورت پیشفرض صفر است، یعنی بهطور پیشفرض هر خریدی که توانش را نداشته باشید دقیقاً همین خطا را میدهد. اگر سقف اعتبار داشته باشید و بعد از پایان مهلت همچنان منفی بمانید، همهی دامنههای مجموعهی شما — مال خودتان و مشتریانتان — تعلیق میشوند.
مشتریان شما سقف دارند، نه کنتور
Section titled “مشتریان شما سقف دارند، نه کنتور”مشتری یک نماینده پرداخت بهازای مصرف ندارد. ترافیک بیش از حجم پلن به هیچکس صورتحساب نمیشود: ردیف مصرف با مبلغ صفر ثبت میشود و هیچ کیف پولی کسر نمیشود. وقتی به سقف پلن برسد، بهجای صورتحساب تعلیق میشود.
پس موجودی مشتری هرگز بهخاطر ترافیک منفی نمیشود، و نباید روی درآمد مصرف مازاد حساب کنید — چنین چیزی وجود ندارد. پلن بالاتر را بفروشید.
تکرارپذیری (Idempotency)
Section titled “تکرارپذیری (Idempotency)”هر نقطهی پایانی که پول جابهجا میکند، هدر Idempotency-Key را الزامی میداند.
curl -X POST https://api.nsin.cloud/reseller/v1/clients/7/credit \ -H "Authorization: Bearer $NSIN_RESELLER_TOKEN" \ -H "Idempotency-Key: 5f9c1b7e-3a2d-4c11-9d40-6f2f0a1b8e33" \ -H "Content-Type: application/json" \ -d '{"amount_rials": 5000000}'کلید هر رشتهی یکتای تولیدشده توسط شماست؛ یک UUID کافی است. کلیدهای بلندتر از ۱۹۱ کاراکتر رد میشوند. تکرار یک کلید همان پاسخ اولیه را برمیگرداند و عملیات را دوباره انجام نمیدهد.
این صرفاً یک ادب اختیاری نیست. قطعشدن ارتباط هنگام فراخوانی credit سرور شما را
در وضعیتی میگذارد که نمیداند پول جابهجا شده یا نه، و تلاش دوبارهی سادهلوحانه دو
بار اعتبار میدهد — و دقیقاً معنای «تایماوت» همین است.
چهار ویژگی که باید رویشان حساب کنید:
- تکرار همیشگی است، نه پنجرهای. هیچ انقضایی وجود ندارد: کلیدی که یک سال پیش استفاده کردهاید امروز هم همان پاسخ اولیه را برمیگرداند. برای هر عملیات منطقی یک کلید تازه بسازید و هرگز آنها را زمانبندیشده بازیافت نکنید.
- تکرار برچسب دارد. پاسخ تکراری هدر
Idempotent-Replay: trueرا همراه دارد. این تنها راه تشخیص تکرار از اجرای تازه است — وضعیت و بدنه عمداً یکساناند. - عملیات ناموفق چیزی ثبت نمیکند، پس با همان کلید آزادانه قابل تکرار است. اگر خریدی بهخاطر کمبودن موجودی شما برگشت خورد، شارژ کنید و با همان کلید دوباره بفرستید.
- استفاده از یک کلید برای درخواستی متفاوت
422است، نه تکرار. کلید در برابر نقطهی پایانی و هش بدنهی درخواست بررسی میشود. پاسخدادن به درخواستی متفاوت با پاسخ ذخیرهشده یعنی به شما بگوییم انتقال دوم موفق بوده، در حالی که اصلاً اجرا نشده است.
کلیدها به شما محدودند، پس دو شریک تجاری با UUID یکسان هرگز تداخل نمیکنند.
محدودیت نرخ
Section titled “محدودیت نرخ”بهصورت پیشفرض ۳۰۰ درخواست در دقیقه بهازای هر اعتبارنامه.
سهمیه روی اعتبارنامه حساب میشود، نه روی نماینده — یک یکپارچهسازی از کنترل خارجشده نباید شما را از داشبورد خودتان بیرون بیندازد، و باطلکردن همان توکن برای متوقفکردنش کافی است.
با عبور از سهمیه، پاسخ 429 است:
{ "error": "Rate limit exceeded. Slow down and retry shortly.", "code": "rate_limited" }عقبنشینی کنید و دوباره تلاش کنید؛ اگر هدر Retry-After بود رعایتش کنید. اگر در حال
درونریزی انبوه هستید، بهجای حلقهزدن روی تکتک رکوردها از نقاط پایانی دستهای
(records/import و records/scan-import) استفاده کنید — پخشکردن درخواست روی زونی
با تعداد واقعی رکورد این محدودیت را فعال میکند.
صفحهبندی
Section titled “صفحهبندی”نقاط پایانی فهرست، page (از ۱) و per_page (بین ۱ تا ۱۰۰، پیشفرض ۲۵) میگیرند و
ردیفها را زیر data بههمراه یک شیء meta برمیگردانند:
{ "data": [ … ], "meta": { "page": 1, "per_page": 25, "total": 137 }}بدنهی هر خطا به شکل {"error": "پیام قابل خواندن"} است، گاهی همراه با یک "code"
ماشینخوان.
| کد | معنی |
|---|---|
400 | ورودی نامعتبر یا بدشکل، از جمله نبودِ Idempotency-Key روی نقطهی پایانی مالی. |
401 | اعتبارنامه نبود، بدشکل بود، منقضی یا باطل شده بود. |
402 | موجودی کیف پول شما کفاف نمیدهد — "code": "credit_limit_reached". کیف پول خودتان را شارژ کنید؛ شارژ مشتری کمکی نمیکند. |
403 | احراز هویت شدید، اما هدف مال شما نیست — یا اصلاً نماینده نیستید. |
404 | چنین منبعی وجود ندارد — و منبعی که متعلق به نمایندهی دیگری است هم عمداً همین پاسخ را میگیرد. |
409 | منبع در وضعیتی است که این عملیات را نمیپذیرد. رایجترین مورد "code": "domain_disabled" است. |
422 | یک Idempotency-Key با بدنهی درخواستِ متفاوت دوباره استفاده شده است. |
429 | محدودیت نرخ — "code": "rate_limited". |
503 | انبار آمار در دسترس نیست — عمداً خطا برمیگردد نه صفحهای پر از صفر، چون صفحهی صفر از «ترافیک مشتریانتان قطع شده» قابل تشخیص نیست. |
غیرفعال، نه تعلیق
Section titled “غیرفعال، نه تعلیق”در زبان رو به نماینده، حساب یا دامنه غیرفعال و فعال میشود — هرگز «معلق».
دامنهی غیرفعال فقطخواندنی است: پیکربندیاش را نگه میدارد و همهی بخشهایش را
میتوانید بخوانید، اما نوشتن تا زمان فعالشدن دوباره 409 میگیرد.
این عمداً 409 است و نه 403. شما کاملاً حق این تغییر را دارید؛ دامنه صرفاً در
وضعیتی است که نمیتواند آن را بپذیرد، و گفتن «دسترسی ندارید» به شریک تجاری او را به
نتیجهگیری کاملاً اشتباهی میرساند.
مشتری را نمیتوانید حذف کنید
Section titled “مشتری را نمیتوانید حذف کنید”مسیری برایش وجود ندارد — نه مسیری محافظتشده، اصلاً هیچ مسیری. بهجایش حساب را غیرفعال کنید.
ایزولاسیون
Section titled “ایزولاسیون”هر مسیر این API با یک ماتریس ایزولاسیون مستأجرها آزموده میشود: با اعتبارنامهی یک
نماینده روی شناسههای نمایندهی دیگر صدا زده میشود و باید 403 یا 404 بدهد،
بدون هیچ دادهای از طرف مقابل در بدنه.
چیزی که مشتریان شما هرگز دریافت نمیکنند
Section titled “چیزی که مشتریان شما هرگز دریافت نمیکنند”همهی پیامکها و ایمیلهای تجاری دربارهی مشتری شما — قطعی دامنه، انقضای پلن، منفیشدن موجودی — به شما فرستاده میشود، نه به او.
دو مورد عمداً مستثنا هستند و همچنان به مشتری میروند: رمز یکبارمصرف ورود و بازنشانی رمز عبور. هر دو بازیابی حساباند و مسدودکردنشان مشتری شما را برای همیشه از پنل بیرون میگذارد.
نسخهبندی
Section titled “نسخهبندی”پیشوند /reseller/v1 است. تغییرات افزایشی — نقطهی پایانی جدید، فیلد اختیاری جدید،
عضو جدید در enum یک پاسخ — داخل همان v1 منتشر میشوند و بهعنوان تغییر شکننده اعلام
نمیشوند. حذف یک فیلد، سختگیرانهترکردن یک نوع، یا تغییر یک کد وضعیت به معنای
/reseller/v2 خواهد بود.
کلاینت خود را طوری بنویسید که فیلدهای ناشناختهی پاسخ را نادیده بگیرد.