Ястремський

Кейс · під NDA, описано узагальнено

Платежі, кліринг і податкові документи

Токенізація карток, кліринг з ключами ідемпотентності та бухгалтерський експорт — у домені, де бути приблизно правим означає бути неправим.

  • Laravel
  • bcmath
  • Ключі ідемпотентності
  • Токенізація за PCI
  • Підписані вебхуки
  • ПДВ

Контекст

Фінансова підсистема всередині бізнес-платформи: приймає оплати картками, проводить кліринг через провайдера, випускає бухгалтерські та податкові документи, які з цього випливають, і зводить усе назад, коли надходять вебхуки провайдера. Мультивалютність, а правила ПДВ залежать від того, що продано і кому.

Обмеження

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

Рішення

Що обрано

Гроші — десяткові рядки через bcmath, ніколи не float. Кожна клірингова транзакція несе ключ ідемпотентності, тож повтор доказово є тією самою операцією, а не другою. Дані картки токенізовані: зберігаються й повертаються токени, номери карток — ніколи. Випуск документів — усе або нічого, а звірка працює від підписаних вебхуків, а не від оптимізму.

Що відхилено

Довіряти відповіді провайдера як джерелу істини, а вебхук вважати сповіщенням. Це простіше, і це працює — доки відповідь не загубиться дорогою. Тоді локальний запис і запис провайдера розходяться, і лише один з них тримає гроші клієнта.

Компроміс

Ключі ідемпотентності та підписана звірка додають стан, який треба зберігати, прострочувати й тримати в голові, і перетворюють просте списання на багатокроковий процес. Це ціна можливості відповісти «чи це насправді сталося?», нікому не телефонуючи.

Неочевидна частина

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

Результат

Працює у продакшні, разом із регулярними списаннями та прямим дебетом, побудованими на тому самому фундаменті, і зі звіркою, яка замикає цикл без ручного втручання.