اتصال سریع پرداخت برای دامنههای مجموعه
این API بدون API Key، رمز یا Secret سایت مبدأ کار میکند. ساخت پرداخت از دامنههای مجاز پذیرفته میشود و تأیید نهایی تراکنش فقط در سرور KING SHOP انجام میشود.
نکته امنیتی: Origin احراز هویت رمزنگاریشده نیست. برای حفظ سادگی اتصال، کنترل دامنه بازگشت، شناسه تصادفی، Verify سمت سرور و Rate Limit نرم در پشتصحنه اعمال میشوند.
دامنههای مجاز
kingshopone.com*.kingshopone.comkingshopone.ir*.kingshopone.irدرخواست backend باید هدر زیر را ارسال کند:
Origin: https://shop.kingshopone.com
در اتصال server-to-server میتوان از X-King-Source-Origin استفاده کرد. آدرس return_url نیز باید HTTPS و روی یکی از دامنههای مجاز باشد.
۱. ساخت یا بازیابی پرداخت
POST
/api/v1/payments{
"source_order_id": "ORDER-1042",
"amount": 250000,
"title": "خرید پلن حرفهای",
"description": "اشتراک یکماهه",
"return_url": "https://shop.kingshopone.com/payment/result",
"payer_name": "نام مشتری",
"payer_identity": "09120000000"
}
مبلغ به تومان است. source_order_id در هر دامنه idempotent است.
رفتار تکرار درخواست: همان شناسه سفارش با همان مبلغ و همان آدرس بازگشت، همان پرداخت فعال را با
reused: true برمیگرداند. تغییر مبلغ یا آدرس بازگشت روی سفارش فعال، پاسخ 409 ایجاد میکند.پاسخ موفق
{
"ok": true,
"payment_id": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6",
"payment_url": "https://kingshopone.com/pay/a1b2...",
"status_url": "https://kingshopone.com/api/v1/payments/a1b2.../status",
"status": "created",
"expires_at": 1783900000,
"reused": false,
"poll_seconds": 2
}
۲. هدایت کاربر
بعد از دریافت پاسخ موفق، مرورگر کاربر را به payment_url هدایت کنید:
window.location.assign(result.payment_url);
۳. بررسی وضعیت
GET
/api/v1/payments/{payment_id}/statusاین endpoint در حالت عادی از Cache حافظه پاسخ میدهد. برای کاهش ترافیک، مقدار ETag را در درخواست بعدی با If-None-Match ارسال کنید.
{
"payment_id": "a1b2...",
"status": "verify_pending",
"paid": false,
"terminal": false,
"poll": true,
"poll_after": 2,
"updated_at": 1783898200,
"expires_at": 1783900000,
"version": 5
}
pollمشخص میکند بررسی ادامه پیدا کند یا متوقف شود.poll_afterفاصله درخواست بعدی به ثانیه است.- پاسخ
304بدنه JSON ندارد. - در پاسخهای
429و503، هدرRetry-Afterرا رعایت کنید.
let etag = "";
let stopped = false;
async function watchPayment(statusUrl) {
if (stopped) return;
let delay = 2000;
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 5000);
try {
const headers = { Accept: "application/json" };
if (etag) headers["If-None-Match"] = etag;
const response = await fetch(statusUrl, {
cache: "no-store",
headers,
signal: controller.signal
});
if (response.status === 304) {
// وضعیت تغییر نکرده است.
} else if (response.status === 429 || response.status === 503) {
delay = Number(response.headers.get("Retry-After") || 3) * 1000;
} else {
if (!response.ok) throw new Error("status_failed");
etag = response.headers.get("ETag") || etag;
const payment = await response.json();
stopped = payment.poll === false;
delay = Number(payment.poll_after || 2) * 1000;
if (payment.paid) {
// سفارش را فقط یک بار فعال کنید.
}
}
} catch {
delay = 5000;
} finally {
clearTimeout(timeout);
}
if (!stopped) setTimeout(() => watchPayment(statusUrl), delay);
}
وضعیتها
| وضعیت | Polling | اقدام |
|---|---|---|
created | ۲ ثانیه | کاربر هنوز پرداخت را شروع نکرده است. |
gateway_creating | ۲ ثانیه | درگاه در حال آمادهسازی است. |
gateway_ready | ۲ ثانیه | لینک درگاه آماده است. |
verify_pending | ۲ ثانیه | پرداخت دوباره انجام نشود؛ تأیید خودکار فعال است. |
manual_review | ۳۰ ثانیه یا توقف | تا زمانی که retry سروری زمانبندی شده ادامه دارد؛ پس از پایان retryها polling متوقف و بررسی دستی ممکن است. |
paid | متوقف | سفارش فعال شود. |
cancelled | ۳۰ ثانیه | همان صفحه قابل تلاش مجدد است و پس از پایان اعتبار متوقف میشود. |
failed | ۳۰ ثانیه | اتصال به درگاه قابل تلاش مجدد است و پس از پایان اعتبار متوقف میشود. |
expired | متوقف | پرداخت تازه ایجاد شود. |
خطاهای API
تمام خطاها شامل کد فنی کوتاه و پیام فارسی خوانا هستند.
{
"ok": false,
"error": "source_order_conflict",
"message": "این شناسه سفارش قبلاً با مبلغ یا آدرس بازگشت متفاوت ثبت شده است."
}
| HTTP | کد | اقدام |
|---|---|---|
| 400 | invalid_json | JSON درخواست اصلاح شود. |
| 403 | source_origin_not_allowed | Origin و دامنه بررسی شود. |
| 409 | source_order_conflict | شناسه سفارش جدید استفاده شود یا مبلغ و return_url ثابت بماند. |
| 415 | unsupported_media_type | Content-Type: application/json ارسال شود. |
| 422 | خطای فیلد | مقدار ورودی اصلاح شود. |
| 429 | rate_limited | Retry-After رعایت شود. |
| 503 | status_unavailable | Polling با فاصله پیشنهادی ادامه پیدا کند. |
نمونه PHP مقاوم در برابر خطا
$payload = [
'source_order_id' => 'ORDER-1042',
'amount' => 250000,
'title' => 'خرید پلن حرفهای',
'return_url' => 'https://shop.kingshopone.com/payment/result'
];
$ch = curl_init('https://kingshopone.com/api/v1/payments');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CONNECTTIMEOUT => 5,
CURLOPT_TIMEOUT => 12,
CURLOPT_HTTPHEADER => [
'Accept: application/json',
'Content-Type: application/json',
'Origin: https://shop.kingshopone.com'
],
CURLOPT_POSTFIELDS => json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR)
]);
$raw = curl_exec($ch);
$httpCode = (int) curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
$error = curl_error($ch);
curl_close($ch);
if ($raw === false || $error !== '') {
throw new RuntimeException('ارتباط با سرویس پرداخت برقرار نشد.');
}
$result = json_decode($raw, true, 512, JSON_THROW_ON_ERROR);
if (!in_array($httpCode, [200, 201], true) || empty($result['payment_url'])) {
throw new RuntimeException($result['message'] ?? 'ساخت پرداخت انجام نشد.');
}
header('Location: ' . $result['payment_url'], true, 303);
exit;