Қысқаша
PHP SDK — бір ғана файл: src/QutPay.php. PHP 7.4 және одан жоғары нұсқада, curl мен json кеңейтулерімен жұмыс істейді, басқа тәуелділігі жоқ. Composer арқылы да, қарапайым require арқылы да қосуға болады.
Ішінде үш класс бар:
| Класс | Не үшін |
|---|---|
\QutPay\Client | API шақырулары: счёт жасау, оқу, болдырмау, қайтару |
\QutPay\Webhook | Кіріс хабарламаның қолтаңбасын тексеру |
\QutPay\ApiException | Қате: ->status (HTTP) және ->errorCode (машиналық код) |
Архив: https://api.qut.kz/downloads/qutpay-sdk-php.zip
Орнату
Ең қарапайымы — архивтегі src/QutPay.php файлын жобаңызға көшіріп, тікелей қосу:
require __DIR__ . '/vendor/qutpay/QutPay.php';
Composer қолдансаңыз, архивті бір қалтаға ашып, жергілікті репозиторий ретінде көрсетіңіз:
{
"repositories": [{ "type": "path", "url": "./vendor-local/qutpay-sdk" }],
"require": { "qutpay/sdk": "*" }
}
Пакет атауы — qutpay/sdk, автозагрузка PSR-4 бойынша QutPay\ кеңістігіне бағытталған.
Инициализация
$qp = new \QutPay\Client(getenv('QUTPAY_API_KEY')); // baseUrl әдепкі https://api.qut.kz
// Қажет болса: new \QutPay\Client($key, 'https://api.qut.kz', 20); // үшіншісі — таймаут, секунд
Кілт бос болса конструктор InvalidArgumentException лақтырады. Кілтті кодқа жазбаңыз — орта айнымалысынан немесе баптау файлынан алыңыз, ал ол файл репозиторийге түспеуі керек.
Счёт жасау
$inv = $qp->createInvoice([
'amount' => 12500,
'kind' => 'qr', // 'qr' (әдепкі) немесе 'phone'
'description' => 'Тапсырыс №4471',
'externalOrderId' => '4471',
'customer' => ['name' => 'Айгүл', 'phone' => '77011234567', 'email' => 'a@b.kz'],
'successUrl' => 'https://site.kz/ok',
'failUrl' => 'https://site.kz/fail',
'metadata' => ['branch' => 'almaty-abay'],
'idempotencyKey' => 'order-4471', // Idempotency-Key тақырыбына кетеді
]);
header('Location: ' . $inv['payUrl']);
exit;
Жауап — ассоциативті массив: id, status, payUrl, qrUrl, deepLink, qrImageUrl, expiresAt.
Қалған әдістер
| Әдіс | Не істейді |
|---|---|
$qp->getInvoice($id) | Счётты оқиды. $qp->getInvoice($id, true) — күйді Kaspi-ден сұрап жаңартады |
$qp->listInvoices(['status' => 'paid', 'limit' => 50]) | Тізім. Сүзгілер: status, externalOrderId, search, from, to, limit, offset |
$qp->cancelInvoice($id) | Ашық счётты болдырмайды |
$qp->refundInvoice($id, $amount = null, $reason = null) | Қайтару. $amount = null — толық қайтару |
$qp->simulateInvoice($id, 'paid') | Тек sandbox: төлемді имитациялау |
$qp->account() | Ұйым туралы қысқа ақпарат — кілтті тексеруге ыңғайлы |
$fresh = $qp->getInvoice($inv['id'], true);
if ($fresh['status'] === 'paid') {
markOrderPaid('4471');
}
$qp->refundInvoice($inv['id'], 5000, 'Тауардың бір бөлігі қайтарылды');
SDK-да жазылым, bulk және форма-хук әдістері жоқ. Оларды тікелей curl арқылы шақырыңыз — эндпоинттер api.qut.kz/docs бетінде.
Webhook: таза PHP
<?php
require __DIR__ . '/QutPay.php';
$secret = getenv('QUTPAY_WEBHOOK_SECRET');
$raw = file_get_contents('php://input'); // өзгертілмеген дене
if (!\QutPay\Webhook::verify($secret, $raw, getallheaders())) {
http_response_code(401);
exit;
}
$e = json_decode($raw, true);
$event = $e['event'] ?? '';
$invoice = $e['invoice'] ?? [];
// Идемпотенттілік: бір оқиға екі рет келуі мүмкін
if (!alreadyHandled($invoice['id'], $invoice['status'])) {
if ($event === 'invoice.paid') markOrderPaid($invoice['externalOrderId'], $invoice);
if ($event === 'invoice.refunded') markOrderRefunded($invoice['externalOrderId']);
rememberHandled($invoice['id'], $invoice['status']);
}
http_response_code(200); // 2xx бермесеңіз 11 рет қайталанады
Webhook::verify($secret, $rawBody, $headers, $toleranceSec = 300) тақырыптарды регистрге қарамай іздейді, sha256= префиксін тексереді, 5 минуттан ескі жеткізілімді қабылдамайды және hash_equals арқылы салыстырады.
getallheaders() кейбір FastCGI баптауларында болмайды. Ондай жағдайда тақырыптарды өзіңіз жинаңыз:
$headers = [
'X-Webhook-Signature' => $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'] ?? '',
'X-Webhook-Timestamp' => $_SERVER['HTTP_X_WEBHOOK_TIMESTAMP'] ?? '',
];
Webhook: Laravel
Laravel-де денені $request->getContent() арқылы алыңыз — ол өзгертілмеген жол қайтарады. $request->all() жарамайды.
// routes/api.php
Route::post('/qutpay-webhook', [QutPayController::class, 'handle']);
// app/Http/Controllers/QutPayController.php
use Illuminate\Http\Request;
class QutPayController extends Controller
{
public function handle(Request $request)
{
$raw = $request->getContent();
$ok = \QutPay\Webhook::verify(
config('services.qutpay.webhook_secret'),
$raw,
$request->headers->all() // мәндері массив — verify оны түсінеді
);
if (!$ok) {
return response()->noContent(401);
}
$e = json_decode($raw, true);
ProcessQutPayEvent::dispatch($e); // ауыр жұмысты кезекке беріңіз
return response()->noContent(200);
}
}
Екі нәрсені ұмытпаңыз:
- CSRF. Маршрутты
routes/api.phpішіне қойыңыз немесеVerifyCsrfTokenішіндегі$exceptтізіміне қосыңыз — әйтпесе Laravel 419 қайтарады да, біз оны сәтсіз жеткізілім деп есептейміз. - Жылдам жауап. Ауыр логиканы (хат жіберу, есеп жаңарту) кезекке шығарып, 2xx-ті бірден қайтарыңыз.
Счёт жасау контроллерінде:
$qp = new \QutPay\Client(config('services.qutpay.key'));
$inv = $qp->createInvoice([
'amount' => $order->total,
'description' => "Тапсырыс №{$order->id}",
'externalOrderId' => (string) $order->id,
'successUrl' => route('orders.show', $order),
'idempotencyKey' => "order-{$order->id}",
]);
return redirect()->away($inv['payUrl']);
Қате өңдеу
Кез келген сәтсіз жауапта SDK \QutPay\ApiException лақтырады:
| Өріс | Мәні |
|---|---|
->status | HTTP күйі. Желі қатесінде 0 |
->errorCode | Машиналық код: invalid_amount, invoice_not_found, request_rate_limited… Желі үзілсе network_error |
->getMessage() | Адамға арналған мәтін |
try {
$inv = $qp->createInvoice([...]);
} catch (\QutPay\ApiException $e) {
switch ($e->errorCode) {
case 'kaspi_session_expired':
notifyAdmin('Кассир байланысы үзілді');
break;
case 'tariff_limit_reached':
case 'tariff_daily_burst':
notifyAdmin('Лимит'); // қайталамаңыз
break;
case 'request_rate_limited':
case 'network_error':
retryLater(); // 1, 2, 4, 8 секунд бэкоффпен
break;
default:
report($e);
}
abort(502);
}
Шешімді әрқашан errorCode бойынша қабылдаңыз: мәтін өзгеруі мүмкін, код өзгермейді. Толық тізім: Қателер каталогы.
Жиі қойылатын сұрақтар
Packagist-тен орнатуға бола ма? Жоқ, пакет жария тізілімде емес. Архивті жүктеп, path репозиторийі ретінде қосыңыз немесе файлды көшіріп алыңыз.
Guzzle керек пе? Жоқ. SDK тек curl кеңейтуін пайдаланады.
WordPress дүкенім бар, осы SDK керек пе? Жоқ. WooCommerce үшін дайын плагин бар: WooCommerce плагині.
Symfony-де raw body қалай аламын? Laravel-дегідей: $request->getContent() және $request->headers->all().
Қолтаңба сәйкес келмейді. Дене бір жерде өзгертілген: денені JSON-ға айналдырып, қайта жолға түрлендірмеңіз, кэш пен қорғаныс плагиндері денеге тимесін. Толығы: Webhook қолтаңбасы сәйкес келмейді.