لوگوی پی‌اکسا مستندات

مستندات پذیرنده

اتصال به درگاه پی‌اکسا، ساده‌تر از آنچه فکر می‌کنید

بیشتر کسب‌وکارها برای دریافت پرداخت به کدنویسی نیاز ندارند: لینک پرداخت، افزونهٔ ووکامرس، فروشگاه کانال و ربات‌های آمادهٔ پی‌اکسا بدون برنامه‌نویسی راه‌اندازی می‌شوند. اگر سایت، اپ یا ربات اختصاصی دارید، اتصال تنها با دو درخواست انجام می‌شود: ایجاد پرداخت و تأیید آن. این صفحه هر دو راه را قدم‌به‌قدم توضیح می‌دهد.

بدون کدنویسیلینک پرداخت، ووکامرس، فروشگاه کانال و ربات آماده
با کدنویسیدو درخواست: ایجاد پرداخت و تأیید آن
آزمایش رایگانمحیط آزمایشی، بدون ثبت‌نام و بدون پرداخت واقعی

۰۱انتخاب روش

روش مناسب کسب‌وکار خود را انتخاب کنید

همهٔ روش‌ها از یک حساب و یک پنل استفاده می‌کنند و می‌توانید هم‌زمان از چند روش بهره ببرید.

روشمناسب برایکدنویسیشروع
لینک پرداخت فروش در اینستاگرام، واتس‌اپ، تلگرام یا به‌صورت حضوری. لینک را برای خریدار می‌فرستید و خریدار از همان صفحه پرداخت می‌کند. لازم نیست راهنمای لینک پرداخت
افزونهٔ ووکامرس فروشگاه‌های وردپرسی. افزونه را نصب می‌کنید و کلید API را در تنظیمات آن وارد می‌کنید. لازم نیست آموزش نصب افزونه
فروشگاه کانال و ربات آماده فروش در کانال یا ربات تلگرام و بله با ابزارهای آمادهٔ پی‌اکسا. لازم نیست فروشگاه کانال
ربات تلگرام
ربات بله
اتصال با API سایت، اپ یا ربات اختصاصی که خودتان یا برنامه‌نویس شما ساخته است. دو درخواست ساده ادامهٔ همین صفحه

۰۲پیش از اتصال

پیش از دریافت پرداخت واقعی، این موارد را در پنل آماده کنید

این موارد برای همهٔ روش‌ها لازم است. اگر یکی از آن‌ها آماده نباشد، پرداخت ایجاد نمی‌شود و پیام خطا علت آن را اعلام می‌کند (فهرست خطاها).

درخواست درگاه تأییدشده و قرارداد امضاشده

پس از تکمیل مشخصات و ثبت درخواست درگاه، کارشناسان پی‌اکسا درخواست را بررسی و تأیید می‌کنند. سپس قرارداد را در پنل بخوانید و با کد پیامکی امضا کنید؛ تا قرارداد امضا نشود، پرداختی پذیرفته نمی‌شود.

نشانی سایت یا ربات ثبت‌شده

نشانی سایت یا ربات خود را در پنل ثبت کنید تا تأیید شود. نشانی بازگشت خریدار و نشانی اعلان پرداخت (callback_url) تنها روی همین نشانی‌ها پذیرفته می‌شود.

کارت بانکی فعال و گوشی دریافت پیامک

در نوع «کارت به کارت شخصی»، دست‌کم یک کارت بانکی به نام خودتان ثبت و تأیید شود و برنامهٔ Payexa Uploader روی گوشی‌ای که پیامک‌های بانک را دریافت می‌کند نصب و به حساب متصل باشد.

اگر گوشی بیش از ۳۰ دقیقه به سامانه متصل نشود، پرداخت تازه پذیرفته نمی‌شود. در نوع «کارت به کارت ادمین» این مورد لازم نیست.

موجودی کیف پول

کارمزد هر پرداخت موفق از کیف پول کسر می‌شود. موجودی کیف پول باید دست‌کم به اندازهٔ حداقل لازم باشد؛ این مقدار در بخش کیف پول پنل نمایش داده می‌شود.

کلید API

برای اتصال با API و افزونهٔ ووکامرس، کلید API را از بخش مدیریت درگاه در پنل بردارید. این کلید را تنها روی سرور خود نگه دارید و هرگز در کد سمت مرورگر یا اپلیکیشن قرار ندهید.

آزمایش، بدون نیاز به هیچ‌کدام از این موارد. پیش از اتصال واقعی، اتصال خود را در محیط آزمایشی بسنجید: به‌جای pay.pexn.ir از sandbox.pexn.ir و به‌جای کلید API از کلید آزمایشی PAYEXA استفاده کنید. در این محیط پرداخت واقعی انجام نمی‌شود، ثبت‌نام لازم نیست و همهٔ مسیرهای هر دو نسخهٔ API در دسترس است.

در آزمایشگاه پی‌اکسا نیز می‌توانید حالت‌های مختلف یک پرداخت (موفق، منقضی و انصراف خریدار) و پاسخ سامانه به هر کدام را ببینید.

۰۳اتصال با API

اتصال با API در سه قدم

ایجاد پرداخت، هدایت خریدار به صفحهٔ پرداخت و تأیید پرداخت پس از بازگشت خریدار. نمونه‌کد کامل برای PHP، Python و Node.js آماده است؛ بخش‌های بعد از آن تنها برای مراجعه‌اند و برای راه‌اندازی لازم نیست همهٔ آن‌ها را بخوانید.

اتصال تازه را با نسخهٔ ۲ پیاده‌سازی کنید. اتصال‌های نسخهٔ ۱ (از جمله افزونهٔ ووکامرس) بدون تغییر به کار خود ادامه می‌دهند و کلید API برای هر دو نسخه یکسان است.

۰۱مراحل اتصال

اتصال به درگاه تنها به دو درخواست نیاز دارد: ایجاد پرداخت و تأیید پرداخت. روند کامل یک پرداخت به شرح زیر است:

۰۱

ایجاد پرداخت

هنگامی که خریدار دکمهٔ پرداخت را در سایت شما می‌زند، سرور شما مبلغ و شمارهٔ سفارش را به پی‌اکسا ارسال می‌کند و در پاسخ، شناسهٔ پرداخت (payment_id) و نشانی صفحهٔ پرداخت (payment_url) را دریافت می‌کند. شناسهٔ پرداخت را همراه سفارش ذخیره کنید.

۰۲

هدایت خریدار به صفحهٔ پرداخت

بلافاصله پس از ایجاد پرداخت، خریدار را به نشانی payment_url دریافت‌شده در پاسخ هدایت (redirect) کنید (بخش ۴). اگر فروش از طریق ربات تلگرام یا بله انجام می‌شود، به‌جای آن نشانی telegram_url یا bale_url را به‌صورت دکمهٔ «پرداخت» زیر پیام ربات قرار دهید (بخش ۶). خریدار در صفحهٔ پرداخت، مبلغ و شمارهٔ کارت مقصد را می‌بیند و مبلغ را کارت به کارت می‌کند. تشخیص واریز به‌صورت خودکار توسط پی‌اکسا انجام می‌شود و اقدامی از سوی شما لازم نیست.

۰۳

تأیید پرداخت پس از بازگشت خریدار

پس از پرداخت، خریدار به نشانی کال‌بک (callback_url) سایت شما بازگردانده می‌شود و payment_id در این نشانی قرار دارد. با این شناسه درخواست تأیید پرداخت را ارسال کنید و در صورتی که پاسخ first_verification: true بود، سفارش را تحویل دهید.

  • کلید API را از پنل پذیرنده دریافت کنید و تنها روی سرور خود نگه دارید؛ این کلید نباید در کد سمت مرورگر (جاوااسکریپت) یا اپلیکیشن قرار گیرد.
  • اگر خریدار پس از پرداخت صفحه را ببندد، نتیجهٔ پرداخت از دست نمی‌رود؛ زیرا پی‌اکسا یک اعلان سرور به سرور نیز به همان نشانی کال‌بک ارسال می‌کند. هر دو حالت با یک کد واحد پردازش می‌شوند (بخش ۵).
  • درخواست تأیید را می‌توان چند بار ارسال کرد؛ مقدار first_verification تنها در نخستین تأیید true است و بنابراین هیچ سفارشی دو بار تحویل نمی‌شود.

۰۲نمونه‌کد کامل

نمونه‌کدهای زیر کل فرایند اتصال را پوشش می‌دهند. کافی است کلید API و نشانی سایت خود را جایگزین کنید و دو تابع save_payment_id (ذخیرهٔ شناسهٔ پرداخت همراه سفارش) و deliver_order (تحویل سفارش) را مطابق سامانهٔ فروشگاه خود پیاده‌سازی کنید.

<?php
// ═══ فایل ۱: payexa.php (تنظیمات مشترک) ═══
const PAYEXA_API_KEY = "YOUR_API_KEY";                 // کلید API از پنل پذیرنده
const PAYEXA_URL = "https://pay.pexn.ir/v2/payments";

function payexa(string $path, array $body = []): array {
  $ch = curl_init(PAYEXA_URL . $path);
  curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 20,
    CURLOPT_HTTPHEADER => ["Authorization: Bearer " . PAYEXA_API_KEY, "Content-Type: application/json"],
    CURLOPT_POSTFIELDS => $body ? json_encode($body) : "{}",
  ]);
  $answer = json_decode((string) curl_exec($ch), true);
  curl_close($ch);
  return is_array($answer) ? $answer : ["ok" => false, "error" => ["message" => "پاسخی از پی‌اکسا دریافت نشد"]];
}


<?php
// ═══ فایل ۲: pay.php (هنگام زدن دکمهٔ «پرداخت» توسط خریدار) ═══
require_once __DIR__ . "/payexa.php";

$result = payexa("", [
  "amount"       => 5000000,                                // ریال
  "order_id"     => "INV-1024",                             // شمارهٔ سفارش شما
  "callback_url" => "https://your-shop.ir/payexa-back.php", // نشانی کال‌بک
]);
if (!$result["ok"]) die($result["error"]["message"]);
save_payment_id("INV-1024", $result["data"]["payment_id"]);
header("Location: " . $result["data"]["payment_url"]);   // هدایت خریدار به صفحهٔ پرداخت
exit;


<?php
// ═══ فایل ۳: payexa-back.php (بازگشت خریدار و اعلان پی‌اکسا) ═══
require_once __DIR__ . "/payexa.php";

$body = json_decode(file_get_contents("php://input"), true) ?: [];
$paymentId = $_GET["payment_id"] ?? $body["payment_id"] ?? "";
$result = payexa("/" . rawurlencode($paymentId) . "/verify");
if ($result["ok"] && $result["data"]["first_verification"]) {
  deliver_order($result["data"]["order_id"]);   // تنها در نخستین تأیید
}
echo $result["ok"] ? "پرداخت با موفقیت انجام شد" : $result["error"]["message"];
  • آزمایش: در محیط آزمایشی، به‌جای pay.pexn.ir از sandbox.pexn.ir و به‌جای کلید API خود از PAYEXA استفاده کنید. همین کد بدون هیچ تغییر دیگری کار می‌کند.
  • نشانی کال‌بک (callback_url) باید روی سایتی باشد که در پنل ثبت و تأیید شده است.
جزئیات هر قدم

اگر نمونه‌کد بالا را به کار برده‌اید، اتصال شما کامل است. بخش‌های زیر جزئیات هر درخواست را برای مراجعه شرح می‌دهند.

۰۳ایجاد پرداخت

POST

درخواست را از سرور خود و با هدر Authorization: Bearer کلید_API به نشانی زیر ارسال کنید:

https://pay.pexn.ir/v2/payments

پارامترهای درخواست

فیلد الزامی توضیح
amount بله مبلغ سفارش به ریال و به‌صورت عدد صحیح؛ در حال حاضر بین ۵۰۰٬۰۰۰ و ۵۰۰٬۰۰۰٬۰۰۰ ریال. اگر مبلغ بیرون از بازهٔ مجاز باشد، بازهٔ مجاز در پیام خطا اعلام می‌شود.
order_id بله شمارهٔ سفارش در سامانهٔ شما؛ حداکثر ۱۰۰ نویسه، شامل حروف انگلیسی، ارقام و نویسه‌های - _ . : /. شمارهٔ سفارش عددی (مانند شمارهٔ سفارش ووکامرس) نیز پذیرفته می‌شود.
callback_url بله نشانی کال‌بک در سایت شما که با https آغاز شود و روی سایت یا ربات تأییدشده در پنل قرار داشته باشد. خریدار پس از پرداخت به این نشانی بازگردانده می‌شود و اعلان سرور به سرور نیز به همین نشانی ارسال می‌شود. نشانی‌های شبکهٔ داخلی یا خصوصی پذیرفته نمی‌شوند. تنها در صورتی که return_url پیوند ربات باشد، ارسال آن الزامی نیست (بخش ۶).
description خیر شرح سفارش (حداکثر ۲۵۵ نویسه)؛ در صفحهٔ پرداخت با عنوان «بابت» نمایش داده می‌شود.
metadata خیر هر اطلاعات دلخواهی که باید همراه پرداخت نگه داشته شود، به‌صورت یک شیء JSON (حداکثر ۲ کیلوبایت)؛ در پاسخ تأیید و در اعلان پرداخت عیناً بازگردانده می‌شود.
return_url خیر نشانی بازگشت خریدار، در صورتی که با نشانی کال‌بک متفاوت باشد؛ معمولاً پیوند ربات (مانند https://t.me/YourBot) برای فروش از طریق ربات (بخش ۶). در صورت ارسال نشدن، خریدار به نشانی کال‌بک بازگردانده می‌شود.

جز این شش فیلد، فیلد دیگری نفرستید؛ فیلد ناشناخته با خطای VALIDATION_ERROR رد می‌شود.

پاسخ

فیلد کاربرد
payment_id شناسهٔ پرداخت؛ تنها مقداری که باید ذخیره کنید.
payment_url نشانی صفحهٔ پرداخت؛ خریدار را بلافاصله به این نشانی هدایت (redirect) کنید (بخش ۴).
telegram_url / bale_url نشانی همین پرداخت در ربات پرداخت پی‌اکسا در تلگرام / بله. برای فروش از طریق ربات، به‌جای payment_url از این نشانی به‌عنوان دکمهٔ «پرداخت» استفاده کنید. اگر ربات مربوط در دسترس نباشد، مقدار آن null است و در این صورت payment_url را در اختیار خریدار قرار دهید.
payable_amount مبلغ دقیقی که خریدار باید واریز کند؛ اندکی با مبلغ سفارش تفاوت دارد تا واریز به‌صورت خودکار شناسایی شود. مقدار به تومان در payable_amount_toman آمده است.
expires_at پایان مهلت پرداخت (۳۰ دقیقه پس از ایجاد آن) به وقت جهانی (UTC).
created true اگر پرداخت تازه ایجاد شده باشد؛ false اگر همان پرداخت قبلی این سفارش بازگردانده شده باشد.
card کارت مقصد واریز: شمارهٔ پوشانده، نام صاحب کارت و بانک.
  • مهلت هر پرداخت ۳۰ دقیقه است.
  • اگر همان order_id با همان مبلغ دوباره ارسال شود (برای نمونه، وقتی خریدار دو بار دکمهٔ پرداخت را بزند) و پرداخت قبلی هنوز در انتظار یا پرداخت‌شده باشد، همان پرداخت قبلی با created: false بازگردانده می‌شود و پرداخت تکراری ایجاد نمی‌شود.
  • اگر همان order_id با مبلغ دیگری ارسال شود، خطای ORDER_ID_CONFLICT بازگردانده می‌شود. پس از منقضی شدن پرداخت قبلی، برای همان سفارش می‌توانید پرداخت تازه ایجاد کنید.
  • ایجاد پرداخت تنها زمانی پذیرفته می‌شود که موارد پیش از اتصال آماده باشد.

۰۴هدایت خریدار به صفحهٔ پرداخت یا ربات

پس از ایجاد موفق پرداخت، خریدار را با یکی از نشانی‌های زیر به پرداخت هدایت کنید. این نشانی‌ها در پاسخ ایجاد پرداخت (بخش ۳) به‌صورت آماده بازگردانده می‌شوند و نیازی به ساختن آن‌ها نیست. نشانی مناسب به محل فروش شما بستگی دارد:

محل فروش فیلد پاسخ روش هدایت خریدار
وب‌سایت payment_url خریدار را بلافاصله به این نشانی هدایت (redirect) کنید؛ نمونه‌کد آن در ادامه آمده است.
ربات تلگرام telegram_url این نشانی را به‌صورت دکمهٔ «پرداخت» زیر پیام ربات خود قرار دهید (بخش ۶). خریدار پرداخت را در ربات پرداخت پی‌اکسا انجام می‌دهد.
ربات بله bale_url همانند تلگرام، این نشانی را به‌صورت دکمهٔ «پرداخت» زیر پیام ربات خود قرار دهید (بخش ۶).

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

در نمونه‌های زیر، result پاسخ درخواست ایجاد پرداخت (بخش ۳) است:

header("Location: " . $result["data"]["payment_url"]);
exit;
  • مهلت هر پرداخت ۳۰ دقیقه است؛ بنابراین خریدار را بلافاصله پس از ایجاد پرداخت هدایت کنید.
  • این نشانی‌ها تنها برای هدایت خریدار هستند و نباید از سمت سرور فراخوانی شوند.
  • اگر telegram_url یا bale_url برابر null باشد (ربات مربوط در دسترس نیست)، payment_url را در اختیار خریدار قرار دهید.
  • در صفحهٔ پرداخت، نام سایت یا ربات شما با عنوان «پرداخت به» نمایش داده می‌شود و خریدار مبلغ و شمارهٔ کارت مقصد را می‌بیند. تشخیص واریز به‌صورت خودکار انجام می‌شود.
  • پس از پرداخت، خریدار به نشانی کال‌بک (یا return_url) بازگردانده می‌شود؛ مرحلهٔ بعد، تأیید پرداخت است (بخش ۵).

۰۵بازگشت خریدار و تأیید پرداخت

POST

اطلاعات ارسالی به نشانی کال‌بک

پس از پرداخت، خریدار با نشانی زیر به سایت شما بازگردانده می‌شود:

https://your-shop.ir/payexa-back?payment_id=A7F3C9E1B5D2468A0C1E3F5B7D9A2C4E6&order_id=INV-1024&status=paid

پارامترهای این نشانی

پارامتر مقدار
payment_id شناسهٔ پرداخت؛ همان مقداری که در پاسخ ایجاد پرداخت گرفتید.
order_id همان شمارهٔ سفارش خودتان که هنگام ایجاد پرداخت فرستادید.
status یکی از paid (پرداخت انجام شده)، expired (مهلت تمام شده) یا canceled (خریدار انصراف داده است).
  • مقصد این بازگشت، return_url است و اگر آن را نفرستاده باشید، callback_url.
  • اگر نشانی شما پارامترهای دیگری در query داشته باشد همه حفظ می‌شوند؛ فقط همین سه پارامتر تنظیم یا بازنویسی می‌شوند.
  • اگر return_url پیوند ربات باشد (بخش ۶)، تلگرام و بله فقط start=<payment_id> را به ربات می‌دهند و سه پارامتر بالا در آن حالت وجود ندارند؛ ربات با همان شناسه تأیید می‌کند.
  • اگر مسیر کال‌بک شما اعلان POST را نپذیرد (برای نمونه، مسیری که فقط GET را پاسخ می‌دهد)، همین سه پارامتر به‌صورت GET به callback_url با status=paid فرستاده می‌شود؛ بهتر است مسیر کال‌بک شما هم POST و هم GET را بپذیرد.

هم‌زمان، پی‌اکسا یک اعلان سرور به سرور (درخواست POST حاوی payment_id در بدنه) نیز به همین نشانی ارسال می‌کند تا در صورت بسته شدن صفحه توسط خریدار، نتیجهٔ پرداخت از دست نرود. هر دو درخواست را به یک شیوه پردازش کنید: payment_id را بخوانید و پرداخت را تأیید کنید.

تأیید پرداخت

https://pay.pexn.ir/v2/payments/{payment_id}/verify

یک درخواست POST با همان هدر کلید API به نشانی بالا ارسال کنید؛ بدنهٔ درخواست الزامی نیست. پاسخ را به شرح جدول زیر تفسیر کنید:

پاسخ مفهوم اقدام لازم
ok و first_verification: true پرداخت انجام شده و این نخستین تأیید است. سفارش را تحویل دهید.
ok و first_verification: false پرداخت پیش‌تر تأیید شده است. تنها پیام «پرداخت موفق» را نمایش دهید و سفارش را دوباره تحویل ندهید.
PAYMENT_NOT_PAID هنوز واریزی ثبت نشده است. وضعیت «در انتظار پرداخت» را نمایش دهید.
PAYMENT_EXPIRED مهلت پرداخت به پایان رسیده و واریزی ثبت نشده است. سفارش را آزاد کنید؛ خریدار می‌تواند پرداخت جدیدی انجام دهد.

نمونهٔ پاسخ‌های خطا

هنوز پرداخت نشده (کد 409):

{
  "ok": false,
  "error": {
    "code": "PAYMENT_NOT_PAID",
    "message": "این پرداخت هنوز انجام نشده است."
  }
}

مهلت تمام‌شده (کد 409):

{
  "ok": false,
  "error": {
    "code": "PAYMENT_EXPIRED",
    "message": "مهلت این پرداخت تمام شده و پرداختی ثبت نشده است."
  }
}

اگر می‌خواهید وضعیت را بدون خطا و برای هر سه حالت با کد 200 بخوانید، از استعلام پرداخت (بخش ۹) استفاده کنید؛ تأیید پرداخت همچنان با همین درخواست انجام می‌شود.

  • مقدار status در نشانی بازگشت (paid، expired، یا canceled به معنای انصراف خریدار) صرفاً جنبهٔ راهنما دارد؛ ملاک قطعی، پاسخ درخواست تأیید است.
  • برای اطمینان بیشتر می‌توانید مبلغ سفارش را نیز در بدنهٔ درخواست تأیید ارسال کنید: {"amount": 5000000}. در صورت مغایرت، خطای AMOUNT_MISMATCH بازگردانده می‌شود.
  • تا زمانی که سرور شما به اعلان پاسخ موفق (کد 2xx) ندهد یا پرداخت را تأیید نکنید، اعلان پرداخت حداکثر ۶ بار ارسال می‌شود: یک بار هنگام پرداخت و سپس ۱، ۵، ۱۵، ۳۰ و ۶۰ دقیقه پس از هر تلاش (روی‌هم حدود دو ساعت). برای پرداختی که در مهلت انجام نشود، اعلان payment.expired فقط یک بار ارسال می‌شود.
  • اگر خریدار در مهلت مقرر واریز کرده باشد اما پرداخت به‌صورت خودکار تأیید نشده باشد، می‌تواند پس از پایان مهلت، رسید واریز را ارسال کند. در صورت تأیید این رسید در پنل توسط شما، همان پرداخت منقضی‌شده به وضعیت paid تغییر می‌کند و اعلان payment.paid طبق روال معمول برای شما ارسال می‌شود؛ نمونه‌کد بخش ۲ این حالت را نیز بدون تغییر به‌درستی پردازش می‌کند.

۰۶ربات تلگرام و بله

اگر فروش شما از طریق ربات تلگرام یا بله انجام می‌شود، مراحل ایجاد و تأیید پرداخت دقیقاً مانند فروش در سایت (بخش‌های ۱ تا ۵) است و تنها در دو مورد زیر تفاوت دارد:

۰۱

دکمهٔ پرداخت در ربات

به‌جای هدایت خریدار به payment_url، نشانی telegram_url (در بله، bale_url) را از پاسخ ایجاد پرداخت بردارید و به‌صورت دکمهٔ «پرداخت» زیر پیام ربات خود قرار دهید. خریدار پرداخت را در ربات پرداخت پی‌اکسا انجام می‌دهد و از تلگرام یا بله خارج نمی‌شود.

۰۲

بازگشت خریدار به ربات شما

پیوند ربات خود را در پارامتر return_url قرار دهید (مانند https://t.me/YourBot). پس از پرداخت، خریدار با یک دکمه به ربات شما بازمی‌گردد و ربات پیام /start <payment_id> را دریافت می‌کند. در این مرحله، با همان payment_id پرداخت را تأیید کنید (بخش ۵).

نمونهٔ بدنهٔ درخواست ایجاد پرداخت برای ربات:

{
  "amount": 2500000,
  "order_id": "TG-88-1024",
  "return_url": "https://t.me/YourBot",
  "callback_url": "https://your-bot-server.ir/payexa-back"
}

سپس نشانی telegram_url دریافت‌شده در پاسخ را به‌صورت دکمهٔ پیام ربات ارسال کنید (در بله، bale_url را با همین ساختار در API بله به کار ببرید). در نمونهٔ زیر، CHAT_ID شناسهٔ گفت‌وگوی خریدار و TELEGRAM_URL مقدار telegram_url از پاسخ ایجاد پرداخت است:

POST https://api.telegram.org/bot<YOUR_BOT_TOKEN>/sendMessage
{
  "chat_id": CHAT_ID,
  "text": "برای پرداخت سفارش، دکمهٔ زیر را بزنید",
  "reply_markup": { "inline_keyboard": [[ { "text": "💳 پرداخت", "url": TELEGRAM_URL } ]] }
}
  • اگر ربات شما وب‌سرور ندارد، می‌توانید callback_url را ارسال نکنید و تنها return_url را بفرستید؛ در این حالت، تأیید پرداخت هنگام دریافت پیام /start انجام می‌شود.
  • ربات شما باید در پنل ثبت و تأیید شده باشد. پیوند ربات را در callback_url قرار ندهید؛ این پارامتر نشانی سرور شماست.
  • راهنمای کامل همراه با نمونهٔ پیام و کد: ربات آمادهٔ تلگرام، ربات آمادهٔ بله و نمایش اطلاعات کارت در ربات اختصاصی.

۰۷خطاها

همهٔ خطاها ساختار {"ok": false, "error": {"code": "…", "message": "…"}} را دارند. مقدار message به زبان فارسی است و می‌توان آن را مستقیماً به کاربر نمایش داد. رایج‌ترین خطاها:

code مفهوم اقدام لازم
UNAUTHORIZEDکلید API ارسال نشده یا نادرست است.کلید را از پنل بردارید و در هدر Authorization: Bearer بفرستید.
VALIDATION_ERRORیکی از فیلدها نامعتبر است.نام فیلد و علت آن در error.fields آمده است.
CALLBACK_NOT_ALLOWEDنشانی کال‌بک یا بازگشت روی سایت یا ربات تأییدشدهٔ شما نیست.نشانی سایت یا ربات را در پنل ثبت کنید تا تأیید شود.
ORDER_ID_CONFLICTبرای این شمارهٔ سفارش، پرداختی با مبلغ دیگر وجود دارد.تا پایان مهلت پرداخت قبلی صبر کنید یا شمارهٔ سفارش تازه بفرستید.
MERCHANT_INACTIVEدرگاه فعال نیست (برای نمونه، هنوز تأیید نشده است).وضعیت درخواست درگاه را در پنل ببینید.
CONTRACT_NOT_SIGNEDقرارداد درگاه هنوز امضا نشده است.قرارداد را در پنل بخوانید و امضا کنید.
INSUFFICIENT_WALLET_BALANCEموجودی کیف پول کمتر از حداقل لازم است.کیف پول را از پنل شارژ کنید.
NO_ACTIVE_CARDکارت فعالی برای دریافت وجه وجود ندارد.کارت بانکی را در پنل ثبت یا فعال کنید.
MERCHANT_TEMPORARILY_INACTIVEگوشی دریافت پیامک ثبت نشده یا بیش از ۳۰ دقیقه به سامانه متصل نبوده است.برنامهٔ Payexa Uploader را روی گوشی باز کنید و از اتصال اینترنت آن مطمئن شوید.
PAYMENT_NOT_PAID / PAYMENT_EXPIREDدر تأیید پرداخت: هنوز واریزی ثبت نشده است / مهلت پرداخت به پایان رسیده است.به بخش ۵ مراجعه کنید.
تکمیلی (اختیاری)

این بخش‌ها برای راه‌اندازی لازم نیستند؛ هر زمان به آن‌ها نیاز داشتید سراغشان بیایید.

۰۸بررسی امضای اعلان و هدرهای وبهوک

این بررسی الزامی نیست؛ زیرا سفارش تنها پس از تأیید پرداخت با کلید API تحویل می‌شود و یک اعلان جعلی نمی‌تواند به تحویل سفارش منجر شود. با این حال، اگر بخواهید درخواست‌های جعلی را در همان ابتدا رد کنید، اعلان POST پی‌اکسا هدرهای زیر را دارد:

X-Payexa-Event: payment.paid          (یا payment.expired)
X-Payexa-Signature: t=<زمان ارسال>,v1=<۶۴ نویسهٔ هگز>

v1 = HMAC-SHA256( key = کلید API شما ,  message = "{t}." + بدنهٔ خام درخواست )
در صورت عدم تطابق v1، یا اختلاف بیش از ۵ دقیقهٔ t با ساعت سرور شما، درخواست را رد کنید.
  • فقط همین دو هدر اضافه می‌آید؛ هدر جداگانه‌ای برای زمان وجود ندارد و زمان داخل همان هدر امضا با t= می‌آید (ثانیهٔ یونیکس).
  • بدنهٔ اعلان JSON فشرده است: بدون فاصله بعد از : و , و کاراکترهای غیرانگلیسی به شکل \uXXXX نوشته می‌شوند (برای نمونه، متن فارسی در metadata). به همین دلیل امضا را روی بایت‌های خام بدنه بگیرید، نه روی JSON بازسازی‌شده.
  • خروجی امضا هگز با حروف کوچک و ۶۴ نویسه است؛ آن را با مقایسهٔ امن (hash_equals در PHP، compare_digest در Python، timingSafeEqual در Node) با مقدار v1 بسنجید.
  • هر تلاش دوبارهٔ اعلان، t و امضای تازه دارد؛ برای تشخیص تکرار از payment_id و event استفاده کنید، نه از امضا.
  • پس از بررسی امضا، پرداخت را تأیید کنید و پاسخ موفق (۲xx) بدهید؛ نتیجهٔ نهایی همان پاسخ درخواست تأیید است.

نمونهٔ کامل دو اعلان (هدرها و بدنه)

برای آزمودن پیاده‌سازی: اگر همین بدنه‌ها را با کلید نمونهٔ example_api_key_for_docs و t=1793371263 امضا کنید، باید دقیقاً همین v1 را بگیرید.

POST /payexa-back HTTP/1.1
Host: your-shop.ir
Content-Type: application/json
X-Payexa-Event: payment.paid
X-Payexa-Signature: t=1793371263,v1=7452d5efd854bea6c9a0ee2f09d898749edc548fe25954b5cecd41e980bef3ee

{"event":"payment.paid","payment_id":"A7F3C9E1B5D2468A0C1E3F5B7D9A2C4E6","order_id":"INV-1024","status":"paid","amount":5000000,"payable_amount":4999440,"paid_at":"2026-10-01T09:41:05Z","metadata":null}
POST /payexa-back HTTP/1.1
Host: your-shop.ir
Content-Type: application/json
X-Payexa-Event: payment.expired
X-Payexa-Signature: t=1793371263,v1=598b8972cc10934edacb72769037aa06f5745f90383ea44d6b4d56aa4dd1a350

{"event":"payment.expired","payment_id":"A7F3C9E1B5D2468A0C1E3F5B7D9A2C4E6","order_id":"INV-1024","status":"expired","amount":5000000,"payable_amount":4999440,"paid_at":null,"metadata":null}

در اعلان انقضا مقدار paid_at تهی است و metadata همان چیزی است که خودتان هنگام ایجاد پرداخت فرستاده‌اید.

function payexa_signature_ok(string $rawBody, string $header): bool {
  parse_str(str_replace(",", "&", $header), $sig);
  return isset($sig["t"], $sig["v1"])
    && abs(time() - (int) $sig["t"]) <= 300
    && hash_equals(hash_hmac("sha256", $sig["t"] . "." . $rawBody, PAYEXA_API_KEY), $sig["v1"]);
}

// در payexa-back.php، برای درخواست POST:
if ($_SERVER["REQUEST_METHOD"] === "POST"
    && !payexa_signature_ok(file_get_contents("php://input"), $_SERVER["HTTP_X_PAYEXA_SIGNATURE"] ?? "")) {
  http_response_code(401);
  exit;
}

۰۹استعلام پرداخت

GET

برای مشاهدهٔ وضعیت یک پرداخت (برای نمونه، در پنل مدیریت سامانهٔ خود)، یکی از درخواست‌های GET زیر را با همان کلید API ارسال کنید:

https://pay.pexn.ir/v2/payments/{payment_id} https://pay.pexn.ir/v2/payments?order_id=INV-1024
  • پاسخ شامل همان اطلاعات پرداخت به‌همراه verified و paid_at است. مقدار status یکی از pending، paid یا expired است.
  • استعلام، پرداخت را تأیید نمی‌کند؛ برای تحویل سفارش همچنان تأیید پرداخت (بخش ۵) الزامی است.

۱۰استعلام کارمزد

GET

کارمزد هر پرداخت موفق از کیف پول شما در پنل کسر می‌شود و از مبلغ واریزی خریدار چیزی کم نمی‌شود. با این استعلام می‌توانید پیش از ایجاد پرداخت، مبلغ این کارمزد را بدانید و در صورت تمایل، آن را از خریدار دریافت کنید. درخواست را از سرور خود و با هدر Authorization: Bearer کلید_API به نشانی زیر ارسال کنید:

https://pay.pexn.ir/v2/fees?amount=1000000

پارامتر درخواست

پارامتر الزامی توضیح
amount بله مبلغ سفارش به ریال و به‌صورت عدد صحیح؛ همان مبلغی که هنگام ایجاد پرداخت (بخش ۳) در فیلد amount ارسال می‌شود، با همان بازهٔ مجاز. نوع درگاه شما (کارت به کارت شخصی یا ادمین) از روی کلید API تشخیص داده می‌شود و ارسال آن لازم نیست.

جز amount پارامتر دیگری نفرستید؛ پارامتر ناشناخته با خطای VALIDATION_ERROR رد می‌شود.

پاسخ

فیلد کاربرد
wallet_fee کارمزدی که پس از موفق شدن پرداختی با این مبلغ از کیف پول شما کسر می‌شود، به ریال؛ همیشه مضربی از ۱۰ ریال (تومان کامل). مقدار به تومان در wallet_fee_toman آمده است.
amount_with_fee کمترین مبلغی که اگر از خریدار دریافت شود، پس از کسر کارمزدِ همان مبلغ، دست‌کم مبلغ amount برای شما باقی می‌ماند؛ مضربی از ۱۰ ریال. مقدار به تومان در amount_with_fee_toman آمده است.
fees ریز کارمزد: عنوان (title)، درصد (rate_percent)، کمینه و بیشینه (min_fee، max_fee) و مبلغ (fee) هر عنوان، به ریال.
merchant_type نوع درگاه شما: personal (کارت به کارت شخصی) یا admin (کارت به کارت ادمین).
testnet برای حساب آزمایشی true است؛ در این حالت کارمزدی کسر نمی‌شود و wallet_fee صفر است.
limits کمترین (min_amount) و بیشترین (max_amount) مبلغ مجاز برای ایجاد پرداخت، به ریال.

دریافت کارمزد از خریدار

اگر می‌خواهید کارمزد را خریدار بپردازد، پیش از ایجاد پرداخت مبلغ سفارش را استعلام کنید و مقدار amount_with_fee را به‌عنوان amount در درخواست ایجاد پرداخت (بخش ۳) ارسال کنید. خریدار همین مبلغ را می‌پردازد، کل آن به کارت شما واریز می‌شود و پس از کسر کارمزد از کیف پول، دست‌کم مبلغ اصلی سفارش برای شما باقی می‌ماند. جمع سادهٔ «مبلغ سفارش + wallet_fee» کافی نیست، زیرا کارمزد روی مبلغ نهایی محاسبه می‌شود.

$ch = curl_init("https://pay.pexn.ir/v2/fees?amount=" . $orderAmount);
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ["Authorization: Bearer " . PAYEXA_API_KEY]]);
$quote = json_decode(curl_exec($ch), true);
$amount = $quote["data"]["amount_with_fee"];   // در درخواست ایجاد پرداخت به‌جای مبلغ سفارش
  • کارمزد بر اساس مبلغ سفارش (amount) محاسبه می‌شود، نه مبلغ واریزی خریدار (payable_amount).
  • تعرفهٔ کارمزد ممکن است تغییر کند؛ استعلام را نزدیک به زمان ایجاد پرداخت انجام دهید و نتیجه را بیش از چند دقیقه نگه ندارید.
  • دریافت کارمزد از خریدار، نحوهٔ کسر آن را تغییر نمی‌دهد: کارمزد همچنان از کیف پول کسر می‌شود و کیف پول باید حداقل موجودی لازم را داشته باشد (پیش از اتصال).
  • اگر amount_with_fee از limits.max_amount بیشتر باشد، ایجاد پرداخت با آن مبلغ پذیرفته نمی‌شود.
  • سقف درخواست برای هر کلید API، ۱۲۰ استعلام در دقیقه است؛ در صورت عبور از سقف، خطای RATE_LIMITED همراه با هدر Retry-After بازگردانده می‌شود.
  • در محیط آزمایشی (sandbox.pexn.ir با کلید PAYEXA)، پاسخ با تعرفهٔ جاری کارت به کارت شخصی محاسبه می‌شود.

۱۱صفحهٔ پرداخت اختصاصی

GET

اگر می‌خواهید اطلاعات کارت و مبلغ را در صفحهٔ سایت خود نمایش دهید، مقادیر card و payable_amount را از پاسخ ایجاد پرداخت بردارید و وضعیت پرداخت را از سمت مرورگر و بدون کلید API از نشانی زیر استعلام کنید:

https://pay.pexn.ir/v2/payments/{payment_id}/client
// در مرورگر خریدار، هر ۵ ثانیه یک بار
const timer = setInterval(async () => {
  const { ok, data } = await (await fetch("https://pay.pexn.ir/v2/payments/" + paymentId + "/client")).json();
  if (ok && data.status !== "pending") {
    clearInterval(timer);
    location.href = "/payexa-back?payment_id=" + paymentId;   // تأیید پرداخت در سرور شما
  }
}, 5000);
  • تنها سایت‌های تأییدشدهٔ شما مجاز به خواندن این پاسخ از مرورگر هستند. اطلاعات کارت تنها تا زمانی که پرداخت باز است نمایش داده می‌شود.
  • تحویل سفارش همچنان تنها پس از تأیید پرداخت در سرور شما مجاز است.

۱۲امنیت و محدودیت‌ها

تحویل تنها پس از تأیید

سفارش را نه بر اساس status در نشانی بازگشت و نه بر اساس اعلان POST تحویل دهید؛ تنها زمانی که پاسخ تأیید، first_verification: true باشد.

نگهداری کلید تنها روی سرور

در صورت افشای کلید API، بلافاصله آن را در پنل تغییر دهید. همچنین می‌توانید در پنل، بخش «IP سرور»، تا ۵ نشانی IP یا بازهٔ سرور خود را ثبت کنید.

«پرداخت به: …»

صفحهٔ پرداخت نام سایت یا ربات شما را نمایش می‌دهد؛ به خریداران توصیه کنید پیش از پرداخت به آن توجه کنند.

  • سقف درخواست برای هر کلید API در دقیقه: ایجاد پرداخت ۶۰، تأیید ۶۰، استعلام پرداخت ۱۲۰ و استعلام کارمزد ۱۲۰ درخواست؛ و از هر نشانی IP روی‌هم ۶۰۰ درخواست در دقیقه. در صورت عبور از سقف، خطای RATE_LIMITED همراه با هدر Retry-After بازگردانده می‌شود.
  • مبالغ به ریال و به‌صورت عدد صحیح هستند؛ زمان‌ها به وقت جهانی (UTC) و در قالبی مانند 2026-10-01T09:30:00Z بیان می‌شوند.
  • هدر قدیمی X-API-KEY نیز به‌جای Authorization: Bearer پذیرفته می‌شود.

۱۳مهاجرت از نسخهٔ ۱

نسخهٔ ۱ همچنان فعال است و مهاجرت الزامی نیست. کلید API برای هر دو نسخه یکسان است. معادل مفاهیم دو نسخه در جدول زیر آمده است:

نسخهٔ ۱ نسخهٔ ۲
POST /v1/payment/requestPOST /v2/payments
meta.order_reforder_id
authority + ساختن /startPay?authority=…payment_id + payment_url آماده
بازگشت با status=OK&authority=…بازگشت با payment_id&order_id&status=paid
تأیید (verify) با order_id و token (مبلغ یکتا)تأیید با payment_id و بدون توکن
کد 200 در نخستین تأیید و 201 در دفعات بعدهمواره 200؛ مقدار first_verification برابر true یا false
  • هر پرداخت با همان نسخه‌ای تأیید می‌شود که با آن ایجاد شده است.
پرسش‌ها

۱۴مشکلات رایج و راه‌حل آن‌ها

مشکل علت و راه‌حل
خریدار پرداخت کرده اما سفارش در سایت ثبت نشده است.سفارش تنها پس از تأیید پرداخت تحویل می‌شود. بررسی کنید که مسیر کال‌بک شما درخواست تأیید را ارسال می‌کند و هم GET و هم POST را می‌پذیرد. وضعیت پرداخت در پنل، بخش تراکنش‌ها، دیده می‌شود و تأیید پرداخت را می‌توان بعداً نیز انجام داد.
خریدار مبلغی متفاوت با مبلغ اعلام‌شده واریز کرده است.واریز تنها با مبلغ دقیق payable_amount به‌صورت خودکار شناسایی می‌شود. خریدار می‌تواند پس از پایان مهلت، رسید واریز را ارسال کند تا شما آن را در پنل بررسی و تأیید کنید.
خطای CALLBACK_NOT_ALLOWEDنشانی کال‌بک باید روی سایت یا ربات ثبت‌شده و تأییدشده در پنل باشد. نشانی را در پنل ثبت کنید و پس از تأیید آن دوباره تلاش کنید.
خطای MERCHANT_TEMPORARILY_INACTIVEگوشی دریافت پیامک بیش از ۳۰ دقیقه به سامانه متصل نبوده است. برنامهٔ Payexa Uploader را روی گوشی باز کنید و اتصال اینترنت و تنظیمات باتری گوشی را بررسی کنید.
نشانی telegram_url یا bale_url برابر null است.ربات پرداخت پی‌اکسا در آن پیام‌رسان در دسترس نیست؛ نشانی payment_url را به خریدار بدهید.
اتصال در محیط آزمایشی کار می‌کند اما در محیط واقعی نه.کلید API واقعی را به‌جای PAYEXA و نشانی pay.pexn.ir را به‌جای sandbox.pexn.ir قرار دهید و موارد پیش از اتصال را بررسی کنید.

اگر مشکل شما در این فهرست نیست، از بخش پشتیبانی پنل تیکت ثبت کنید و شناسهٔ پرداخت (payment_id) را در آن بنویسید.

PAYEXA ASSISTANT

من دستیار پی اکسا هستم، شما را در تمام مسیر راهنمایی می‌کنم و اگر به من نیازی ندارید با ۲ بار کلیک خاموش می‌شوم.