Open source
Payment systems
Programming
Distributed systems
Cryptocurrencies
May 20

Как запустить микро-платежи в своем приложении

Tutorial
Прошлую неделю я провёл разрабатывая свою первую публичную программку — Telegram-бота который работает в качестве Bitcoin-кошелька и позволяет «бросать монетки» другим участникам групповых чатов а так же совершать внешние Bitcoin-платежи себе или другим т.н. “Lightning Apps”. Подразумеваю, что в целом читатель знаком c Bitcoin и Telegram, т.к. буду стараться писать кратко, не вникая в детали. Выборка ресурсов о Bitcoin доступна по этой ссылке, ну а Телеграм это средство мгновенного обмена сообщениями на мобильных устройства и пк, позволяющее на их платформе создавать свои небольшие приложения (чат-ботов).

Какие ключевые функции этого приложения?


  • Позволяет отблагодарить или поощрить собеседника действительно материальной ценностью, а не просто «виртуальным лайком», это выводит электронное общение на совершенно новый уровень
  • Реальный пример приложения, которое может финансово взаимодействовать с другими приложениями используя открытый платёжный протокол
  • Все компоненты приложения являются проектами с открытым исходным кодом и их возможно изменять и применять для своих задач. Приложение не использует закрытых и\или коммерческих решений, несмотря на то, что подходит под сферу электронной коммерции, которую на сегодняшний день назвать открытой довольно сложно.

И как это применить в деле?


как-то так…

image

или так…

image

Что значат эти числа? Это единицы биткойна — сатоши. По биржевым ценам на середину 2019 года 1000 сатоши это приблизительно $0.06. Сатоши можно потратить онлайн или легко обменять на национальную валюту. Именно по этой причине (ликвидность) это не просто очередная «запись в БД» или «баллы, которые вы можете потратить в магазинах наших партнёров», а самые настоящие интернет деньги.
*сразу хочу отметить, что некоторые слова или словосочетания заведомо не пытаюсь писать на русском — либо не знаю сам контекстного перевода в некоторых случаях, либо такой перевод на мой взгляд звучит скорее заблуждающим, чем корректным

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

imageimageimage

Когда разжились биткойнами (а скорее сатош-ами(?)) — запускаем @atomic_tipbot и делаем /deposit.

Бот в ответ спросит сколько сатоши внести на баланс и пришлет к оплате счёт, который можно оплатить мобильным кошельком отсканировав QR-код или нажав кнопку («открыть в кошельке») на странице инвойса.

imageimageimage

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

До этого момента мы затронули две компоненты приложения — back-end telegram-бота и систему процессинга платежей криптовалютой (скорее ее front-end).

Для создания Telegram-бота я использовал Python Telegram Bot. Ну и конечно, сам бот с открытым исходным кодом.

В качестве системы процессинга платежей и решения UX-задач выбор пал (на самом деле, выбора почти нет) на BTCPayServer, на который документация по этой ссылке.

Если искать решения обработки событий платежей напрямую, то стоит обратить внимание на Electrum CLI manual pages для т.н. on-chain платажей, а для мгновенных микро-платежей (Lightning) решения вроде Lightning-charge или Sparko.

Так оно self-hosted или нет!?


Да, это так. В своем приложении я использую бесплатный хостинг BTCPayServer — BTCPayJungle, но это абсолютно возможно и не требует много времени что бы запустить собственный процессинговый центр. Однако стоит помнить, что Вам будут необходимы ~500GB доступные на ЖД для хранения блокчейнов Bitcoin (и Litecoin). Так же потребуются вычислительные и сетевые ресурсы на время синхронизации и в зависимости от ваших нужд необходимые «обёртки» для блокчейнов, что бы было с ними удобно взаимодействовать. BTCPayServer не содержит by-design приватных ключей или любой другой информации, позволяющей контролировать ваш кошелек.

Для on-chain (обычных) платежей необходимо добавить т.н. xpub-ключ кошелька в BTCPayServer для генерации адресов на получение (публичных ключей). Это делает возможным и безопасным использовать BTCPayServer даже в «расшареном» виде. Все эти xpub-ключи и адреса можно легко получить в хороших кошельках вроде Electrum, для дальнейшей настройки процессинга.

Все кто сталкивался с Bitcoin слышали про то, что биткойн медленный и тразакции дорогие, особенно когда хайп, поэтому для мгновенных микро-платежей стоит использовать (off-chain) транзакции Lightning Network.

Что это за Lightning Network?


Это что-то вроде плагина к биткойну, который надстраивает дополнительный слой абстракции и позволяет совершать мгновенные и практически бесплатные транзакции в сети Bitcoin. Всё это возможно благодаря криптографической магии с мульти-подписями, чаще называемой смарт-контракт. Есть несколько реализаций протокола Lightning Network, я использую c-lightning реализацию на C.

Основное различие между on-chain и off-chain (Lightning) платежами в том, как они хранятся. Все on-chain транзакции ретранслируются по всем узлам сети, пока одна из нод биткойна нод не анонсирует новый намайненый блок с включенными в него транзакциями. (На хабре даже есть BitFury, они как раз производят железо для майнинга). Именно по этой причине мы называем такие транзакции on-chain. Такие транзакции буду записаны в общий блокчейн и сохранятся в нём до конца его существования. Так же такие транзакции находятся в общем доступе и их возможно мониторить, отслеживать и анализировать.

В случае off-chain (Lightning) транзакций все происходит иначе. Каждая нода Lightning-сети имее один (или более) on-chain адресов — это те самые обычные адреса биткойна, с которых все и начинается. Когда появляются биткойны на таком адресе возможно открыть т.н. lightning канал к другой ноде, что значит, что при помощи криптографических техник эти две ноды договорились о доступности средств между собой. Позже, все транзакции (как свои, так и транзакции посторонних нод) что проходят через такой канал сохраняются только на нодах участников транзакции (и нодах-посредниках). Единственные транзакции, которые записываются в блокчейн — это транзакции об открытии или закрытии канала. В более полном масштабе получается такая сеть взаимо-подключенных нод которая строит между участниками тысячи различных путей для «переливания» средств из одной ноды в другую по определенным жестким правилам. Практически все такие транзакции хранятся только в файле БД самих нод. Именно по этой причине мы их называем off-chain.

На диаграме видна визуализация всех компонент приложения


image

Вкратце:

1) Пользователь отправляет комунду боту в Telegram

2) Telegram отправляет сообщение о событии python приложению бота

3) python-приложение отправляет запрос на BTCPayServer

4) BTCPayServer генерирует входящие адреса BTC и LTC, а так же отправляет запрос на инвойс к lightning-charge, который в свою очередь общается с демоном c-lightning. На выходе мы получаем красивую HTML-форму обработки платежа

5) Пользователь оплачивает инвойс и видит подтверждение

6) BTCPayServer отправляет уведомление оплаты инвойса (IPN) на указанный callback_url, в нашем случае callbacks.py (еще одно python приложении для получения уведомлений)

7) Согласно полученным данным баланс пользователя изменяется

… и если пользователь захотел вывести средства (монетки что получил от других) …

8) Python-приложение бота ждет текста или картинки QR-кода, и когда валидные данные получены то совершается платёж утилитойpaylightning.py*, которая инициализирует транзакцию посредством c-lightning RPC.

image

*Справедливости ради, стоит отметить что моя обертка paylightning.py не совсем закончена и может вернуть ложно-отрицательный результат в специфических сценариях. Рекомендую к использованию хорошо отлаженную обертку в случаях приближенных к продакшену

Что мы имеем? Пользователи нашего бота могут внутри группового чата или напрямую могут передавать материальную ценность без ограничений. И это еще не все — благодаря протоколу Lightning Network, который в некотором смысле является унифицированной и упрощенной платежной сетью, появляется возможность отправлять те же деньги что получены в чате совершенно сторонним приложениям. Например, в Telegram уже был первый бот для биткойн-платежей — @lntxbot. «Единый баланс» позволяет использовать любого из ботов в зависимости от предпочтений конкретного группового чата (да, проблема абсолютно надуманна и решение весьма бесполезно, но дело в сути). Перекинуть баланс с одного бота на другой ничего не стоит и занимает секунды! Важно заметить, что создатели этих ботов вообще никогда не связывались между собой.

image image

Достаточно просто переправить сообщение с запросом на депозит от @lntxbot к @atomic_tipbot и voul’a!

Это уже не просто «перебрасывание чисел в БД» а платёж между двумя разными, не связанными между собой БД. Таким образом в бот интегрировано уже несколько «LApps», среди них: sat2.io и lnsms.world.

Что значит интеграция LApp?

Каждая Lightning Network нода это по своей сути биткойн-кошелек. У нее два баланса: on-chain и off-chain. On-chain баланс необходим для открытия новых каналов и хранения средств с закрытых каналов. А вот off-chain баланс как раз то, что ходит между нодами. Основные требования к ноде это получение и отправка платежей, поэтому каждая нода как ПО имеет функции pay и invoice. Я продемонстрирую как это происходит:

user@ln-node:/ % lightning-cli invoice 1000000000 internal_description external_description
{
   "payment_hash" : "64c0c8f5f8f708b08487ad1376f3d256f92ccb9606987ba395c2b4193efde5a0",
   "expires_at" : 1558709834,
   "bolt11" : "lnbc10m1pwdaj72pp5vnqv3a0c7uytppy845fhdu7j2mujejukq6v8hgu4c26pj0hauksqdpqv4u8getjdeskchmyv4ekxunfwp6xjmmwxqyjw5qcqp2rzjqw3qcrp2u3ggke56wrjlstcg76drmfw680cvjum88sl7ja7mpas7xzxz8sqqfecqqyqqqqlgqqqqqqgqjqn8e3ml733dkms2txxldnuwsllwhhkldss73268hdka3e7f083vjhjqurwqrndqd2rcd85pw7vkywnr4dq7yfd59r0g2mw4wzztwr6dcprxzej6"
}

Мы получаем «инвойс» спецификации BOLT11 в этом случае, которая является форматом платёжек в сети Bitcoin (и Litecoin, к слову, тоже). Это строка, содержащая в себе информацию о получателе, сумме транзакции, время истечения запроса итп., подробную информацию можно получить запустив decodepay в отношении данной строки:

user@ln-node:/ % lightning-cli decodepay lnbc10m1pwdaj72pp5vnqv3a0c7uytppy845fhdu7j2mujejukq6v8hgu4c26pj0hauksqdpqv4u8getjdeskchmyv4ekxunfwp6xjmmwxqyjw5qcqp2rzjqw3qcrp2u3ggke56wrjlstcg76drmfw680cvjum88sl7ja7mpas7xzxz8sqqfecqqyqqqqlgqqqqqqgqjqn8e3ml733dkms2txxldnuwsllwhhkldss73268hdka3e7f083vjhjqurwqrndqd2rcd85pw7vkywnr4dq7yfd59r0g2mw4wzztwr6dcprxzej6
{
   "currency" : "bc",
   "created_at" : 1558105034,
   "expiry" : 604800,
   "payee" : "025a14b8ed40583d67aec92da19453e0b2d1fbbf75f96f85d3dd0ff61a51ee0490",
   "msatoshi" : 1000000000,
   "amount_msat" : "1000000000msat",
   "description" : "external_description",
   "min_final_cltv_expiry" : 10,
   "routes" : [
      [
         {
            "pubkey" : "03a20c0c2ae4508b669a70e5f82f08f69a3da5da3bf0c973673c3fe977db0f61e3",
            "short_channel_id" : "574012x1255x1",
            "fee_base_msat" : 1000,
            "fee_proportional_millionths" : 1,
            "cltv_expiry_delta" : 144
         }
      ]
   ],
   "payment_hash" : "64c0c8f5f8f708b08487ad1376f3d256f92ccb9606987ba395c2b4193efde5a0",
   "signature" : "304502210099f31dffd18b6db8296637db3e3a1ffbaf7b7db087a2ad1eedb7639f25e78b25022079038370073681aa1e1a7a05de6588e98ead078896d0a37a15b755c212dc3d37"
}

А вот для оплаты такого «инвойса» нам необходима функция pay от c-lightning:

user@ln-node:/ % lightning-cli pay lnbc2u1pwdana3pp5c0nyfgq974hr8huflt9uutyalj4maaw3q5594xp89jkvw74jme3sdql2pshjgr5dus8q6r0dejjqampd3kx2aqcqzpgwjgn45gy80jwjhgm3tpsxg33j6h6pehdus0mnjerrad943cz3vs83g30lyhlhfjxtqvtl76vttkuhs5jekuxpsqmf98l8265pwmm76gp4e7z6j
{
   "id" : 163,
   "payment_hash" : "c3e644a005f56e33df89facbce2c9dfcabbef5d105285a98272cacc77ab2de63",
   "destination" : "03021c5f5f57322740e4ee6936452add19dc7ea7ccf90635f95119ab82a62ae268",
   "msatoshi" : 200000,
   "amount_msat" : "200000msat",
   "msatoshi_sent" : 200003,
   "amount_sent_msat" : "200003msat",
   "created_at" : 1558106072,
   "status" : "complete",
   "payment_preimage" : "1a9552b9f4e9199e26839353f870b12cc85b7674a2fb134e78aa370032611019",
   "bolt11" : "lnbc2u1pwdana3pp5c0nyfgq974hr8huflt9uutyalj4maaw3q5594xp89jkvw74jme3sdql2pshjgr5dus8q6r0dejjqampd3kx2aqcqzpgwjgn45gy80jwjhgm3tpsxg33j6h6pehdus0mnjerrad943cz3vs83g30lyhlhfjxtqvtl76vttkuhs5jekuxpsqmf98l8265pwmm76gp4e7z6j"
}

Все состояния транзакций обрабатывает вся сеть и ваша собственная нода в частности. Нет необходимости (но есть такая возможность) взаимодействовать с авторами приложения-получателя платежа или каким либо их API для получения информации о состоянии платежа. Нет нужды проверять состояние платежа в системе процессинга-посредника. Не нужно ожидать одобрения вашей транзакции со стороны банка или государственного учреждения. В конце концов тут нет чардж-бэков и других внешних факторов, которые могут повлиять на остаток на вашем счету. Это решение для перемещения ценности посредством сети Интернет, без необходимости стороннего арбитра, который решает соответсвует транзакция правилам (порой даже внутренним правилам, о которых не все участники проинформированы заранее) или нет. В биткойне нет «внутренних правил». Единственные правила которым нужно следовать это правила сети — открытые и всем известные.

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

Если у вас есть возможность получить данные оплаты от стороннего приложения, то появляется возможность взаимодействовать в финансовом смысле с этим приложением. В качестве примера можно рассмотреть сервис lnsms.world, который позволяет отправить SMS-сообщение на произвольный номер, оплатив его биткойнами. Для интеграции этого приложения нам нужно всего лишь передать данные из веб-формы напрямую скрипту отправки смс — эмулировать действия пользователя на веб-сайте:

payload = {                                                                                                                           
        'number': phone_number,
        'text': text,
        'force_unicode': 0
    }

    send_req = requests.post('https://lnsms.world/invoice', data=payload)
    if send_req.status_code == 201:
        plain_invoice = str(send_req.text)

Не знаю по какой причине, но lnsms отвечает 201 HTTP status-кодом и возвращает инвойс формата BOLT11. Что ж, этого достаточно что бы пользователи бота могли произвести оплату за отправку смс со своего баланса, полученного в чатиках. Пользователь даже не знает что там происходит, он просто заплатил пару центов за отправку смс. И я, как разработчки не вдаюсь в подробности работы смс-сервисов. Просто переправил платеж и получил услугу. Можно сверху наценку сделать, а можно и не делать — это мой свободный выбор. Никаких аккаунтов, соглашений и очередного API для пустяковых задач вроде отправки смс.

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

Если Вам понравился пост и Вы хотите попробовать биткойны в действии — зашлите мне на кружку пива, да мягких французских булочек.
Интерестны ли Вам сугубо-технические статьи про биткойн и смежное ПО?
83.3% Да 25
13.3% Нет 4
3.3% Мне больше интерестны рыночные аспекты криптовалюты 1
30 users voted. 6 users abstained.

+15
3.6k 47
Comments 11
Top of the day