Coinbase SDK
Для работы с Coinbase вы можете воспользоваться специальными инструментами и SDK, которые доступны абсолютно каждому (с некоторой оговоркой о цене).
- Прием платежей при помощи Coinbase осуществляется абсолютно бесплатно;
- Вы соглашаетесь заплатить взнос (1%) в случае, если захотите перевести деньги на ваш банковский счет, но только если ваши продажи превышают $1.000.000 (один миллион долларов);
- Минимальная сумма перевода в сети Bitcoin составляет 0,001 BTC. Используя Coinbase, можно понизить этот предел до 1 Satoshi (0,00000001 BTC);
Еще одним важный момент: вы можете подключать услугу “Мгновенный обмен”. Данная услуга преобразует сумму платежа Bitcoin сразу в выбранную вами валюту без дополнительных действий.
Типы интеграции
Как и многие другие сервисы онлайн-платежей, Coinbase предлагает два основных способа интеграции. Первый быстрее и легче, второй сложнее, но предоставляет расширенные возможности, которые больше подходят для больших проектов.
Первый вариант заключается в использовании одного из инструментов Coinbase, а именно MerchantTools. Вы можете использовать кнопки, страницы и фрэймы. Если вы используете CMS или системой управления электронной коммерцией (WordPress, WooCommerce, Magento. ), то наверняка найдёте много соответствующих плагинов.
Второе способ заключается в полной интеграции сервиса, исключая обращение к Coinbase. На самом деле, мы будем использовать конкретный PHP SDK.
Что мы можем сделать при помощи данного SDK?
- продавать или покупать bitcoin-ы (или совершать валютный обмен);
- отправить/запрашивать bitcoin-ы по электронной почте или по bitcoin адресу;
- принимать платежи bitcoin как мерчант-сервис;
- хранить bitcoin-ы в одном или нескольких кошельках;
- иметь доступ к списку операций над bitcoin-ами (блоки, транзакции и т.д.);
- обрабатывать текущие и микро платежи;
На данный момент существует три версии SDK: для Ruby, Java и PHP. Есть также много неофициальных библиотеки для других языков (Python, .NET, Node.js . ). В нашем случае мы будем использовать PHP SDK, который вы можете найти на GitHub.
Примечание: прежде чем перейти к следующему шагу, вам необходимо быть зарегистрированным в Coinbase.
Применение
Ну чтож, написав всего несколько десятков строк, мы можем генерировать для каждого пользователя свой адрес кошелка, проверять его баланс, переводить биткойны с одного кошелька на любой другой.
Для демонстрации работы BTC оплаты, я напишу простенького телеграм бота, который будет выполнять роль клиента Blockchain.com, то есть вы сможете хранить в нем свои биткойны и от туда же переводить другим людям. Ссылка на исходники бота будут в конце.
Проверить работу бота можно тут: https://t.me/Blockchain_client_bot
Задеплоил на heroku, так что надеюсь не будет падать)
Функционал бота
Регистрация пользователя
В качестве БД я использовал sqlite3 и создал одну таблицу пользователей:
import sqlite3 conn = sqlite3.connect(«my.db») # или :memory: чтобы сохранить в RAM cursor = conn.cursor() cursor.execute(«CREATE TABLE users (chatid INTEGER , name TEXT, balance INTEGER, btc_wallet TEXT, wif TEXT, btc_sent TEXT, state INTEGER)») conn.commit()
При нажатии start мы регистрируем пользователя, генерируем для него адрес биткойн кошелька, wif и добавляем данные в БД:
sql = «SELECT COUNT(*) FROM users » cursor.execute(sql) user = cursor.fetchone() address, wif= gen_address(user[0]+1) sql_insert = «INSERT INTO users VALUES ({}, ‘{}’, 0,'{}’,'{}’,’no’,0)».format(message.chat.id, message.chat.first_name,address,wif) cursor.execute(sql_insert) conn.commit()
Проверка баланса
if message.text == ‘? Ваш баланс’: url = f’https://blockchain.info/rawaddr/{data[3]}’ x = requests.get(url) wallet = x.json() await bot.send_message(message.chat.id, f»’? *Итоговый баланс:* {format(wallet[‘final_balance’] / 100000000, ‘.9f’)} BTC *Всего получено:* {format(wallet[‘total_received’] / 100000000, ‘.9f’)} BTC *Всего отправлено:* {format(wallet[‘total_sent’] / 100000000, ‘.9f’)} BTC https://www.blockchain.com/ru/btc/address/{data[3]}»’, parse_mode= «Markdown»)
PHP SDK
Установка
Давайте начнем с установки пакета библиотеки. На GitHub-овскйо странице данного SDK вы не обнаружите возможность взаимодействия с Composer. Тем не менее, при помощи простого поиска можем найти соответствующий пакет coinbase/coinbase .
Для установки помещаем следующий код в файл composer.json :
Далее используем composer (если он у вас установлен) для скачивания библиотеки:
Аутентификация
Перед тем как приступить к написанию кода, давайте поговорим об аутентификации. У разработчиков есть два варианта аутентификации, для получения доступа к методом API. Первый — это использование пары ключей API и API Secret. Второй, использование OAuth2.
Документация Coinbase вполне понятна: если вы намереваетесь взаимодействовать только с своим ??аккаунт-ом, вносить в него изменения, то можете использовать API Key. Если же вам необходимо, чтобы пользователь использовал свой аккаунт в рамках вашего приложения, то лучше всего воспользоваться OAuth2.
Ключ API + Secret
Создать ключ API очень просто, если у вас есть аккаунт Coinbase. Все, что вам нужно сделать, это перейти сюда и нажать на кнопку «+ New API Key».
Если вы делаете это впервые, то вам скорее всего придётся подтвердить аккаунт при помощи Authy.
Перед вами должна отобразиться следующая форма:
Вам нужно будет указать данные учетной записи и список прав, которые необходимо присвоить для конкретного ключа. Также вы можете выбрать один или несколько IP адресов для внесения их в “белый список”.
Для создания и активации ключа API нажмите сначала кнопку “Create”, а затем “Enable”.
OAuth 2.0
Если вы хотите использовать OAuth 2.0 , то сначала придётся пройти простую процедуру. На это раз вам нужно создавать не ключ API, тем не менее, а OAuth 2.0 приложение. Для этого отправляетесь по адресу https://coinbase.com/oauth/applications. Далее нажмите на кнопку “+ Create an Application”:
Проверка баланса и транзакции:
Теперь когда у каждого пользователя свой личный адрес биткойн кошелька, нужно проверить баланс этого адреса. Для этого мы будем обращаться к сайту Blockchain.com дабы получить нужную информацию.
import requests # Адрес кошелька пользователя wallet = ’12VeK1eRgPHRUikNLXq3Nuz99gS2S46QMD’ # wallet = gen_address(0) url = f’https://blockchain.info/rawaddr/{wallet}’ x = requests.get(url) wallet = x.json() print(‘Итоговый баланс:’+str(wallet[‘final_balance’])) print(‘Транзакции:’+str(wallet[‘txs’])) if wallet[‘total_received’]==0: print(‘баланс пустой’)
Вот таким простым кодом мы можем получить всю информацию по балансу и транзакциях пользователя. Дальше все зависит от логики самого приложения.
Получение информации из биткоин сети
Самый “тяжеловесный” пункт. Классическим решением является поднятие собственного эталонного полного узла Биткоин, он же — каноничный bitcoind. Это позволит нам общаться с ним по JSON-RPC. С ним мы сможем как получать информацию из сети, так и пушить транзакции. На что стоит обратить внимание:
- После установки, синхронизация узла может занять длительное время. Только после синхронизации узел можно использовать.
- Займет немало места. Уже 40+ гигабайт.
- Мне лично неизвестно какую нагрузку по запросам сможет выдержать.
- Любые проблемы с падением/обновлением лягут на ваши плечи.
Альтернатива — имплементация полного узла на Ruby+PostgreSQL, Toshi. Неканоничная, но стремящаяся к полной совместимости реализация. Обратите внимание, из-за дополнительных индексов, база данных займет 220+ гигабайт места. Опять таки, требуется синхронизация с сетью. Возможно, есть другие имплементации полного узла (мне неизвестны). Еще одна альтернатива
— использование публичного API провайдера. На его плечи ляжет получение информации из сети и трансляция транзакций.
Лично я рекомендую подключить несколько решений с фейловером.
Трансляция транзакций
Результатом создания и подписи транзакций являются двоичные данные (hex), готовые к пушу в сеть. Пока сеть не увидит транзакцию, считайте, нет никакой транзакции. Когда сеть увидела транзакцию, она считается неподтвержденной. Транзакцию достаточно передать одному узлу биткоин, после чего за считаные секунды транзакцию увидит большая часть Биткоин сети. Транслировать транзакции умеют некоторые клиентские либы из раздела “Работа с адресами” (через какието свои захардкодженые ендпоинты), или любой полный узел. Транслировать транзакцию можно даже руками, зайдя на специальную страничку Биткоин API провайдера и вбив транзакцию в специальную форму. Канонично, подтвержденной транзакцией является транзакция, включенная в 6 и больше последовательных блоков (или в 1-3. Неканонично, но быстрее). Транзакции с нулевой (или недостаточной) комиссией могут оставаться неподтвержденными долгое время (до месяца, в моей практике). Такие транзакции желательно периодически ретранслировать.
Общие принципы работы платежного шлюза
Вариант 1
Предположим, у нас есть уникальный счет (invoice, order), представленый к оплате клиенту, и платить клиент будет в биткоинах. Начнем с того, что надо сконвертировать валюту оригинального счета (USD например) в BTC. Задача это тривиальная и рассматривать мы ее не будем. Далее. Стандартом де факто является генерация нового уникального адреса биткоин под каждый заказ (он же счет, он же invoice, он же ордер). Ожидается, что средства на этот счет переведет только наш клиент, только 1 раз, и только строго указанную сумму (можно больше, никто не обидится, но никак не меньше). Т.о. при поступлении средств на указанный биткоин адрес в нужном количестве, заказ считается оплаченным.
Вкратце, цепочка такая:
- ордер в системе ->
- генерируем соответствующий ордеру уникальный адрес биткоин ->
- показываем клиенту ->
- ждем оплату на адрес ->
- ордер закрыт (отмена по истечению срока годности или же поступление BTC и засчитываем факт оплаты)
При поступлении биткоинов на адрес у вас есть варианты засчитать неподтвержденный или подтвержденный баланс. Есть небольшой шанс что транзакция откатится, причем это может быть как по вине плательщика (который на самом деле злоумышленник), так и по независящим от него обстоятельствам.
Если вы имеете возможность “отобрать” предоставленный товар или услугу у клиента в случае выявленного факта отмены транзакции, я рекомендую засчитывать неподтвержденный баланс. Это будет означать почти мгновенный процесс оплаты для клиента (в противовес часа ожидания, например). А если какие то транзакции выявятся откаченными в итоге, запросить клиента о повторном платеже, угрожая отобрать услугу/товар.
Не ожидайте что подобный фрод вас тут же массово настигнет, откат транзакций это очень большая редкость, а “вручную” стимулировать подобный откат (на который кстати у злоумышленника нет никаких гарантий успеха) технически неподкованным клиентам нереально (в противовес чарджбекам по кредитным картам).
Еще один пример когда можно засчитывать неподтвержденный баланс — если на подготовку заказа клиента уходит больше одного часа (например обрабатывается корзина покупателя, готовится к отправке курьерской службой). Тут куча времени перепроверить баланс перед самой отправкой товаров.
Для остальных случаев можно ввести некий порог, выше которого обязательно ожидать подтвержденного баланса (например 0.25 BTC). Для максимальной надежности сделать его нулевым.
После закрытия ордера вы можете оставить биткоины на этом адресе до востребования, или для удобства перевести на единый “агрегационный” кошелек мерчанта. Будьте осторожны, в последнем случае вы можете скомпрометировать такой коммерческий показатель как “оборот”, т.к. транзакцию оплаты может отследить каждый платящий клиент. Для переводов вам понадобится создавать, подписывать и транслировать транзакции, используя приватные ключи от адресов.
Пару слов о времени жизни ордера.
Если ваш товар или услуга жестко привязаны к эквиваленту в фиатной валюте (например USD), то типичный срок жизни ордера составляет 7-15 минут из-за волатильности курса.
Вариант 2
Подходит когда вы не выставляете счета на оплату, а аккаунт юзера содержит некий единый баланс, который он пополняет и с которого тратит. Тут понадобится сгенерить биткоин адрес на пользователя, и показывать ему, с просьбой пополнить на любую сумму. В данном случае надо мониторить адрес на входящие транзакции, пополнять юзеру внутренний баланс при наличии оных. В данном случае я рекомендую засчитывать только подтвержденные транзакции (от 3х блоков и выше).
- генерация адреса пользователю ->
- мониторинг транзакций на адрес ->
- пополнение внутреннего счета при наличии входящих транзакций
Генерация дочернего адреса кошелька для каждого пользователя:
Чтобы получить наш нулевой адрес Биткойн кошелька на основе seed фразы (12VeK1eRgPHRUikNLXq3Nuz99gS2S46QMD), нам нужно пройти всю цепочку преобразований. Методом проб и ошибок мне все-таки удалось получить адрес кошелька следующим кодом:
from bipwallet.utils import * def gen_address(index): # Наша seed фраза seed = ‘vivid area able second bicycle advance demand alpha flip stable drift route’ # Мастер ключ из seed фразы master_key = HDPrivateKey.master_key_from_mnemonic(seed) # Public key из мастер ключа по пути ‘m/44/0/0/0′ root_keys = HDKey.from_path(master_key, «m/44’/0’/0’/0″)[-1].public_key.to_b58check() # Extended public key xpublic_key = str(root_keys, encoding=»utf-8″) # Адрес дочернего кошелька в зависимости от значения index address = Wallet.deserialize(xpublic_key, network=’BTC’).get_child(index, is_prime=False).to_address() rootkeys_wif = HDKey.from_path(master_key, f»m/44’/0’/0’/0/{index}»)[-1] # Extended private key xprivatekey = str(rootkeys_wif.to_b58check(), encoding=»utf-8″) # Wallet import format wif = Wallet.deserialize(xprivatekey, network=’BTC’).export_to_wif() return address, str(wif, ‘utf-8’) print(gen_address(0))
Данная функция возвращает адрес кошелька и wif в зависимости номера. Максимальное число с которым удалось получить адрес это 999999999.
wif (Wallet import format) — это просто кодирование байтов ключа в кодировку Base58 + контрольная сумма. Он нам понадобится в дальнейшем при генерации транзакции.
Это все значит, что имея только одну seed фразу мы можем создать 1 млрд дочерних адресов. Каждому пользователю при регистрации мы будем выдавать новый адрес, через который он сможет оплачивать по BTC. Появляется ограничение на 1 млрд пользователей, но нам никто не запрещает использовать несколько seed фраз или генерировать каждому юзеру новую фразу, но тогда каждая оплата будет кидаться не в общий ваш кошелек, а по разным.