В Словении по указанию ЕС несколько лет назад начали борьбу со всем малым бизнесом и даже физлицами. Можно сказать стоят на страже как бы лишний евро в страну не зашёл. За переводы от физика к физику прилетает письмо из банка: “А ну-ка рассказывай каким бизнесом занимаешься”. Лезут просто в трусы в буквальном смысле. В России в последние годы творилась такая же вакханалия с закрытием счетов, при этом в России “бангстеры” стригли комиссии до 20%. В Словении хотя бы деньги без комиссий переводили. Сначала Unicredit и прочие брендовые банки нам отказали в счёте, потом и ноунейм банк. Вместе с корпоративным счётом закрывают и личный. Но есть лайфхак пункт 2 статьи 16 директивы 2014/92/EU, где сказано, что личный базовый счёт обязаны открывать даже бомжам и лицам, высылка которых невозможна.

2. Государства-члены должны гарантировать, что потребители, проживающие в Союзе на законных основаниях, включая потребителей без фиксированного адреса и лиц, ищущих убежища, а также потребителей, которым не предоставлен вид на жительство, но высылка которых невозможна по юридическим или фактическим причинам, имели право открывать и использовать платежный счет с базовыми функциями в кредитных организациях, расположенных на их территории. Такое право действует независимо от места жительства потребителя.

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

Государства-члены должны гарантировать, что осуществление права не будет слишком трудным или обременительным для потребителя.

В результате мы перешли в Revolut. А в Revolut нет ни загрузки платёжных поручений в XML, ни выгрузки выписок в XML, зато есть API. 

Интегрироваться нужно было с самописной CRM, которая у нас с 2004 года постоянно дорабатывается на PHP.

Недостатки API Revolut банка

  1. Scope не работает. Когда запрашиваем READ, то почему-то можно и писать, и отправлять платежи. Это позор! На мою претензию Revolut просто убрал scope из документации.

  2. Нет возможности отозвать токен. Опять позор! Токен можно перезапросить и не сохранить, тогда старый перестанет действовать. Но это какой-то костыль.

  3. Нет возможности в справочник контрагентов добавить второй счёт IBAN, хотя в ЛК на сайте такая возможность есть. Отправка платёжек работает через uuid контрагентов и uuid их IBAN. Удалить контрагента и создать заново с его счетами плохое решение, так как старые платежи же привязаны к старым UUID.

  4. Можно создать несколько контрагентов с одинаковым именем и разными счетами. И получить бардак в данных.  У контрагента нет уникального поля вроде ИНН или регистрационного номера, чтобы можно было различить две разных компании Ромашка.

  5. Нет отдельной подписи платёжек. Когда отправляем платёж, то он сразу улетает в оплату — раздолье для хакеров. Нужен отдельный канал для подписи платёжек. Это может быть SMS, а лучше числовой генератор ENUM (TOTP).

Вот скрин, где запрашивался scope READ и Revolut отвечает, что будет выдан токен на чтение и на отправку платежей.

Думал может это просто интерфейс такой, верстальщики так сверстали. Так же не может быть в банке с 10М+ клиентов. Это же какой-то бред. Однако, работало на запись отлично! 

В российских банках API работает по уму. Через DirectBank можно читать данные и писать — отправлять платёжки, зарплатные ведомости и что там ещё API DirectBank позволяет. При этом деньги никуда не отправятся пока лицо с правом подписи не зайдёт в ЛК или приложение и не подпишет платёжки. Подписание платёжек производится через независимый канал — СМС. Есть тут минус, что зависим от сотовой сети, СМСки не всегда доставляются оперативно. Значительно лучше бы использовать ENUM (TOTP) или аппаратный токен. 

А у Revolut получаешь токен и отправляй платежи. Токен удобно хранить, чтобы не авторизовываться руками каждый раз. Однако, хранить токен с правами на отправку денег рискованно. Лично я так делать не стал. 

Почему пришлось изобретать велосипед

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

Во-вторых, этот модуль тянул с собой много зависимостей других модулей. А Revolut имеет такую особенность, что на непротиворечивые на первый взгляд параметры запроса отвечает:

Client error: resulted in a response: {"message":"Required 'profile id' is missing","code":2101}

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

const REVOLUT_PRODUCTION_ENDPOINT = 'https://b2b.revolut.com/api/';

на 'http://localhost:12345' и вывести запрос в консоль. В нашем модуле адрес apiUrl вынесен в параметры.

Ну и третье, что было важно, конкретно нам, это необходимость иметь модуль в старом проекте на php 5.6.

Поскольку ничего хитрого в отправке параметров нет и никакой математики внутри этого модуля Revolut-php тоже нет, то решил написать свой, но чтобы всё было в рамках одного файла, одного класса. Тут важен вопрос безопасности ещё, чтобы можно было проверить быстро исходный код, что в нём нет никаких закладок.

Наше решение Revolut-php

Итак, вот наш модуль Revolut-php. Всё в одном файле

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

При этом мы не рекомендуем их сохранять, если есть риск, что кто-то может ими воспользоваться кроме вас.

Благодарности

Отдельное спасибо Павлу @maslick Маслову за помощь в тестировании и наведении порядка в нашем модуле Revolut-php. Павел специализируется на разработке облачных решений в AWS, любит open-source и открыт к новым челленджам и интересным предложениям.