این راهنما قرارداد احرازشده supported-assets سرویکس را با PHP 8.2 یا جدیدتر پیادهسازی میکند. نمونه کامل و بدون وابستگی Composer در مسیر docs/examples/php، لایه HTTP را از اعتبارسنجی قرارداد جدا کرده است تا همه مسیرهای موفق و ناموفق بدون تماس با production تست شوند.
کد را فقط روی سرور، queue worker، job زمانبندیشده یا CLI قابل اعتماد و با افزونه cURL اجرا کنید. API Key یا response احرازشده سرویکس نباید وارد کد مرورگر، URL، HTML عمومی، analytics، گزارش خطا یا log عمومی شود.
تنظیم سمت سرور
export SERVIX_API_KEY="<your-servix-api-key>"
export SERVIX_ASSET_CODE="USD_RLS"
php -l docs/examples/php/src/ServixApiClient.php
php docs/examples/php/tests/ServixApiClientTest.php
php docs/examples/php/example.php
ServixApiClient::fromEnvironment() کلید را هنگام شروع process میخواند. برای توسعه و production کلید جدا بسازید، دسترسی به environment یا secret manager را محدود کنید و بعد از احتمال افشا کلید را فوری لغو کنید.
ارسال درخواست با cURL
transport تستشده redirect را میبندد، timeout اتصال و کل درخواست را مشخص میکند، فقط protocolهای HTTP و HTTPS را میپذیرد و status، header امن و body را به validator میدهد. API Key فقط در header درخواست قرار میگیرد.
<?php
$handle = curl_init(
$baseUrl . '/api/v1/assets/supported',
);
$responseHeaders = [];
curl_setopt_array($handle, [
CURLOPT_CONNECTTIMEOUT_MS => 3_000,
CURLOPT_TIMEOUT_MS => 10_000,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_HTTPHEADER => [
'Accept: application/json',
'X-API-Key: ' . $apiKey,
],
CURLOPT_PROTOCOLS => CURLPROTO_HTTPS | CURLPROTO_HTTP,
CURLOPT_RETURNTRANSFER => true,
]);
$body = curl_exec($handle);
if ($body === false) {
throw new ServixNetworkException(
'Servix network request failed.',
);
}
$status = (int) curl_getinfo(
$handle,
CURLINFO_RESPONSE_CODE,
);
curl_close($handle);
try {
$payload = json_decode(
$body,
true,
64,
JSON_THROW_ON_ERROR,
);
} catch (JsonException $exception) {
throw new ServixSchemaException(
'Servix returned malformed JSON.',
);
}
بررسی دسترسی و تازگی
$asset = null;
foreach ($payload['assets'] ?? [] as $candidate) {
if (is_array($candidate)
&& ($candidate['code'] ?? null) === $assetCode) {
$asset = $candidate;
break;
}
}
if ($asset === null) {
throw new ServixUnavailableException();
}
foreach (['providerSupported', 'latestValueAvailable', 'stale'] as $field) {
if (!array_key_exists($field, $asset)
|| !is_bool($asset[$field])) {
throw new ServixSchemaException();
}
}
if (!$asset['providerSupported']
|| !$asset['latestValueAvailable']) {
throw new ServixUnavailableException();
}
if ($asset['stale']
|| ($asset['availabilityStatus'] ?? null) !== 'FRESH') {
throw new ServixStaleDataException();
}
if ((!is_int($asset['latestValue'])
&& !is_float($asset['latestValue']))
|| !is_finite((float) $asset['latestValue'])) {
throw new ServixSchemaException();
}
نسخه کامل HTTPS، HTTP محدود به loopback تست، شکل asset code، timezone در businessTime، هماهنگی تازگی، عدد finite و request ID امن را هم پیش از ساخت AssetQuote immutable بررسی میکند.
قرارداد response احرازشده
{
"code": "USD_RLS",
"providerSupported": true,
"latestValueAvailable": true,
"latestValue": "<protected-market-value>",
"businessTime": "<source-timestamp>",
"availabilityStatus": "FRESH",
"stale": false
}
businessTime زمان خود داده است. وضعیتهای STALE، MISSING و UNKNOWN را حالتهای واقعی محصول بدانید. برای داده unavailable صفر نسازید و مقدار قدیمی را بدون برچسب stale استفاده نکنید. کدهای پایانیافته به _RLS ریالی هستند؛ تبدیل به تومان را در لایه نمایش خودتان انجام دهید.
خطای HTTP و retry محدود
| وضعیت | رفتار PHP |
|---|---|
| خطای اتصال یا timeout در cURL | حداکثر سه بار با فاصله نمایی retry کنید و بعد فقط دسته خطای امن را برگردانید. |
| 400، 401، 403، 404 | درخواست بدون تغییر را retry نکنید؛ request، API Key، دسترسی حساب یا asset code را اصلاح کنید. |
| 429 | سهمیه روزانه تمام شده است؛ polling را تا پنجره بعدی متوقف کنید. |
| 500، 502، 503، 504 | retry محدود خطای موقت را اجرا کنید و بعد امن fail شوید. |
| JSON یا قرارداد خراب | response را رد کنید؛ نوع فیلد را حدس نزنید و مقدار ساختگی نسازید. |
تستها یک transport ساختگی تزریق میکنند و URL، header، timeout، mapping موفق، backoff شبکه و 5xx، رفتار 401 و 429، JSON خراب، boolean ناقص، داده stale و unavailable، asset ناشناخته و تنظیم ناامن را میسنجند. هیچ کلید واقعی یا response بازار production برای تست لازم نیست.
cache و ادامه مسیر
فقط response احرازشده و معتبر را در بکاند cache کنید و businessTime را کنار آن نگه دارید. cache را میان کاربران مشترک کنید، TTL را کوتاهتر از پنجره تازگی قابل قبول بگذارید و برای هر مرورگر polling جدا نسازید. احراز هویت، نمادهای پشتیبانیشده، خطا و سهمیه، راهنمای Spring Boot و راهنمای Python را هم ببینید.
ساخت حساب و فعالسازی دسترسی API · مقایسه پلنها و سهمیه روزانه
اتصال کلاینت Vue از مرز امن PHP
راهنمای تستشده Vue 3، Pinia و Composition API برای سرویکس مکمل فرانتاند این اتصال PHP است. store و client تایپشده آن بدون افشای API Key، پاسخ محدود و هممبدأ برنامه را مصرف میکنند.
قیمت خرید و فروش همان مشاهده
قیمت سمتهای مشاهدهٔ ذخیرهشده: buyPrice قیمت خرید فروشنده از مشتری و sellPrice قیمت فروش به مشتری است. هر سمت متعلق به همان مشاهدهٔ value با همان quoteUnit و businessTime است و به صورت عدد دقیق JSON یا، در صورت ذخیرهنشدن، صریحاً null برمیگردد. در مشاهدههای قدیمی ممکن است یک یا هر دو سمت ناموجود باشند. قیمت میانی، قیمت قبلی یا قیمت فعلی جایگزین نمیشود. calendar=persian فقط تاریخ مسیر را به همان روز میلادی تبدیل میکند؛ include=ohlc کندل قیمت میانی را اضافه میکند و مشاهدهٔ انتخابشده را تغییر نمیدهد. مسیر GET تاریخچه و بازهٔ POST معادل، آخرین مشاهدهٔ انتخابشدهٔ هر روز موجود در نتیجهٔ جستوجوی قبلی را برمیگردانند؛ روز ناموجود پر نمیشود. این تغییر دادهٔ تاریخی تامینکننده را دریافت یا بازسازی نمیکند.
نمونهٔ مشاهدهٔ ذخیرهشده (مقادیر محافظتشده):
{
"code": "USD_RLS",
"name": "USD_RLS",
"slug": "usd_rls",
"labelEn": "US Dollar / Iranian Rial",
"labelFa": "دلار آمریکا / ریال ایران",
"quoteUnit": "RLS",
"value": "<protected-market-value>",
"businessTime": "<source-timestamp>",
"buyPrice": "<protected-market-value>",
"sellPrice": "<protected-market-value>"
}نمونهٔ جداگانهٔ سمت ناموجود (مقادیر محافظتشده):
{
"buyPrice": "<protected-market-value>",
"sellPrice": null
}