Files
alfa-bank-sdk/examples/20_sbp_qr_to_nominal_account.php
Anatoliy Poloz b15d29f8e9 fix(formating)
2026-01-28 03:36:26 +03:00

272 lines
13 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
/**
* Пример: SBP QR → Номинальный счёт → Идентификация → Бенефициар
*
* Сценарий:
* 1. Генерируем SBP QR код для пополнения номинального счёта
* 2. Клиент оплачивает через QR (переводит деньги на номинальный счёт)
* 3. Получаем список неидентифицированных пополнений
* 4. Идентифицируем платёж (привязываем к конкретному бенефициару)
* 5. Проверяем статус идентификации
*/
require_once __DIR__ . '/../vendor/autoload.php';
use Apoloz\AlfaBank\AlfaBank;
use Apoloz\AlfaBank\Core\AlfaConfig;
use Apoloz\AlfaBank\Data\NominalAccounts\Identification\AmountDistributionData;
use Apoloz\AlfaBank\Data\NominalAccounts\Identification\IdentifyRequestData;
use Apoloz\AlfaBank\Data\NominalAccounts\Payouts\DigestSignatureData;
use Apoloz\AlfaBank\Enums\Environment;
use Apoloz\AlfaBank\Requests\NominalAccounts\Identification\GetIdentifyStatusRequest;
use Apoloz\AlfaBank\Requests\NominalAccounts\Identification\GetUnidentifiedTransactionsRequest;
use Apoloz\AlfaBank\Requests\NominalAccounts\Identification\IdentifyTransactionRequest;
use Apoloz\AlfaBank\Sbp\Data\QrCodeRequestData;
use Apoloz\AlfaBank\Sbp\Requests\GetQrCodeRequest;
use Apoloz\AlfaBank\Sbp\SbpConnector;
use Spatie\LaravelData\DataCollection;
echo "=== SBP QR → Номинальный счёт → Идентификация ===\n\n";
// =============================================================================
// КОНФИГУРАЦИЯ
// =============================================================================
// SBP QR конфигурация (mTLS + подпись)
$sbpMtlsCert = __DIR__ . '/certs/sbp_mtls.crt';
$sbpMtlsKey = __DIR__ . '/certs/sbp_mtls.key';
$sbpSigningKey = __DIR__ . '/certs/sbp_signing.key'; // 4096-bit для подписи
$sbpKeyAlias = 'your-alias-from-bank';
$sbpTermNo = '30003970'; // Номер терминала
$sbpCaBundle = __DIR__ . '/certs/russian_trusted_ca-bundle.crt';
// BaaS конфигурация (для номинальных счетов)
$baasCert = __DIR__ . '/certs/baas_transport.crt';
$baasKey = __DIR__ . '/certs/baas_transport.key';
$baasClientId = getenv('ALFA_BAAS_CLIENT_ID');
$baasClientSecret = getenv('ALFA_BAAS_CLIENT_SECRET');
$baasCaBundle = __DIR__ . '/certs/russian_trusted_ca-bundle.crt';
// Данные для идентификации
$nominalAccountNumber = '40702810XXXXXXXXXXX'; // Ваш номинальный счёт
$beneficiaryId = '3fa85f64-5717-4562-b3fc-2c963f66afa6'; // ID бенефициара
echo "Конфигурация:\n";
echo " Номинальный счёт: $nominalAccountNumber\n";
echo " Бенефициар ID: $beneficiaryId\n\n";
// =============================================================================
// ЭТАП 1: Генерация SBP QR кода для пополнения номинального счёта
// =============================================================================
echo "=== ЭТАП 1: Генерация SBP QR кода ===\n";
try {
// Создаём SBP connector с раздельными ключами (mTLS + signing)
$sbpConfig = new AlfaConfig(
baseUrl: Environment::SBP_SANDBOX->value,
certificatePath: $sbpMtlsCert,
privateKeyPath: $sbpMtlsKey,
caBundlePath: $sbpCaBundle,
signingPrivateKeyPath: $sbpSigningKey // 4096-bit ключ для подписи
);
$sbp = new SbpConnector($sbpConfig, $sbpKeyAlias);
// Создаём динамический QR код с уникальным OrderNumber для идентификации
$orderNumber = 'ORDER-' . uniqid();
$qrPayload = new QrCodeRequestData(
termNo: $sbpTermNo,
qrcType: '02', // Dynamic QR
amount: '50000', // 500.00 RUB
currency: 'RUB',
paymentPurpose: "Пополнение номинального счета {$nominalAccountNumber}",
orderNumber: $orderNumber, // Уникальный номер заказа для отслеживания
messageID: 'sbp-' . uniqid(),
qrTtl: '60', // QR активен 60 минут
notificationUrl: 'https://your-domain.com/webhooks/sbp', // Webhook для уведомлений
width: '300',
height: '300'
);
$request = new GetQrCodeRequest($qrPayload);
$response = $sbp->send($request);
$qrData = $response->dto();
if ($qrData->ErrorCode !== 0) {
throw new \RuntimeException("QR generation failed: {$qrData->message}");
}
echo "✅ QR код сгенерирован!\n";
echo " Order Number: $orderNumber\n";
echo " QRC ID: {$qrData->qrcId}\n";
echo " QR Payload: {$qrData->payload}\n";
echo " Status: {$qrData->status}\n";
echo "\n💡 Клиент должен отсканировать QR и оплатить {$qrData->payload}\n\n";
// Сохраняем QR изображение для показа клиенту
if (! empty($qrData->image)) {
$qrImagePath = __DIR__ . '/qr_payment.png';
file_put_contents($qrImagePath, base64_decode($qrData->image));
echo " 📁 QR изображение сохранено: $qrImagePath\n\n";
}
echo "⏳ Ждём, пока клиент оплатит...\n";
echo " (В тестовой среде используйте Sandbox для эмуляции оплаты)\n\n";
// =============================================================================
// ЭТАП 2: Получение неидентифицированных пополнений
// =============================================================================
echo "=== ЭТАП 2: Поиск неидентифицированных пополнений ===\n";
echo " (После оплаты через SBP деньги поступают на номинальный счёт\n";
echo " как 'неидентифицированное пополнение')\n\n";
// Инициализируем BaaS connector (требуется H2H авторизация)
$baas = AlfaBank::baas(
certificatePath: $baasCert,
privateKeyPath: $baasKey,
environment: Environment::SANDBOX,
clientId: $baasClientId,
clientSecret: $baasClientSecret
);
// Авторизация через Client Credentials Flow
$baas->login($baasClientId, $baasClientSecret, ['beneficiary-payments']);
// Получаем список неидентифицированных пополнений
$unidentifiedRequest = new GetUnidentifiedTransactionsRequest(
accountNumber: $nominalAccountNumber,
limit: 10,
offset: 0
);
$unidentifiedResponse = $baas->send($unidentifiedRequest);
$unidentifiedCollection = $unidentifiedResponse->dto();
echo 'Найдено неидентифицированных пополнений: ' . count($unidentifiedCollection->items) . "\n\n";
if (count($unidentifiedCollection->items) === 0) {
echo "⚠️ Нет неидентифицированных пополнений. Возможные причины:\n";
echo " - Платёж ещё не поступил (нужно время на обработку)\n";
echo " - Неправильный номер номинального счёта\n";
echo " - Платёж уже идентифицирован\n";
exit(0);
}
// Ищем нужную транзакцию (обычно по сумме или назначению платежа)
$targetTransaction = null;
foreach ($unidentifiedCollection->items as $transaction) {
echo " Транзакция ID: {$transaction->id}\n";
echo " Сумма: {$transaction->amount} RUB\n";
echo " Назначение: {$transaction->purpose}\n";
echo " Дата: {$transaction->date}\n";
echo " Плательщик: {$transaction->payer?->name}\n\n";
// В реальном сценарии ищем по сумме, назначению или другим критериям
// Здесь берём первую для примера
if ($targetTransaction === null) {
$targetTransaction = $transaction;
}
}
if ($targetTransaction === null) {
throw new \RuntimeException('Не найдена подходящая транзакция для идентификации');
}
echo "✅ Выбрана транзакция для идентификации: {$targetTransaction->id}\n";
echo " Сумма: {$targetTransaction->amount} RUB\n\n";
// =============================================================================
// ЭТАП 3: Идентификация платежа (привязка к бенефициару)
// =============================================================================
echo "=== ЭТАП 3: Идентификация платежа ===\n";
echo " Привязываем платёж к бенефициару: $beneficiaryId\n\n";
// Подготавливаем распределение суммы
// Можно распределить между несколькими бенефициарами
$amountDistribution = DataCollection::create([
new AmountDistributionData(
beneficiaryId: $beneficiaryId,
amount: $targetTransaction->amount
),
], AmountDistributionData::class);
// Подготавливаем данные для идентификации
$identifyData = new IdentifyRequestData(
transactionId: $targetTransaction->id,
accountNumber: $nominalAccountNumber,
amountDistribution: $amountDistribution,
// Подпись добавляется через sign() метод
digestSignatures: DataCollection::create([
new DigestSignatureData(
base64Encoded: 'base64-encoded-signature-here', // Реальная подпись
signatureType: 'rsa'
),
], DigestSignatureData::class)
);
// Создаём запрос на идентификацию
$identifyRequest = new IdentifyTransactionRequest(
id: $targetTransaction->id,
data: $identifyData
);
$identifyResponse = $baas->send($identifyRequest);
$identifyResult = $identifyResponse->dto();
echo "✅ Запрос на идентификацию создан!\n";
echo " Identify ID: {$identifyResult->identifyId}\n";
echo " Статус: {$identifyResult->status}\n\n";
// =============================================================================
// ЭТАП 4: Проверка статуса идентификации
// =============================================================================
echo "=== ЭТАП 4: Проверка статуса ===\n\n";
$statusRequest = new GetIdentifyStatusRequest($identifyResult->identifyId);
$statusResponse = $baas->send($statusRequest);
$statusResult = $statusResponse->dto();
echo "Статус идентификации: {$statusResult->status}\n";
echo "Возможные статусы:\n";
echo " - PENDING: В обработке\n";
echo " - VALIDATED: Провалидировано\n";
echo " - COMPLETE: Успешно завершено\n";
echo " - FAILED: Ошибка\n\n";
if ($statusResult->status === 'COMPLETE') {
echo "🎉 ИДЕНТИФИКАЦИЯ УСПЕШНО ЗАВЕРШЕНА!\n";
echo " Деньги {$targetTransaction->amount} RUB зачислены бенефициару $beneficiaryId\n\n";
}
// =============================================================================
// РЕЗЮМЕ
// =============================================================================
echo "=== РЕЗЮМЕ ===\n";
echo "✅ 1. Сгенерирован SBP QR код\n";
echo "✅ 2. Клиент оплатил через SBP (эмитация в тесте)\n";
echo "✅ 3. Деньги поступили на номинальный счёт\n";
echo "✅ 4. Платёж идентифицирован и привязан к бенефициару\n";
echo "\n💡 Теперь бенефициар может распоряжаться этими средствами\n";
echo " (например, запросить выплату через CreatePayoffRequest)\n";
} catch (\Apoloz\AlfaBank\Exceptions\AlfaBankException $e) {
echo "\n❌ AlfaBankException: {$e->getMessage()}\n";
if ($e->getResponse()) {
echo "Ответ API: {$e->getResponse()->body()}\n";
}
} catch (\Exception $e) {
echo "\n❌ Ошибка: {$e->getMessage()}\n";
echo "Файл: {$e->getFile()}:{$e->getLine()}\n";
}
echo "\n=== Пример завершён ===\n";