کرپٹو ادائیگیاں قبول کریں
Paysell، TON نیٹ ورک پر TON اور USDT کا تصفیہ کرتا ہے۔ آپ ایک انوائس بناتے ہیں، ہم آپ کو ایک لنک دیتے ہیں، اور جیسے ہی رقم چین پر تصدیق ہو کر آپ کے بیلنس میں جمع ہو جاتی ہے آپ کو ایک دستخط شدہ کال بیک ملتا ہے۔
جائزہ#
Paysell کیا کرتا ہے، اور کیا نہیں کرتا۔
Paysell ایک ادائیگی پروسیسر ہے، بٹوہ نہیں۔ آپ کبھی بھی پرائیویٹ کیز نہیں سنبھالتے، بلاک چین کی نگرانی نہیں کرتے، اور یہ فیصلہ نہیں کرتے کہ کوئی ٹرانزیکشن حتمی کب ہے — یہ ذمہ داری ہم اٹھاتے ہیں۔
ہر انوائس کا اپنا وصولی پتہ ہوتا ہے۔ جب خریدار اسے ادا کرتا ہے، ہم نیٹ ورک کے ذریعے منتقلی کی تصدیق کا انتظار کرتے ہیں، اپنی فیس منہا کرتے ہیں، اور باقی رقم آپ کے بیلنس میں جمع کر دیتے ہیں۔ آپ کسی بھی پتے پر رقم نکلوا سکتے ہیں۔
ادائیگی کیسے کام کرتی ہے#
چھ مراحل، جن میں سے زیادہ تر ہمارے ہیں۔
چھ مراحل، جن میں سے زیادہ تر ہمارے ہیں:
- 1
آپ کا گاہک ادائیگی پر کلک کرتا ہے
آپ کا سرور رقم اور آپ کے اپنے آرڈر ریفرنس کے ساتھ ہمارا API کال کرتا ہے۔
- 2
ہم ایک پتہ فراہم کرتے ہیں
پہلے سے تیار پول سے ایک تازہ وصولی پتہ لیا جاتا ہے اور اس انوائس سے منسلک کیا جاتا ہے۔ ایک پتہ بالکل ایک کھلے انوائس کا ہوتا ہے، اسی طرح ادائیگی کو اس سے ملایا جاتا ہے۔
- 3
گاہک کوائنز بھیجتا ہے
وہ QR کوڈ اسکین کرتا ہے یا پتہ کاپی کرتا ہے۔ انہیں ہمارے دیے گئے
payment_urlپر بھیجیں اور صفحہ آپ کے لیے سنبھالا جاتا ہے — رقم، پتہ، QR، کاؤنٹ ڈاؤن، لائیو اسٹیٹس۔ - 4
ہم منتقلی کا پتہ لگاتے ہیں
بلاک چین ڈیٹا کے دو آزاد ذرائع سے پوچھا جاتا ہے، اور ان کے جوابات کا موازنہ کیا جاتا ہے۔ اگر وہ متفق نہ ہوں، تو ہم زیادہ آسان جواب چننے کے بجائے رک جاتے ہیں۔
- 5
ہم حتمیت کا انتظار کرتے ہیں
ماسٹرچین میں شمولیت جمع مزید تین بلاکس۔ تقریباً پندرہ سیکنڈ — ایک ادائیگی جو طے شدہ نظر آئے مگر بعد میں غائب ہو جائے آپ کا نقصان ہوگی، اس لیے ہم یہ خطرہ مول نہیں لیتے۔
- 6
جمع ہو گئی، اور آپ کو بتا دیا گیا
فیس منہا کی جاتی ہے، باقی آپ کے بیلنس میں پہنچتی ہے، اور آپ کے
order_idکے ساتھ ایک دستخط شدہ ویب ہک آپ کے سرور کو بھیجا جاتا ہے۔
ادائیگی سے کال بیک تک تقریباً ایک منٹ: نیٹ ورک کی تصدیقات کے تقریباً پندرہ سیکنڈ، باقی ہمارا نگرانی شدہ پتوں کا معائنہ ہے۔
پیسہ کہاں جاتا ہے#
فیس، اور یہ کس چیز پر شمار کی جاتی ہے۔
فیس 0.2% ہے، جو آپ کی دکان کے لیے اس کی رجسٹریشن کے وقت طے ہو جاتی ہے۔ اگر معیاری شرح بعد میں بدلے، تو آپ کی نہیں بدلتی — یہ ہر انوائس میں ایک نمبر کے طور پر لکھی جاتی ہے، کسی سیٹنگ کے حوالے کے طور پر نہیں۔
فیس اس پر لی جاتی ہے جو حقیقت میں پہنچا، نہ کہ اس پر جو انوائس میں مانگا گیا تھا۔ 5 USDT کا بل بنائیں اور 20 وصول کریں، تو فیس 20 پر شمار ہوگی۔ کم ادائیگی کریں، تو یہ اس پر شمار ہوگی جو آیا۔
Invoice: 5.000000 USDT
Received: 20.000000 USDT (the buyer sent more)
Fee 0.2%: 0.040000 USDT (on 20, not on 5)
Credited: 19.960000 USDTزائد ادائیگی مکمل طور پر جمع کی جاتی ہے — ہم فرق اپنے پاس نہیں رکھتے۔ کم ادائیگی انوائس کو کھلا چھوڑ دیتی ہے تاکہ خریدار اسی پتے پر مزید رقم بھیج سکے۔
فوری آغاز#
آپ کے پہلے انوائس تک پانچ منٹ۔
پانچ مراحل۔ دو آپ کے اکاؤنٹ ایریا میں کلکس ہیں، ایک آپ کے سرور سے بھیجی گئی صرف ایک درخواست ہے، اور آخری دو خود بخود ہو جاتے ہیں۔
- 1
ایک دکان بنائیں
اپنے اکاؤنٹ ایریا میں۔ یہ فوراً ادائیگیاں قبول کرنا شروع کر دیتی ہے — جائزے کا انتظار نہیں کرنا پڑتا۔ تصدیق خاموشی سے پس منظر میں ہوتی ہے اور صرف رقم نکلوانے کو محدود کرتی ہے، آنے والی ادائیگیوں کو نہیں۔
- 2
ایک API کلید جاری کریں
آپ کی دکان → API کیز → نئی کلید۔ کلید اور ویب ہک سیکرٹ صرف ایک بار دکھائے جاتے ہیں، اس کے بعد کبھی نہیں۔ انہیں ویسے ہی محفوظ کریں جیسے آپ ڈیٹا بیس پاس ورڈ محفوظ کرتے ہیں، اور کبھی بھی انہیں براؤزر میں نہ بھیجیں۔
- 3
ایک انوائس بنائیں
آپ کے سرور سے ایک درخواست، جواب میں ایک لنک۔ نیچے دیے گئے چاروں نمونے بالکل ایک ہی چیز بھیجتے ہیں۔
- 4
خریدار کو
payment_urlپر بھیجیںیہی پورا چیک آؤٹ ہے — رقم، پتہ، QR کوڈ، کاؤنٹ ڈاؤن، لائیو اسٹیٹس — اور بنانے کو کچھ نہیں۔ خریدار کو حقیقت میں کیا نظر آتا ہے، اس کے لیے دیکھیں چیک آؤٹ۔
- 5
ویب ہک کا انتظار کریں
جیسے ہی رقم چین پر تصدیق ہو کر جمع ہو جاتی ہے، ہم آپ کے سرور پر دستخط شدہ
payment.creditedایونٹ POST کرتے ہیں۔ دستخط کی تصدیق کریں، پھر آرڈر کو ادا شدہ نشان زد کریں — لیکن صرف اُس وقت جبdata.statuspaidیاoverpaidہو۔ دیکھیں ویب ہکس۔
The same request, four ways
curl -X POST https://paysell.me/api/merchant/v1/invoices \
-H "Authorization: Bearer sk_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"asset": "USDT_TON",
"amount": "5",
"order_id": "order-1042",
"idempotency_key": "order-1042"
}'خریدار کو جواب میں موجود payment_url پر بھیج دیں۔ آپ کا کام مکمل ہے — باقی ویب ہک کی صورت میں آئے گا۔
What to do next
- Write the webhook receiver — Webhooks and A complete receiver. Nothing else on this page matters as much: it is what turns a payment into a paid order.
- Handle
underpaidandoverpaid, not justpaid— see Status reference. - Read Typical mistakes, then walk the go-live checklist before you point real customers at it.
تصدیقِ ہویت#
آپ کی API کلید، اور یہ کیسے استعمال ہوتی ہے۔
ہر درخواست Authorization ہیڈر میں آپ کی کلید لے کر آتی ہے:
Authorization: Bearer sk_live_IH4SNdnYsv-yabTuPCVln4vOvvGoEyxAیہاں جاری کی گئی ہر کلید sk_live_ سے شروع ہوتی ہے۔ sk_test_ سابقہ صرف اُس ڈپلائمنٹ میں موجود ہے جو ٹیسٹ نیٹ ورک پر چلتی ہو، اور ایسی کوئی ڈپلائمنٹ پیش نہیں کی جاتی — دیکھیں ٹیسٹنگ۔ ہم ایک یک طرفہ ہیش محفوظ کرتے ہیں، خود کلید نہیں، اس لیے کوئی بھی، بشمول ہمارے، آپ کو یہ دوبارہ نہیں دکھا سکتا۔ کھو دی؟ نئی جاری کریں اور پرانی منسوخ کر دیں۔
دکان کلید سے اخذ کی جاتی ہے، اسی لیے کوئی درخواست کبھی دکان کی id نہیں لیتی۔ ایک کلید صرف اپنی دکان پر عمل کر سکتی ہے۔
پتے میں ورژن ہوتا ہے: /api/merchant/v1/…۔ ایک ورژن کے اندر ہم صرف فیلڈ شامل کرتے ہیں — کسی کا نام نہیں بدلتا اور نہ ہی خاموشی سے معنی بدلتے ہیں۔ جو تبدیلی آپ کا کوڈ توڑ دے گی، اسے نیا سابقہ /v2 ملتا ہے، اور /v1 اعلان کردہ مدت تک چلتا رہتا ہے۔
Endpoints at a glance#
Four calls, three of them authenticated.
This is the whole merchant API. Balances, payouts and history are not in it — they live in your account area, where a person is looking at them.
| Endpoint | Method | Auth | What it does |
|---|---|---|---|
| /invoices | POST | API key | Open an invoice and get a payment link. Details. |
| /invoices/{invoice_id} | GET | API key | Read one invoice's current state. Details. |
| /invoices/{invoice_id}/cancel | POST | API key | Close an invoice that is still open and free its address. Details. |
| /public/invoices/{invoice_id} | GET | none | What the hosted checkout page reads. Only needed if you build your own. Details. |
Every path is relative to https://paysell.me/api/merchant/v1. There is no list endpoint and no refund endpoint — see Refunds.
انوائس بنائیں#
POST /api/merchant/v1/invoices
/api/merchant/v1/invoicesدرخواست کا باڈی
| فیلڈ | قسم | لازمی | تفصیل |
|---|---|---|---|
| asset | string | ہاں | یا تو TON یا USDT_TON۔ |
| amount | string | ہاں | سکے کی عام اکائیاں، اسٹرنگ کے طور پر: "5" یعنی 5 USDT۔ سکے کے پاس جتنے اعشاریہ ہیں، اس سے زیادہ نہیں۔ دیکھیں رقوم۔ |
| order_id | string | نہیں | آپ کا اپنا ریفرنس، زیادہ سے زیادہ 200 حروف۔ ہر ویب ہک میں واپس آتا ہے — اسی طرح آپ ادائیگی کو آرڈر سے ملاتے ہیں۔ |
| description | string | نہیں | زیادہ سے زیادہ 1000 حروف۔ خریدار کو ادائیگی کے صفحے پر دکھایا جاتا ہے۔ |
| ttl_minutes | number | نہیں | انوائس کتنی دیر تک قابلِ ادائیگی رہتی ہے، منٹوں میں۔ 1–1440؛ اسے چھوڑ دیں تو ڈیفالٹ لاگو ہوتا ہے — فی الحال 2 گھنٹے۔ |
| idempotency_key | string | نہیں | زیادہ سے زیادہ 200 حروف۔ دوبارہ کوشش کرتے وقت وہی ویلیو بھیجیں اور آپ کو دوسرے کی بجائے وہی انوائس واپس ملے گا۔ یہ باڈی کی فیلڈ ہے، Idempotency-Key ہیڈر نہیں — وہ ہیڈر یہاں پڑھا ہی نہیں جاتا۔ |
جواب · 201
{
"invoice_id": "12c22c1a-a496-4c1e-abe3-72661ef8706e",
"payment_url": "https://paysell.me/pay/12c22c1a-a496-4c1e-abe3-72661ef8706e",
"address": "UQAvDJp7QDwqRcuNQBiK2GhBt71Xh1_UMYPCzMkQAoBPmZKl",
"asset": "USDT_TON",
"amount": "5",
"amount_minor": "5000000",
"status": "pending",
"paid": "0",
"paid_minor": "0",
"order_id": "order-1042",
"description": "Pro subscription",
"expires_at": "2026-09-06T17:20:55Z",
"created_at": "2026-09-06T15:20:55Z"
}اسے اپنے آرڈر پر لاگو کرنا
| فیلڈ | اس کے ساتھ کیا کریں |
|---|---|
| invoice_id | اسے اپنے آرڈر کے ساتھ محفوظ کریں۔ یہی وہ چیز ہے جو ہر جگہ ادائیگی کی شناخت کرتی ہے۔ |
| payment_url | خریدار کو یہاں بھیج دیں۔ اس کے علاوہ کچھ بنانے کی ضرورت نہیں۔ |
| address | صرف اگر آپ اپنا چیک آؤٹ بناتے ہیں۔ اسے بالکل ویسے ہی دکھائیں جیسے دیا گیا — نیچے دی گئی وارننگ دیکھیں۔ |
| amount | عام اکائیوں میں رقم، بالکل ویسی جیسی آپ نے بھیجی۔ دکھانے کے لیے یہی لیں۔ |
| amount_minor | وہی رقم سب سے چھوٹی اکائی میں مکمل نمبر کے طور پر۔ حساب اسی سے کریں۔ |
| expires_at | کاؤنٹ ڈاؤن دکھائیں۔ اس کے گزرنے کے بعد پتہ اس انوائس کے لیے نگرانی میں نہیں رہتا۔ |
| status | یہاں ہمیشہ pending۔ اصل تبدیلیاں ویب ہک کے ذریعے آتی ہیں۔ |
UQ…، ٹیسٹ نیٹ پر 0Q…)۔ اسے تبدیل کریں، خوبصورت بنائیں، یا اسی پتے کی کسی اور انکوڈنگ سے بدلیں، اور ابھی تک ڈپلائے نہ ہونے والے والٹ کو بھیجے گئے کوائنز بھیجنے والے کو واپس اچھل جاتے ہیں۔انوائس پڑھیں#
GET /api/merchant/v1/invoices/{invoice_id}
/api/merchant/v1/invoices/{invoice_id}اوپر جیسی ہی شکل، جس میں status، paid اور paid_minor موجودہ صورتحال ظاہر کرتے ہیں: paid بتاتا ہے کہ عام اکائیوں میں کتنا آ چکا ہے، اور paid_minor وہی رقم سب سے چھوٹی اکائی میں مکمل نمبر کے طور پر۔ اس وقت مفید جب کوئی ویب ہک چھوٹ گیا ہو، یا شکریہ کے صفحے پر۔
اسے چند سیکنڈز میں زیادہ سے زیادہ ایک بار پوچھیں، اور ویب ہکس کو بنیادی چینل سمجھیں۔ کسی اور دکان سے تعلق رکھنے والے انوائسز 404 کا جواب دیتے ہیں — 403 نہیں، تاکہ id کے وجود کی جانچ نہ کی جا سکے۔
انوائس منسوخ کریں#
POST /api/merchant/v1/invoices/{invoice_id}/cancel
/api/merchant/v1/invoices/{invoice_id}/cancelایسا انوائس بند کرتا ہے جو ابھی کھلا ہے — pending یا underpaid — اور اس کا پتہ آزاد کر دیتا ہے۔ اسے اس وقت استعمال کریں جب گاہک چیک آؤٹ چھوڑ دے: پتے ایک محدود وسیلہ ہیں، اور انہیں واپس کرنا پول کو صحت مند رکھتا ہے۔
جو انوائس اب کھلا نہیں رہا وہ 409 کا جواب دیتا ہے۔ کسی underpaid انوائس کو منسوخ کرنے سے کسی کو سکے واپس نہیں ملتے: جو رقم پہلے ہی جمع ہو چکی ہے وہ آپ کے بیلنس پر رہتی ہے، اور بند صرف یہ ہوتا ہے کہ اب مزید رقم قبول نہیں کی جائے گی۔
ویب ہکس#
کیا آتا ہے، اور اسے کیسے تصدیق کریں۔
کلید بناتے وقت ویب ہک کا پتہ مقرر کریں۔ جب ادائیگی جمع ہوتی ہے — اور جب اضافی جانچ کے لیے روکا گیا کوئی ڈپازٹ مسترد ہوتا ہے — تو ہم وہاں POST کرتے ہیں۔ ہر ڈیلیوری دستخط شدہ ہوتی ہے، اور ہم تقریباً ڈیڑھ دن تک دوبارہ کوشش کرتے رہتے ہیں جب تک آپ 2xx کا جواب نہ دیں۔ مال status: paid یا overpaid پر دیں، محض کال پہنچ جانے پر نہیں۔
ایونٹس
| ایونٹ | کب | باڈی میں کیا آتا ہے |
|---|---|---|
| payment.credited | ٹرانسفر چین پر تصدیق شدہ ہے، ہماری فیس کاٹ لی گئی، اور باقی رقم آپ کے بیلنس میں ہے۔ | نیچے درج فیلڈز۔ |
| payment.rejected | اضافی جانچ کے لیے روکا گیا ڈپازٹ (اسٹیٹس حوالہ دیکھیں) مسترد کر دیا گیا۔ رقم آپ کے بیلنس تک نہیں پہنچے گی۔ | invoice_id، order_id، asset، amount، tx_hash اور reason۔ سامان جاری نہ کریں؛ اگر انوائس پہلے کے کسی ٹرانسفر سے پہلے ہی paid تھی، تو یہ ایونٹ اس ادائیگی کے بارے میں نہیں بلکہ اضافی ڈپازٹ کے بارے میں ہے۔ |
کیا آتا ہے
{
"event_id": "99f74f58-efbb-4af1-b0a3-76b0073f9e6b",
"type": "payment.credited",
"data": {
"invoice_id": "12c22c1a-a496-4c1e-abe3-72661ef8706e",
"order_id": "order-1042",
"asset": "USDT_TON",
"amount": "5000000",
"credited": "4905000",
"fee": "95000",
"status": "paid",
"paid_minor": "5000000",
"tx_hash": "97a1f0…"
}
}{
"event_id": "0a1b2c3d-4e5f-4a6b-8c9d-0e1f2a3b4c5d",
"type": "payment.rejected",
"data": {
"invoice_id": "12c22c1a-a496-4c1e-abe3-72661ef8706e",
"order_id": "order-1042",
"asset": "USDT_TON",
"amount": "5000000",
"tx_hash": "97a1f0…",
"reason": "could not be matched to any order"
}
}فیلڈ میپنگ
| فیلڈ | مطلب |
|---|---|
| event_id | ہر ایونٹ کے لیے منفرد؛ X-Paysell-Event-Id ہیڈر میں بھی ہوتا ہے۔ اسے محفوظ کریں اور تکرار کو نظر انداز کریں — نیچے دیکھیں۔ |
| data.order_id | آپ کا ریفرنس۔ اس سے اپنا آرڈر تلاش کریں۔ |
| data.amount | خریدار نے اس ٹرانسفر میں کتنا بھیجا، سب سے چھوٹی اکائی میں — API کے برعکس، جو عام اکائیاں لیتا ہے۔ |
| data.fee | ہم نے کتنا لیا، سب سے چھوٹی اکائی میں۔ |
| data.credited | آپ کے بیلنس میں کتنا آیا: amount − fee، سب سے چھوٹی اکائی میں۔ |
| data.paid_minor | اس انوائس پر اب تک کل کتنا موصول ہوا، سب سے چھوٹی اکائی میں۔ underpaid پر یہی فیلڈ اہم ہے: اسٹیٹس بتاتا ہے کہ کم آیا، اور یہ بتاتا ہے کہ کتنا کم۔ |
| data.asset | وہ سکہ جو حقیقت میں آیا۔ ضروری نہیں کہ وہی سکہ ہو جو انوائس نے مانگا تھا۔ |
| data.asset_mismatch | صرف اسی وقت موجود اور true ہوتا ہے جب آنے والا سکہ انوائس کے سکے سے مختلف ہو۔ رقم آپ کے کھاتے میں جمع ہو جاتی ہے، مگر انوائس غیر ادا شدہ رہتا ہے اور status کبھی paid نہیں ہوتا۔ |
| data.invoice_asset | asset_mismatch کے ساتھ آتا ہے: وہ سکہ جو انوائس حقیقت میں مانگتا ہے۔ |
| data.status | انوائس کا موجودہ اسٹیٹس: pending، underpaid، paid، overpaid یا expired۔ اس کا موازنہ اس سے کریں جس کی آپ کو توقع تھی۔ |
| data.tx_hash | آن چین ٹرانزیکشن، آپ کے ریکارڈز اور سپورٹ کے لیے۔ |
ہر ڈیلیوری کے ساتھ آنے والے ہیڈرز
X-Paysell-Event: payment.credited
X-Paysell-Event-Id: 99f74f58-efbb-4af1-b0a3-76b0073f9e6b
X-Paysell-Timestamp: 1789000000
X-Paysell-Signature: sha256=6f1c0e6a…| ہیڈر | مطلب |
|---|---|
| X-Paysell-Event | ایونٹ کی قسم: payment.credited یا payment.rejected۔ |
| X-Paysell-Event-Id | ہر ایونٹ کے لیے منفرد۔ تکرار ہٹانے کے لیے یہی ویلیو استعمال کریں۔ |
| X-Paysell-Timestamp | ہم نے کب دستخط کیا، یونکس سیکنڈز میں۔ یہ دستخط شدہ اسٹرنگ کا حصہ ہے۔ |
| X-Paysell-Signature | sha256= کے بعد ہیکس HMAC۔ نیچے دیکھیں۔ |
دستخط کی تصدیق
ہر درخواست اُس ویب ہک سیکرٹ سے دستخط شدہ ہوتی ہے جو کلید بناتے وقت ایک ہی بار دکھایا گیا تھا۔ دستخط HMAC-SHA256(secret, "{timestamp}.{raw_body}") ہے — ٹائم اسٹیمپ X-Paysell-Timestamp سے، پھر ایک اصل نقطہ، پھر باڈی کی بائٹس۔ عمل کرنے سے پہلے اسے چیک کریں: اس کے بغیر جو بھی آپ کا URL جان لے وہ آپ کو ادا شدہ آرڈر تھما سکتا ہے۔
Python:
import hmac, hashlib, time
def is_ours(body: bytes, signature: str, timestamp: str, secret: str) -> bool:
if abs(time.time() - int(timestamp)) > 300: # ±5 minutes
return False
signed = timestamp.encode() + b"." + body
expected = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
# compare_digest, not ==: a plain comparison leaks the answer through timing
return hmac.compare_digest("sha256=" + expected, signature)Node.js:
const crypto = require("node:crypto")
function isOurs(body, signature, timestamp, secret) {
if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false // ±5 min
const expected = "sha256=" + crypto
.createHmac("sha256", secret)
.update(timestamp + ".").update(body) // body is the raw Buffer, not a parsed object
.digest("hex")
const a = Buffer.from(expected), b = Buffer.from(signature)
// timingSafeEqual throws when the lengths differ, so check that first
return a.length === b.length && crypto.timingSafeEqual(a, b)
}خام باڈی بائٹس پر دستخط کریں، بالکل ویسے ہی جیسے موصول ہوئیں۔ JSON کو پارس کر کے دوبارہ سیریلائز کریں تو بائٹس بدل جاتی ہیں — کلیدوں کی ترتیب، اسپیسنگ — اور دستخط میل نہیں کھائے گا۔ موازنہ مستقل وقت میں کریں (hmac.compare_digest، crypto.timingSafeEqual): سادہ == غلط پہلے بائٹ پر تیزی سے واپس آتا ہے، اور یہی فرق ایک ایک بائٹ کر کے دستخط کا اندازہ لگانے کے لیے کافی ہے۔
ٹائم اسٹیمپ کی کھڑکی
ہر ایسی درخواست مسترد کریں جس کا ٹائم اسٹیمپ آپ کی اپنی گھڑی سے پانچ منٹ سے زیادہ دور ہو، کسی بھی سمت میں۔ ٹائم اسٹیمپ دستخط شدہ اسٹرنگ کے اندر اسی لیے ہے کہ دستخط توڑے بغیر اسے بدلا نہ جا سکے؛ اور کھڑکی ہی اسے تحفظ میں بدلتی ہے۔ اس کے بغیر ایک بار پکڑی گئی درخواست ہمیشہ کے لیے درست رہتی ہے اور کسی بھی وقت دوبارہ چلائی جا سکتی ہے — تنہا دستخط کبھی ختم نہیں ہوتا۔ اپنے سرور کی گھڑی NTP پر رکھیں، ورنہ یہ جانچ درست ڈیلیوریز کو مسترد کرنا شروع کر دے گی۔
تکرار
ایک ہی ایونٹ ایک سے زیادہ بار آ سکتا ہے۔ یہ بگ نہیں ہے: ہم اس وقت تک دوبارہ کوشش کرتے ہیں جب تک آپ 2xx کا جواب نہ دیں، اور ایک ڈیلیوری جو کامیاب ہوئی لیکن جس کا جواب ہم تک نہیں پہنچا وہ دوبارہ بھیجی جاتی ہے۔ X-Paysell-Event-Id ریکارڈ کریں (یہ باڈی میں event_id کے طور پر بھی آتا ہے) اور دوسری آمد کچھ نہ کرے یہ یقینی بنائیں۔
دوبارہ کوششیں
پہلی کوشش ادائیگی جمع ہوتے ہی روانہ ہو جاتی ہے۔ اگر وہ ناکام ہو — ٹائم آؤٹ، کنکشن مسترد، TLS ایرر، ری ڈائریکٹ، یا کوئی بھی غیر 2xx اسٹیٹس — تو ہم ایک مقررہ شیڈول پر دوبارہ کوشش کرتے ہیں:
1 منٹ → 5 منٹ → 15 منٹ → 1 گھنٹہ → 6 گھنٹے → 24 گھنٹےکل سات کوششیں، تقریباً 31 گھنٹوں میں پھیلی ہوئی۔ ابتدائی کوششیں ایک دوسرے کے قریب ہیں کیونکہ عام وجہ یہ ہوتی ہے کہ وصول کنندہ ری اسٹارٹ ہو رہا تھا اور اب واپس آ چکا ہے؛ بعد والی بکھری ہوئی ہیں کیونکہ ایک دن سے بند سرور کو بار بار پیٹنے سے کسی کا فائدہ نہیں۔
آخری کوشش کے بعد ڈیلیوری کو dropped نشان زد کر دیا جاتا ہے اور ہم خود رک جاتے ہیں۔ یہ ضائع نہیں ہوتی: آپ کے اکاؤنٹ ایریا میں ادائیگی کی سطر حالت، کوششوں کی تعداد اور ایرر کی قسم دکھاتی ہے، اور اس کے ساتھ ایک دوبارہ بھیجیں بٹن ہوتا ہے جو ساتوں کوششوں کا نیا دور شروع کرتا ہے۔ آپ کا دوسرا سہارا GET /api/merchant/v1/invoices/{invoice_id} ہے — انوائس ہمیشہ اپنا اسٹیٹس جانتا ہے۔
ویب ہک URL کیسا ہونا چاہیے
URL کی جانچ اُس وقت ہوتی ہے جب آپ اسے محفوظ کرتے ہیں، اور پھر ہر ایک ڈیلیوری سے پہلے دوبارہ۔ جو URL جانچ میں ناکام ہو، محفوظ کرتے وقت اسے 422 اور code: "webhook_url_rejected" کے ساتھ جواب ملتا ہے، اور اگر وہ بعد میں ناکام ہونے لگے تو ڈیلیوری کو failed نشان زد کر دیا جاتا ہے — بغیر کسی دوبارہ کوشش کے۔ قواعد یہ ہیں:
- صرف `https://`، اور پورٹ 443۔ ویب ہک ادائیگی کی تفصیلات لے کر جاتا ہے؛ سادہ http میں راستے میں موجود کوئی بھی انہیں پڑھ سکتا ہے۔
- ڈومین نام، IP پتہ نہیں۔ آپ کو بہرحال سرٹیفکیٹ چاہیے، اور ننگے IP کے لیے سرٹیفکیٹ جاری نہیں کیے جاتے۔
- کوئی `localhost` نہیں، اور نہ ہی
.local،.internal،.corp،.lanیا.testنام — ہمارے سرور آپ کے نیٹ ورک تک نہیں پہنچ سکتے، اور جو نام ہمارے اپنے نیٹ ورک کے اندر حل ہوتا ہے وہی ہے جسے ہمیں ہرگز کال نہیں کرنا چاہیے۔ - URL میں کوئی کریڈنشلز نہیں (
https://user:pass@…)۔ اگر ٹوکن درکار ہو تو اپنا ٹوکن پاتھ میں یا کوئری پیرامیٹر میں رکھیں۔ - نام جن جن پتوں پر حل ہو وہ سب عوامی ہونے چاہئیں — A اور AAAA دونوں۔ پرائیویٹ، لوپ بیک، لنک لوکل اور CGNAT رینجز مسترد کی جاتی ہیں، اور یہ جانچ ہر ڈیلیوری سے پہلے دہرائی جاتی ہے، اس لیے بعد میں ریکارڈ کو
127.0.0.1پر موڑنا بھی کام نہیں کرتا۔ - ری ڈائریکٹ ناکامی ہے، کوئی اگلا قدم نہیں۔ ہم ان کی پیروی نہیں کرتے: جو پتہ آپ نے ہمیں دیا تھا اس کی جانچ ہوئی تھی، اور
Locationہیڈر والے کی نہیں ہوئی۔
جلدی جواب دیں
دس سیکنڈ کے اندر کوئی بھی 2xx کافی ہے — یہی ہمارا پورا ٹائم آؤٹ ہے، کنکشن سمیت۔ پہلے جواب دیں، سست کام بعد میں کریں؛ جو اینڈ پوائنٹ جواب دینے سے پہلے اپنے ڈیٹا بیس کا انتظار کرتا ہے وہ آخرکار ٹائم آؤٹ کے طور پر ریکارڈ ہو گا اور دوبارہ بھیجا جائے گا، اور آپ ایک ہی ایونٹ کو دو بار پروسیس کریں گے۔ اس کے علاوہ سب کچھ — 4xx، 5xx، ری ڈائریکٹ، اٹک جانا — ناکام کوشش شمار ہوتا ہے اور اوپر والے شیڈول میں واپس چلا جاتا ہے۔
ڈیلیوری، سچ سچ
جس کی ضمانت ہے وہ ڈیلیوری کا طریقہ کار ہے: تقریباً 31 گھنٹوں میں سات کوششیں، آپ کے اکاؤنٹ ایریا سے دستی طور پر دوبارہ بھیجنا، اور ایک انوائس اینڈ پوائنٹ جو ہمیشہ اصل اسٹیٹس جانتا ہے۔ اپنا فلو ایسا بنائیں کہ کبھی نہ پہنچنے والا ویب ہک آپ کو کچھ بھی نقصان نہ دے — انوائس کو اپنے شکریہ صفحے پر پڑھیں، یا گھنٹے میں ایک بار کھلے انوائسز کا ملان کر لیں۔ ویب ہکس تیز راستہ ہیں، واحد راستہ نہیں۔
A complete receiver#
Signature, deduplication and a fast answer, end to end.
The snippets above verify one signature. This is the whole endpoint: raw body, signature check, deduplication by event_id, a fast 2xx, and the one condition that is allowed to mark an order paid.
Node.js with Express. express.raw is the part people get wrong: express.json() hands you a parsed object, and bytes you re-serialise from it are not the bytes we signed.
const express = require("express")
const crypto = require("node:crypto")
const app = express()
const SECRET = process.env.PAYSELL_WEBHOOK_SECRET
function isOurs(body, signature, timestamp) {
const sentAt = Number(timestamp)
if (!Number.isFinite(sentAt)) return false
if (Math.abs(Date.now() / 1000 - sentAt) > 300) return false // ±5 minutes
const expected = "sha256=" + crypto
.createHmac("sha256", SECRET)
.update(timestamp + ".").update(body) // raw Buffer, not a parsed object
.digest("hex")
const a = Buffer.from(expected), b = Buffer.from(signature ?? "")
return a.length === b.length && crypto.timingSafeEqual(a, b)
}
app.post(
"/paysell/webhook",
express.raw({ type: "application/json" }), // NOT express.json()
async (req, res) => {
const signature = req.get("X-Paysell-Signature")
const timestamp = req.get("X-Paysell-Timestamp")
if (!isOurs(req.body, signature, timestamp)) return res.sendStatus(401)
const event = JSON.parse(req.body.toString("utf8"))
// Answer first: 10 seconds is the whole timeout, connection included.
res.sendStatus(200)
// Deduplicate. In real code this is a unique column, not a Set.
if (await alreadyHandled(event.event_id)) return
await remember(event.event_id)
if (event.type !== "payment.credited") return
const { order_id, status, credited, asset, tx_hash } = event.data
// The only condition that may release the goods.
if (status !== "paid" && status !== "overpaid") return
await markOrderPaid(order_id, { credited, asset, tx_hash })
}
)Python with Flask. request.get_data() is the raw body; request.form and request.json are not.
import hashlib
import hmac
import json
import os
import time
from flask import Flask, request
app = Flask(__name__)
SECRET = os.environ["PAYSELL_WEBHOOK_SECRET"]
def is_ours(body: bytes, signature: str, timestamp: str) -> bool:
try:
sent_at = int(timestamp)
except (TypeError, ValueError):
return False
if abs(time.time() - sent_at) > 300: # ±5 minutes
return False
signed = timestamp.encode() + b"." + body
expected = hmac.new(SECRET.encode(), signed, hashlib.sha256).hexdigest()
return hmac.compare_digest("sha256=" + expected, signature)
@app.post("/paysell/webhook")
def paysell_webhook():
body = request.get_data() # raw bytes, unparsed
if not is_ours(body, request.headers.get("X-Paysell-Signature", ""),
request.headers.get("X-Paysell-Timestamp", "")):
return "", 401
event = json.loads(body)
# Deduplicate. In real code this is a unique column, not a set.
if already_handled(event["event_id"]):
return "", 200 # a repeat is still a success
remember(event["event_id"])
if event["type"] == "payment.credited":
data = event["data"]
# The only condition that may release the goods.
if data["status"] in ("paid", "overpaid"):
mark_order_paid(data["order_id"], data)
return "", 200 # 2xx within 10 secondsWhat the code is doing, and why
- Verify before anything acts on the body. An unsigned request that reaches your business logic is a paid order for whoever found your URL.
- Answer 2xx first, work afterwards. Ten seconds is the whole timeout, connection included. A handler that waits for its own database gets recorded as a timeout and retried, and you process the same event twice.
- Deduplicate on `event_id` in storage that survives a restart. The in-memory set in the examples keeps them short; a real one is a unique column in your database.
- Mark the order paid only on `status: paid` or `overpaid`.
underpaidmeans part of the money arrived and the invoice is still open, and a deposit in the wrong coin never makes an invoice paid either. - Answer 2xx to a duplicate too. A repeat that gets a 4xx looks like a failure to us and comes back again on the schedule.
payment.rejected arrives at the same endpoint. It means a deposit held for an additional check was declined and the money will not be credited: release nothing, and if the invoice was already paid by an earlier transfer, this event is about the extra deposit, not about that payment.
اسٹیٹس حوالہ#
ہر انوائس اور ادائیگی اسٹیٹس، وضاحت کے ساتھ۔
انوائس
| اسٹیٹس | مطلب | کیا کریں |
|---|---|---|
| pending | ادائیگی کا انتظار۔ | آرڈر کھلا رکھیں۔ |
| paid | مکمل طور پر ادا شدہ۔ | سامان جاری کریں۔ |
| overpaid | جتنا مانگا گیا اس سے زیادہ آیا۔ زائد رقم مکمل طور پر آپ کو جمع کی جاتی ہے۔ | سامان جاری کریں؛ چاہیں تو فرق واپس کریں۔ |
| underpaid | جتنا مانگا گیا اس سے کم آیا۔ انوائس کھلا رہتا ہے اور اپنا پتہ برقرار رکھتا ہے: خریدار اسی جگہ پر مزید رقم بھیج سکتا ہے، اور paid_minor بتاتا ہے کہ کتنا پہلے ہی آ چکا ہے۔ یہ اپنی باقی مدت کے دوران اور expires_at کے بعد 24 گھنٹے کی رعایتی مدت تک قابل ادائیگی رہتا ہے۔ | مزید رقم کا انتظار کریں، یا گاہک کے ساتھ طے کریں۔ سامان جاری نہ کریں — انوائس ادا نہیں ہوا۔ |
| expired | وقت ختم ہو گیا، رعایتی مدت سمیت۔ پھر بھی اس میں رقم ہو سکتی ہے: جو کچھ آیا تھا وہ آپ کے بیلنس میں رہا، اور paid_minor بتاتا ہے کہ کتنا۔ | نیا انوائس پیش کریں۔ پرانے پتے پر ادائیگی قبول نہ کریں: انوائس کی میعاد ختم ہوتے ہی وہ پتہ واپس پول میں چلا جاتا ہے، اور بہت دیر سے آنے والی منتقلی خودکار طور پر جمع ہونے کے بجائے سپورٹ کا معاملہ بن جاتی ہے۔ گاہک کو یہ بتانے سے پہلے کہ کچھ موصول نہیں ہوا، paid_minor چیک کریں۔ |
| cancelled | آپ نے منسوخ کیا۔ پتہ واپس پول میں چھوڑ دیا جاتا ہے۔ | کچھ نہیں۔ |
ادائیگی
آپ کے اکاؤنٹ ایریا میں نظر آتا ہے؛ گاہک کی ادائیگی کے دوران مدد کرنے میں مفید۔
| اسٹیٹس | مطلب |
|---|---|
| detected | چین پر دیکھا گیا، تصدیقات کا انتظار۔ |
| confirmed | نیٹ ورک نے تصدیق کر دی۔ اگلا مرحلہ جمع کرنا۔ |
| credited | آپ کے بیلنس میں۔ اسی وقت ویب ہک چلتا ہے۔ |
| review | اضافی جانچ کے لیے روکا گیا — مثال کے طور پر، بغیر کھلے انوائس والے پتے پر کوائنز آنا۔ |
| rejected | جمع نہیں کیا گیا۔ وجہ ریکارڈ کی گئی ہے۔ |
جب ادائیگی `review` میں جاتی ہے
کچھ ڈپازٹس فوراً جمع کرنے کے بجائے اضافی جانچ کے لیے روک لیے جاتے ہیں: غیر معمولی بڑی رقم، بغیر کھلے انوائس والے پتے پر کوائنز کا آنا، یا وہ دو بلاک چین ذرائع جن سے ہم پوچھتے ہیں آپس میں اختلاف کریں کہ ہوا کیا۔ کچھ ضائع نہیں ہوتا — رقم فیصلے کا انتظار کرتی ہے، اور فیصلہ ہوتے ہی ویب ہک چل پڑتا ہے، جس میں منٹ بھی لگ سکتے ہیں اور گھنٹے بھی۔ اس لیے review دکھانے والی ادائیگی پر کال بیک نہ آنے کو ناکامی نہیں بلکہ معمول سمجھیں۔ اگر یہ کسی آرڈر کے لیے اہم ہو تو سپورٹ سے پوچھیں اور tx_hash بتائیں۔
رقوم#
بھیجتے وقت عام اکائیاں، واپسی میں سب سے چھوٹی۔
رقوم سکے کی عام اکائیوں میں، اسٹرنگ کے طور پر بھیجیں — "1.5" کا مطلب ڈیڑھ ہے۔ نہ JSON نمبر، نہ سب سے چھوٹی اکائی۔
| ایسٹ | ڈیسیملز | آپ بھیجتے ہیں | جواب میں amount_minor |
|---|---|---|---|
| TON | 9 | "1.5" | "1500000000" |
| USDT_TON | 6 | "1.5" | "1500000" |
نمبر نہیں بلکہ اسٹرنگ، کیونکہ JSON نمبر IEEE-754 ڈبل ہوتے ہیں اور نینوٹن میں بڑی رقم ان میں بالکل درست نہیں سماتی۔ سکے کے پاس جتنے اعشاریہ ہیں اس سے زیادہ دینے پر 422 ملتا ہے — آپ کے پیسے کو خاموشی سے گول نہیں کیا جاتا۔ ویب ہک میں معاملہ الٹ ہے: وہاں amount، fee اور credited سب سے چھوٹی اکائی میں مکمل نمبر ہیں، کیونکہ اُس طرف کو کوڈ پڑھتا ہے، انسان نہیں۔
// Send amounts in the coin's normal units, as a string:
const amount = "1.5" // one and a half TON or USDT
// In responses, amount is that same human string; amount_minor is the
// integer in smallest units — use it for exact maths, as a string or BigInt:
BigInt(invoice.amount_minor) // e.g. 1500000nحدود#
کم از کم، زیادہ سے زیادہ، اور شرح کی حدود۔
| حد | ویلیو | خلاف ورزی پر |
|---|---|---|
| کم از کم انوائس | 0.1 TON · 3 USDT | 422 |
| زیادہ سے زیادہ انوائس | 7000 TON · 10000 USDT | 422 |
| فی گھنٹہ، فی دکان انوائسز | 60 | 429 |
| بیک وقت کھلے انوائسز | 20، ہر ادا شدہ انوائس کے ساتھ بڑھتے ہوئے، 200 تک | 429 |
| انوائس کی مدت | 1 منٹ – 24 گھنٹے (ڈیفالٹ 2 گھنٹے) | 422 |
| فی کلید API درخواستیں | 120 فی منٹ | 429 + Retry-After |
کم از کم حد بیوروکریسی نہیں ہے۔ ہماری فیس فیصد کی صورت میں ہے، لیکن ادائیگی وصول کرنے کی ایک مقررہ لاگت ہے: وصولی پتے سے USDT منتقل کرنے کے لیے پہلے اسے اپنی جیب سے گیس کے ساتھ فنڈ کرنا ہوتا ہے۔ چند ڈالر سے کم پر فیس ہینڈلنگ کا احاطہ نہیں کرتی، اور ایسی ادائیگی قبول کرنے کا مطلب یہ ہوگا کہ آپ کو ایسی رقم جمع کی جائے جس کا منتقل کرنا غیر اقتصادی ہو۔
زیادہ سے زیادہ حد بڑے تاجروں کے بارے میں نہیں — یہ اکائیوں کی غلطی کے لیے بچھایا گیا جال ہے۔ "5000000" بھیج دیں جہاں آپ کا مطلب "5" تھا، تو بصورت دیگر آپ کو پچاس لاکھ ڈالر کا انوائس مل جاتا: خریدار مہمل رقم دیکھ کر چلا جاتا ہے۔ حقیقی آرڈر اس چھت تک کبھی نہیں پہنچتا؛ غلطی ہمیشہ پہنچتی ہے۔ دونوں چھتیں سیٹنگز ہیں (invoice_max_ton، invoice_max_usdt) اور آپ کی دکان کے لیے بڑھائی جا سکتی ہیں — ہم سے کہیں۔
فی گھنٹہ حد اور کھلے انوائسز کی حد، دونوں پتوں کے پول کی حفاظت کرتی ہیں۔ ہر کھلا انوائس ایک وصولی پتہ روکتا ہے، اور ایک سائٹ پر بے قابو لوپ بصورت دیگر سب کے لیے پول خالی کر دیتا۔ نئی دکان بیک وقت 20 انوائسز کھلے رکھ سکتی ہے؛ ہر اُس انوائس پر جو حقیقت میں وصول ہوا، یہ گنجائش ایک بڑھ جاتی ہے، 200 کی چھت تک۔ underpaid کھلا شمار ہوتا ہے — وہ اب بھی اپنا پتہ روکے ہوئے باقی رقم کا انتظار کر رہا ہے۔ ترک شدہ انوائس منسوخ کرنے سے اس کا پتہ فوراً واپس مل جاتا ہے۔ ایک ہی idempotency_key کے ساتھ دوبارہ کوششیں فی گھنٹہ حد میں شمار نہیں ہوتیں۔
درخواستوں کی حد فی API کلید 120 فی منٹ ہے — دو کالیں فی سیکنڈ، جو کسی بھی حقیقی آرڈر فلو سے کہیں زیادہ ہے۔ 429 کے ساتھ سیکنڈوں میں Retry-After ہیڈر آتا ہے: تنگ لوپ میں دوبارہ کوشش کرنے کے بجائے اتنی دیر انتظار کریں، کیونکہ ایسا کرنے سے کھڑکی اور آگے کھسک جاتی ہے۔
ایررز#
وہ اسٹیٹس کوڈز جو آپ حقیقت میں دیکھیں گے۔
ایررز JSON کی صورت میں دو شکلوں میں واپس آتے ہیں۔ جو کچھ ہم یا پروسیسنگ کور طے کرتے ہیں، وہ detail کے تحت {code, message} کا جوڑا رکھتا ہے۔ جس درخواست کا باڈی تصدیق میں ناکام ہو، اس کے لیے وہاں اس کے بجائے فیلڈ ایررز کی فہرست آتی ہے۔ detail.code پڑھنے سے پہلے دیکھ لیں کہ آپ کو کون سی شکل ملی — اور `code` پر شاخ بنائیں، کبھی `message` پر نہیں: الفاظ کسی بھی وقت بدل سکتے ہیں، کوڈ نہیں بدلے گا۔
{
"detail": {
"code": "invalid_input",
"message": "invoice amount below the minimum: 0.010000 USDT_TON, minimum 3.000000 USDT_TON"
}
}{
"detail": [
{
"type": "string_type",
"loc": ["body", "amount"],
"msg": "Input should be a valid string",
"input": 5
}
]
}| اسٹیٹس | کب | کیا کریں |
|---|---|---|
| 401 | کلید غائب، غلط، یا منسوخ شدہ۔ | ہیڈر چیک کریں۔ اگر منسوخ ہو گئی تھی تو کلید دوبارہ جاری کریں۔ |
| 404 | ایسا کوئی انوائس نہیں، یا یہ کسی اور دکان کا ہے۔ | id چیک کریں۔ دونوں صورتوں کا جواب جان بوجھ کر یکساں ہے، تاکہ کسی id کو ٹٹولا نہ جا سکے۔ |
| 409 | انوائس ایسی حالت میں ہے جو اسے منع کرتی ہے۔ | پہلے اس کا موجودہ اسٹیٹس پڑھیں۔ |
| 422 | درخواست کی ساخت غلط ہے، یا رقم انوائس کی حدود سے باہر ہے۔ | پیغام میں بھیجی گئی ویلیو اور حد دونوں کا نام لیا گیا ہے۔ |
| 429 | اس گھنٹے میں بہت زیادہ انوائسز، بیک وقت بہت زیادہ کھلے، یا بہت زیادہ درخواستیں۔ | Retry-After کا انتظار پورا کریں، پھر دوبارہ کوشش کریں۔ |
| 502 | ہم پروسیسنگ کور تک نہیں پہنچ سکے۔ | اسی idempotency key کے ساتھ دوبارہ کوشش کریں۔ |
کوڈز
ہمارے طے کردہ فیصلوں کی شکل یہ ہے: {"detail": {"code": …, "message": …}}۔ یہ وہ کوڈز ہیں جو مرچنٹ API واپس کرتا ہے۔
| کوڈ | اسٹیٹس | مطلب |
|---|---|---|
| invalid_api_key | 401 | کلید غائب، بگڑی ہوئی، نامعلوم یا منسوخ شدہ ہے۔ چاروں صورتوں کا جواب یکساں ہے، تاکہ کسی کلید کو ٹٹولا نہ جا سکے۔ |
| not_found | 404 | ایسی کوئی چیز نہیں، یا یہ کسی اور دکان کی ہے۔ |
| invalid_input | 422 | درخواست کور میں تصدیق سے نہیں گزری — غلط رقم، بہت زیادہ اعشاریہ، یا انوائس کی حدود سے باہر رقم۔ |
| conflict | 409 | کارروائی موجودہ حالت سے متصادم ہے، جیسے ایسے انوائس کو منسوخ کرنا جو اب کھلا نہیں رہا۔ |
| too_many_requests | 429 | شرح کی حد: فی گھنٹہ انوائسز، کھلے انوائسز، یا فی منٹ درخواستیں۔ Retry-After بتاتا ہے کہ کتنی دیر انتظار کرنا ہے۔ |
| cbc_unreachable | 502 | ہم پروسیسنگ کور تک نہیں پہنچ سکے۔ اسی idempotency_key کے ساتھ دوبارہ کوشش کریں۔ |
| webhook_url_rejected | 422 | صرف کلید محفوظ کرتے وقت: ویب ہک URL اوپر دی گئی جانچ میں ناکام رہا۔ detail.reason بتاتا ہے کہ کون سا قاعدہ — scheme، port، ip_literal، local_hostname، private_address، dns_error، credentials، وغیرہ۔ |
ایک 502 کا مطلب یہ نہیں کہ انوائس نہیں بنا — درخواست کامیاب ہو سکتی تھی جبکہ جواب واپسی کے راستے میں گم ہو گیا۔ اسی idempotency_key کے ساتھ دوبارہ کوشش کریں اور آپ کو یا تو موجودہ انوائس ملے گا یا نیا، کبھی دو نہیں۔
Checkout: what the buyer sees#
The hosted payment page, and when to build your own.
payment_url points at https://paysell.me/pay/{invoice_id}. One page, no account, no login, mobile first, and nothing for you to build.
On the page
- Your shop's name, the amount and the coin, large, with the invoice's description underneath.
- A countdown to
expires_at— plus the 24-hour grace period when the invoice isunderpaid. - The invoice's short id with a copy button, so a buyer can quote it to your support.
- Wallet buttons: Tonkeeper and MyTonWallet open with the address and the amount already filled in. Other reveals a QR code and the address with a copy button.
- A warning that only this invoice's coin, on the TON network, may be sent — anything else is lost.
How the page reacts
| Invoice | What the buyer sees |
|---|---|
| pending | “Waiting for payment”, with the wallet choices and the countdown. The page re-reads the invoice every five seconds. |
| underpaid | “Received X of Y”, the exact remainder still owed, and the same address to send it to. The wallet link is prefilled with what is missing, not with the original total — otherwise the buyer would pay twice. |
| paid · overpaid | “Payment received”, and a button back to your shop if the shop has a URL. |
| expired | “Payment window closed”. If money did arrive, the amount is named with a note to contact you — silence here would send the buyer looking for their coins. |
| cancelled | “Payment cancelled”, with a link back to your shop. |
If you build your own
You gain your own branding and take on all of the above: the exact address string, the right coin, the countdown with its grace period, the underpayment case, and polling. GET /api/merchant/v1/public/invoices/{invoice_id} is the same unauthenticated read the hosted page uses — rate limited per IP, so poll it no more often than every few seconds. Print the address exactly as returned.
Typical integration mistakes#
The handful that account for most broken integrations.
None of these are exotic. Every one of them has cost somebody a day.
Sending the amount as a number
{"amount": 5}is a422. It has to be the string"5": JSON numbers are IEEE-754 doubles, and a large sum in nanotons stops being exactly representable in one.Sending the smallest unit
"5000000"for 5 USDT is a mistake in units, and the upper invoice limit exists to catch it. Smallest units are what comes back in webhooks, not what goes out in requests.Treating the webhook's arrival as payment
Read
data.status.underpaidis not paid, and a deposit in a coin the invoice did not ask for never makes it paid either. Release the goods onpaidoroverpaid, on nothing else.Not checking the timestamp
A signature on its own never expires. Without the ±5 minute window on
X-Paysell-Timestamp, a delivery captured once can be replayed at any time and will still verify.Verifying the signature over re-serialised JSON
Parse the body and serialise it again and the bytes change — key order, spacing — and the HMAC no longer matches. Sign the raw bytes exactly as received.
No deduplication
The same
event_idwill arrive twice sooner or later: we retry until you answer 2xx, and a response lost on the way back looks like a failure from here. The second arrival must do nothing.Using the
Idempotency-KeyheaderThis API reads
idempotency_keyfrom the request body; the header is not read at all. A retry without the field opens a second invoice for the same order.Assuming
detailis always an objectIt is
{code, message}for anything we or the core decide, and a list of field errors when the body itself fails validation. Check which one you got before readingdetail.code.
رقم کی واپسی#
گاہک کو رقم کیسے واپس کریں۔
رقم کی واپسی API کال سے نہیں، سپورٹ کے ذریعے ہوتی ہے۔ رقم کی واپسی دراصل ایک نیا ٹرانسفر ہے اُس پتے پر جو کسی شخص نے دیا ہو، اور ایسا پیمنٹ پروسیسر جو API کال پر خودکار طور پر رقم واپس بھیج دے، وہی پروسیسر ہے جسے حملہ آور کے پتے پر رقم بھیجنے پر مجبور کیا جا سکتا ہے۔ اسی لیے یہ جان بوجھ کر دستی ہے۔
کسی خریدار کو رقم واپس کرنے کے لیے، اپنے اکاؤنٹ ایریا سے سپورٹ ٹکٹ کھولیں جس میں invoice_id یا tx_hash، رقم، اور وہ پتہ ہو جس پر بھیجنا ہے۔ آپریٹر ادائیگی کی جانچ کرتا ہے، رقم آپ کے بیلنس سے نکالتا ہے، اور اسی ٹکٹ میں جواب دیتا ہے۔ اس میں ایک کام کا دن لگنے کی توقع رکھیں، ایک منٹ کی نہیں۔
دو نتیجے ایسے ہیں جنہیں سامنے رکھ کر ڈیزائن کرنا چاہیے۔ زائد ادائیگی مکمل طور پر آپ کو جمع کی جاتی ہے — ہم اس میں سے کچھ نہیں رکھتے — اس لیے جس خریدار نے زیادہ بھیجا اسے فرق واپس کرنا آپ کا فیصلہ ہے اور وہ بھی اسی راستے سے ہوتا ہے۔ اور کم ادا شدہ انوائس جب تک کھلا ہے، رقم واپسی کا معاملہ نہیں ہے: رقم آپ کے بیلنس میں ہے، پتہ اب بھی زیرِ نگرانی ہے، اور خریدار سیدھا مزید رقم بھیج سکتا ہے۔ صرف رعایتی مدت کے بعد، جب انوائس رقم سمیت expired ہو جائے، تب کوئی فیصلہ کرنا ہوتا ہے۔
ٹیسٹنگ#
لانچ سے پہلے اپنے انضمام کی جانچ کیسے کریں۔
یہاں کی کلیدیں لائیو ہیں: جاری کی گئی ہر کلید sk_live_ کلید ہے جو پروڈکشن کور اور TON مین نیٹ کے خلاف چلتی ہے۔ کوئی الگ ٹیسٹ ماحول نہیں ہے، اور اس کا ایک فائدہ ہے: آپ بالکل وہی راستہ آزماتے ہیں جس سے آپ کے حقیقی آرڈر گزریں گے۔
تو اسی طرح ٹیسٹ کریں جیسے آپ حقیقی پیسے کو چھونے والی کسی بھی چیز کو ٹیسٹ کرتے: چھوٹی رقوم پر۔ کم از کم حد کا انوائس بنائیں (0.1 TON یا 3 USDT)، اسے اپنے والٹ سے ادا کریں، اور پورا راستہ دیکھیں — ادائیگی کا صفحہ، ویب ہک، دستخط کی جانچ، اور آپ کے آرڈر کا ادا شدہ ہو جانا۔ فیس لگتی ہے، اور سکے واقعی منتقل ہوتے ہیں۔
وہ حصے جنہیں آپ بغیر کچھ خرچ کیے آزما سکتے ہیں: انوائس بنانا اور پڑھنا، اسے منسوخ کرنا، غلط ساخت والی رقم پر 422، غلط کلید پر 401، اور آپ کی اپنی دستخط کی تصدیق — نمونہ باڈی کو اپنے سیکرٹ سے دستخط کریں اور اپنے ہی ہینڈلر کو دیں۔ جس کے لیے واقعی حقیقی ادائیگی درکار ہے وہ صرف آخری قدم ہے: ایک اصل payment.credited ویب ہک۔
انضمام کی منصوبہ بندی ایسے کریں کہ وہ سینڈ باکس یا نقلی ادائیگی پر منحصر نہ ہو: لائیو راستہ زیادہ تیزی سے — اور زیادہ سچائی سے — جانچا جاتا ہے۔
For AI agents and LLMs#
Machine-readable copies of this page, and a prompt to start from.
Everything on this page also exists in a form a model can read directly. Point your assistant at one of these instead of pasting screenshots of documentation into a chat.
The three files
| File | What it is | Use it for |
|---|---|---|
| /llms-full.txt | The whole documentation as one markdown file: endpoints, fields, statuses, limits, errors, webhooks with working verification code, the fee, the checkout page, the checklist. | Pasting into a model's context, or letting an agent fetch it. Start here. |
| /llms.txt | A short index in the llms.txt format: what Paysell is, the five rules that decide whether an integration works, and links to everything else. | Letting an agent discover the rest on its own. |
| /openapi.json | OpenAPI 3.1, generated from the running application's own models, both webhook events included. | Generating a client, or loading into anything that speaks OpenAPI. |
A prompt to start from
Copy this, replace the stack, and hand it to your assistant. It names the four things that go wrong most often, so the answer does not have to be corrected afterwards.
Read https://paysell.me/llms-full.txt and implement Paysell payments in my <stack>:
create invoices (POST /api/merchant/v1/invoices, Bearer sk_live_ key, amount as a
decimal string in normal units), redirect the buyer to payment_url, verify webhook
signatures (HMAC-SHA256 over "{timestamp}.{raw_body}", header X-Paysell-Signature,
reject anything whose X-Paysell-Timestamp is more than 300 seconds off), deduplicate
by event_id, answer 2xx within 10 seconds, and mark orders paid only on a
payment.credited event whose data.status is "paid" or "overpaid".Feeding it to a specific tool
- Agents with web access — Claude Code, Cursor, Windsurf and the like: give them the
/llms-full.txtlink. One fetch, no setup. - A chat window — ChatGPT, Claude, Gemini: paste the contents of
/llms-full.txtinto the conversation or attach it as a file. It is written to fit in one message. - OpenAPI tooling — client generators, Postman, an agent's tool schema: point it at
https://paysell.me/openapi.json. Itsserversentry already carries the production base URL, so generated calls go to the right place.
لانچ سے پہلے چیک لسٹ#
آغاز سے پہلے دس باتیں۔
- کلید صرف سرور کی جانب ہے، کبھی براؤزر جاوا اسکرپٹ میں نہیں۔
- ویب ہک دستخط کی تصدیق
"{timestamp}.{raw_body}"کے خلاف، مستقل وقت میں کی جاتی ہے۔ - پانچ منٹ سے پرانی ڈیلیوریز مسترد کی جاتی ہیں، اور سرور کی گھڑی NTP پر ہے۔
- دہرایا گیا
X-Paysell-Event-Idدوسری بار کچھ نہیں کرتا۔ - ویب ہک دس سیکنڈ کے اندر 2xx جواب دیتا ہے؛ سست کام بعد میں ہوتا ہے۔
- ویب ہک URL پورٹ 443 پر ایک https:// ڈومین ہے، جس کے آگے کوئی ری ڈائریکٹ نہیں۔
- چھوٹ جانے والا ویب ہک برداشت کے قابل ہے: انوائس اینڈ پوائنٹ کو شکریہ صفحے پر یا حساب ملانے کے جائزے میں پڑھا جاتا ہے۔
idempotency_keyفی آرڈر ایک بار بنایا جاتا ہے اور دوبارہ کوششوں پر دوبارہ استعمال ہوتا ہے۔- رقوم عام اکائیوں میں اسٹرنگ کے طور پر جاتی ہیں؛ ویب ہک کے اعداد سب سے چھوٹی اکائی کے طور پر پڑھے جاتے ہیں۔
- پتہ بالکل ویسے ہی دکھایا جاتا ہے جیسے واپس آیا، بغیر کسی تبدیلی کے۔
overpaidاورunderpaidکو ہینڈل کیا جاتا ہے، صرفpaidکو نہیں؛expiredمیں پھر بھیpaid_minorہو سکتا ہے۔- مال
status: paidیاoverpaidپر دیا جاتا ہے، محض کال بیک پہنچ جانے پر کبھی نہیں۔ 429کوRetry-Afterکا انتظار پورا کر کے ہینڈل کیا جاتا ہے، فوراً دوبارہ کوشش کر کے نہیں۔- بیلنس ہم سے پڑھے جاتے ہیں، سچائی کے طور پر الگ سے ٹریک نہیں کیے جاتے۔
کچھ غیر واضح ہے؟
اگر اس صفحے نے آپ کے سوال کا جواب نہیں دیا، تو یہ دستاویزات میں ایک خلا ہے اور ہمیں اس کے بارے میں بتانا ضروری ہے۔ اپنے اکاؤنٹ ایریا سے لکھیں اور ہم صرف جواب نہیں بلکہ صفحہ ہی درست کریں گے۔