پرش به محتوا
مستندات مهراد

authentication اپلیکیشن پذیرنده

سوپر اپلیکیشن مهراد

این سند نحوهٔ دریافت و استفاده از access token سرور-به-سرور را توضیح می‌دهد. این token برای تأیید پرداخت و مدیریت سفارش‌ها از سمت سرور پذیرنده استفاده می‌شود و به هیچ کاربر خاصی وابسته نیست.


کاربرد این روش

عملیات حساس سفارش — تأیید پرداخت، ثبت ارسال و تحویل، اصلاح و لغو — باید از سرور پذیرنده انجام شود و نباید به token کاربری که در حال خرید است وابسته باشد. برای این کار یک اپلیکیشن پذیرنده تعریف می‌شود که با client_id و client_secret خود token دریافت می‌کند.

تفاوت آن با ورود یکپارچه: token SSO هویت یک کاربر را حمل می‌کند و عمر ۱۲ ساعته دارد، در حالی که token اپلیکیشن پذیرنده هویت کسب‌وکار پذیرنده را حمل می‌کند و ۲ ساعت اعتبار دارد.

endpointهای قابل استفاده با این token

endpointکاربرد
POST /gateway/order/payment/verifyتأیید پرداخت
GET /gateway/order/payment/{authority}/statusاستعلام وضعیت پرداخت
POST /gateway/order/{order_id}/shippedثبت ارسال
POST /gateway/order/{order_id}/deliverثبت تحویل
POST /gateway/order/{order_id}/updateاصلاح کاهشی سفارش
POST /gateway/order/{order_id}/cancelلغو سفارش
GET /gateway/order/{order_id}دریافت جزئیات و تاریخچهٔ سفارش
POST /gateway/order/draft/requestایجاد خرید حضوری

شرح کامل این endpoint‌ها در مستندات سفارش و مستندات خرید حضوری آمده است.

ایجاد پرداخت (POST /gateway/order/payment/request) با این token کار نمی‌کند؛ آن endpoint به Access Token کاربر از SSO و scope order نیاز دارد.


پیش‌نیازها

موردتوضیح
client_idID اپلیکیشن پذیرنده
client_secretکلید محرمانه؛ تنها یک‌بار هنگام ساخت یا بازتولید نمایش داده می‌شود
لیست سفید آی‌پی (اختیاری)در صورت تنظیم، request‌ها فقط از همان آی‌پی‌ها پذیرفته می‌شوند

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


مرحله ۱ — دریافت token

POST/gateway/merchant-app/token

Authentication: این request بدون header Authorization ارسال می‌شود؛ اعتبارسنجی با client_id و client_secret در body انجام می‌شود.

ورودی

پارامترهای ورودی2 فیلد
client_idstringالزامی
ID اپلیکیشن پذیرنده.
client_secretstringالزامی
کلید محرمانهٔ اپلیکیشن.
{
  "client_id": "8f3c2a1b-4d5e-4f7a-8b9c-0d1e2f3a4b5c",
  "client_secret": "YOUR_CLIENT_SECRET"
}

Response

نمونه پاسخ
{
  "success": true,
  "data": {
    "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "token_type": "bearer",
    "expires_in": 7200
  },
  "meta": null
}

مقدار expires_in برابر ۷۲۰۰ ثانیه (۲ ساعت) است. این مقدار با مدت اعتبار token SSO (۴۳۲۰۰ ثانیه) متفاوت است.


مرحله ۲ — استفاده از token

token در header Authorization و با پیشوند Bearer ارسال می‌شود:

POST /gateway/order/payment/verify
Content-Type: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

{
  "authority": "8cdb1c8c-bd3c-4f0f-9c8b-9c72c1f2e41c",
  "amount": 11300000
}

ID پذیرنده از خود token استخراج می‌شود؛ نیازی به ارسال آن در body نیست.


مدیریت چرخهٔ عمر token

  • token باید در حافظهٔ سرور نگهداری و تا پیش از انقضا از همان استفاده شود؛ نباید برای هر request، token جدید دریافت شود.
  • token باید کمی زودتر از انقضا (مثلاً ۵ دقیقه) تازه شود تا در میانهٔ ارسال requestها منقضی نشود.
  • اگر response با کد ۴۰۱ برگردانده شود، یک‌بار token تازه دریافت و همان request دوباره ارسال می‌شود. اگر باز هم کد ۴۰۱ برگردانده شود، اعتبارنامه‌ها باید بررسی شوند و نباید حلقهٔ تلاش مجدد ایجاد شود.
  • در پیاده‌سازی چندنخی یا چندپردازشی، باید از دریافت هم‌زمان token توسط چند request جلوگیری شود؛ دریافت token باید پشت یک قفل (lock) قرار گیرد.
Python — نمونهٔ مدیریت token
import threading
import time

import requests

BASE_URL = "https://api.mehrad.app"


class MerchantAppClient:
    def __init__(self, client_id: str, client_secret: str):
        self._client_id = client_id
        self._client_secret = client_secret
        self._token = None
        self._expires_at = 0.0
        self._lock = threading.Lock()

    def _fetch_token(self) -> str:
        response = requests.post(
            f"{BASE_URL}/gateway/merchant-app/token",
            json={"client_id": self._client_id, "client_secret": self._client_secret},
            timeout=10,
        )
        response.raise_for_status()
        data = response.json()["data"]
        self._token = data["access_token"]
        # refresh 5 minutes before the real expiry
        self._expires_at = time.time() + data["expires_in"] - 300
        return self._token

    def access_token(self) -> str:
        with self._lock:
            if not self._token or time.time() >= self._expires_at:
                return self._fetch_token()
            return self._token

    def verify_payment(self, authority: str, amount: int) -> dict:
        response = requests.post(
            f"{BASE_URL}/gateway/order/payment/verify",
            headers={"Authorization": f"Bearer {self.access_token()}"},
            json={"authority": authority, "amount": amount},
            timeout=10,
        )
        response.raise_for_status()
        return response.json()["data"]
Node.js — نمونهٔ دریافت token
const BASE_URL = "https://api.mehrad.app";

let cached = { token: null, expiresAt: 0 };

async function getAccessToken(clientId, clientSecret) {
  if (cached.token && Date.now() < cached.expiresAt) return cached.token;

  const res = await fetch(`${BASE_URL}/gateway/merchant-app/token`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ client_id: clientId, client_secret: clientSecret }),
  });
  if (!res.ok) throw new Error(`token request failed: ${res.status}`);

  const { data } = await res.json();
  cached = {
    token: data.access_token,
    // refresh 5 minutes before the real expiry
    expiresAt: Date.now() + (data.expires_in - 300) * 1000,
  };
  return cached.token;
}

لیست سفید آی‌پی

اگر برای اپلیکیشن پذیرنده لیست سفید آی‌پی تنظیم شده باشد، هم دریافت token و هم request‌های بعدی فقط از همان آدرس‌ها پذیرفته می‌شوند و در غیر این صورت response آن با کد ۴۰۳ و خطای MERCHANT_APP_IP_ADDRESS_NOT_ALLOWED برمی‌گردد. در تأیید پرداخت و استعلام وضعیت، به‌جای آن response با کد ۴۰۱ و خطای HTTP_EXCEPTION برگردانده می‌شود. پیش از تغییر زیرساخت یا افزودن سرور جدید، لازم است آدرس‌های جدید اعلام شود.


فهرست خطاها

اگر request با خطا مواجه شود، response آن کدی غیر از ۲xx دارد و success در آن false است. کد خطا در فیلد error.code و متن قابل نمایش در error.message برگردانده می‌شود. ساختار کامل خطا در بخش مقدمه آمده است.

دریافت token

POST/gateway/merchant-app/token
کد خطاHTTPتوضیح و اقدام پیشنهادی
VALIDATION_ERROR422client_id یا client_secret ارسال نشده، یا client_id قالب UUID ندارد.
MERCHANT_APP_INVALID_CREDENTIALS401client_id وجود ندارد یا client_secret نادرست است.
MERCHANT_APP_NOT_ACTIVE400اپلیکیشن غیرفعال است؛ فعال‌سازی آن نیازمند هماهنگی با مهراد است.
MERCHANT_APP_IP_ADDRESS_NOT_ALLOWED403آی‌پی request در لیست سفید نیست.
نمونه پاسخ
{
  "success": false,
  "error": {
    "code": "MERCHANT_APP_INVALID_CREDENTIALS",
    "message": "اطلاعات کلاینت خارجی (client_id / client_secret) نامعتبر است.",
    "details": null
  }
}

استفاده از token

کد خطاHTTPتوضیح و اقدام پیشنهادی
HTTP_EXCEPTION401header Authorization ارسال نشده یا قالب Bearer <token> ندارد (message برابر Not authenticated). در تأیید پرداخت و استعلام وضعیت، هر مشکل token — نبود header، token نامعتبر یا منقضی، اپلیکیشن غیرفعال یا آی‌پی خارج از لیست سفید — با همین کد و message برابر Unauthorized برگردانده می‌شود.
MERCHANT_APP_TOKEN_EXPIRED_OR_INVALID401token نامعتبر یا منقضی است، یا اپلیکیشن غیرفعال شده است؛ token تازه باید دریافت شود.
MERCHANT_APP_IP_ADDRESS_NOT_ALLOWED403آی‌پی request در لیست سفید نیست.
نمونه پاسخ
{
  "success": false,
  "error": {
    "code": "MERCHANT_APP_TOKEN_EXPIRED_OR_INVALID",
    "message": "توکن کلاینت خارجی نامعتبر یا منقضی شده است.",
    "details": null
  }
}

خطاهای مربوط به خود سفارش‌ها (مانند ORDER_ALREADY_PAID یا ORDER_AMOUNT_NOT_MATCHED) در مستندات سفارش فهرست شده‌اند.


نکات امنیتی

  • client_secret و token فقط باید روی سرور نگهداری شوند و نباید در کد سمت مرورگر، اپلیکیشن موبایل یا مخزن کد قرار گیرند.
  • token نباید در لاگ‌ها ثبت شود.
  • ارتباط باید فقط روی HTTPS برقرار شود.
  • برای محیط تست، https://dev-api.mehrad.app و اعتبارنامه‌های همان محیط استفاده می‌شود.

چک‌لیست پیاده‌سازی

  • دریافت و نگهداری امن client_id و client_secret
  • پیاده‌سازی دریافت token از POST /gateway/merchant-app/token
  • نگهداری token در حافظه و تازه‌سازی پیش از انقضای ۲ ساعته
  • افزودن header Authorization: Bearer به request‌های محافظت‌شده
  • مدیریت response با کد ۴۰۱ با یک‌بار تازه‌سازی token و تکرار request
  • هماهنگی لیست سفید آی‌پی برای سرورهای عملیاتی
  • جداسازی اعتبارنامه‌های محیط تست و عملیاتی

در این صفحه