جلسههای پرداخت
چرخه حیات جلسه پرداخت، ایدمپوتنسی بر پایه reference_id، و اینکه چرا در هر ارزی که میپذیرید قیمت میگذارید
جلسه پرداخت یک پیشنهاد است به یک مشتری: این سفارش، با این قیمتها، تا این مهلت. شما میسازیدش، مشتری پرداختش میکند، و دقیقاً در یکی از سه وضعیت پایان مییابد.
همه چیز درباره یک جلسه پرداخت در لحظه ساخت تعیین میشود. فراخوانی برای ویرایش وجود ندارد — اگر قیمت عوض
شد، جلسه را لغو کنید و یکی تازه با reference_id جدید بسازید.
چرخه حیات
┌──▶ completed
│
open ──────┼──▶ expired
│
└──▶ canceledopenتنها وضعیتی است که ویجت در آن پرداخت میگیرد. مشتری میتواند در طول این مدت هر چند بار که بخواهد صفحه را باز کند و رهایش کند.completedپایانی است، و جلسه پرداخت از آن پس شیءpaymentخود را همراه دارد. پولی که جابهجا شده جابهجا میمانَد: در این ریل نه لغو وجود دارد، نه بازگشت، نه دریافت جزئی. اگر لازم است پول را برگردانید، برای مشتری یک انتقال بفرستید.expiredخودش درexpires_atرخ میدهد. چیزی رزرو نشده و چیزی جابهجا نشده، پس یک جلسه منقضی برای هیچکس هزینهای ندارد.canceledیعنی شما تصمیم گرفتهاید سفارش پیش از پرداخت منتفی است. لغو ایدمپوتنت است: لغو یک جلسه لغوشده همان جلسه را بدون تغییر برمیگرداند و خطا نمیدهد. لغو یک جلسه تکمیلشده باPAY_SESSION_STATE(۴۰۹) رد میشود.
هرگز لازم نیست برای انقضا استعلام دورهای بزنید. کوینلند وبهوک session.expired را میفرستد و
GET /api/pay/v1/sessions/{id} همیشه وضعیت فعلی را گزارش میکند.
مدت اعتبار جلسه
مدت اعتبار پیشفرض از کنسول کسبوکار شما میآید. برای هر جلسه میتوانید با ttl_minutes بین ۵ و ۱۴۴۰
(۲۴ ساعت) آن را بازنویسی کنید.
آن را متناسب با چیزی که میفروشید انتخاب کنید. TTL کوتاه برای وقتی درست است که کالا کمیاب است یا قیمتش به یک بازار متغیر گره خورده، چون قیمتی که اعلام کردهاید بعد از مدتی دیگر قیمتی نیست که اعلام میکردید — کوینلند هیچ تبدیلی انجام نمیدهد، پس قیمت بیتکوین یکساعتپیش، دقیقاً قیمت بیتکوین یکساعتپیش است. TTL بلند برای فاکتوری درست است که انتظار دارید فردا پرداخت شود. هر دو منطقیاند؛ آنچه مشکل میسازد انتخاب بیفکر است.
ایدمپوتنسی
reference_id شناسه سفارش شماست و در همان حال کلید ایدمپوتنسی شماست. این API هدر جداگانه
Idempotency-Key ندارد.
| آنچه میفرستید | آنچه میگیرید |
|---|---|
یک reference_id تازه | یک جلسه پرداخت تازه (۲۰۱) |
همان reference_id با محتوای یکسان | همان جلسه اصلی، بدون تغییر |
همان reference_id با محتوای متفاوت | PAY_DUPLICATE_REFERENCE (۴۰۹) |
این کل قاعده است، و همین است که تلاش مجدد را ایمن میکند. وقتی POST /sessions با وقفه زمانی شکست
میخورد، هیچ چیزی به شما نمیگوید که جلسه ساخته شده یا نه، پس پاسخ درست ارسال دوباره همان درخواست است —
نه ساختن یک شناسه تازه، که همان کاری است که یک سفارش را به دو جلسه پرداخت و در نهایت به دو پرداخت تبدیل
میکند.
هرگز برای تلاش مجدد شناسه تازه نسازید
خطای ۴۰۹ روی محتوای تغییریافته یک ویژگی است: حالتی را میگیرد که شناسه یک سفارش را برای سفارشی دیگر
دوباره استفاده کردهاید. اگر واقعاً به قیمتهای متفاوت برای همان سبد نیاز دارید، آن در سیستم خودتان یک
سفارش تازه است و reference_id تازه میگیرد.
reference_id در تمام عمر حساب شما یگانه است، نه فقط میان جلسههای باز. همچنین یک کلید جستوجو است:
GET /api/pay/v1/sessions/{id} هم شناسه یکتای جلسه را میپذیرد و هم reference_id خودتان را، پس
میتوانید یک جلسه را بخوانید بدون آنکه چیزی از آنچه ما تولید کردهایم ذخیره کرده باشید.
قیمتگذاری در چند ارز
amounts فهرستی از جفتهای {currency, amount} است، حداکثر ده تا، و مشتری دقیقاً یکی را انتخاب میکند.
"amounts": [
{ "currency": "usdt", "amount": "24.90" },
{ "currency": "btc", "amount": "0.00027" },
{ "currency": "eth", "amount": "0.0069" }
]کوینلند هیچ تبدیلی انجام نمیدهد. هر گزینه یک قیمت مستقل برای همان سفارش است، و هر کدام را که مشتری انتخاب کند، همان مبلغ دقیقاً از او کسر میشود. این ریل هیچ نرخ لحظهای اعلام نمیکند، هیچ اسپردی اعمال نمیکند، و یک ارز را به ارز دیگر تبدیل نمیکند.
پیامدش این است که همخوانی قیمتها بین ارزها کار شماست. اگر یک قیمت تتری و یک قیمت بیتکوینی اعلام کنید، شما بودهاید که تصمیم گرفتهاید این دو ارزش یکسانی دارند، و شما هستید که ریسک حرکت بازار پیش از پرداخت جلسه را میپذیرید. دو راه برای مدیریت آن:
- در لحظه ساخت جلسه از یک نرخ زنده قیمت بگیرید و
ttl_minutesکوتاهی بگذارید، تا بازهای که بازار میتواند به زیان شما حرکت کند کوچک باشد. - فقط یک ارز اعلام کنید. یک
amountsتکگزینهای کاملاً عادی است، و پرسش ریسک را از بین میبرد.
تومان پذیرفته نمیشود
کوینلند پی فقط از ارزهای دیجیتال پشتیبانی میکند؛ پرداخت تومانی در این سرویس ارائه نمیشود. تومان
(IRT) بهعنوان ارز پذیرفتهشده یک کسبوکار قابل انتخاب نیست و قرار دادن آن در amounts با
PAY_CURRENCY_NOT_ACCEPTED رد میشود.
قواعدی که به آنها برمیخورید:
- هر
currencyباید درaccepted_currenciesشما ازGET /api/pay/v1/meباشد، وگرنه کل درخواست باPAY_CURRENCY_NOT_ACCEPTED(۴۲۲) رد میشود. آن فهرست را در زمان راهاندازی بخوانید و ارزها را در کد ثابت نکنید، چون در کنسول و بدون انتشار نسخه جدید از سمت شما تغییر میکند. amountیک رشته اعشاری است:"24.90"، نه24.9. عددهای اعشاری شناور نمیتوانند هر مبلغ دهدهی را نمایش دهند، و خطای گردکردن در اینجا یعنی خطای گردکردن در آنچه دریافت میکنید.- مبالغ باید مثبت و در محدوده دقت آن ارز باشند.
PAY_AMOUNT_INVALID(۴۲۲) هم مبلغ غیرمثبت را پوشش میدهد، هم تعداد ارقام اعشار بیش از حد آن ارز، و هم هر چیزی بیرون از محدودههای پلتفرم. - هر ارز یک بار میآید. دو گزینه برای یک ارز یعنی یک باگ در کد قیمتگذاری شما، نه انتخابی میان دو قیمت.
قیمتگذاری به دلار
بهجای amounts میتوانید یک price_usd بفرستید و کوینلند آن را در همه ارزهایی که میپذیرید با
نرخ زنده، در همان لحظه ساخت جلسه، قیمت میگذارد.
{
"reference_id": "order-10492",
"title": "Order 10492",
"price_usd": "24.90"
}یا amounts بفرستید یا price_usd — نه هر دو، نه هیچکدام. هر دو اشتباه با PAY_AMOUNT_INVALID
(۴۲۲) رد میشوند.
پاسخ جلسه با pricing_mode: "usd"، همان price_usd که فرستادهاید، و آرایه amounts از قیمتهای
گرفتهشده برمیگردد که هرکدام usd_value مبنای خود را همراه دارند. از آن به بعد دقیقاً مثل یک جلسه
با amounts رفتار میکند: مشتری یک ارز را انتخاب میکند و همان عدد را میپردازد.
همین قیمتها قفلِ نرخ هستند
تایمر جداگانهای برای قفل نرخ وجود ندارد. قیمتها یک بار و در لحظه ساخت گرفته میشوند، و مهلت خودِ
جلسه (expires_at) همان بازهای است که این قیمتها در آن معتبرند. مشتری همان مبلغی را میپردازد که
به او نشان داده شده. اگر بیش از حد طول بکشد، جلسه منقضی میشود و شما یکی تازه با نرخ همان لحظه
میسازید.
ارزی که نرخ زنده نداشته باشد بیصدا کنار گذاشته میشود. بقیه ارزهای پذیرفتهشده شما کار میکنند و
مشتری فقط گزینههای کمتری میبیند. تنها اگر هیچ ارزی قابل قیمتگذاری نباشد درخواست رد میشود، با
PAY_RATE_UNAVAILABLE (۵۰۳) — درخواستی که شکل درستی دارد و در سمت ما شکست خورده، پس دوباره تلاش
کنید نه اینکه تغییرش دهید.
ایدمپوتنسی روی عدد دلاری است، نه روی قیمتها
ارسال دوباره همان reference_id با همان price_usd جلسه اصلی را با همان قیمتها برمیگرداند،
حتی اگر نرخ زنده از آن زمان جابهجا شده باشد و قیمتهای تازه فرق کنند. اثر انگشت ایدمپوتنسی روی عدد
دلاریای که فرستادهاید گرفته میشود، نه روی مبالغ ارزی مشتقشده از آن. همین است که تلاش مجدد را ایمن
میکند: همان پیشنهادی را میگیرید که مشتری همین حالا جلوی چشمش است، نه یک قیمتگذاری تازه.
price_usd متفاوت با همان reference_id همچنان یک تعارض است و همچنان PAY_DUPLICATE_REFERENCE
(۴۰۹) میگیرد.
متادیتا
metadata یک شیء JSON آزاد است که در هر خواندن جلسه پرداخت و روی پرداخت به شما بازگردانده میشود. جای
درست شناسه سبد، کانال فروش و کمپین شماست — هر چیزی که وگرنه باید جداگانه جستوجو کنید.
فقط شما آن را میبینید، اما ذخیره میشود، پس اطلاعات محرمانه و داده شخصی را در آن نگذارید. محتوای موردنظر همان شناسههای خودتان است.
خواندن یک جلسه پرداخت
const API = "https://my.coinlandexchange.com";
const auth = { Authorization: `Bearer ${process.env.COINLAND_PAY_KEY}` };
// Either the session UUID or your own reference_id works as the id.
export async function getSession(id: string) {
const res = await fetch(`${API}/api/pay/v1/sessions/${encodeURIComponent(id)}`, {
headers: auth,
});
if (!res.ok) throw new Error(`coinland pay ${res.status}`);
return res.json();
}
// Idempotent: cancelling a cancelled session returns it unchanged.
export async function cancelSession(id: string) {
const res = await fetch(
`${API}/api/pay/v1/sessions/${encodeURIComponent(id)}/cancel`,
{ method: "POST", headers: auth },
);
if (!res.ok) throw new Error(`coinland pay ${res.status}`);
return res.json();
}این وضعیت معتبر است. وقتی status برابر completed باشد، پرداخت کامل در پاسخ
جایگذاری شده است، پس یک فراخوانی هم به «آیا پرداخت کرد» جواب میدهد و هم به «دقیقاً چه چیزی پرداخت کرد».
یک شناسه ناشناس — یا شناسهای که به کسبوکار دیگری تعلق دارد — با PAY_SESSION_NOT_FOUND (۴۰۴) پاسخ
میگیرد. این دو حالت عامدانه از هم قابل تشخیص نیستند: پاسخ متفاوت برای «وجود دارد اما مال شما نیست» به هر
کسی که یک کلید دارد اجازه میداد سفارشهای کسبوکارهای دیگر را شمارش کند.