ESPN DeportesVladi Jr. se disculpa por comentarios de contratoRTP DesportoRenato Veiga sobre Haaland. "É um excelente jogador mas não me tira o sono"PunchNigeria Open golf tournament returns in 2027 after 28 yearsESPNTexas-Tennessee: The battle for UT, SEC orange and Manning loyaltyInquirerPalace to Sara Duterte: Respond with evidence, stop using fallaciesThe Jerusalem PostWhy Joseph Hotel TLV offers comfort, community, great value - reviewBollywood HungamaEXCLUSIVE: Yash Raj Films bags overseas distribution rights of Sooraj Barjatya’s Yeh Prem Mol LiyaSRF NewsBesuch in Paris – Papst Leo XIV. und die französische LaizitätWirtualna PolskaSkradzione busy i motocykle warte 186 tys. zł. Dwóch paserów z zarzutamiBBC News BrasilPor que a Bíblia evangélica é diferente da católicaRai NewsLo sfratto di Maricarmen, la crisi abitativa in Spagna e le tensioni nel governo Sanchez7sur7Les lignes de métro 2 et 6 partiellement interrompues en raison d’un dégagement de fumée
The Daily Newsstand · Free, Always
Saturday, September 26, 2026

Как мы создали Open-Source альтернативу закрытым платёжным системам на React, Node.js и WebNFC

Translate

Представьте парк аттракционов, фуд-корт на фестивале, корпоративную столовую или сеть автомоек. Что их объединяет? Всем им нужна своя закрытая платёжная система или внутренняя программа лояльности (клубные карты).

Обычно внедрение такой системы — это боль. Нужно покупать дорогие терминалы, нанимать интеграторов и пилить самописный софт. А что делать, если система нужна «на один раз» (для трёхдневного фестиваля) или у малого бизнеса просто нет бюджета?

Меня зовут Никита, и я решил закрыть эту боль, заодно прокачав своё портфолио. Я разработал легковесную, open-source альтернативу закрытым платёжкам, которая работает прямо в браузере, не требует скачивания приложений и сложного железа.

Под капотом — классический стек React + Node.js и капелька технологии в виде WebNFC.

🛠 Архитектура и технические костыли (с пользой для юзера)

Главная идея проекта — превратить любой обычный смартфон в платёжный терминал без установки лишнего софта. Всё работает через веб-приложение.

Основной упор был сделан на технологию WebNFC, которая позволяет считывать физические NFC-метки и карты прямо из браузера. Но тут мы сразу сталкиваемся с суровой реальностью: WebNFC сейчас поддерживается только в Chrome на Android.

if (!("NDEFReader" in window)) {
      alert(
        "Ошибка: Web NFC не поддерживается. Используйте Chrome на Android и HTTPS.",
      );
      return;
    }
    ndef.current = new NDEFReader();
    ndef.current.onreading = async ({ message, serialNumber }) => {
      if (readingLock.current) return;
      setIsReading(true);
      readingLock.current = true;
      let cardObject = null;

      try {
        setUid(serialNumber);
        for (const record of message.records) {
          if (record.recordType !== "text") continue;
          const decoder = new TextDecoder();

          const textData = decoder.decode(record.data);
          try {
            cardObject = JSON.parse(textData);
            setCardData(cardObject);
          } catch {
            console.log("Not JSON");
            return;
          }

          console.log(
            `UID: ${serialNumber} \n Balance: ${cardObject.balance}  \n Name: ${cardObject.name}`,
          );
          addLog("read", serialNumber, cardObject.balance);
        }
      } catch (e) {
        console.log("ошибка в карте", e);
      } finally {
        setIsReading(false);
        readingLock.current = false;
      }
    };

Оставлять пользователей iOS за бортом было нельзя, поэтому я придумал элегантный фолбек:

  • Для Android: Терминал (смартфон администратора/кассира) считывает карту клиента в один клик через WebNFC по её уникальному идентификатору (UID).

  • Для iOS: На карте генерируется или печатается QR-код, содержащий тот же UID. Кассир просто сканирует его камерой внутри того же веб-приложения.

Как ходят данные:

На этапе MVP я не стал перегружать архитектуру тяжелыми СУБД, поэтому схема выглядит максимально лаконично:

Frontend (React) — отправляет UID карты и сумму —> Backend (Node.js) — парсит данные и проводит транзакцию —> JSON DB (быстрое файловое хранилище на первое время).

💼 В чём профит для бизнеса?

Зачем владельцу условной автомойки или кофейни внедрять такое решение вместо обычного банковского эквайринга? Я выделил 5 главных причин:

  • Деньги сразу в обороте. Клиент покупает локальную валюту или абонемент заранее. Деньги уже у вас в кассе, а услугу вы окажете потом. Это гарантирует, что клиент вернётся именно к вам, а не к конкурентам.

  • Психология «виртуальных денег». Тратить внутренние баллы или баланс с клубной карты психологически гораздо проще, чем расставаться с реальными рублями на балансе банка. Это кратно поднимает средний чек.

  • Прощай, грабительский эквайринг. Меньше транзакций через банк — меньше комиссий платежным системам. Бизнес экономит на каждом чихе.

  • Доступность «из коробки». Не нужно покупать терминалы за десятки тысяч рублей и оплачивать лицензии на софт. Нужен только копеечный набор NFC-карт (или распечатанных QR-кодов) и любой смартфон.

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

🚀 Что дальше и где посмотреть код?

Получилась гибкая open-source система, которую можно развернуть за полчаса и адаптировать под задачи конкретного локального бизнеса или мероприятия.

В планах — прикрутить полноценную базу данных (MongoDB) вместо JSON-файлов, расширить ролевую модель (суперадмин, кассир, клиент) и добавить более красивую аналитику в реальном времени.

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

👉 Ссылка на мой GitHub: https://github.com/lqdope/NFCwebPay/

Буду рад фидбеку в комментариях: взлетит ли такая альтернатива «на коленке» для мелкого бизнеса или WebNFC в вебе — всё ещё слишком нестабильная история?

View the original on Хабр →

KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.