Интеграция платёжной системы Fondy на сайт
Fondy — украинский платёжный шлюз, активно используемый в Украине, и имеющий лицензии для работы в странах ЕС. Поддерживает карты Visa, Mastercard, Google Pay, Apple Pay, локальные украинские методы (Приват24, MONO, Укрбанк). Документация на русском и английском языках, REST API стабилен.
Параметры подключения
Из личного кабинета merchant.fondy.eu нужно получить:
-
merchant_id— идентификатор магазина -
secret_key— для подписи запросов
Тестовые транзакции проходят через тот же URL, отличие только в тестовых данных карт.
Hosted Payment Page — базовая интеграция
Самый распространённый вариант — перенаправление на страницу Fondy:
function buildFondyPayment(int $orderId, int $amountInKopecks, string $currency = 'UAH'): array
{
$merchantId = env('FONDY_MERCHANT_ID');
$secretKey = env('FONDY_SECRET_KEY');
$params = [
'merchant_id' => $merchantId,
'order_id' => $orderId . '_' . time(), // уникальность обязательна
'order_desc' => "Заказ #{$orderId}",
'amount' => $amountInKopecks,
'currency' => $currency,
'response_url' => 'https://example.com/payment/return',
'server_callback_url' => 'https://example.com/webhook/fondy',
'lang' => 'ru',
];
// Подпись: SHA1 от конкатенации значений, отсортированных по ключу, через |
ksort($params);
$signString = $secretKey . '|' . implode('|', array_values($params));
$params['signature'] = sha1($signString);
return $params;
}
// Отправляем POST на https://pay.fondy.eu/api/checkout/redirect/
Форма может отправляться напрямую как HTML POST, либо через API с получением checkout_url.
API-метод с получением URL
$response = Http::post('https://pay.fondy.eu/api/checkout/url/', [
'request' => buildFondyPayment(12345, 150000),
]);
$checkoutUrl = $response->json('response.checkout_url');
return redirect($checkoutUrl);
Проверка подписи в webhook
public function handleCallback(Request $request): Response
{
$data = $request->all();
// Убираем signature из данных для проверки
$received = $data['signature'];
unset($data['signature']);
// Убираем пустые поля
$data = array_filter($data, fn($v) => $v !== '' && $v !== null);
ksort($data);
$expected = sha1(env('FONDY_SECRET_KEY') . '|' . implode('|', array_values($data)));
if (!hash_equals($expected, $received)) {
return response('Bad signature', 403);
}
if ($data['order_status'] === 'approved') {
// order_id содержит суффикс времени, отделяем исходный ID
$orderId = (int) explode('_', $data['order_id'])[0];
Order::where('id', $orderId)->update([
'status' => 'paid',
'transaction_id' => $data['payment_id'],
]);
}
// Fondy ожидает HTTP 200 с телом {"response":"accept"}
return response()->json(['response' => 'accept']);
}
Статусы order_status: approved, declined, expired, processing, reversed (возврат).
Встроенная форма (Checkout JS)
Для встройки формы прямо на странице без перехода на Fondy:
<script src="https://pay.fondy.eu/static_common/v1/checkout/ipsp.js"></script>
$ipsp.get('checkout').config({
merchantId: FONDY_MERCHANT_ID,
amount: 1500,
currency: 'UAH',
orderId: 'order-12345',
orderDesc: 'Тестовый заказ',
signature: serverGeneratedSignature,
lang: 'ru',
fields: false, // использовать нативные поля Fondy
fee: false,
theme: {
preset: 'silver',
},
});
Signature для JS-виджета генерируется на сервере и передаётся на клиент. Никогда не генерировать её на стороне браузера — секретный ключ нельзя передавать во фронтенд.
Возвраты
$params = [
'merchant_id' => env('FONDY_MERCHANT_ID'),
'order_id' => $originalOrderId,
'amount' => 75000, // частичный возврат
'currency' => 'UAH',
'comment' => 'Customer request',
];
ksort($params);
$params['signature'] = sha1(env('FONDY_SECRET_KEY') . '|' . implode('|', array_values($params)));
Http::post('https://pay.fondy.eu/api/reverse/order/', ['request' => $params]);
Нюансы
Уникальность order_id в Fondy — обязательное требование. Если один и тот же order_id использован повторно, транзакция отклоняется. Распространённая практика — добавлять timestamp или UUID к внутреннему ID заказа. Сохранять маппинг fondy_order_id → local_order_id нужно до создания транзакции.
Срок подтверждения аккаунта в боевом режиме — 2–5 рабочих дней.







