Принимаем оплату в bitcoin: Часть четвертая, ближе к делу!

Принимаем оплату в bitcoin: Часть четвертая, ближе к делу!

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

Для понимания дальнейших шагов, желательно хотя бы базовое представление, что такое JSON и как работает протокол http. 

Если говорить коротко - то JSON (JavaScript Object Notation) - это простой формат, позволяющий передать разнородные данные в виде простого текста. Важно, что JSON позволяет не только передать данные, но и восстановить их структуру. Изначально (как несложно догадаться из названия) он был предложен для языка JavaScript, но из-за простоты стал достаточно распространенным. Нам он интересен в первую очередь потому, что именно такой форма данных используется при обмене информацией между нашим кодом и bitcoind.

Я не буду приводить примеры готового кода. Для web-разработки сейчас применяются разные языки программирования, и расписывать примеры на каждом из них выходит за объем и рамки нашего цикла статей. Вместо этого я буду показывать результат выполнения с помощью curl - утилиты командной строки unix/linux. В случае, если без примера будет не обойтись - я буду использовать псевдокод.

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

Для начала попробуем получить информацию о состоянии ноды, но не командой bitcoin-cli getwalletinfo, как мы это делали раньше, а с помощью RPC-запроса. Выполним следующую команду:

curl  --data-binary  '{"jsonrpc":"1.0","method":"getwalletinfo","params":[]}' http://username:userpassword@localhost:18332

В этой команде мы передает сформированный JSON-запрос. Собственно сам вызов: "method":"getwalletinfo". Этот запрос передается как обычный http-запрос на адрес localhost:18332 - то есть наша локальная машина, и RPC-порт, который мы указали при конфигурации тестовой сети. А username:userpassword - соответствуют параметрам rpcuser и rpcpassword, которые мы указали в том же файле конфигурации. В результате мы получим что то подобное:

{
  "result": {
    "walletname": "wallet.dat",
    "walletversion": 159900,
    "balance": 14717.18723440,
    "unconfirmed_balance": 0.00000000,
    "immature_balance": 78.12526560,
    "txcount": 1002,
    "keypoololdest": 1528970690,
    "keypoolsize": 999,
    "keypoolsize_hd_internal": 1000,
    "paytxfee": 0.00000000,
    "hdmasterkeyid": "be875fb1dc27d408a5c032d8714cf21cbc3b9af0"
  },
  "error": null,
  "id": null
}

Первый шаг для получения платежа с помощью bitcoin (как и большинства криптовалют) - это получение нового уникального адреса, который по совместительству будет выполнять функцию идентификатора платежа. Именно это мы делали, когда выполняли команду bitcoin-cli getnewaddress. На самом деле эта команда имеет дополнительные параметры, но сейчас они нам не интересны. Попробуем проделать это еще раз (подразумевается, что у вас есть установленный bitcoind и настроена тестовая есть, как было описано в прошлых статьях цикла)

bitcoin-cli getnewaddress

2N49FdQ7Ppb1W8BXfNYUPT4BcnWu5B4zFVt

Теперь нам нужно проделать то же самое, но с помощью JSON-RPC. Сам запрос будет иметь такоей же вид как и в прошлом примере, только вместо "method":"getwalletinfo" мы укажем "method":"getnewaddress":

curl  --data-binary \
  '{"jsonrpc":"1.0","method":"getnewaddress","params":[]}' \
  http://username:userpassword@localhost:18332

И результат соответственно будет:

{
  "result": "2NBqem3SZijakVD4Cy6ZQTMTZzCPRjNMeKF",
  "error": null,
  "id": null
}

Если мы внимательно рассмотрим структуру ответа, то в обоих случаях мы увидим общие элементы:

{
  "result": ... ,
  "error": null,
  "id": null
}

Поле result содержит результат выполнения запроса, а error - соответственно описание ошибки в случае, если запрос выполнить не удалось. Поскольку мы пока все делали правильно, мы ни разу не наблюдали, чтобы это поле было отличным от пустого (null). Но реальность не настолько безоблачна, по этому разбирая ответ от сервера это поле всегда нужно проверять на наличие ошибок, и только потом переходить к обработке результата.

Вернемся к нашему новому адресу.

 Итак, мы знаем, каким образом получить новый адрес, причем сделать это можно в неограниченном количестве (ну в рамках человеческой жизни это точно). А что, собственно, с ним делать дальше? Формируя форму оплаты для клиента, вы выдаете ему этот адрес, с указанием суммы, которую на него необходимо перечислить. Сам же адрес вы сохраняете в надежном месте, с указанием к какому именно заказу и какому пользователю он относится. Сейчас оставим в стороне интерфейсные вопросы реализации - такие как qr-код, волшебные кнопочки вроде "Оплатить через кошелек Bitcoin", сейчас нам важно освоить сам принцип.

После получения адреса, нам нужно понять, когда клиент сделал оплату. Один из самых простых и наглядных способов - это регулярно проверять, получены ли средства или нет. Конечно мы можем это сделать через getwalletinfo - но эта команда покажет нам общее состояние нашего кошелька и никак не укажет, кто именно сделал оплату. Если ваш магазин работает наполовину в ручном режиме, а заказы у вас приходят несколько раз в день, и вы нужны вашим клиентам больше, чем они вам - вы можете запрашивать у клиента номер bitcoin-транзакции после оплаты. 

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

ce397d610c0066f1f4f38d532bc527daac3bd6aac25081618be63620d9078c7b

Вы можете воспользоваться публичным блокэксплорером, например blockchain.info, но это явно не то, чем мы тут занимаемся. Только bitcoind, только хардкор. Попробуем проверить транзакцию сами, с помощью командной строки:

bitcoin-cli gettransaction ce397d610c0066f1f4f38d532bc527daac3bd6aac25081618be63620d9078c7b

В ответ мы получим нечто подобное (если транзакция конечно реально существует и прошла по сети):

{
  "amount": 0.00000000,
  "fee": -0.00007880,
  "confirmations": 0,
  "trusted": true,
  "txid": "ce397d610c0066f1f4f38d532bc527daac3bd6aac25081618be63620d9078c7b",
  "walletconflicts": [
  ],
  "time": 1529143485,
  "timereceived": 1529143485,
  "bip125-replaceable": "no",
  "details": [
    {
      "account": "",
      "address": "2NBqem3SZijakVD4Cy6ZQTMTZzCPRjNMeKF",
      "category": "send",
      "amount": -10.00000000,
      "label": "",
      "vout": 0,
      "fee": -0.00007880,
      "abandoned": false
    },
    {
      "account": "",
      "address": "2NBqem3SZijakVD4Cy6ZQTMTZzCPRjNMeKF",
      "category": "receive",
      "amount": 10.00000000,
      "label": "",
      "vout": 0
    }
  ],
  "hex": "02000000.....12bf2ed000e9030000"
}

Я удалил часть информации, чтобы не затруднять чтение, но в целом это именно то, что мы получим в ответ. Во-первых, вам всегда нужно будет обращать внимание на параметр confirmations, он указывает включена ли транзакция в блок, то есть подтверждена ли она. В bitcoin принято считать что транзакция подтверждена после 6 подтверждений, но в последнее время часто ограничиваются 3 и даже одним подтверждением. Второе, на что нам нужно обратить внимание, это список details , в нем видны все входы и выходы транзакции. Нас интересуют блоки, в которых содержится category": "receive . Это логично, мы же ожидаем оплату. И вот в этих блоках мы уже должны найти адрес, который мы выдали клиенту и сравнить сумму, которую он отправил. Информации у нас значительно больше, но это именно та, которая интересует нас в первую очередь.

Проделаем то же самое, но с помощью RPС-запроса:

curl  --data-binary \
	'{"jsonrpc":"1.0","method":"gettransaction","params":["ce397d610c0066f1f4f38d532bc527daac3bd6aac25081618be63620d9078c7b"]}' \
	http://username:userpassword@localhost:18332

Я не буду дублировать ответ, он полностью идентичен полученному с помощью командной строки. А вот в самом запросе у нас появилось что то новое. Обращаем внимание на блок "params". Он перестал быть пустым. Вместо этого у нас в нем появился номер транзакции. При выполнении RPC запросов помните - порядок параметров имеет значение, поскольку они не именованы. Он же понадобится нам в следующем примере.

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

bitcoin-cli  getreceivedbyaddress 2NBqem3SZijakVD4Cy6ZQTMTZzCPRjNMeKF

Хозяйке на заметку: при запросе по умолчанию в ответе мы увидим только средства, которые уже подтверждены. Если мы хотим посмотреть факт неподтвержденной оплаты, то нам нужно добавить в конец команды 0. 

Если же мы хотим посмотреть, какая сумма имеет, например, не менее 6 подтверждений, то соответсвенно нам нужно добавить 6. То есть мы указываем минимальное количество подтверждений, которое нас интересует. Попробуем выполнить тот же запрос, но уже в виде RPC-запроса.

curl  --data-binary \
 '{"jsonrpc":"1.0","method":"getreceivedbyaddress","params":["2NBqem3SZijakVD4Cy6ZQTMTZzCPRjNMeKF", 0]}' \
  http://username:userpassword@localhost:18332

И увидим результат, подобный следующему:

{
  "result": 10.00000000,
  "error": null,
  "id": null
}

Мы видим, что какой то добрый человек перечислил нам 10 биткоинов. Но поскольку мы указали в запросе второй параметр 0 - то мы не знаем, есть ли у них подтверждения. Можно изменить этот параметр например на 1 и повторить запрос. Будем считать, что это домашнее задание, и с этим вы вполне справитесь самостоятельно.

Мы получили не слишком много информации, и она может вызывать вопросы. Например мы не знаем, года была оплата. Пользователь мог например оплатить не одним платежом а несколькими. В общем хотелось бы получить больше информации. Хорошо, для этого вместо getreceivedbyaddress мы можем использовать listreceivedbyaddress.

curl  --data-binary  '{"jsonrpc":"1.0","method":"listreceivedbyaddress","params":[]}' http://username:userpassword@localhost:18332

Эта команда вернет нам все платежи на все наши выданные адреса с номерами транзакций.

{
  "result": [
    {
      "address": "...",
      "account": "",
      "amount": ...,
      "confirmations": 7,
      "label": "",
      "txids": [
        "..."
      ]
    },
    {
      "address": "2NBqem3SZijakVD4Cy6ZQTMTZzCPRjNMeKF",
      "account": "",
      "amount": 10.00000000,
      "confirmations": 6,
      "label": "",
      "txids": [
        "ce397d610c0066f1f4f38d532bc527daac3bd6aac25081618be63620d9078c7b"
      ]
    }
  ],
  "error": null,
  "id": null
}

И уже среди этих данных нам нужно будет найти все наши адреса и выяснить - кто и сколько нам перечислил. Важно помнить, что по умолчанию listreceivedbyaddress вернет вам только подтвержденные транзакции. Если вы хотите получить полный список, то в качестве параметра нужно передать 0 - все как в случае с getreceivedbyaddress

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


В целом мы получили все необходимое, чтобы принимать платежи с помощью bitcoin. Конечно, описаный способ не самый удобный, но тем не менее он уже позволяет организовать простой платежный шлюз.


Читайте также: ПРИНИМАЕМ ОПЛАТУ В BITCOIN: ЧАСТЬ ПЕРВАЯ, ТЕОРЕТИЧЕСКАЯ

                             ПРИНИМАЕМ ОПЛАТУ В BITCOIN: ЧАСТЬ ВТОРАЯ. ИНСТРУМЕНТЫ И ПОДГОТОВКА

                             ПРИНИМАЕМ ОПЛАТУ В BITCOIN: ЧАСТЬ ТРЕТЬЯ, СВОЙ TESTNET. С БЛЭКДЖЕКОМ

                            

Читайте также

44742018-07-31

Lity. Новый язык смарт-контрактов

На данный момент в сети Ethereum опубликовано более чем 1700 децентрализованных приложений (DApps), и их число не перестает увеличиваться. И хотя все Dapps полагаются на смарт контракты, надежность самих смарт контрактов стоит под вопросом - киберпреступники заработали уже более миллиарда долларов на их взломах.

Ethereum, Разработчику
51082018-06-29

Принимаем оплату в bitcoin: Часть шестая. Нюансы, опять нюансы

В прошлой части мы остановились на том, что не плохо было бы узнать о факте платежа от bitcoind, вместо того, чтобы перебирать все выданные адреса.

Разработчику

Последние статьи из раздела Разработчику

Свежее видео на канале

Выбор редакции

645872020-10-30

Топ 10 крипто кошельков в 2020 году

По мере роста популярности криптовалют растет и спрос на качественные и безопасные криптовалютные кошельки.

Кошельки
568872017-12-10

Bitcoin: пирамида или нет?

С января 2009 года, когда был сгенерирован первый генезиз-блок bitcoin-сети, прошло уже девять лет, но до сих пор всякого рода "эксперты" ломают копья в спорах: являются ли криптовалюты финансовой пирамидой или нет. Быстрый рост доходности bitcoin и прибыли тех, кто раньше стал участником этой системы, пугает схожестью с пирамидами 90-х.

Bitcoin
536412018-04-28

on-chain и off-chain управление: за и против

Чтобы понять важность управления блокчейном и дискуссии вокруг этого вопроса, сначала нужно определить что такое управление блокчейном, его роль и цели. Управление блокчейном в сфере криптовалют состоит из двух пунктов: правил протокола (кода) и экономических стимулов, на которых основана сеть.

Blockchain
418602021-05-08

Какие альткоины принесут своим держателям доход в 2021 году?

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

Альткоины
382792018-05-12

Эволюция человека и денег

Развитие биткойна и блокчейна началось приблизительно 70000 лет назад, когда хомо сапиенс превзошли свои биологические лимиты как вид. Это история, которая уходит глубоко корнями в эволюцию человечества.

Это интересно
193672024-02-03

Сканер блоков (Blockchain explorer) своими руками: Зачем он нужен?

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

Обучение, Bitcoin, Ethereum, Технологии