لوگوی کینگ شاپKINGAPI OpenAPI YAML
KING SHOP Payment API

اتصال سریع پرداخت برای دامنه‌های مجموعه

این 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کداقدام
400invalid_jsonJSON درخواست اصلاح شود.
403source_origin_not_allowedOrigin و دامنه بررسی شود.
409source_order_conflictشناسه سفارش جدید استفاده شود یا مبلغ و return_url ثابت بماند.
415unsupported_media_typeContent-Type: application/json ارسال شود.
422خطای فیلدمقدار ورودی اصلاح شود.
429rate_limitedRetry-After رعایت شود.
503status_unavailablePolling با فاصله پیشنهادی ادامه پیدا کند.

نمونه 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;