پرش به مطلب اصلی

SDK پی‌اچ‌پی

از SDK رسمی PHP اورکاریل استفاده کنید تا در هر اپلیکیشن PHP پرداخت کریپتو بپذیرید. SDK احراز هویت Basic، قالب‌بندی JSON درخواست/پاسخ و بررسی امضای وب‌هوک را انجام می‌دهد.

کلاینت از سبک مدرن Stripe PHP پیروی می‌کند: یک OrcaRailClient، ویژگی‌های سرویس که در اولین دسترسی ساخته می‌شوند، آرایه‌های انجمنی برای پارامترهای درخواست و اشیای پاسخ سازگار با نسخه‌های آینده.

پیش‌نیازها​

  • PHP 8.1 یا جدیدتر
  • ext-curl و ext-json

نصب​

پکیج را با Composer نصب کنید:

composer require orcarail/orcarail-php

نام پکیج orcarail/orcarail-php است و namespace به نام OrcaRail\ را (PSR-4) autoload می‌کند. برای نصب از یک نسخه checkout مخزن، یک مخزن path یا vcs به composer.json اضافه کنید:

{
"repositories": [{ "type": "path", "url": "../orcarail/sdks-php" }],
"require": { "orcarail/orcarail-php": "*" }
}

پیکربندی​

یک کلاینت با کلید و secret API بسازید. به‌طور اختیاری آدرس پایه سفارشی (مثلاً برای توسعه محلی) و مهلت‌های زمانی تعیین کنید.

<?php

require __DIR__ . '/vendor/autoload.php';

use OrcaRail\OrcaRailClient;

$orcarail = new OrcaRailClient([
'api_key' => getenv('ORCARAIL_API_KEY'), // e.g. "ak_live_xxx"
'api_secret' => getenv('ORCARAIL_API_SECRET'), // e.g. "sk_live_xxx"
'base_url' => 'https://api.orcarail.com/api/v1', // optional; default
'timeout' => 30000, // optional; milliseconds, default 30000
'connect_timeout' => 10000, // optional; milliseconds, default 10000
]);

برای توسعه محلی، base_url را به API خود اشاره دهید:

$orcarail = new OrcaRailClient([
'api_key' => getenv('ORCARAIL_API_KEY'),
'api_secret' => getenv('ORCARAIL_API_SECRET'),
'base_url' => 'http://127.0.0.1:3000/api/v1',
'timeout' => 65000, // /complete can take up to ~65s
]);

سرویس‌ها در اولین دسترسی ساخته و دوباره استفاده می‌شوند: $orcarail->paymentIntents، $orcarail->subscriptions، $orcarail->pay، $orcarail->rates، $orcarail->products و $orcarail->prices.

اشیای پاسخ​

هر شیء JSON در پاسخ به یک OrcaRail\OrcaRailObject تبدیل می‌شود. فیلدهای ناشناخته حفظ می‌شوند، پس فیلدهای جدید API بدون ارتقای SDK قابل خواندن‌اند. اشیا از دسترسی ویژگی، دسترسی آرایه‌ای، پیمایش، count()، toArray() و json_encode() پشتیبانی می‌کنند.

$intent = $orcarail->paymentIntents->retrieve($intentId);

echo $intent->status; // property access
echo $intent['client_secret']; // array access
echo $intent->payment_link->link; // nested object
$asArray = $intent->toArray(); // plain PHP array

کلیدهای ناموجود به‌جای هشدار null برمی‌گردانند، پس پیش از استفاده بررسی کنید:

if ($intent->payment_link !== null) {
header('Location: ' . $intent->payment_link->link);
}

live در برابر sandbox​

کلیدهای یک سازمان sandbox با ak_test_ شروع می‌شوند (شبکه‌های آزمایشی، بدون وجوه واقعی). $orcarail->paymentIntents->simulate($id) یک پرداخت sandbox را بدون کیف پول تکمیل می‌کند. رویدادهای وب‌هوک livemode دارند (برای رویدادهای sandbox $event->livemode === false).

کمیسیون (تقسیم و گرفتن سهم خودتان)​

کلیدهای API می‌توانند درصد کمیسیون اختیاری (۰ تا ۱۰۰) داشته باشند. آن را در داشبورد ← کلیدهای API یا با PATCH /api/v1/api-keys/:id و commissionPercent تنظیم کنید. پرداخت‌هایی که با آن کلید ساخته می‌شوند کمیسیون را در پیش‌فاکتور (قابل مشاهده برای پرداخت‌کننده) دارند و هنگام برداشت وجوه، مبلغ کمیسیون به آدرس تنظیمات برداشت شما فرستاده می‌شود.

Payment Intentها​

ساخت Payment Intent​

یک payment intent را یا با price_id کاتالوگ یا با مجموعه کامل پارامترهای مبلغ مستقیم (amount، currency، tokenId، networkId) بسازید. این دو شکل با هم قابل جمع نیستند؛ SDK پیش از ارسال درخواست، ترکیب آن‌ها را رد می‌کند.

$intent = $orcarail->paymentIntents->create([
'amount' => '100.00',
'currency' => 'usd',
'tokenId' => '550e8400-e29b-41d4-a716-446655440000', // token UUID
'networkId' => '3f1a0c1e-9d7b-4f8a-9d21-4a2f6b7c8d90', // network UUID
'return_url' => 'https://yourapp.com/success',
'cancel_url' => 'https://yourapp.com/cancel',
'description' => 'Order #12345',
'metadata' => ['order_id' => '12345'],
// optional: override where funds are withdrawn, keyed by chain family
'withdrawal_addresses' => ['evm' => '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb'],
]);

echo $intent->id; // e.g. "550e8400-e29b-41d4-a716-446655440000"
echo $intent->client_secret;
echo $intent->payment_link?->link;

اگر payment_method_types را نفرستید، پیش‌فرض آن ['crypto'] است. برای گرفتن UUID شبکه و توکن از پرداخت‌ها: فهرست شبکه‌ها و پرداخت‌ها: فهرست توکن‌ها بر اساس شبکه استفاده کنید.

با قیمت کاتالوگ، فقط price_id را بفرستید:

$intent = $orcarail->paymentIntents->create([
'price_id' => $priceId,
'return_url' => 'https://yourapp.com/success',
]);

تأیید و انتقال به پرداخت​

intent را تأیید کنید تا آدرس صفحه پرداخت میزبانی‌شده را بگیرید، سپس مشتری را منتقل کنید. تأیید با کلید API شما احراز هویت می‌شود و client secret را بررسی می‌کند.

$confirmed = $orcarail->paymentIntents->confirm($intent->id, [
'client_secret' => $intent->client_secret,
'return_url' => 'https://yourapp.com/success',
]);

$payUrl = $confirmed->pay_url ?? $confirmed->next_action?->redirect_to_url?->url;
header('Location: ' . $payUrl);

دریافت Payment Intent​

$intent = $orcarail->paymentIntents->retrieve('550e8400-e29b-41d4-a716-446655440000');
echo $intent->status;

ویرایش Payment Intent​

پیش از پرداخت مشتری، مبلغ، فراداده، آدرس‌ها یا آدرس‌های برداشت را تغییر دهید:

$updated = $orcarail->paymentIntents->update('550e8400-e29b-41d4-a716-446655440000', [
'amount' => '150.00',
'metadata' => ['order_id' => '12345'],
'withdrawal_addresses' => ['evm' => '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb'],
]);

لغو Payment Intent​

$canceled = $orcarail->paymentIntents->cancel('550e8400-e29b-41d4-a716-446655440000');

تکمیل Payment Intent​

وقتی مشتری به آدرس موفقیت/بازگشت شما رسید، intent را در حال پردازش علامت بزنید (این کار وب‌هوک payment_intent.processing را فعال می‌کند):

$completed = $orcarail->paymentIntents->complete('550e8400-e29b-41d4-a716-446655440000');

پرداخت با slug​

اگر از لینک‌های پرداخت با slug استفاده می‌کنید، جزئیات پرداخت را بخوانید و با slug لغو کنید:

$pay = $orcarail->pay->retrieve('pl_abc123'); // get() is an alias
$orcarail->pay->cancel('pl_abc123');

اشتراک‌ها​

مسیرهای مجموعه اشتراک‌ها سازمان را از کلید API احراز هویت‌شده تشخیص می‌دهند. یا با price_id یا با مجموعه کامل پارامترهای مستقیم (amount، currency، token_id، network_id، interval) بسازید؛ توجه کنید که اینجا نام فیلدها مطابق قرارداد REST به‌صورت snake_case است.

$subscription = $orcarail->subscriptions->create([
'description' => 'Monthly Pro Plan',
'amount' => '10.00',
'currency' => 'usd',
'token_id' => $tokenId,
'network_id' => $networkId,
'interval' => 'month',
'interval_count' => 1,
'collection_method' => 'send_payment_link',
'total_cycles' => 12,
'payer_email' => '[email protected]',
'metadata' => ['plan_id' => 'pro_monthly'],
]);

echo $subscription->id;
echo $subscription->current_period_end;
echo $subscription->latest_payment_link?->link;

فهرست، ویرایش، لغو و ازسرگیری:

$page = $orcarail->subscriptions->all([
'status' => 'active',
'limit' => 20,
'current_period_end' => ['lte' => '2026-12-31T00:00:00.000Z'], // sent as current_period_end[lte]
]);

foreach ($page->data as $sub) {
echo $sub->id . ' ' . $sub->description . PHP_EOL;
}
echo $page->has_more ? 'more pages' : 'last page';

$orcarail->subscriptions->update($subscription->id, ['cancel_at_period_end' => true]);

$orcarail->subscriptions->cancel($subscription->id, [
'cancellation_details' => ['comment' => 'Switching to annual', 'feedback' => 'other'],
]);

$orcarail->subscriptions->resume($subscription->id);

$links = $orcarail->subscriptions->listPaymentLinks($subscription->id, ['limit' => 10]);

نام متد all() است و نه list()، چون list در PHP کلمه رزروشده است.

فراخوانی‌های برداشت خودکار سمت پرداخت‌کننده (approve-auto-charge، revoke-auto-charge، set-collection-method) بخشی از SDK نیستند؛ آن‌ها را از رابط پرداخت و از طریق HTTP بدون کلید API فراخوانی کنید. برداشت خودکار را ببینید.

کاتالوگ: محصولات و قیمت‌ها​

مسیرهای کاتالوگ وابسته به سازمان‌اند، پس UUID سازمان را به‌عنوان آرگومان اول بفرستید.

$products = $orcarail->products->all($organizationId);
$product = $orcarail->products->create($organizationId, [
'name' => 'Pro Plan',
'description' => 'Monthly access',
'active' => true,
]);

$prices = $orcarail->prices->all($organizationId, ['active' => true]);
$recurring = $orcarail->prices->listActiveRecurring($organizationId);

// Find or create a one-time price for a fixed amount (idempotent by amount + currency + product name)
$price = $orcarail->prices->ensureOneTime($organizationId, [
'amount' => '25.00',
'currencyCode' => 'usd',
'tokenId' => $tokenId,
'networkId' => $networkId,
'productName' => 'One-time checkout',
]);

$intent = $orcarail->paymentIntents->create([
'price_id' => $price->id,
'return_url' => 'https://yourapp.com/success',
]);

products->all() و prices->all() پوشش فهرست API را باز می‌کنند و آرایه‌ای از OrcaRailObject برمی‌گردانند.

نرخ‌ها​

$quote = $orcarail->rates->getFiatQuote(['amount' => '100.00', 'currency' => 'eur']);
echo $quote->amountUsd;

$currencies = $orcarail->rates->getCurrencies(['active' => true]);
foreach ($currencies as $currency) {
echo $currency->code . PHP_EOL;
}

برای فیلدهای پاسخ API قیمت را ببینید.

وب‌هوک‌ها​

helperهای وب‌هوک ایستا هستند و به کلاینت نیاز ندارند، چون بررسی فقط به بدنه خام، هدر x-webhook-signature و secret وب‌هوک شما نیاز دارد.

بررسی امضا​

use OrcaRail\WebhookSignature;

$payload = file_get_contents('php://input'); // raw body, never a re-encoded array
$signature = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'] ?? '';

if (!WebhookSignature::verifyHeader($payload, $signature, $webhookSecret)) {
http_response_code(400);
echo 'Invalid signature';
exit;
}

ساخت رویداد (بررسی و تجزیه)​

use OrcaRail\Enum\WebhookEventType;
use OrcaRail\Exception\SignatureVerificationException;
use OrcaRail\Webhook;

try {
$event = Webhook::constructEvent(
file_get_contents('php://input'),
$_SERVER['HTTP_X_WEBHOOK_SIGNATURE'] ?? '',
getenv('ORCARAIL_WEBHOOK_SECRET'),
);
} catch (SignatureVerificationException $exception) {
http_response_code(400);
exit;
}

$object = $event->data->object;
$type = WebhookEventType::tryFrom((string) $event->type);

match ($type) {
WebhookEventType::PaymentIntentCompleted => fulfilOrder($object->id),
WebhookEventType::PaymentIntentProcessing => markProcessing($object->id),
WebhookEventType::PaymentIntentCanceled => releaseOrder($object->id),
WebhookEventType::SubscriptionPaymentLinkPaid => creditCycle($object->id),
// tryFrom() returns null for event types this SDK version does not know
default => null,
};

http_response_code(200);
echo json_encode(['received' => true]);

constructEvent() برای امضای نادرست، JSON ناسالم یا بدنه‌ای که شیء JSON نیست، SignatureVerificationException پرتاب می‌کند. همیشه فقط پس از پذیرش رویداد 200 برگردانید.

نمونه لاراول​

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

use Illuminate\Http\Request;
use OrcaRail\Exception\SignatureVerificationException;
use OrcaRail\Webhook;

Route::post('/webhooks/orcarail', function (Request $request) {
try {
$event = Webhook::constructEvent(
$request->getContent(),
$request->header('x-webhook-signature', ''),
config('services.orcarail.webhook_secret'),
);
} catch (SignatureVerificationException) {
return response('Invalid signature', 400);
}

// Dispatch a job keyed by $event->data->object->id, then acknowledge.
return response()->json(['received' => true]);
});

این مسیر را از بررسی CSRF مستثنا کنید و مطمئن شوید هیچ middlewareی پیش از بررسی، بدنه را بازنویسی نمی‌کند.

نمونه Symfony​

use OrcaRail\Exception\SignatureVerificationException;
use OrcaRail\Webhook;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;

final class OrcaRailWebhookController
{
#[Route('/webhooks/orcarail', methods: ['POST'])]
public function __invoke(Request $request): Response
{
try {
$event = Webhook::constructEvent(
$request->getContent(),
$request->headers->get('x-webhook-signature', ''),
$_ENV['ORCARAIL_WEBHOOK_SECRET'],
);
} catch (SignatureVerificationException) {
return new Response('Invalid signature', Response::HTTP_BAD_REQUEST);
}

return new JsonResponse(['received' => true]);
}
}

مدیریت خطا​

SDK exceptionهای نوع‌داری پرتاب می‌کند که همه از OrcaRail\Exception\OrcaRailException (که خودش یک RuntimeException است) ارث می‌برند.

use OrcaRail\Exception\ApiException;
use OrcaRail\Exception\AuthenticationException;
use OrcaRail\Exception\OrcaRailException;
use OrcaRail\Exception\TransportException;

try {
$intent = $orcarail->paymentIntents->create([
'price_id' => $priceId,
'return_url' => 'https://yourapp.com/success',
]);
} catch (AuthenticationException $exception) {
// 401 — invalid API key or secret
error_log($exception->getMessage());
} catch (ApiException $exception) {
// Any other non-2xx response
error_log($exception->statusCode . ' ' . $exception->type . ' ' . $exception->getMessage());
error_log(json_encode($exception->request)); // method and URL of the failed call
} catch (TransportException $exception) {
// Network failure, timeout, or unparseable response
error_log($exception->getMessage());
} catch (OrcaRailException $exception) {
error_log($exception->getMessage());
}

ترکیب‌های نامعتبر پارامترها (مثلاً price_id همراه amount) پیش از هر درخواست HTTP یک InvalidArgumentException پرتاب می‌کنند.

تست بدون شبکه​

transport خودتان را به‌عنوان http_client بفرستید تا درخواست‌ها در آزمون‌ها ثبت شوند. هر کلاسی که OrcaRail\HttpClient\ClientInterface را پیاده کند کار می‌کند و کلید API لازم نیست.

use OrcaRail\HttpClient\ClientInterface;
use OrcaRail\OrcaRailClient;

$recorder = new class implements ClientInterface {
/** @var list<array{string, string, ?array<string, mixed>, bool}> */
public array $calls = [];

public function request(string $method, string $path, ?array $body = null, bool $requireAuth = true): mixed
{
$this->calls[] = [$method, $path, $body, $requireAuth];

return ['id' => 'pi_test', 'status' => 'requires_confirmation'];
}
};

$orcarail = new OrcaRailClient(['http_client' => $recorder]);
$intent = $orcarail->paymentIntents->create([
'price_id' => 'price_test',
'return_url' => 'https://example.com/return',
]);

// $recorder->calls[0] === ['POST', 'payment_intents', [...], true]

همچنین ببینید​