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

Мы были стартапом в строго регулируемой отрасли, поэтому у нашей платежной интеграции были очень специфические требования. От нас требовалось не только поддерживать 3D Secure, но и отклонять любую транзакцию, не связанную с 3D. Это должно было быть 3D Secure, и вы не могли использовать кредитную карту. Только дебетовые карты.

Наш партнер по обработке платежей не мог справиться с этим «из коробки», поэтому нам пришлось выполнить некоторую работу на нашей стороне с двухэтапным потоком платежей: авторизация, а затем захват.

Интеграция была сложной задачей, и из-за плохой документации нам пришлось начинать все заново один или два раза, но в итоге мы добились того, чего должны были достичь. Соответствие порадовало. Команда продукта была счастлива. Разработчики были счастливы. А потом QA сказали, что не могут платить за Chrome. Firefox и Safari были в порядке, но Chrome не работал.

Я запустил платформу локально и попытался произвести оплату. Все прошло без проблем. Хм.

Команда работала удаленно из-за COVID-19, поэтому я зашел в чат Discord с QA и скрам-мастером. Мы прошли этот процесс вместе, используя промежуточную среду, а не какой-то локальный экземпляр. Мы ввели одни и те же данные одновременно — и когда мы нажали «Отправить», мой платеж прошел, а платеж от QA — нет. Они были отклонены на этапе 3D Secure.

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

Теперь мой платеж был отклонен. Хм.

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

Это не удавалось всегда и только в том случае, если вы использовали браузер Chrome со встроенным дисплеем на MacBook Pro.

Сетевая панель в DevTools показала, что в конечном итоге оказалось проблемой. Как и большинство устройств за последние 20 лет, внешний монитор поддерживал 24-битный цвет. Это по 8 бит для красного, зеленого и синего цветов. Но дисплей MacBook Pro с его широкой гаммой поддерживает 10 бит на канал, что в общей сложности составляет 30. В рамках снятия отпечатков пальцев браузера, используемого 3D Secure v2, глубина цвета браузера отправляется в запросе авторизации.

Проблема в том, что в то время как спецификация CSSOM говорит, что Screen.colorDepth является беззнаковым длинным (поэтому может иметь любое значение от нуля до четырех миллиардов), спецификация 3D Secure v2 говорит, что это значение является строковым перечислением. и может иметь только значения 1, 4, 8, 15, 16, 24, 32 или 48. Согласно спецификации 3D Secure, 30 не является допустимой битовой глубиной.

Firefox и Safari справляются с этим, всегда возвращая 24 из Screen.colorDepth (что, согласно спецификации CSSOM, разрешено по «соображениям совместимости»), но Chrome настаивал на возврате истинного значения 30, и это полностью сломалось. наш поток платежей.

Я связался с другом из команды Chrome DevRel, и он предложил мне сообщить об ошибке, что я и сделал. За исключением того, что Chrome технически не ошибается. Битовая глубина экрана на самом деле равна 30. Проблема заключается в спецификации 3D Secure v2, утверждающей, что 30 – недопустимая битовая глубина, хотя это действительно так.

Как только люди из команды Chrome поняли проблему, они должным образом забеспокоились. Я закончил тем, что переписывался по электронной почте с кем-то, кто, казалось, знал, что он делает. К сожалению, сказал он мне, изменение в Chrome, которое привело к тому, что он сообщил о 30-битной глубине цвета, появилось несколькими месяцами ранее, и поэтому они не смогли отменить его. Он предложил мне поговорить с моим платежным провайдером, чтобы узнать, могут ли они быть более гибкими. Понятно, что они отказались, сославшись на спецификацию 3D Secure v2, в которой говорилось, что они верны.

Команда Chrome обратилась к EMVCo, чтобы узнать, могут ли они обновить неверную спецификацию 3D Secure. Насколько я понимаю, EMVCo не смогла обновить спецификацию, но согласилась разослать разработчикам уведомление об этой потенциальной ошибке.

Могли пройти недели или месяцы, прежде чем клиентские библиотеки для моего конкретного платежного провайдера были обновлены, поэтому мне пришлось самостоятельно исправлять проблему. В конце концов, мы отправили что-то вроде этого:

const handler = {
  get(target, prop) {
    const propsToPatch = ["colorDepth", "pixelDepth"];
    if (propsToPatch.includes(prop) === false) {
      return target[prop];
    }
    
    const normal = [48, 32, 24, 16, 15, 8, 4, 2, 1];
    return normal.find((depth) => depth <= target[prop]) || 24;
  }
};
if ("Proxy" in window) {
  window.screen = new Proxy(window.screen, handler);
}

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

Эпилог

У меня есть своя теория, как эта ошибка закралась в спецификацию 3D Secure v2.

По данным Wayback Machine, где-то между августом и ноябрем 2013 года на странице W3Schools в API Screen.colorDepth было внесено обновление. Это изменение (неверно) перечисляло возможные возвращаемые значения как 1, 4, 8, 15, 16, 24, 32 и 48.

Затем авторы 3D Secure v2 восприняли это как неопровержимый факт и включили в свой стандарт в виде перечисления. Ничто из этого никого не беспокоило в то время, и не беспокоило, пока Apple не начала поставлять дисплеи с широкой цветовой гаммой.

Забавно, как иногда бывает в жизни.