Подключение интернет-эквайринга к программному обеспечению на заказ, вопреки мнению большинства команд, не сводится к тому, чтобы «вызвать API iyzico или PayTR». Интеграция, готовая к промышленной эксплуатации, должна одновременно решать четыре отдельные задачи: (1) правильно выстроить процесс 3D Secure (3DS) — отправить пользователя на экран проверки в банке и вернуть обратно; (2) надёжно проверять callback-уведомления и вебхуки — подписью подтверждать, что уведомление действительно пришло от провайдера, сохранять идемпотентность при повторных уведомлениях и быстро возвращать код 2xx; (3) сверять платёж с заказом — привязывать к нужному заказу то событие, на котором вы фактически получили деньги; и (4) обрабатывать сценарии после оплаты, такие как возвраты, отмены и рассрочка. Если хотя бы одного из этих элементов не хватает, интеграция только выглядит «рабочей», а в промышленной эксплуатации в ней остаются незаметные ошибки, которые стоят денег: заказ, обработанный дважды, корзина со статусом «оплачено» без подтверждённой оплаты или бесплатная выдача товара по поддельному callback-уведомлению. В этой статье мы, B2B- и white-label-разработчик ПО из провинции Sakarya, делимся подходом, который сами применяем при интеграции интернет-эквайринга: банковский интернет-эквайринг или платёжная организация, платёжная форма на стороне провайдера (hosted form) или прямой API, процесс 3DS, безопасные вебхуки, сверка, возвраты и рассрочка, а также чек-лист перед запуском.
Мы рассматриваем платёжную интеграцию и как проект API-интеграции, и как вопрос инфраструктуры электронной коммерции. Ту же дисциплину мы применяли в работе с ERP и e-Fatura (турецкая система электронных счетов-фактур); если вы выбираете для интеграции между готовым и заказным решением, прочитайте нашу статью об интеграции электронной коммерции, ERP и e-Fatura. Приведённые ниже технические детали основаны на официальной документации iyzico и PayTR; поскольку любой провайдер со временем может вносить изменения, единственным достоверным источником остаётся официальная документация на момент интеграции.
Банковский интернет-эквайринг или платёжная организация?
Первое архитектурное решение — выбрать канал приёма платежей по картам. В Турции интернет-эквайринг доступен в двух формах: (a) банковский интернет-эквайринг по прямому договору с банком и (b) независимые от банков платёжные организации (например, iyzico и PayTR). Платёжные организации работают на основании закона № 6493 — Закона о платёжных системах и системах расчётов по ценным бумагам, платёжных услугах и организациях электронных денег, а лицензию на деятельность получают не от банковского регулятора BDDK, а от Центрального банка Турции, TCMB (статья 12 закона) (TCMB — платёжные организации).
На практике разница такова: при банковском интернет-эквайринге обычно нужны отдельная интеграция и отдельный договор с каждым банком, а независимая платёжная организация позволяет принимать карты большинства банков через одну интеграцию. iyzico особенно выделяется в сценариях с маркетплейсами и субмерчантами (submerchant). Правило выбора простое:
- Если ваш приоритет — один банк, большой объём и минимальная комиссия и у вас прочные отношения с этим банком, банковский интернет-эквайринг может быть оправдан; однако если понадобится работать с несколькими банками, объём интеграционных работ многократно вырастет.
- Если вам нужны быстрый запуск, приём карт разных банков и одна интеграция, в большинстве сценариев B2B и малого и среднего бизнеса платёжная организация (iyzico/PayTR) обойдётся дешевле в сопровождении.
- Если вам нужны функции маркетплейса, распределение комиссий или субмерчанты, платёжная организация с поддержкой submerchant практически обязательна.
Форма провайдера или прямой API? Именно здесь определяется область действия PCI DSS
Второе и, пожалуй, самое важное решение — через какие системы будут проходить данные карт, потому что от этого напрямую зависит область действия PCI DSS в вашей компании. Возможны два подхода:
- Платёжная форма на стороне провайдера: Данные карты вводятся на странице или в iframe, размещённых у провайдера. В iFrame API сервиса PayTR пользователь вводит данные карты в iframe на стороне PayTR; в iyzico CheckoutForm — это страница оплаты, размещённая на стороне iyzico (CF-Initialize возвращает
paymentPageUrl/токен, а встроить её можно с параметромiframe=true). В обоих случаях конфиденциальные данные карты никогда не попадают на ваш сервер, поэтому область действия PCI DSS заметно сужается. - Прямой API: Платёж принимается через вашу собственную форму, а данные карты проходят через вашу инфраструктуру. Такой вариант даёт полный контроль и брендированный интерфейс, но существенно увеличивает ваши обязательства по PCI DSS.
И iyzico, и PayTR берут защиту по PCI на себя на стороне провайдера; iyzico позиционирует себя как поставщика услуг уровня PCI DSS Level 1. Но уточним: какой именно тип SAQ (SAQ A, SAQ A-EP и т. д.) подойдёт вам, зависит от деталей интеграции, и в этой статье мы этого не утверждаем; общее правило таково: область действия сужается, когда данные карт не проходят через ваш сервер. Наш практический совет однозначен:
Если у вас нет реального бизнес-требования к брендированной и полностью контролируемой странице оплаты, выбирайте форму провайдера. Несколько пикселей кастомизации не стоят той нагрузки по аудиту PCI DSS и той ответственности, которые вы на себя берёте.
Как работает процесс 3DS? (на примере iyzico)
3D Secure — это дополнительная проверка, которую выполняет банк держателя карты (одноразовый код, подтверждение в банковском приложении и т. д.). Если этот шаг пропустить, риск чарджбэка и ответственность в основном остаются на бизнесе. В iyzico 3DS — это двухэтапный процесс на POST-запросах:
- Шаг 1 — Init 3DS: На эндпоинт
/payment/3dsecure/initializeотправляется POST-запрос с данными карты и покупателя и параметромcallbackUrl. В ответе приходит закодированный в base64threeDSHtmlContent. Этот HTML отображается в браузере и перенаправляет пользователя на экран проверки 3DS в банке. - Шаг 2 — Auth 3DS: Когда пользователь завершит проверку в банке и вернётся на ваш
callbackUrl, на эндпоинт auth отправляется второй POST-запрос сpaymentId, полученным на этапе Init. Здесь iyzico рекомендует использовать эндпоинт v2 (/payment/v2/3dsecure/auth, то есть 3DS 2.0); устаревший/payment/3dsecure/auth— это 3DS 1.0.
Результат проверки передаётся в поле mdStatus: значение 1 означает успех, а значения 0 и 2–8 — неудачную проверку. Важная оговорка: мы не приводим здесь как окончательный точный состав полей POST-запроса, который банк отправляет на ваш callbackUrl, поскольку он может различаться в зависимости от версии и интеграции; эту деталь нужно проверить по актуальной официальной документации перед запуском (iyzico — Init 3DS, iyzico — Auth 3DS).
На протяжении всего процесса запрос и ответ сопоставляются с помощью conversationId. iyzico определяет его как «уникальный идентификатор для сопоставления запроса и ответа»; сюда можно подставить собственный номер заказа, а значение, отправленное в запросе, возвращается в ответе без изменений. Кроме того, iyzico присваивает каждому успешному платежу собственный paymentId; возвраты и отмены выполняются именно через этот paymentId.
Безопасная проверка вебхуков и callback-уведомлений: подпись, идемпотентность, быстрый ответ 2xx
Это самая недооценённая и самая опасная часть платёжной интеграции. Callback-уведомление или вебхук — это то самое межсерверное событие, по которому вы отмечаете деньги как полученные. Три правила не обсуждаются: проверяйте подпись, соблюдайте идемпотентность, быстро возвращайте 2xx.
1) Проверка подписи — докажите, что уведомление действительно пришло от провайдера
Никогда не считайте POST-запрос, пришедший на ваш callback-адрес, подтверждением оплаты, не проверив подпись. Иначе злоумышленник сможет отправить поддельное уведомление об «успешной оплате» и получить бесплатный заказ. Механизмы у двух провайдеров различаются:
- PayTR (iFrame API): В уведомлении, отправляемом на
notify_url, хеш вычисляется какbase64_encode(hash_hmac('sha256', merchant_oid + merchant_salt + status + total_amount, merchant_key, true)). Если значениеhashво входящем POST-запросе не совпадает с этим значением в точности, запрос следует считать поступившим не от PayTR и отклонить. Эта формула относится к iFrame API (четыре поля); в других API сервиса PayTR набор полей может быть иным, поэтому не путайте их. - iyzico (вебхук): Проверка выполняется по заголовку
X-IYZ-SIGNATURE-V3с помощью HMAC-SHA256 (HEX); более старые версии заголовка больше не поддерживаются. Критически важный момент: порядок полей, которые объединяются для подписи, зависит от типа вебхука. Для прямой оплаты, HPP и подписки порядок параметров разный — единого «универсального» порядка для подписи вебхука нет; сверяйте порядок для своего типа интеграции с официальной таблицей.
Кроме того, iyzico предлагает отдельный механизм подписи ответа для синхронных ответов API: определённые параметры объединяются через двоеточие (:), хешируются с ключом secretKey по алгоритму HMAC-SHA256 и кодируются в HEX; лишние конечные нули в ценах отбрасываются (10.50 → 10.5, 10.00 → 10). Порядок параметров зависит от эндпоинта; например, для auth без 3DS он такой: paymentId:currency:basketId:conversationId:paidPrice:price, и в auth 3DS версии v2 используется тот же порядок полей (iyzico — проверка подписи ответа, iyzico — вебхуки, PayTR — iFrame API, шаг 2).
2) Идемпотентность — если одно и то же уведомление пришло дважды, не обрабатывайте его дважды
Оба провайдера могут отправлять несколько уведомлений по одной и той же транзакции. Если не отсеивать дубли по идентификатору заказа, один и тот же заказ будет подтверждён дважды, остаток на складе спишется дважды и дважды будет создана отправка. PayTR говорит об этом прямо: контроль повторов нужно вести по уникальному полю merchant_oid; для подтверждения или отмены заказа следует обрабатывать только первое уведомление, а на повторные — просто отвечать. Практическая реализация: храните статус уведомления (обработано/не обработано) в базе данных как единственную уникальную запись, привязанную к идентификатору заказа, и выполняйте обработку в рамках одной атомарной операции.
3) Быстро возвращайте 2xx — тяжёлую работу выполняйте асинхронно
Провайдер ожидает положительного ответа на уведомление в течение короткого времени, а если не получает его, повторяет отправку. Вебхук iyzico при отсутствии ответа 2xx от продавца повторяется каждые 15 минут, до 3 раз. PayTR, не получив «OK», повторяет отправку через 1 минуту и требует, чтобы тело ответа содержало только обычный текст OK, без HTML и другого содержимого. Отсюда следуют два правила:
- Сначала проверьте, затем сохраните, потом ответьте: проверьте подпись, однозначно отметьте заказ и только затем верните ожидаемый ответ. Тяжёлые операции, такие как отправка писем, выставление счетов или запись в ERP, переносите в асинхронную очередь; не выполняйте их синхронно, блокируя ответ на callback-уведомление.
- Возвращайте тело ответа в формате, который ожидает провайдер: для PayTR — ровно
OK, для iyzico — HTTP-код статуса 2xx. Иначе вы столкнётесь с лишними повторами и «потерянными» платежами.
| Параметр | iyzico (вебхук) | PayTR (callback iFrame API) |
|---|---|---|
| Метод подписи | HMAC-SHA256 (HEX), заголовок X-IYZ-SIGNATURE-V3 |
HMAC-SHA256 + base64, поле hash в POST-запросе |
| Порядок полей подписи | Зависит от типа вебхука (для прямой оплаты, HPP и подписки он разный) | merchant_oid + merchant_salt + status + total_amount (iFrame API) |
| Ожидаемый ответ | HTTP-код статуса 2xx | Обычный текст OK |
| Повторные отправки | Если нет 2xx: каждые 15 мин, до 3 раз | Если нет OK: повтор через 1 мин |
| Ключ дедупликации | paymentId / conversationId |
merchant_oid (обрабатывается только первое уведомление) |
Сверка платежей с заказами
Сверка — это мост между «деньгами, которые получил провайдер», и «заказом в вашей системе». Плохо выстроенная сверка доставляет больше всего хлопот бухгалтерии и службе поддержки клиентов. Оба провайдера строят этот мост на уникальном идентификаторе заказа, который формирует продавец:
- PayTR:
merchant_oid(до 64 буквенно-цифровых символов) — это уникальный номер заказа, который вы присваиваете транзакции; он возвращается вам в callback-уведомлении и делает сверку возможной. Кроме того, на шаге 1paytr_tokenформируется с помощью HMAC-SHA256 + base64, а объединяемая строка включает поляmerchant_id + user_ip + merchant_oid + email + payment_amount + user_basket + no_installment + max_installment + currency + test_mode + merchant_salt. - iyzico: Используйте
conversationIdкак собственный номер заказа; значение, отправленное в запросе, возвращается в ответе без изменений. Его аналог на стороне провайдера —paymentId: через него выполняются возвраты и отмены. Храните оба идентификатора в записи о заказе.
Практические принципы надёжной сверки:
- Решение принимает callback-уведомление, а не браузер: Страница «успеха», на которую возвращается браузер пользователя, — это лишь подсказка, а не доказательство. Подтверждайте заказ прежде всего по межсерверному callback-уведомлению с проверенной подписью.
- Сравнивайте сумму и валюту: Сопоставляйте сумму из callback-уведомления с суммой, ожидаемой по заказу; при расхождении не обрабатывайте уведомление и поднимайте оповещение.
- Закрывайте незавершённые состояния: Если callback-уведомление так и не пришло, с помощью фоновой задачи проверяйте заказы со статусом «оплата начата, но не завершена» через API запросов провайдера и закрывайте их.
- Ежедневная сверка: Регулярно сравнивайте отчёты провайдера по транзакциям и расчётам с собственными записями; не полагайтесь только на вебхук.
Возвраты, частичные возвраты и рассрочка
Вторая половина платёжного процесса — сценарии после оплаты; именно здесь решается, каким будет клиентский опыт и насколько точным останется учёт. Ключевые различия в iyzico:
- Возврат — полный или частичный: Есть два пути.
/payment/refundработает на уровне позиции корзины (сpaymentTransactionId, то есть идентификатором транзакции для позиции корзины, иprice)./v2/payment/refundработает на уровне платежа (сpaymentIdиprice); какую позицию корзины вернуть, система определяет сама. Обе версии поддерживают полный и частичный возврат и выдают банковский референс (refundHostReference,authCode). - Отмена:
/payment/cancelвызывается только сpaymentId, не поддерживает частичную сумму и отменяет весь платёж. Согласно документации iyzico, отмену можно выполнить в тот же день, что и платёж, и она не создаёт записей о списании или зачислении в выписке по карте; возврат же отражается в выписке и может занять несколько дней в зависимости от банка. Точные правила «того же дня» и время отсечения (cutoff) зависят от банка и даты валютирования, поэтому считайте это общим принципом, а не точным бухгалтерским правилом. - Рассрочка и запрос BIN: С помощью
POST /payment/iyzipos/installmentможно запросить варианты рассрочки, ставки и суммы, а также семейство и платёжную систему карты и данные о банке, передавprice(обязательный параметр) и необязательныйbinNumber(8-значный BIN). Ставки рассрочки можно получить и только по сумме (без BIN);conversationIdпередаётся в запросе и возвращается в ответе без изменений (iyzico — возврат и отмена, iyzico — рассрочка и BIN).
Бизнес-правило: заложите в систему логику «в тот же день — отмена, позже — возврат»; при частичном возврате фиксируйте на своей стороне, какая позиция или сумма была возвращена, и храните банковские референсы.
Чек-лист перед запуском
- Проверка подписи: Проверяется ли подпись каждого callback-уведомления и вебхука? Безусловно ли отклоняется непроверенный запрос? Используется ли в iyzico правильный порядок полей для вашего типа интеграции?
- Идемпотентность: Если уведомление по тому же идентификатору заказа (
merchant_oid/conversationId) приходит повторно, остаётся ли оно без повторной обработки и возвращается ли только нужный ответ? - Быстрый ответ: Возвращает ли callback-обработчик быстрый ответ 2xx /
OKв ожидаемом формате? Вынесены ли тяжёлые операции в асинхронную очередь? - Проверка суммы: Сравниваются ли сумма и валюта из callback-уведомления со значением, ожидаемым по заказу?
- Источник подтверждения: Опирается ли подтверждение заказа на межсерверное уведомление, а не на редирект браузера?
- Закрытие незавершённых платежей: Есть ли фоновый запрос для платежей со статусом «начат, но не завершён», по которым callback-уведомление так и не пришло?
- Возврат и отмена: Протестированы ли сценарии полного и частичного возврата, отмены и рассрочки? Сохраняются ли банковские референсы?
- Секреты и HTTPS: Хранятся ли секреты, такие как
secretKey/merchant_key/merchant_salt, только на сервере, в переменных окружения? Работает ли callback-адрес по HTTPS? - Логирование: Записывается ли в лог каждое платёжное событие (запрос, ответ, callback-уведомление, результат проверки подписи) так, чтобы его можно было отследить?
- Тестовая среда: Выполнена ли сквозная проверка (включая 3DS) в песочнице или тестовом режиме провайдера?
Как Partnerfy может помочь
Как компания по разработке ПО из провинции Sakarya, работающая в формате B2B и white-label, мы рассматриваем интеграцию интернет-эквайринга не как «вызов API», а как задачу безопасности и сверки. В проекте веб-приложения на заказ или электронной коммерции мы сначала определяемся с решением: платёжная организация или банковский интернет-эквайринг, форма провайдера или прямой API (с учётом вашей области действия PCI DSS). Затем мы выстраиваем и от начала до конца проверяем в тестовой среде процесс 3DS, callback-обработчик с проверкой подписи и идемпотентностью, сверку сумм и сценарии возврата и рассрочки. Обычно мы связываем всё это с вашими действующими ERP и учётными системами в виде API-интеграции.
Честная оговорка: ни одна интеграция не может гарантировать, что проблем не будет никогда, — банки, провайдеры и документация со временем меняются. Мы обещаем не гарантию, а дисциплину: проверять подпись, соблюдать идемпотентность, доверять callback-уведомлению, логировать и сверять каждый шаг. Если вы хотите обсудить, что вам подходит — готовый плагин или заказная интеграция, в этом решении поможет и наша статья о заказном и готовом ПО.
Заключение
Интеграция интернет-эквайринга, готовая к промышленной эксплуатации, начинается с вызова API, но не заканчивается им. Настоящая работа состоит в том, чтобы выбрать правильный канал (платёжная организация или банковский интернет-эквайринг), осознанно сузить область действия PCI DSS (форма провайдера или прямой API), правильно выстроить процесс 3DS, проверять подпись callback-уведомлений, соблюдать идемпотентность, быстро возвращать 2xx/OK, сверять сумму с заказом и полностью управлять сценариями возврата, отмены и рассрочки. Именно эти шаги отличают интеграцию, которая «вроде бы работает», от действительно безопасной. Если вам нужен партнёр для проектирования платёжной интеграции или защиты существующего решения, свяжитесь с нами: мы вместе составим план по каналу, области действия PCI DSS и сверке с учётом вашего сценария.
Источники
- iyzico — Init 3DS (/payment/3dsecure/initialize, threeDSHtmlContent, conversationId)
- iyzico — Auth 3DS (рекомендуется v2, завершение с paymentId, mdStatus)
- iyzico — проверка подписи ответа (HMAC-SHA256 + HEX)
- iyzico — вебхуки (X-IYZ-SIGNATURE-V3, требование 2xx, повторы)
- iyzico — сервис рассрочки и BIN (/payment/iyzipos/installment)
- iyzico — возврат и отмена (/payment/refund, /v2/payment/refund, /payment/cancel)
- iyzico — CheckoutForm (страница оплаты на стороне провайдера, суженная область действия PCI)
- PayTR — iFrame API, шаг 1 (paytr_token, iframe на стороне провайдера, merchant_oid)
- PayTR — iFrame API, шаг 2 (хеш callback-уведомления, ответ «OK», дедупликация по merchant_oid)
- TCMB — платёжные организации (закон № 6493, ст. 12, лицензия на деятельность)