У попередніх статтях ми розглянули загальні принципи організації платіжного шлюзу 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: ЧАСТИНА ТРЕТЯ, СВІЙ TESTNET. З БЛЕКДЖЕКОМ
Читайте також
Торговий робот своїми руками: що у коробках?
У перших двох частинах ми зібрали кістяк радника: котирування, промпт, модель, зберігання, ядро. Тепер головне питання – у якій формі можна його зібрати?
EthBackNode: навіщо вашому додатку “прокладка” між ним та Ethereum-нодою
Якщо ви коли-небудь пробували написати крипто-гаманець, платіжний шлюз або просто "бекенд, який вміє відправляти та приймати ETH", то досить швидко з'ясовується неприємна річ: Ethereum-нода - це не ваш бекенд
