Чего ждать при работе с API: 5 (не)обычных проблем при интеграции приложений

    Где-то на просторах мультивселенной…

    Представьте на минуту, что вы капитан Сиракузии, которая в 239 году до н. э. приближается к острову Фарос, что близ города Александрии. Вслед­ст­вие узо­сти про­хо­да, вой­ти в гавань Александрии — непростая задача, в особенности, для такого корабля, как ваш. Вы слышали, что за последний год навигация около острова улучшилась, так как был завершен Фа́росский маяк, уникальное сооружение, заложенное еще при Птолемее I.

    Но, приблизившись к острову, вы понимаете, что промахнулись мимо гавани, так как гигантский маяк был спроектирован башней под воду, а свет сигнальных костров виден только в сумерках и при сильной облачности, ведь зеркала подводной башни отражают свет исключительно вверх. Проклиная свою работу, вы пишете письмо Гиерону II, с описанием того, как неудачно спроектирован интерфейс взаимодействия с портом, а он, как человек с поистине царским чутьем, решает подарить корабль правителю Александрии, а сам начинает смотреть в сторону альтернативных торговых маршрутов…

    Почему-то, когда я думаю об интеграционных API, с которыми неудобно работать, в голове появляется образ именно такого вот маяка, и в этой статье хотелось бы рассказать о конкретных проблемах, с которыми мы в Smartcat сталкивались в процессе интеграции с различными внешними сервисами, а также высказать своё субъективное мнение о том, что именно делает интеграционное API удобным. Итак, капитан очевидность поднялся на борт, поплыли.

    Проблема #1: external_id курильщика

    На мой взгляд, external_id – незаменимая концепция при проектировании API. Идентификатор, сгенеренный на стороне потребителя API и задаваемый при создании сущности, обеспечивает её уникальность и позволяет однозначно идентифицировать сущность во внешней системе. Допустим, мы хотим создать транзакцию на стороне платежного провайдера, если положить в поле external_id наш идентификатор заказа, то при повторной попытке создать транзакцию с таким идентификатором мы получим ошибку и защитимся от создания дубликата.

    Не все интеграционные API поддерживают этот концепт, из-за чего приходится писать дополнительный код, обрабатывающий различные сценарии отказов в работе с API.  Всегда перед созданием сущности приходится искать её по каким-то дополнительным признакам во внешней системе, и, только после этого что-то создавать. Сам процесс создания в таком случае необходимо явно ограничивать до одного потока выполнения, чтобы избежать проблем с конкурентностью.

    Как эту концепцию можно превратить в «подводный маяк»? Ну, например, один из интегрируемых сервисов позволял задавать external_id, который обеспечивал уникальность в течении приблизительного промежутка времени с момента создания сущности, после чего его можно использовать повторно. В отсутствии четкого способа идентифицировать данные при поиске - это проблема. Еще у одного сервиса поиск по external_id работал только два месяца, после чего данные пропадали из выдачи.

    Проблема #2: нет разделения моделей для запросов и ответов

    Иногда в документации к API описываются модели, которые используются и в запросах к серверу, и в ответах – я называю такие модели Jack of all trades (мастер на все руки). Ничего страшного в этом вроде нет, ровно до тех пор, пока у нас  не появляются объекты вроде{ foo:”value1”, bar:”value2” … }, где поле “foo” заполняется в моделях запроса, а “bar” - в ответах сервера. Но это уже выясняется эмпирически в процессе интеграции.

    На мой взгляд, контекст моделей участвующие в запросах и ответах лучше сразу разделять: да, мы получаем оверхед по поддержке большего числа моделей (современные IDE с инструментами по рефакторингу и навигации позволяют ощутимо его снизить), но зато мы получаем возможность работать с ними независимо друг от друга и снимаем когнитивную нагрузку с потребителей нашего API.

    Проблема #3: избыточная защита в неожиданных местах

    Вполне обоснованно ожидать, что сервис, выполняющий асинхронные операции над данными, будет отправлять callback-уведомления об изменении созданных потребителем API сущностей — это позволяет снизить частоту опроса состояния сущностей на стороне потребителя, сохранив возможность получать информацию по изменениям максимально оперативно.

    Хорошая практика - убедиться, что получатель колбека действительно его получил - обычно достаточно проверить код ответа: 200 — порядок, у нас в клубе джентльменам принято верить на слово. Если хочется подстраховаться, то можно договориться, что получатель, например, будет передавать в ответе идентификатор колбека. Так по крайней мере мы будем уверены, что объект получен, и мы не стучимся в абстрактный эндпоинт в вакууме, возвращающий на любой запрос 200.

    Но однажды мы встретили ситуацию, которую назвали «MITM-рояль в кустах»: для того, чтобы просто сообщить отправителю о том, что колбек получен, необходимо прочитать всю модель, и вернуть в ответе хеш на основании её полей. Само собой, в том порядке, в котором они идут в JSON — отложим в сторону «новомодные» фреймворки с маппингами в типизированные модели. 

    То есть несмотря на то, что по идее мы защищены https-сертификатом, разработчики «городят» дополнительный слой безопасности в идентификации получения коллбека.

    Еще одно гипотетическое неудобство такого подхода — сервис отправитель принуждает нас разбирать и обрабатывать (пусть даже частично) модель коллбека синхронно, при получении. Хотя при работе с несколькими типами событий через один эндпоинт вариант – сохранить «сырой» коллбек, а обработку провести асинхронно - может быть предпочтительнее.

    А вы часто встречаете такую реализацию защиты от MITM-атаки при обработке коллбека?

    Проблема #4: изменение типа в зависимости от параметров

    {transaction: {id:”1”}}

    {transaction: [{id:”1”}]}

    Как вам метод, который в зависимости от параметра в запросе будет возвращать в одном из полей ответа либо объект, либо массив, содержащий в себе один элемент?

    Я не вижу ничего плохого в том, чтобы давать потребителю возможность кастомизировать получаемый им ответ (в разумных пределах, само собой), но одно дело – добавить дополнительные необязательные поля в модель, которые будут заполняться в зависимости от параметров, и, совершенно другое – менять тип уже существующего поля, при этом, не обозначить это явно в документации.

    Особенно неприятно то, что, при работе с JSON, например, через библиотеку Newtonsoft Json.NET вы будете получать высокоуровневую ошибку десериализации поля, а дальше долго и вдумчиво всматриваться в два одинаковых объекта, пока глаз не заметит пресловутую пару квадратных скобок…

    Проблема #5: делегированная безопасность

    Представьте, что у вас есть интернет магазин, и вы хотите принимать оплату покупок в нем онлайн по карте, давая возможность пользователю запоминать данные его карты, но совсем не хотите заморачиваться с полноценным прохождением сертификации по PCI DSS.

    На помощь придет огромное множество платежных систем, которые берут на себя всю работу по привязке карты пользователя. По завершению вы получите идентификатор карты во внешней системе, и наверняка – маскированный номер карты. Эти данные вы спокойно сохраняете у себя в БД магазина для дальнейшего использования.

    Но есть нюанс. Если платежная система в качестве такого идентификатора возвращает её хешированный номер, то в случае, если наша база будет скомпрометирована, наличие хеша, бина и последних четырех цифр карты позволят злоумышленникам за обозримое время перебором получить полный номер карты. И тут уже никакая блокировка идентификаторов не поможет.

    Заключение: как всё исправить

    Конечно, перечисленные в статье примеры – самые «выдающиеся» представители подводных маяков. Если повезет, то вы никогда с ними не познакомитесь. Но объединяет все эти примеры, на мой взгляд, одно – в них явно не были продуманы сценарии использования с точки зрения потребителей (о злом умысле при проектировании вопрос ставить желания нет).

    Поэтому возникает вопрос, а всегда ли мы при создании API думаем о его конечных пользователях? Чтобы сделать интерфейс по-настоящему удобным, стоит учитывать ряд моментов:

    • Внутренняя кухня приложения не должна влиять на интеграцию. «Обеспечение уникальности в течение интервала ~N» – явно как-то связано с ненужными внешнему пользователю особенностями системы. Функции external_id должны быть одинаковыми на протяжение всего времени жизни сущности в системе.

    • Контекст моделей запросов и ответов нужно разделять. Может показаться, что это сильно избыточно. Однако в конечном итоге, максимальная прозрачность в вопросе использования моделей даёт плюсы не только потребителям API, но и разработчикам.

    • Выбирайте подходящие методы защиты. Реализуя защиту от какой-либо атаки задумайтесь, действительно ли этот вектор атаки актуален в реализуемом вами сценарии.

    • Нужно избегать чрезмерной кастомизации ответов. Модель ответа – более-менее строгий контракт, излишняя кастомизация которого может говорить о том, что рядом нужен просто еще один, другой контракт.

    • Думайте о том, как передаваемые данные могут быть использованы злоумышленниками. Всегда нужны серьезные основания для того, чтобы отдавать чувствительные данные во внешние системы, даже в зашифрованном виде, особенно если эти данные используются для идентификации. Компрометации данных на стороне потребителя может обесценить любые удобства таких идентификаторов.

    А какие трудности при работе над интеграционными проектами встречались вам? Делитесь описанием и своими решениями/советами в комментариях – сделаем материал полезнее.

    Комментарии 32

      0
      А вы часто встречаете реализацию защиты от MITM-атаки при обработке коллбека?
      во всех вменяемых платёжных сервисах (и большинстве невменяемых, поскольку они подражают вменяемым)
        +1
        Подписать контент коллбека — более чем уместно. Передавать в ответе свою подпись этого контента, на мой взгляд, несколько избыточно. Если злоумышленник смог скомпрометировать подпись отправителя, модифицировал контент и заставил получателя считать, что коллбек достоверный, то он вполне сможет при взаимодействии с отправителем эмулировать, например, отвал по таймауту — это даст получателю время на обработку подмененных данных. Я не встречал сервисов, где факт отвала по таймауту при получении коллбека был бы достаточным основанием, например, для блокировки транзакции, о которой передается уведомление.
          +1

          Автор не раскрыл полный кейс. Но не обязательно это защита от атаки злоумышленника. Я бы это воспиринимал как своего рода роспись в получении. Наученные горьким опытом провайдеры когда им выставляли претензию что не было подтверждения там чего-то или неверная скажем сумма оплаты могут сказать как же не было если было и вы своим же хэшем это подписали.

            +1
            В принципе, интересная теория. ;)

            Сначала убедимся, что разработчик смог правильно распарсить наш особенный формат данных.
            Потом, мы понимаем, что он умеет сортировать поля.
            Потом, он должен сдать 1й разряд по байтоукладчеству и посчитать хеш.
            Потом, он должен правильно кроме кода еще и сформировать ответ.

            Где-то забыл сохранить данные? Ну это мы твоей компании потом припомним!

            Когда пишу интеграции с платежными шлюзами я просто логирую в БД весь трафик в текстовом виде.
            И входящий и исходящий.
            Спится спокойнее…
              0

              Генерировать подпись кэлбэка все се равно приходится так как входные данные от например платежных систем приходят с подписью обычно чтобы клиент мог убедиться что это действительно платежная система прислала данные. Так что весь стек технологий уже используется при работе с кэлбеком.то что хэш нужно вернуть конечно более редкий кейс.


              Пропуски кэллбэков это почти неизбежное явление. Поэтому я периодически запускаются задания по уточнению платежей которые не пришли в свое окончательно не состояние.

          +1
          Платежные шлюзы все еще отдают коллбеки на HTTP?
          Если нет, то кажется, это уже достаточная защита от MITM.

          Так вот в этом и вопрос: Зачем еще усложнять жизнь интегратору?
          0

          У меня по поводу api в подарок был клубок вопросов на которые которые я так и не нашел ответ. Пустые значения. Можем не передавать пустые свойства, передавать null и передавать для сороковых пустую строку. Я склоняюсь к вам снова не передавать. Но фронтенду часто проще если это будет пустая строка. И в запросах и в ответах. А как быть если клиент хочет удалить свойство? Значит null?
          Хорошо. Пусть я набрался терпения и начал требовать чтобы или было значение непустое или не было свойства вообще. Ну тут дело доходит до массивов и весь скоуп вопросов нужно решать заново. Так как у массива есть еще пустой масив и все вопросы заново. Как правильно пустой массив или null, или полностью удалить свойство?
          Дальше связанные объекты. Скажем блог у поста есть автор authorId. Вы возвращаетесь объект со связанным полем author в котором данные из связанной таблицы и есть поле id равное authorId. Что делать с authorId. Его возвращать в ответе или достаточно только author? Вроде бы достаточно. Но как быть если нужно обновить новым значением это поле? Присылать в authorId или в author.id?

            0
            При полном удалении свойства или вводя в ответе Null нужно менять версию API, а если оставаться на той-же API, То возвращать пустую строку
              0

              Для удаления свойства или Delete resource/property или полноценный json patch. Правда фронт его не любит почему-то.

                0

                Это совершенно 3 разные ситуации:


                • поле имеет допустимое для своего типа значение;
                • поле имеет значение null;
                • поля вообще нет в запросе или ответе.

                Не надо пытаться экономить на спичках и интерпретировать одно через другое.
                Пустая строка — это всё ещё строка, а не null и тем более не отсутствие поля. Тоже самое и с пустым массивом.
                Null не тоже самое, что отсутствие поля. Например, в запросе на обновление сущности, передав в поле expires_time значение null мы сообщим что ресурс больше не имеет срока жизни. А если вообще не передать поле — значит его не надо менять, а оставить текущее значение.

                  0

                  С Вашим комментом а все 100 согласен. Но тут со всех сторон начинают лезть подробности. Например чем семантические пустой массив отличается от значения null. Например у статьи нет комментома это null скорее всего если исходить из логики Вашего комментария. Но что тогда пустой массив когда его применять? Или не применять никогда?
                  Или такой вопрос. Если идет создание сущности с фронтенду что обычно делают запросом POST. На бэк нужно прислать явно значения null или все же null возникнет при сохранении в базу данных? Или null не нужно хранить в базе данных если она документоориентированная?

                    +1

                    Из моего опыта null нужен обычно только в тех случаях, когда он уместен в конкретной ситуации и не может быть иначе выражен конкретным типом данных. Например у типа datetime нет ни какого подходящего значения, которое бы можно было во всех ситуациях использовать в качестве "нет значения". Тут только null подходит. Для целых чисел иногда можно использовать 0, если бизнес-логика не допускает использование нуля в качестве нормального значения. С пустыми строками аналогично.
                    В случае коментов пустой массив это нормальная абстракция для "ещё нет коментов". Хотя бы потому что коменты надо куда-то добавлять. В null их не добавишь, а потому это не очень подходящее значение. Только если не использовать его как признак того, что коменты запрещены и их нельзя добавлять вообще.
                    Я лично стараюсь избегать использовать null без необходимости, т.к. он добавляет проверок в программе.
                    Что и как надо посылать при создании сущности — зависит от того нужено этоили нет. Если null допустимое значение для поля, то почему бы его не послать в POST запросе. Но можно так же сделать null как дефолтное значение поля при создании сущности. И тогда это поле можно вообще не посылать.

                      0

                      Я время от времени размышляю над этими вопросами так как хочется сделать типа "руководства", чтобы в пределах проекта была один подход и как я понимаю что за пределами TODOAPP сталкиваешься со сложными вопросами. Вернее их сложность заключается в том что нужно соблюдать согласованность на бэке и фронте. И при этом не превратить работу в бег с препятствиями Например когда любая отправка данных с фронтенда будет проходить через какой то сложный маппинг. По идее пустой строки быть не должно. Но если у фронтенда есть пустой input то он часто сериализируется в пустую строку это проще всего. Иначе на фронте нужно давать полный анализ и преобразование всех поле ввода.
                      Про пустой массив я склоняюсь к тому что значение null и пустой массив инвариантны. И проще использовать пустой массив, так как если нужно добавлять значение то проще сделать это без проверко к пустому массиву. Только в этом случае неободимо чтобы на уровне системы всегда соблюдалось что поле типа массив не может иметь значение null. Чтобы уйти от проверок в стиле "если пусто создать массив и добавить значение, иначе добавить значение" или "если пусто или значение типа массив и количство элементов массива равно нулю".

                        +1

                        Тут обязательно надо разделять "схему" входных параметров, и "схему" того что возвращает API (о чём говорится в статье). Входные параметры могут иметь более свободную форму. Например можно сделать так, что отсутствие в запросе поля или если оно равно null, будет преобразованно десериализатором-валидатором в "значение по умолчанию" (пустой массив).
                        А вот в схеме возвращаемого результата лучше избегать вольностей. Если поле должно быть всегда массивом — всегда возвращать массив, даже если по каким то причинам этого массива нет в хранилище данных.

                          0

                          Я согласен. Так например делают в graphql. Но в традициях restfullapi в котором я не сильно верю как раз то что объект на вход и выход подается один и тот же. Более того многие считают что в отахвете нужно вернуть только ко измененные на бэк поля например сгенерированный идентификатор или время изменения объекта. На этом собственно есть много готовых тулзов. Тот же backbone хотя он уже не актуален.

                            +1

                            Про restfull вы несколько заблуждаетесь. Нет такого "правила" что возвращать надо то же что получил. Можно вообще получать в json, а отдавать в xml с другим набором полей. И в rest передают в обе стороны не сущности, а их некую репрезентацию. Какая при этом будет репрезентация — дело ваше, лишь бы её было достаточно для решения задач.
                            Но естественно часто просто удобнее если набор входных параметров является подмножеством результирующих свойств созданной сущности.
                            Возвращать только то что изменилось — это не удобно, нарушает схему ответа (разработчики клиентов вам точно спасибо не скажут), и накладно — если сущность была изменена другим клиентом, то придётся запрашивать её заново полностью, т.к. ваш запрос на изменение вернёт только то что он изменил.

                              0

                              Я же говорил что в restfull я не верю. Так как его не существует в отличие от rest. Потому что кроме tutorial todoapp по rest null нет никакой информации что и порождает вот те самые вопросы которые я озвучил. Даже неясно кто его первый придумал. Вроде бы это так было в ruby-on-rails. Но если посмотреть на реализации то наверное чаще всего будет именно такая реализация. Когда объект в том виде как он представлен на фронтенде и какой он был получен с бэка после изменений сохраняется в той же структуре как и пришел с бэка. Хотя тут как всегда возникают подробности если в объекте есть связанные сущности

                                +1

                                Насчёт RESTfull я с вами согласен — это просто абстрактный термин, которым принято обозначать архитектуру приложений, якобы сделанную по принципам REST. Особой конкретики в этом нет, т.к. следуя принципам REST можно сделать разные архитектуры. По моему опыту 95% разработчиков, к сожалению, вообще не понимают REST, но при этом пытаются делать приложения и писать статьи про всё это. В результате интернет заполнен кодом и текстами не соответствующими принципам REST. И это только ещё больше запутывает, особенно новичков.
                                Но тема null-ов и отсутствующих полей не имеет отношения к REST как таковому. Просто есть 3 разных состояний поля в сущности: поле есть и имеет значение, оно null или его вообще нет. И эти состояния можно использовать в разных целях, в зависимости от контекста. Какие из них использовать и где — обычно понять не сложно, просто не забывая, что это действительно 3 разных состояния, которые лучше не смешивать. А потом задаться вопросами: какие из этих состояний допустимы, что каждое их них означает в конкретном случае. И если получается, что два разных состояния означают одно и то-же, то это повод хорошо подумать, действительно ли именно это вы хотели.

                                  0

                                  Ну вот для меня как раз и не просто понять семантику чем пустой массив отличается о значение null и полного отсутсвия значения (отсутствия поля с таким именем) Но возможно это только моя личная проблема.

                                    0

                                    Пустой массив — комментариев пока нет, но можете добавить свой
                                    Null — в теории могли бы быть, но автор запретил
                                    нет значения — комментарии к этому материалу вообще не предусмотрены.


                                    В любом случае, семантика определяется автором API

                +1

                Самая частая проблема — Id ресурса генерирует сервер в ответ на запрос Post, если говорить о http apu. External ID — костыль для обхода этого факта. Есть uuid, есть составные ключи, например clientid+externalid, нет, обязательно надо весь мир заставлять использовать свои id. Причём, блин, целочисленные unsigned int, а в некоторых (большинстве?) языках unsigned типов нет скалярных. Делайте свой id строковым, если уж решили его экспозить на весь мир, хотя бы на этапе сериализации — ваши клиенты будут вам благодарны. Да и вы себе когда-нибудь спасибо скажете, возможно, решив перейти на uuid например

                  +1

                  Был такой случай с системой оплат. Нужно было генерировать уникальный номер заказа. Я его сделал числами, но естественно что-то было уже использовано мною же в процессе тестировнаия. И и потом пошли заказы с ошибкми дубликатов. Пришлось на ходу переделывать. Но вот GUID тоже не всегда хоршо. В том случае идентиыикатор заказа мало того что должен был быть униальным он еще должен быть человекопонятным так как по нему шли обращения в службу поддержки

                    0

                    В любом (почти) случае текстовый ид лучше числового в API и его генерациию лучше делать на клиенте

                    0
                    Самая частая проблема — Id ресурса генерирует сервер в ответ на запрос Post, если говорить о http apu. External ID — костыль для обхода этого факта.

                    Не совсем согласен с такой постановкой вопроса — internal_id и external_id решают разные задачи. Internal_id должен больше удовлетворять потребностям самой системы, где создается, external_id — потребителям API. У нас была интеграция, где сервис вынуждал нас генерировать internal_id на нашей стороне, предоставив в документации алгоритм генерации — это неудобно + это распыляет бизнес логику системы на внешних потребителей. Если представить, что internal_id используется, например, для шардинга данных в базе, то мы не сможем сменить логику генерации\шардинга не затронув потребителей API, которые поддержали генерацию id на своей стороне. Текстовый external_id — лучший выбор для потребителя, захочет — положит туда uuid в строковом представлении, а захочет — человекочитаемую альтернативу.
                      0

                      Я о ситуации, когда вы отправляете POST /orders, возможно с вашим id 45477 в теле, а в ответе получаете Location: /orders/123 или иным способом internal_id сервиса как "первичный ключ" ресурса. В терминах HTTP/"Rest" именно 123, internal_id основной идентификатор, а ваш external_id — так, для справки, может для контроля уникальности. Это заставляет клиента поддерживать постоянную связь его id с internal_id сервиса

                    0
                    По-моему первый пункт, это про тех, кто документацию не прочитал. И никогда тут проблем не возникало. Так же за уши можно притянуть и idempotency key.

                    Бывает возня так же с запросами, результаты которых появляются позднее текущего соединения.

                    Пункт 2… ну, можно и согласиться.

                    Пункт 3. Про защиту https сертификатом, это не совсем понял, кто и что защищает, когда методы открыты наружу. Так же не понял: по тексту вы то на стороне клиента, то на стороне сервера. От себя могу добавить, что встречал реализации, когда сервер ничего не отвечает, если не проходит формальная проверка (mime не тот, авторизация не прошла и т.п.). Просто игнорирует. Основание — не выполнение требований документации. А если авторизация пройдена, то сервер уже может пояснить что именно ему не понравилось, иначе — лесом.

                    по пункту 5 по-моему любые платежные данные запрещено сохранять в транзитной системе в любом виде, аудит не пройдет. А если такая необходимость сохранять подобные данные все таки есть, то система не должна хранить ключ для дешифровки данных. Причем это защищает так же от внутренних диверсий со стороны сотрудников.
                      0
                      По-моему первый пункт, это про тех, кто документацию не прочитал. И никогда тут проблем не возникало

                      Проблемы возникают тогда, когда у нас нет возможности однозначно идентифицировать созданный нами объект во внешней системе, но есть возможность создать такой же. В таких кейсах, если external_id обеспечивает уникальность только определенное время, мы вынуждены сохранять на своей стороне timestamp первой попытки создания (перед отправкой запроса), а в случае отвала, при последующих попытках проверять находимся ли мы в интервале, когда external_id еще не превратился в тыкву. При должном невезении и проблемах в сети с такой интеграцией легко отхватить множество объектов, которые нужно обрабатывать руками через тех-поддержку.
                      Про защиту https сертификатом, это не совсем понял, кто и что защищает, когда методы открыты наружу.

                      Отправитель колбека вынуждает получателя сформировать свою подпись полученной модели и вернуть её отправителю. Если это форма защиты — то мне не понятен механизм атаки. Кажется, сейчас уже подавляющее большинство платежных систем интегрируются исключительно по https, на ваш взгляд, этого не достаточно, чтобы идентифицировать получателя колбека?
                      сервер ничего не отвечает, если не проходит формальная проверка (mime не тот, авторизация не прошла и т.п.). Просто игнорирует

                      А можете чуть подробнее? Как именно «игнорирует»? 404, 408?
                      по-моему любые платежные данные запрещено сохранять в транзитной системе в любом виде, аудит не пройдет

                      Да, интересный вопрос, как эта неназванная платежная система проходила аудит.
                      А если такая необходимость сохранять подобные данные все таки есть, то система не должна хранить ключ для дешифровки данных

                      В этом ведь и суть: платежная система отдавала интегратору хэш номера карты как её идентификатор для дальнейшего использования, ну и маскированный номер, до кучи.

                        0
                        Кажется, сейчас уже подавляющее большинство платежных систем интегрируются исключительно по https, на ваш взгляд, этого не достаточно, чтобы идентифицировать получателя колбека?

                        Я не так вас понял. Да, вполне достаточно.
                        Еще в копилку: за рубежом встречал просто фильтр по IP, даже без авторизаций.

                        А можете чуть подробнее? Как именно «игнорирует»? 404, 408?

                        Да, 404. Правда это раздражает разработчиков, которые привыкли работать с API у которого ноги раздвинуты, зато усложняет исследование API злоумышленниками, если такой доступ возможен.

                        На стороне сервера проверка примерно такая: если совпало наименование метода, присутствуют обязательные параметры и заголовки в запросе, то запрос передается в обработчик, если что-то не совпало, то сервер говорит 404, до скриптов дело даже не доходит, формальные проверки на уровне веб-сервера.
                          0

                          Он хэш номера карты это кажется обычная практика чтобы привязать карту к клиенту и таким образом дать возможность оплачивать не вводя номер карты что должно защитить клиента. Обычно процессинг дает разрешение на использование такого api после аудита информационной системы. Хотя наверное есть такие которые дают всем подряд. С учетом того что все равно транзакция требует подтверждения клиентом это не выглядит слишком не секьюрно.

                            0

                            Точно есть (ну или были года три назад) такие, которые всем давали. Но вот про хэши они ничего не говорили, давали токен для списаний. Без подтверждений, кстати.

                              0

                              Это уже за гранью моего понимания.

                                0

                                Для подписок ака реккурентные платежи это обычное дело — один раз подтверждаешь и всё.

                      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                      Самое читаемое