پرداخت به مشتری و بازپرداخت
فرستادن پول به یک مشتری کوینلند، بازپرداخت یک پرداخت، و کلاس کلید جداگانهای که هر دو لازم دارند
کوینلند پی پول را در دو جهت جابهجا میکند. گرفتن پرداخت یعنی یک جلسه پرداخت که مشتری تأییدش میکند. فرستادن پول یعنی پرداخت به مشتری: شما یک گیرنده و یک مبلغ تعیین میکنید و ارز از کیفپول کسبوکار شما به موجودی کوینلند او میرود.
بازپرداخت همان عمل است که به پرداختی نشانه رفته که قبلاً گرفتهاید، و همان شیء را برمیگرداند. تفاوتش این است که گیرنده و ارزِ بازپرداخت از رکورد همان پرداخت خوانده میشوند نه از شما، و اینکه بازپرداخت رایگان است.
پرداخت به مشتری همزمان است. کد ۲۰۱ یعنی پول رفته
نه وضعیت «در انتظار»ی هست، نه مرحله تأییدی، و نه چیزی برای استعلام. تا وقتی پاسخ را بخوانید، کیفپول
شما بدهکار و گیرنده بستانکار شده است. وقفه زمانی روی این اندپوینت را مثل وقفه زمانی روی یک حواله بانکی
بگیرید: همان درخواست را با همان reference_id دوباره بفرستید و بگذارید ایدمپوتنسی به شما بگوید چه شده.
کلید پرداخت به مشتری
پرداخت به مشتری کلاس کلید مخصوص خودش را میخواهد. کلید پرداختگیریِ شما نمیتواند آن را صدا بزند، و کلید پرداخت به مشتری نمیتواند جلسه بسازد.
clpay_live_<64 hex> کلاس پرداختگیری -- جلسهها، پرداختها، رسیدها
clpay_payout_<64 hex> کلاس پرداخت به مشتری -- پرداخت به مشتری، بازپرداختدر کنسول کسبوکار یکی بسازید و کلاس پرداخت به مشتری را انتخاب کنید. مثل کلید پرداختگیری، فقط یک بار نمایش داده میشود و فقط هَش آن ذخیره میگردد، و از هر کلاس تا ۵ کلید فعال میتوانید داشته باشید.
کلید پرداخت به مشتری GET /payments، GET /payments/{id} و GET /me را هم میخواند، چون مغایرتگیری
میان آنچه فرستادهاید و آنچه گرفتهاید به هر دو طرف نیاز دارد. بیرون از این، روی سطح پرداختگیری کاری از آن
برنمیآید.
استفاده از کلاس اشتباه PAY_WRONG_KEY_KIND (۴۰۳) میگیرد، نه خطای احراز هویت — کلید معتبر است، فقط آن
یکی را میخواهید.
چرا این جدایی ارزش یک اعتبارنامه دوم را دارد
کلید پرداختگیری همان کلیدی است که سر از جاهای بیشتری درمیآورد: در سرویسی که جلسه میسازد، در محیط آزمایشی، در خط استقرار. جدا کردن دو کلاس یعنی کلیدی که بیشتر کپی میشود نمیتواند از کیفپول شما پول بیرون بفرستد، و باطل کردنش جلوی پرداختگیری شما را نمیگیرد.
پیش از اولین پرداخت به مشتری
پرداخت به مشتری خاموش است تا وقتی کوینلند آن را برای کسبوکار شما روشن کند، و روشن کردنش یعنی دو چیز، نه یکی:
- پرداخت به مشتری برای حساب شما فعال شده باشد.
- هر دو سقف دلاری شما تعیین شده باشد — یک بیشینه برای هر پرداخت، و یک سقف برای ۲۴ ساعت گذشته.
سقفها اختیاری نیستند و حالت «بینهایت» وجود ندارد. حسابی که پرداخت به مشتریاش فعال شده اما سقفی برایش تنظیم نشده، مسلح نیست و هر پرداختی را رد میکند: پولی که بیرون میرود هرگز بهخاطر جاافتادن یک تنظیم بیحدومرز نمیشود.
تا وقتی همه اینها سر جایشان نباشند، هر نوشتنی MERCHANT_PAYOUTS_DISABLED (۴۰۳) میگیرد. همین یک کد،
کسبوکار معلق و ریلی را که کوینلند در کل پلتفرم متوقف کرده هر دو پوشش میدهد، چون راهحل هر دو یکی است:
با ما تماس بگیرید. این کدی نیست که با تلاش مجدد رد شود.
پرداخت به مشتریای که به شما پرداخت کرده
هر شیء پرداخت یک payer_id دارد: یک دستگیره مبهم برای مشتریای که پرداخت کرده، محدود به کسبوکار شما.
{
"id": "b92e4d17-6c38-4a05-9f2b-1e7d3c8a5049",
"reference_id": "order-10492",
"currency": "usdt",
"amount": "24.90",
"payer_id": "7c1e5b90-3f42-4a86-9d05-2b8e4c1f6a37",
"paid_at": "2026-08-11T08:12:44.000Z"
}این دستگیره پایدار است: همان مشتری اگر ماه بعد دوباره به شما پرداخت کند، همین مقدار را میآورد. شناسه کاربری کوینلند نیست، برای هیچ کسبوکار دیگری معنایی ندارد، و چیزی درباره خودِ شخص فاش نمیکند. ضمناً تمام آن چیزی است که برای تعیین گیرنده لازم دارید — کنار رکورد مشتریِ خودتان ذخیرهاش کنید و دیگر هیچوقت برای پرداخت به او به نشانی ایمیل نیاز نخواهید داشت.
برای هر پرداختی که تا امروز روی این ریل گرفته شده دستگیره وجود دارد، حتی پرداختهایی که پیش از وجود این قابلیت انجام شدهاند.
curl -X POST https://my.coinlandexchange.com/api/pay/v1/payouts \
-H "Authorization: Bearer $COINLAND_PAY_PAYOUT_KEY" \
-H "Content-Type: application/json" \
-d '{
"reference_id": "payout-2291",
"currency": "usdt",
"amount": "25.00",
"payer_id": "7c1e5b90-3f42-4a86-9d05-2b8e4c1f6a37",
"comment": "Cashback for order 10492"
}'{
"id": "9e3c7a41-0b52-4f18-8d6a-3c7e1f9b40d5",
"reference_id": "payout-2291",
"kind": "payout",
"status": "completed",
"currency": "usdt",
"amount": "25.00",
"debited_amount": "25.125",
"fee_amount": "0.125",
"fee_percent": "0.5",
"usd_value": "25.00",
"payer_id": "7c1e5b90-3f42-4a86-9d05-2b8e4c1f6a37",
"payment_id": null,
"comment": "Cashback for order 10492",
"created_at": "2026-08-11T09:31:04.000Z",
"settled_at": "2026-08-11T09:31:04.000Z"
}لازم نیست ارز، یکی از ارزهایی باشد که در پرداختگیری میپذیرید. هر ارز فعالی که کیفپول کسبوکارتان دارد قابل پرداخت است، و این وقتی اهمیت پیدا میکند که درآمدتان را به یک استیبلکوین تبدیل میکنید. تومان رد میشود: این ریل در هر دو جهت فقط رمزارز است.
پرداخت با نشانی ایمیل
برای گیرندهای که تا حالا به شما پرداخت نکرده دستگیرهای در کار نیست، پس مسیر دومی وجود دارد: نشانی را جستوجو کنید، نامی را که برمیگردد به یک انسان نشان دهید، و توکن حاصل را خرج کنید.
این مسیر بهصورت پیشفرض خاموش است. کوینلند آن را برای هر کسبوکار جداگانه فعال میکند، و جایی که فعال
نباشد جستوجو MERCHANT_PAYOUTS_DISABLED میگیرد — همان پاسخی که یک نشانی غیرقابلپرداخت میگیرد، پس با
این اندپوینت حتی نمیشود فهمید قابلیت روشن است یا نه.
نشانی را جستوجو کنید.
curl -X POST https://my.coinlandexchange.com/api/pay/v1/payouts/recipients/lookup \
-H "Authorization: Bearer $COINLAND_PAY_PAYOUT_KEY" \
-H "Content-Type: application/json" \
-d '{"email": "customer@example.com"}'{
"recipient_token": "v1.eyJtZXJjaGFudElkIjo0Miwi….9f3c1d60ab72",
"masked_name": "A**** B****",
"expires_at": "2026-08-11T09:41:22.000Z"
}توکن ده دقیقه اعتبار دارد، فقط برای کسبوکاری که درخواستش کرده کار میکند، و گیرنده حلشده را داخل امضای خودش حمل میکند. نشانی ایمیل هرگز روی درخواست پرداخت سفر نمیکند.
masked_name را به یک انسان نشان دهید و تأیید بگیرید.
ماسک آنقدر هست که کسی را که از قبل قصد پرداخت به او را داشتهاید بازبشناسید، و آنقدر نیست که یک غریبه را شناسایی کند. کل نکتهاش همین است: نشانی اشتباه تایپشده را پیش از جابهجا شدن پول میگیرد، بیآنکه فهرست مشتریان ما را به چیزی خواندنی تبدیل کند.
پرداخت را بسازید و نام ماسکشده را عیناً برگردانید.
curl -X POST https://my.coinlandexchange.com/api/pay/v1/payouts \
-H "Authorization: Bearer $COINLAND_PAY_PAYOUT_KEY" \
-H "Content-Type: application/json" \
-d '{
"reference_id": "payout-2292",
"currency": "usdt",
"amount": "40.50",
"recipient_token": "v1.eyJtZXJjaGFudElkIjo0Miwi….9f3c1d60ab72",
"recipient_confirm": "A**** B****"
}'recipient_confirm باید کاراکتر به کاراکتر با masked_name یکی باشد. ناهمخوانی رد میشود، و همین است
که مرحله دوم را به یک بررسی واقعی تبدیل میکند، نه صفحهای که کسی از رویش رد شود.
پاسخ برای گیرنده یک payer_id میآورد، پس پرداخت بعدی به همان شخص میتواند همه این مراحل را رد کند و از
دستگیره استفاده کند.
هر شکستِ گیرنده یک کد دارد
نشانیای که حساب کوینلند ندارد، حساب غیرفعال، حسابی که احراز هویتش را تمام نکرده، توکن منقضی، توکنی که
برای کسبوکار دیگری ساخته شده، تأییدی که نمیخوانَد — همه PAY_RECIPIENT_INVALID (۴۲۲) میگیرند.
اندپوینت عامدانه به شما نمیگوید کدامیک، چون جستوجویی که اینها را از هم جدا کند راهی میشود برای
فهمیدن اینکه چه کسی نزد ما حساب دارد.
جستوجو برای هر کسبوکار سهمیه هم دارد. عبور از سهمیه PAY_LOOKUP_THROTTLED (۴۲۹) است و فقط جستوجو را
متوقف میکند — پرداخت با دستگیره سر جایش کار میکند.
بازپرداخت
بازپرداخت، ارز را به مشتریای که به شما پرداخت کرده برمیگرداند. آن را با شناسه پرداخت یا شماره رسید خطاب کنید و فقط یک مبلغ بفرستید:
curl -X POST https://my.coinlandexchange.com/api/pay/v1/payments/CLP-10492-8F3A/refund \
-H "Authorization: Bearer $COINLAND_PAY_PAYOUT_KEY" \
-H "Content-Type: application/json" \
-d '{
"reference_id": "refund-10492-1",
"amount": "10.00",
"comment": "One item returned"
}'فیلد گیرنده و فیلد ارز وجود ندارد، و این عمدی است: هر دو از خودِ پرداخت میآیند، پس بازپرداخت فقط میتواند از همان راهی برگردد که پول آمده بود.
- بازپرداخت جزئی مجاز است و میتوانید چند بار روی یک پرداخت انجامش دهید.
- سقف، تجمعی است و برابر
charged_amountهمان پرداخت. بازپرداختی که جمع را از آن عبور دهدPAY_REFUND_EXCEEDS_PAYMENT(۴۲۲) میگیرد، و خطا میگوید تا حالا چقدر بازپرداخت شده است. - بازپرداخت رایگان است.
fee_amountبرابر"0"وdebited_amountبرابرamountاست، پس بازپرداخت فقط خودِ ارز را برای شما هزینه دارد.
کوینلند کارمزدی را که روی پرداخت اصلی گرفته نگه میدارد و در مسیر برگشت کارمزد تازهای نمیگیرد. یک جریان یک بار کارمزد دارد: نه برای فروشی که برگشته دو بار از شما گرفته میشود، و نه کارمزد اصلی برگردانده میشود.
نتیجه بهشکل یک پرداخت با kind برابر refund خوانده میشود و شناسه پرداختِ بازپرداختشده در
payment_id میآید.
کارمزد
پرداخت به مشتری با همان نرخ پلکانی پرداختهایتان حساب میشود و همیشه شما آن را میپردازید. گیرنده
دقیقاً همان amount را که تعیین کردهاید دریافت میکند — هیچ حالتی وجود ندارد که به او یک عدد نشان داده
شود و عدد دیگری به حسابش بنشیند.
گیرنده دریافت میکند amount
کیفپول شما میپردازد debited_amount == amount + fee_amountرابطه debited_amount - amount == fee_amount روی هر پرداختی دقیقاً برقرار است. کیفپولتان را با
debited_amount و آنچه به گیرنده قول دادهاید را با amount مغایرتگیری کنید؛ استفاده از یک عدد برای هر
دو همان چیزی است که دفتر حساب را دچار انحراف میکند.
حجم پرداخت به مشتری در حجم دلاری ۳۰ روزهای که پله کارمزد شما را تعیین میکند به حساب میآید، پس پولی که میفرستید به رسیدن شما به نرخ بهتر کمک میکند. آستانههای تعداد همچنان فقط پرداختها را میشمارند.
سقفها
دو سقف اعمال میشود، هر دو دلاری، و هر دو را کوینلند تعیین میکند نه شما.
| سقف | روی چه چیزی | عبور از آن |
|---|---|---|
| بیشینه هر پرداخت | یک پرداخت | PAY_PAYOUT_LIMIT (۴۲۲)، مقدار details.limit برابر per-payout |
| سقف ۲۴ ساعت گذشته | جمع ۲۴ ساعت اخیر | PAY_PAYOUT_LIMIT (۴۲۲)، مقدار details.limit برابر daily |
بازپرداخت از بیشینه هر پرداخت معاف است — سقفی کمتر از یک پرداخت نباید آن پرداخت را غیرقابلبازگشت کند — اما همچنان در سقف روزانه به حساب میآید.
هر دو با دلار سنجیده میشوند، یعنی پرداخت فقط وقتی میرود که همین حالا بشود ارز را ارزشگذاری کرد. اگر نرخ
زندهای در دسترس نباشد پرداخت با PAY_RATE_UNAVAILABLE (۵۰۳) رد میشود، بهجای آنکه بدون سنجش برود.
درخواست مشکلی ندارد؛ دوباره تلاش کنید.
اگر کیفپول کسبوکارتان amount + fee را پوشش ندهد، پاسخ INSUFFICIENT_BALANCE (۴۲۲) است. در کنسول از
موجودی اسپات به کیفپول کسبوکار منتقل کنید و دوباره تلاش کنید.
ایدمپوتنسی
reference_id کلید ایدمپوتنسی شماست و دقیقاً مثل جلسههای پرداخت کار میکند:
- همان
reference_idبا محتوای یکسان، پرداخت اصلی را با کد۲۰۱برمیگرداند. انتقال دومی رخ نمیدهد. - همان
reference_idبا محتوای متفاوت،PAY_DUPLICATE_REFERENCE(۴۰۹) میگیرد.
// A timeout tells you nothing about whether the payout settled. Resend the
// identical request -- never a fresh reference_id, which is how one payout
// becomes two.
async function payOut(body) {
for (let attempt = 0; attempt < 3; attempt++) {
try {
return await post("/api/pay/v1/payouts", body);
} catch (err) {
if (!isTimeout(err)) throw err;
await sleep(2 ** attempt * 1000);
}
}
// Still unsure? Read it back by your own id.
return get(`/api/pay/v1/payouts/${body.reference_id}`);
}GET /payouts/{id} هم شناسه پرداخت را میپذیرد و هم reference_id خودتان را، و همین خط آخر را به راهی
مطمئن برای بستن پرونده پس از یک خطای شبکه تبدیل میکند.
چطور بفهمید انجام شده
چون پرداخت داخل همان درخواست تسویه میشود، پاسخ خودش معتبر است و معمولاً به چیز دیگری نیاز ندارید. یک
وبهوک payout.completed هم ارسال میشود، برای حالتی که پرداخت جایی بیرون از کد خودتان ساخته شده باشد —
مثلاً از کنسول کسبوکار:
{
"event_id": "a17b3e50-9d24-4c81-b6f3-5e0a2c7d1948",
"type": "payout.completed",
"payout_id": "9e3c7a41-0b52-4f18-8d6a-3c7e1f9b40d5",
"reference_id": "payout-2291",
"kind": "payout",
"status": "completed"
}همان طرح امضا، همان پنجره پنجدقیقهای، همان قاعده حذف تکراریها، و همان هشدار: محتوا یک سرنخ است. هیچ
مبلغی حمل نمیکند و هر چیزی که بر پایهاش عمل میکنید باید از GET /payouts/{id} بیاید. کد اعتبارسنجی در
وبهوکها است.
توجه کنید که kind روی همین رویداد پرداخت به مشتری را از بازپرداخت جدا میکند، پس هندلری که مثلاً موجودی
وفاداری مشتری را روی پرداختها بهروز میکند باید بر پایه آن شاخه بگذارد، نه اینکه فرض کند.
خطاها
| کد | HTTP | معنا | چه کاری کنید |
|---|---|---|---|
MERCHANT_PAYOUTS_DISABLED | ۴۰۳ | پرداخت به مشتری برای کسبوکار شما مسلح نیست، یا مسیر ایمیل برایتان فعال نشده | با کوینلند تماس بگیرید. با تلاش مجدد رد نمیشود |
PAY_WRONG_KEY_KIND | ۴۰۳ | کلید پرداختگیری روی مسیر پرداخت به مشتری، یا برعکس | از کلید کلاس دیگر استفاده کنید |
PAY_RECIPIENT_INVALID | ۴۲۲ | گیرنده قابل پرداخت نیست، یا هر دو مسیر گیرنده را فرستادهاید، یا هیچکدام | دستگیره را بررسی یا جستوجو را تکرار کنید. یک کد عامدانه همه دلیلها را پوشش میدهد |
PAY_PAYOUT_LIMIT | ۴۲۲ | عبور از بیشینه هر پرداخت یا سقف ۲۴ ساعته | details.limit را بخوانید. پرداخت را بشکنید، منتظر پایان بازه بمانید، یا از کوینلند افزایش سقف بخواهید |
PAY_REFUND_EXCEEDS_PAYMENT | ۴۲۲ | جمع بازپرداختها از charged_amount آن پرداخت عبور میکند | باقیمانده را بازپرداخت کنید؛ details.already_refunded میگوید چقدر رفته است |
PAY_LOOKUP_THROTTLED | ۴۲۹ | سهمیه جستوجوی دقیقه یا روز تمام شده | عقب بکشید. پرداخت با دستگیره تأثیری نمیگیرد |
INSUFFICIENT_BALANCE | ۴۲۲ | کیفپول کسبوکار amount + fee را پوشش نمیدهد | کیفپول را از موجودی اسپات شارژ و دوباره تلاش کنید |
PAY_RATE_UNAVAILABLE | ۵۰۳ | ارز نرخ زنده دلاری ندارد، پس سقفها قابل اعمال نیستند | دوباره تلاش کنید؛ خودِ درخواست مشکلی ندارد |
PAY_DUPLICATE_REFERENCE | ۴۰۹ | همان reference_id با محتوای متفاوت | محتوای اصلی را دوباره بفرستید، یا برای پرداختی که واقعاً تازه است شناسه تازه بگذارید |
فهرست کامل، شامل کدهای مشترک با پرداختگیری، در صفحه خطاها است.
فهرست بررسی
- کلید پرداخت به مشتری جداگانه ساخته و جدا از کلید پرداختگیری ذخیره شده باشد.
payer_idدر لحظه پرداخت کنار رکورد مشتری خودتان ذخیره شود.reference_idاز چیزی پایدار در سیستم خودتان بیاید، نه یک مقدار تصادفی برای هر تلاش.- وقفه زمانی با همان درخواست تکرار شود، هرگز با شناسه تازه.
- هر دو مقدار
amountوdebited_amountبهصورت رشته اعشاری ذخیره شوند. - پیش از هر پرداخت از مسیر ایمیل، یک انسان
masked_nameرا تأیید کند. PAY_PAYOUT_LIMITیک وضعیت کسبوکاری تلقی شود نه یک باگ — کسی باید از آن باخبر شود.