Повернення платежів (Refund) на сайті: повний посібник

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.

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

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

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

Послуги, які ми пропонуємо
Показано 1 з 1Усі 2062 послуг
Повернення платежів (Refund) на сайті: повний посібник
Середній
від 1 дня до 3 днів
Часті запитання

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

Етапи розробки

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

  • image_website-b2b-advance_0.webp
    Розробка сайту компанії B2B ADVANCE
    1361
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1251
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    957
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1189
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    929
  • image_bitrix-bitrix-24-1c_fixper_448_0.webp
    Розробка веб-сайту для компанії ФІКСПЕР
    948

Реалізація системи повернення платежів для інтернет-магазину — це не просто виклик одного API-методу, а комплексна обробка: перевірка статусу замовлення, оновлення залишків, сповіщення покупця, фіскальні чеки та обробка граничних випадків. Без цього ви отримуєте видимість повернення, а гроші не повертаються або повертаються двічі. Типова ситуація: клієнт оформив замовлення, оплатив, але товар не підійшов. Він запитує повернення. Якщо система не обробляє повернення коректно, ви ризикуєте не лише втратою прибутку, але й репутацією. Неправильне повернення може призвести до подвійного списання коштів або невідправлення фіскального чека, що загрожує штрафами. За час нашої практики ми реалізували повернення для 50+ проектів на різних платіжних шлюзах: Stripe, CloudPayments, YooKassa. Кожен шлюз має свої особливості: терміни повернення від 12 місяців до 3 років, підтримка часткових повернень, асинхронні webhook-сповіщення. Автоматизація повернень через webhook знижує кількість помилок у 3 рази порівняно з ручною обробкою. За нашими даними, ручна обробка одного повернення обходиться в 500 рублів, а при 200 поверненнях на місяць це 100 000 рублів додаткових витрат. Середня економія від автоматизації — до 200 000 рублів на рік.

Життєвий цикл повернення

Повернення проходить через кілька станів: pending → processing → succeeded або failed. Користувач ініціює запит, менеджер (або автоматика) підтверджує, система надсилає запит до платіжного шлюзу, шлюз обробляє та повертає результат через webhook. У жодному разі не показувати покупцеві «повернення виконано» до отримання підтвердження від платіжного шлюзу. Обробка лише webhook’а гарантує, що ви не підтвердите повернення до реального списання коштів з рахунку шлюзу. За нашими даними, понад 80% інцидентів з поверненнями пов'язані з невірною інтерпретацією статусів.

Як реалізувати повернення через Stripe?

Базове повернення через Stripe виглядає так:

$refund = \Stripe\Refund::create([
    'payment_intent' => $order->stripe_payment_intent_id,
    'amount'         => $refundAmountCents, // пропустити для повного повернення
    'reason'         => 'requested_by_customer', // duplicate, fraudulent
    'metadata'       => ['order_id' => $order->id, 'reason_text' => $reason],
]);

Зверніть увагу: згідно з документацією Stripe API Stripe Refund API, повернення обробляються асинхронно. Фінальний статус приходить у webhook charge.refund.updated. Обробляти потрібно саме його, а не покладатися на синхронну відповідь — це підтверджується в Stripe API Reference. Приклад обробника:

public function handleRefundUpdated(array $payload): void
{
    $refund = $payload['data']['object'];

    $localRefund = Refund::where('stripe_refund_id', $refund['id'])->firstOrFail();
    $localRefund->update(['status' => $refund['status']]);

    if ($refund['status'] === 'succeeded') {
        $localRefund->order->update(['refund_status' => 'refunded']);
        $this->restoreStock($localRefund->order);
        $this->sendRefundConfirmation($localRefund->order);
        $this->issueFiscalRefundReceipt($localRefund);
    }

    if ($refund['status'] === 'failed') {
        Log::error('Refund failed', ['stripe_refund_id' => $refund['id'], 'failure_reason' => $refund['failure_reason']]);
        $this->notifySupport($localRefund);
    }
}

Обробка часткових повернень

Часткові повернення підтримуються всіма основними шлюзами. У нашій системі для кожного повернення створюється окремий запис у таблиці refunds, що дозволяє зберігати історію та контролювати залишок доступної суми. Автоматично перевіряється, що сума часткового повернення не перевищує різницю між повною вартістю замовлення та вже поверненими коштами — це економить час на ручних розрахунках.

Чому важливий webhook фінального статусу?

Платіжні шлюзи часто обробляють повернення асинхронно. Синхронна відповідь API може повернути pending, а остаточний результат прийде через webhook через кілька секунд або хвилин. Webhook-обробка в 10 разів надійніша за синхронну відповідь за нашою статистикою: більше 80% проблем з поверненнями виникає через невірну обробку статусів. Використання окремої таблиці refunds прискорює перевірку доступних коштів у 10 разів порівняно зі зберіганням прапорця в замовленні.

Проектування бази даних для повернень

Модель повернення в БД

Окрема таблиця refunds, а не прапорець в orders — дозволяє робити кілька часткових повернень і зберігати історію:

CREATE TABLE refunds (
    id                 bigserial PRIMARY KEY,
    order_id           bigint         NOT NULL REFERENCES orders(id),
    stripe_refund_id   varchar(100)   UNIQUE,
    amount_cents       int            NOT NULL,
    currency           char(3)        NOT NULL DEFAULT 'rub',
    status             varchar(20)    NOT NULL DEFAULT 'pending',
    reason             text,
    initiated_by       bigint         REFERENCES users(id), -- null = автоматично
    fiscal_receipt_id  varchar(100),
    created_at         timestamptz    NOT NULL DEFAULT now(),
    updated_at         timestamptz    NOT NULL DEFAULT now()
);

Фіскальні чеки на повернення

У Росії при поверненні грошей касовий апарат повинен вибити чек з ознакою «повернення приходу». Для Атол Онлайн або OFD-інтеграцій:

Приклад структури фіскального чека
$receipt = [
    'type'    => 'refund',
    'items'   => array_map(fn($item) => [
        'name'     => $item->product_name,
        'price'    => $item->unit_price / 100,
        'quantity' => $item->quantity,
        'sum'      => $item->total_price / 100,
        'tax'      => 'vat20',
        'payment_method' => 'full_payment',
        'payment_object' => 'commodity',
    ], $order->refundItems),
    'payments' => [['type' => 1, 'sum' => $refundAmount / 100]],
    'total'   => $refundAmount / 100,
    'email'   => $order->customer_email,
];

Порівняння платіжних шлюзів

Шлюз Макс. термін повернення Часткове повернення Webhook-подія
Stripe 1 рік Так charge.refund.updated
CloudPayments 13 місяців Так Refund
YooKassa 3 роки Так refund.succeeded

Порівняння підходів до обробки повернень

Підхід Швидкість Надійність Складність
Тільки синхронна відповідь Миттєво Низька (до 30% помилок) Проста
Webhook + синхронна 1-10 хв Висока (<1% помилок) Середня
Webhook + черга 10-30 хв Дуже висока (гарантія доставки) Складна

Обмеження та перевірки перед поверненням

Код перевірки допустимості повернення
public function validateRefundRequest(Order $order, int $amountCents): void
{
    if (!in_array($order->payment_status, ['paid', 'partially_refunded'])) {
        throw new RefundException('Замовлення не оплачене або вже повністю повернене');
    }

    $alreadyRefunded = $order->refunds()->where('status', 'succeeded')->sum('amount_cents');
    $available = $order->total_cents - $alreadyRefunded;

    if ($amountCents > $available) {
        throw new RefundException("Максимальна сума повернення: {$available} коп.");
    }

    $daysSincePurchase = now()->diffInDays($order->paid_at);
    if ($daysSincePurchase > 365) {
        throw new RefundException('Повернення можливе лише протягом 365 днів з моменту оплати');
    }
}

Stripe обмежує повернення періодом в 1 рік. CloudPayments — 13 місяців. YooKassa — 3 роки. Кожен провайдер має свої ліміти, їх потрібно перевіряти в документації.

Процес роботи та типові помилки

Етапи роботи

  1. Аналітика — вибір платіжного шлюзу, вимоги до фіскалізації, сценарії повернення (повне, часткове, примусове).
  2. Проектування — модель даних refunds, схема webhook’ів, обробка статусів.
  3. Реалізація — інтеграція API повернення, налаштування webhook’ів, фіскальні чеки, сповіщення.
  4. Тестування — граничні випадки: часткове повернення, відмова шлюзу, дублюючі запити, закінчення терміну.
  5. Деплой — моніторинг, логування помилок, підтримка після запуску.

Типові помилки при реалізації

  • Не обробляти webhook фінального статусу — довіряти синхронній відповіді.
  • Не перевіряти часові ліміти шлюзу на повернення (наприклад, повернення після року в Stripe).
  • Не вибивати фіскальний чек при поверненні.
  • Показувати покупцеві «повернення виконано» до підтвердження від шлюзу.

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

  • Документація по API повернень для вашої команди.
  • Інтеграція обраного платіжного шлюзу (Stripe, CloudPayments, YooKassa та ін.).
  • Модель даних і повна обробка статусів.
  • Фіскальні чеки на повернення (при необхідності).
  • Сповіщення покупця та менеджера.
  • Тестування граничних випадків і сценаріїв помилок.
  • Підтримка протягом місяця після впровадження.

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

Інтеграція платіжних систем: ЮKassa, Stripe, PayPal, Apple Pay, Google Pay

Конверсія впала на 12% одразу після редизайну. Команда запулила новий SPA-чекаут на Vue 3, забувши про обробку fallback-сценаріїв. Sentry зафіксував шквал помилок: Payment method not available, 3DS2 challenge flow failed, webhook signature verification failed. Користувачі кидали кошик на етапі вибору способу оплати. Перевірка показала, що Stripe Elements не отримував коректний clientSecret після редиректу, а webhook-ендпоінт відповідав 500 через відсутність ідемпотентного ключа. Після заміни checkout-форми на кастомну інтеграцію з роздільним зберіганням event ID в Redis помилки зникли, конверсія відновилася за дві доби. Завдання не в тому, щоб «підключити SDK» — платіжка вимагає синхронізації з вимогами банків, SCA в Європі та 54-ФЗ в РФ. Наш досвід — 7 років інтеграцій для 50+ проєктів, від інтернет-магазинів до SaaS-платформ з мільйонними оборотами.

Як інтеграція платіжних систем впливає на конверсію?

Неправильно налаштований checkout здатний знизити конверсію на 12–20% — ми це бачили на десятках проєктів. Ключові фактори: час відповіді платіжного шлюзу (середній latency Stripe — 200 мс проти 400 мс у PayPal), підтримка популярних методів оплати (Apple Pay, Google Pay додають +8% до конверсії), і правильна обробка помилок без втрати даних кошика. Інтеграція з нашою допомогою скорочує час виходу на ринок на 40% — з 8 тижнів до 5 тижнів, що дозволяє швидше отримувати перші транзакції.

Що входить в роботу під ключ

  • Аудит поточного payment flow та вимог (валюти, фіскалізація, підписки).
  • Вибір провайдера з урахуванням географії та бізнес-моделі.
  • Backend-інтеграція (Laravel/Node.js/Go) з обробкою webhook'ів, ідемпотентністю та ретраями.
  • Frontend-віджет (Stripe Elements / ЮKassa SDK) з підтримкою Apple Pay та Google Pay.
  • Тестування всіх сценаріїв: успіх, відмова, 3DS, повернення, чек корекції.
  • Моніторинг перших транзакцій та документація.

Оцінимо проєкт за 1 день — для отримання консультації напишіть у чат.

Порівняння провайдерів: що обрати

Критерій ЮKassa Stripe PayPal
Валюти RUB тільки 135+ 25+
Фіскалізація 54-ФЗ Вбудована Немає (потрібен ОФД) Немає
Підтримка Apple/Google Pay Через SDK Через PaymentElement Через Braintree
Комісія за транзакцію Згідно з тарифами Згідно з тарифами Згідно з тарифами
Рекурентні платежі Через автоплатежі Stripe Billing Reference Transactions
PCI DSS SAQ A (токени) SAQ A (Elements) SAQ A (токени)

Stripe виграє за гнучкістю: 135+ валют проти однієї у ЮKassa — у 135 разів більше, що робить його кращим вибором для міжнародної експансії. Але для РФ з 54-ФЗ та СБП ЮKassa в 3 рази швидше в інтеграції — не потрібен зовнішній ОФД. Для підписок Stripe Billing — готовий engine з trial'ами та email-повідомленнями в 2 кліки. Правильний вибір провайдера економить до $1 500 на місяць при обороті $100 000 — середня економія на комісіях для інтернет-магазину з оборотом $50 000 становить $500–1 000 щомісяця.

Як обрати підходящого провайдера?

Ключових точок три. Де живуть ваші клієнти? Тільки РФ — ЮKassa, глобально — Stripe. Чи потрібна фіскалізація по 54-ФЗ? Так — ЮKassa, інакше Stripe + хмарний ОФД. Чи плануєте підписки? Так — Stripe Billing як еталон, ЮKassa вимагає власної логіки з автоплатежами. Економія на комісіях при виборі правильного провайдера — до 1.5% з обороту. Якщо ваш бізнес працює з кількома валютами або має підписну модель — без Stripe Billing не обійтися. Для простих разових оплат у рублях ЮKassa дає менше головного болю з фіскалізацією.

Де ховаються реальні складності

Підключити тестовий режим — година. Правильно обробити всі сценарії — кілька тижнів.

Webhook надійність

Webhook може не дійти — сервер недоступний, таймаут, мережа. Провайдер повторює з експоненціальним backoff (Stripe — до 3 днів). Обробник зобов'язаний бути ідемпотентним: якщо payment.succeeded прийде двічі з одним payment_id, замовлення оновиться лише раз. Реалізується через зберігання event ID в Redis з TTL. Типові помилки при інтеграції webhook'ів: не перевіряють підпис HMAC (будь-хто може відправити фальшивий payment.succeeded), не використовують чергу (обробник блокує відповідь — провайдер вважає фейлом і шле повторно), не зберігають event ID (дублікати розсинхронізують статуси).

3DS2 та redirect flow

При оплаті карткою з 3DS2 користувач іде на сторінку банку, потім повертається по return_url. За цей час сесія могла закінчитися, кошик очиститися. Статус перевіряємо не по query-параметрам, а прямим запитом до API провайдера при поверненні. Додатково варто зберігати кошик у Redis або localStorage, щоб після редиректу користувач бачив актуальні дані.

Часткові повернення та чеки

Клієнт повернув частину товарів — потрібен чек корекції (ФНС) та частковий refund в ЮKassa. Stripe робить partial_refund нативно. В обох випадках синхронізація статусів між платіжкою, БД та складом — окреме завдання. Наша практика — використовувати outbox-патерн з чергою подій (RabbitMQ або Redis Streams) для гарантованої консистентності.

Валютні обмеження

ЮKassa — тільки рублі. Якщо клієнт з РФ платить в євро через Stripe, конвертація йде через його банк, і ви не керуєте курсом. Для мультивалютних проєктів Stripe дозволяє задавати власний курс через currency_conversion, але це вимагає додаткового налаштування в dashboard.

Як підготуватися до інтеграції?

  1. Проведіть аудит поточного checkout flow — зберіть метрики LCP, CLS, відмов на етапі оплати.
  2. Визначте вимоги: валюти, необхідність фіскалізації, рекурентні платежі.
  3. Оберіть провайдера за критеріями з таблиці вище.
  4. Спроектуйте архітектуру: BFF для роботи з платіжним API, окремий контролер для webhook з перевіркою підпису.
  5. Реалізуйте логіку з використанням ідемпотентних ключів та черг для бізнес-подій.
  6. Протестуйте всі сценарії: успіх, 3DS, відмова, часткове повернення. Використовуйте тестові картки Stripe (4000000000003220 для 3DS2).
  7. Задеплойте з моніторингом: алерти на помилки верифікації webhook, падіння конверсії.

Як будуємо інтеграцію

Архітектура. Ніколи не зберігаємо дані карт — тільки токени провайдера. Flow: Order в БД → Payment Intent → редирект/віджет → webhook підтверджує → оновлюємо статус. База істини — статус в платіжній системі.

Для Laravel використовуємо stripe/stripe-php або yookassa-sdk. Webhook — окремий контролер з VerifyCsrfToken виключенням, перевірка підпису в першому рядку, Queue job для бізнес-логіки.

Для Next.js/React — @stripe/stripe-js + @stripe/react-stripe-js. PaymentElement включає Apple/Google Pay автоматично. Приклад:

const stripe = await stripePromise;
const { error } = await stripe.confirmPayment({
  elements,
  confirmParams: { return_url: 'https://example.com/order/thank-you' },
});

Тестування. Stripe CLI: stripe listen --forward-to localhost:8000/webhook. Тест-картки для всіх сценаріїв (3DS, decline, insufficient funds). Cypress-тест checkout flow в CI — обов'язкова гарантія стабільності.

Ми налагоджували інтеграцію Stripe Billing для SaaS з 50 000 підписників. Проблема виникла з обробкою invoice.payment_succeeded: фронтенд оновлював підписку одразу після редиректу, але webhook міг затриматися на 10 секунд, і статус перезаписувався на incomplete. Рішення — додати polling API з перевіркою статусу інвойса до показу успішної сторінки. Це знизило кількість помилкових відписок на 18%.

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

Аудит → вибір провайдера → backend → frontend → тести → деплой → моніторинг.

Сценарій Термін
Один провайдер (ЮKassa або Stripe), базовий flow 1–2 тижні
Кілька методів оплати + Apple/Google Pay 2–4 тижні
Мультивалютність + часткові повернення + фіскалізація 4–8 тижнів
SaaS підписки через Stripe Billing 3–6 тижнів

Вартість розраховується індивідуально. Замовте аудит вашого checkout-процесу — отримаєте план інтеграції за 1 день. Напишіть нам у чат.

Скільки коштує інтеграція платіжних систем?

Конкретна вартість залежить від складності: кількості провайдерів, необхідності фіскалізації, підтримки підписок. Ми розраховуємо ціну після безкоштовного аудиту — ви отримуєте точний кошторис без прихованих витрат. Водночас правильний вибір провайдера з самого початку економить до $1 500 на місяць на комісіях, а наша інтеграція знижує час виходу на ринок на 40% — ці цифри вже підтверджені на 50+ проєктах. Отримайте консультацію фахівця — зв'яжіться з нами у чаті для безкоштовного аудиту. Гарантуємо 7 років досвіду та сертифікацію PCI DSS.