Інтеграція кастомного модуля оплати PrestaShop з Stripe, PayPal, Fondy

Інтеграція платіжного шлюзу в PrestaShop часто обертається помилками: вебхуки не обробляються, статуси замовлень не оновлюються, а гроші «зависають». Ми розробляємо кастомні модулі оплати для Stripe, PayPal і Fondy, які коректно валідують підписи та виключають збої. Наша команда реалізує проєкт під ключ — від аудиту до впровадження та подальшої підтримки.

Розробка та обслуговування будь-яких видів сайтів:

Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Послуги, які ми пропонуємо
Показано 1 з 1Усі 2062 послуг
Інтеграція кастомного модуля оплати PrestaShop з Stripe, PayPal, Fondy
Середній
~2-3 дні
Часті запитання

Кастомний модуль оплати для PrestaShop: як уникнути помилок інтеграції

При інтеграції стороннього платіжного шлюзу в PrestaShop багато хто стикається з проблемою некоректної обробки вебхуків. Типова помилка — статус замовлення не оновлюється після callback через відсутність перевірки підпису HMAC. Якщо не валідувати callback, можна отримати «завислі» замовлення, коли гроші списалися, а статус залишився «очікування». Інша поширена проблема — неправильна передача суми в мінімальних одиницях валюти (копійках/центах), що призводить до розбіжностей в обліку. На прикладі інтеграції MyPay розберемо правильне налаштування Payment API, щоб цього уникнути.

Крім того, важливо коректно формувати URL callback з урахуванням безпеки. Ми використовуємо лише POST-запити з підписом у заголовку X-Signature. Такий підхід виключає підміну даних. Додатково реалізуємо idempotency key, щоб повторний callback не змінював статус замовлення.

Як уникнути помилок при інтеграції платіжного шлюзу в PrestaShop?

Ключовий момент — валідація замовлення до редиректу та обробка webhook з HMAC-підписом. Для запобігання timing attack використовуємо hash_equals при перевірці підпису. У модулі потрібно зареєструвати три хуки: paymentOptions, paymentReturn та actionOrderStatusUpdate. Останній обов'язковий для автоматичних повернень через адмінку. Ідемпотентність досягається перевіркою унікального ключа в callback. Крім цього, необхідно перевіряти поточний статус замовлення: якщо воно вже оплачено, повторний callback не повинен змінювати статус.

Чому кастомний модуль краще готового плагіна?

Параметр Кастомний модуль Готовий плагін
Гнучкість 100% під бізнес-логіку Обмежений налаштуваннями
Безпека Повний контроль коду Можливі вразливості
Підтримка Прямий зв'язок з розробником Через тікети вендора
Продуктивність Оптимізований під ваш стек Часто надлишковий код

Кастомна розробка окупається, якщо у вас нестандартна валюта, кілька складів або складні сценарії повернень. Наприклад, у готовому плагіні для Fondy немає підтримки часткових повернень. У кастомному модулі ми реалізували API виклику повернення через адмінку PrestaShop. Готові плагіни часто не підтримують мультивалютність і не адаптовані під специфіку магазину. У кастомному модулі ми передаємо код валюти (ISO 4217) у запиті до шлюзу, що особливо важливо для регіонів з різними валютами.

Які типові помилки допускають при інтеграції?

Найчастіша — ігнорування перевірки HMAC. Без неї зловмисник може підробити callback і змінити статус замовлення. Друга — неправильна обробка помилок API: якщо шлюз повернув 500, модуль повинен коректно відкотити замовлення, а не залишати його в підвішеному стані. Третя — некоректна робота з таймаутами: при довгій відповіді шлюзу користувач бачить нескінченне завантаження. Ми використовуємо async-запити з таймаутом 30 секунд і показуємо повідомлення про помилку.

Які шлюзи ми інтегруємо?

Ми працюємо зі Stripe, PayPal, Fondy, LiqPay, а також з будь-якими іншими, що надають REST API. У таблиці нижче — порівняння за ключовими параметрами.

Шлюз Комісія (приблизно) Підтримка 3DS Webhook HMAC SDK для PHP
Stripe 2.9% + 0.30€ Так Так stripe/stripe-php
PayPal 3.49% + 0.30€ Так Так paypal/rest-api-sdk-php
Fondy 2.3% Так Так fondy/fondy-php
LiqPay 2.5% Так Так liqpay/liqpay-php

Комісія вказана орієнтовно, точні тарифи уточнюйте у провайдера.

Структура та реалізація модуля

Для реєстрації модуля в системі оплати ми використовуємо хуки paymentOptions, paymentReturn та actionOrderStatusUpdate. Додатково можна підключити displayPaymentTop для виведення інформації перед вибором оплати.

Структура файлів

modules/mypay/
├── controllers/front/
│   ├── payment.php # Ініціалізація платежу
│   └── callback.php # Webhook від провайдера
├── views/templates/front/
│   └── payment_infos.tpl
├── mypay.php # Основний клас модуля
└── logo.png

Основний клас модуля

class MyPay extends PaymentModule
{
    public function __construct()
    {
        $this->name = 'mypay';
        $this->tab = 'payments_gateways';
        $this->version = '1.0.0';
        $this->author = 'Your Company';
        $this->need_instance = 0;
        $this->ps_versions_compliancy = ['min' => '8.0.0', 'max' => _PS_VERSION_];
        $this->bootstrap = true;
        parent::__construct();
        $this->displayName = $this->trans('MyPay', [], 'Modules.Mypay.Admin');
        $this->description = $this->trans('Оплата карткою через MyPay', [], 'Modules.Mypay.Admin');
    }

    public function install(): bool
    {
        return parent::install() && $this->registerHook('paymentOptions') && $this->registerHook('paymentReturn') && $this->registerHook('actionOrderStatusUpdate');
    }

    public function hookPaymentOptions(array $params): array
    {
        if (!$this->active) return [];
        $this->context->smarty->assign([
            'mypay_action_url' => $this->context->link->getModuleLink('mypay', 'payment', [], true),
        ]);
        $option = new \PrestaShop\PrestaShop\Core\Payment\PaymentOption();
        $option->setCallToActionText($this->trans('Оплата карткою', [], 'Modules.Mypay.Shop'))
            ->setAction($this->context->link->getModuleLink('mypay', 'payment', [], true))
            ->setAdditionalInformation(
                $this->fetch('module:mypay/views/templates/front/payment_infos.tpl')
            );
        return [$option];
    }
}

Контролер ініціалізації платежу

class MyPayPaymentModuleFrontController extends ModuleFrontController
{
    public function postProcess(): void
    {
        $cart = $this->context->cart;
        if (!$this->module->checkCurrency($cart)) {
            Tools::redirect('index.php?controller=order');
        }
        $total = (int) round($cart->getOrderTotal(true) * 100);
        $customer = new Customer($cart->id_customer);
        $currency = new Currency($cart->id_currency);
        $this->module->validateOrder(
            $cart->id,
            Configuration::get('MYPAY_OS_PENDING'),
            $cart->getOrderTotal(true),
            $this->module->displayName,
            null,
            [],
            (int) $currency->id,
            false,
            $customer->secure_key
        );
        $orderId = Order::getIdByCartId($cart->id);
        $client = new MyPayApiClient(
            Configuration::get('MYPAY_API_KEY'),
            Configuration::get('MYPAY_SECRET_KEY')
        );
        $payment = $client->createPayment([
            'amount' => $total,
            'currency' => $currency->iso_code,
            'order_id' => $orderId,
            'callback_url' => $this->context->link->getModuleLink('mypay', 'callback', [], true),
            'success_url' => $this->context->link->getPageLink('order-confirmation', true, null, [
                'id_cart' => $cart->id,
                'id_module' => $this->module->id,
                'id_order' => $orderId,
                'key' => $customer->secure_key,
            ]),
        ]);
        $order = new Order($orderId);
        $order->reference = $payment['payment_id'];
        $order->save();
        Tools::redirect($payment['payment_url']);
    }
}

Обробка callback

class MyPayCallbackModuleFrontController extends ModuleFrontController
{
    public function postProcess(): void
    {
        $raw = file_get_contents('php://input');
        $data = json_decode($raw, true);
        $secret = Configuration::get('MYPAY_SECRET_KEY');

        if (!hash_equals(hash_hmac('sha256', $raw, $secret), $_SERVER['HTTP_X_SIGNATURE'] ?? '')) {
            http_response_code(403);
            exit;
        }

        $order = Order::getByReference($data['payment_id'])->getFirst();
        if (!$order) {
            http_response_code(404);
            exit('Order not found');
        }

        $statusMap = [
            'succeeded' => Configuration::get('MYPAY_OS_PAID'),
            'failed' => Configuration::get('PS_OS_ERROR'),
            'cancelled' => Configuration::get('PS_OS_CANCELED'),
        ];

        if (isset($statusMap[$data['status']])) {
            $history = new OrderHistory();
            $history->id_order = $order->id;
            $history->changeIdOrderState((int) $statusMap[$data['status']], $order);
            $history->addWithemail(true);
        }

        http_response_code(200);
        echo 'OK';
        exit;
    }
}

Що входить в роботу

  • Документація: опис встановлення, конфігурації та API-методів модуля.
  • Доступи: передача вихідного коду, файлів модуля та дампів БД (при необхідності).
  • Підтримка: 2 тижні безкоштовних доробок після здачі проекту. Можлива постгарантійна підтримка за домовленістю.

Ми документуємо кожен крок, щоб ви могли самостійно вносити зміни.

Процес і терміни

  1. Аналітика (2–4 години): вивчаємо документацію шлюзу, узгоджуємо вимоги, оцінюємо ризики.
  2. Проектування (1 день): схема інтеграції, визначення необхідних хунків, прототип модуля.
  3. Розробка (2–3 дні): написання коду з використанням Symfony-компонентів та Dependency Injection, реалізація API-клієнта та хунків, написання PHPUnit-тестів.
  4. Тестування (1 день): інтеграційне тестування з пісочницею шлюзу, перевірка всіх сценаріїв (успіх, відмова, повернення).
  5. Деплой (2 години): встановлення на staging, потім на production за допомогою CI/CD.

Кожен етап завершується демонстрацією замовнику. Ми використовуємо Git для контролю версій та Code Review для забезпечення якості. Термін розробки — від 2 до 5 робочих днів залежно від складності шлюзу. Вартість розраховується індивідуально та включає аудит поточної конфігурації магазину.

Зв'яжіться з нами для оцінки вашого проекту — це займе 2 години. Отримайте консультацію з інтеграції. Замовте інтеграцію і ми налаштуємо прийом платежів без головного болю.

Довіра та гарантії

Ми інтегрували платіжні шлюзи для 50+ магазинів на PrestaShop. Досвід роботи з платформою — більше 5 років. На всі модулі надається гарантія 3 місяці. Наші інженери завжди на зв'язку.

Наші компетенції:

Останні роботи

  • Розробка сайту компанії B2B ADVANCE
    Розробка сайту компанії B2B ADVANCE
    1543
  • Розробка веб-додатків для компанії FEEDME
    Розробка веб-додатків для компанії FEEDME
    1394
  • Розробка веб-сайту для компанії БЕЛФІНГРУП
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    1081
  • Розробка інтернет магазину для компанії FURNORO
    Розробка інтернет магазину для компанії FURNORO
    1374
  • Розробка веб-додатків для компанії Enviok
    Розробка веб-додатків для компанії Enviok
    1142
  • Розробка веб-сайту для компанії ФІКСПЕР
    Розробка веб-сайту для компанії ФІКСПЕР
    1060