📜 ⬆️ ⬇️

Making payments by cryptocurrency with your own hands

Hi, Habr!


From time to time I notice questions about how to accept Bitcoin payments on my website without using third-party services. It is quite simple, but we must bear in mind that there are pitfalls.


In this article I will try as much as possible, without an accent in any programming language, to describe how to accept Bitcoin payments (and also, if desired, Litecoin, Dash, Bitcoin Cash, Steep, ONION, etc.), starting with expanding the full node and completing the verification of receipt of payment.


Prerequisites


This implies that you have a website hosted on a VPS, to which you have root access, and are also willing to spend $ 15 + to pay for the server for the wallet.


Wallet installation


The first step is to allocate a separate server to host the wallet. Why a separate server? A separate server will reduce the risk of withdrawal of all your funds by the attacker in case of hacking the main site. Well, do not forget that to store the blockchain requires a lot of disk space (~ 150Gb of disk space, etc. - details on the link ).


What are the options for cheap servers? Their mass, in my opinion the most adequate - the server from hetzner.de or chipcore.com. On chipcore.com, for example, you can get dedicated with a 500Gb disk (enough for BTC and a couple more blockchains) for only 990 rubles (about 17 bucks). If you know something cheaper - write in the comments, very interesting (I think, not only me).


After you have consciously decided that you want to accept cryptocurrencies on your website and bought a server (or used an existing one), you need to install a bitcoin node.


The server should have any suitable operating system installed, the simplest option is Ubuntu 16.10 (yes, in fact, this is not the best choice, it is better to install 16.04 or wait for 18.04 and wait another couple of months for stabilization). As a rule, it does not make sense to bother with a disk partitioning and you can safely use 2-4Gb on swap and let the rest go to the root partition (/ or root).


After the server is available, the first thing to do is to disable password authorization and set up authorization using ssh keys. Making it simple is a good description from DigitalOcean .


After the server is configured, just a couple of commands are enough to run a full purse node.


Install bitcoind


sudo apt-add-repository ppa:bitcoin/bitcoin sudo apt-get update sudo apt-get install bitcoind 

This is all that is required to install the node.


Configure bitcoind


The first step is to create a bitcoin user:


 adduser bitcoin #       -   

and create service directories:


 mkdir -p /etc/bitcoin chown bitcoin: /etc/bitcoin mkdir -p /run/bitcoind chown bitcoin: /run/bitcoind mkdir -p /var/lib/bitcoind chown bitcoin: /var/lib/bitcoind 

Now the trifle remains - to correctly configure the node to receive JSON RPC requests.


The minimum config will look like this:


 rpcuser=USERNAME rpcpassword=PASSWORD rpcbind=127.0.0.1 rpcallowip=127.0.0.1/32 

It should be put at /etc/bitcoin/bitcoin.conf . And do not forget to set the correct owner:


 chown bitcoin: /etc/bitcoin/bitcoin.conf 

Important: The use of USERNAME and PASSWORD is a deprecated method and is not a bit safe. More correctly to use rpcauth, an example can be found on the link .


Further, it is enough to configure the systemd service to start the node (including after a reboot).


To do this, you can simply copy the unit file located at the address in the /etc/systemd/system/ directory:


 wget https://raw.githubusercontent.com/bitcoin/bitcoin/master/contrib/init/bitcoind.service -O /etc/systemd/system/bitcoind.service 

After that start it and configure autorun:


 systemctl daemon-reload systemctl start bitcoind systemctl enable bitcoind 

Now you can check the node performance:


 curl --data-binary '{"jsonrpc": "1.0", "method": "getinfo", "params": [] }' -H 'Content-Type: application/json' http://USERNAME:PASSWORD@127.0.0.1:8332/ 

If everything is ok, the following message will be sent back:


 {"result":{"balance":0.000000000000000,"blocks":59952,"connections":48,"proxy":"","generate":false, "genproclimit":-1,"difficulty":16.61907875185736},"error":null,"id":"curltest"} 

Configure the primary site server


It remains only to configure the server on which your site is located.


The safest and easiest way to make the wallet API available on the backend is to push the ssh tunnel through the systemd service (well, or any other init service). In the case of using systemd, the configuration of the service is as simple as possible:


 [Unit] Description=SSH Tunnel BTC After=network.target [Service] Restart=always RestartSec=20 User=tunnel ExecStart=/usr/bin/ssh -NT -o ServerAliveInterval=60 -L 127.0.0.1:8332:127.0.0.1:8332 tunnel@YOUR_SERVER_IP [Install] WantedBy=multi-user.target 

This configuration must be placed along the path /etc/systemd/system/sshtunnel-btc.service .


After that, we put the service in autostart and run:


 systemctl enable sshtunnel-btc.service systemctl start sshtunnel-btc.service 

To check, you can knock on the port of localhost and check that everything is ok:


 curl --data-binary '{"jsonrpc": "1.0", "method": "getinfo", "params": [] }' -H 'Content-Type: application/json' http://USERNAME:PASSWORD@127.0.0.1:8332/ 

API documentation


The list of all methods is most conveniently available through the link .


It is very simple to call them even through curl, we have already used the sample query earlier when getting information about a node with the getinfo method.


There are two options for the transfer of parameters - an array or a dictionary.


Below you can see examples of a request for a new address with the transfer of parameters in an array and a dictionary:


 # array curl --data-binary '{"jsonrpc": "1.0", "method": "getnewaddress", "params": ["test"] }' -H 'Content-Type: application/json' http://USERNAME:PASSWORD@127.0.0.1:8332/ # object curl --data-binary '{"jsonrpc": "1.0", "method": "getnewaddress", "params": {"account": "test"} }' -H 'Content-Type: application/json' http://USERNAME:PASSWORD@127.0.0.1:8332/ 

Simple API client


For use it is convenient to write a simple wrapper with the functions we need (or use the existing library for your language). Example for ruby:


 class Btc class BtcError < StandardError; end SUPPORTED_METHODS = %w(get_new_address get_addresses_by_account get_info get_net_totals get_balance get_received_by_address send_to_address list_transactions get_transaction) class << self def method_missing(method_name, *args) if SUPPORTED_METHODS.include?(method_name.to_s) send_request(method_name.to_s, args.empty? ? nil : args) else super end end def respond_to_missing?(method_name, _include_private = false) SUPPORTED_METHODS.include?(method_name) || super end protected def host ENV["HOST"] || "http://username:password@127.0.0.1:8332" end private def send_request(method, params = nil) uri = URI.parse(host) request = Net::HTTP::Post.new(uri) request.basic_auth uri.user, uri.password request.body = JSON.dump( jsonrpc: "1.0", method: method.tr("_", ""), params: params ) req_options = { use_ssl: uri.scheme == "https" } response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http| http.request(request) end begin result = JSON.parse(response.body) rescue JSON::ParserError raise BtcError.new(response.body) end raise BtcError.new(result["error"].to_json) if result["error"] result["result"].is_a?(Hash) ? result["result"].deep_symbolize_keys : result["result"] end end end 

After that, you can conveniently use it in approximately the following form:


 # run with HOST env variable (HOST=http://username:password@localhost:8332) Btc.get_info # => {result: {...}} 

Analogous example for node.js:


 var http = require('http'); function BtcApi(host, port, username, password) { this.host = host; this.port = port; this.username = username; this.password = password; }; BtcApi.methods = [ 'getNewAddress', 'getAddressesByAccount', 'getInfo', 'getNetTotals', 'getBalance', 'getReceivedByAddress', 'sendToAddress', 'listTransactions', 'getTransaction', ]; BtcApi.prototype.sendRequest = function(method, params, callback) { if (BtcApi.methods.indexOf(method) === -1) { throw new Error('wrong method name ' + method) }; if (callback == null) { callback = params; }; var body = JSON.stringify({ jsonrpc: '1.0', method: method.toLowerCase(), params: params, }); var auth = 'Basic ' + Buffer.from(this.username + ':' + this.password).toString('base64'); var options = { host: this.host, port: this.port, path: '/', method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': auth }, }; var request = http.request(options, function (response) { var result = ''; response.setEncoding('utf8'); response.on('data', function (chunk) { result += chunk; }); // Listener for intializing callback after receiving complete response response.on('end', function () { try { callback(JSON.parse(result)); } catch (e) { console.error(e); callback(result); } }); }); request.write(body) request.end() }; for (var i = 0; i < BtcApi.methods.length; i++) { BtcApi.prototype[BtcApi.methods[i]] = function (method) { return function (params, callback) { this.sendRequest(method, params, callback) } }(BtcApi.methods[i]) } module.exports = BtcApi 

Which can be used in approximately the following way:


 var BtcApi = require('./btc'); var client = new BtcApi('127.0.0.1', 8332, 'username', 'password'); client.listTransactions({ count: 1 }, function (response) { console.log('response: ', JSON.stringify(response)); }); // {"result":[{...}]} 

For Python, it's still easier - the official way is to use :


 from jsonrpc import ServiceProxy access = ServiceProxy("http://user:password@127.0.0.1:8332") access.getinfo() 

Actually, there are no problems with PHP either (it is recommended to use http://jsonrpcphp.org/ ):


  require_once 'jsonRPCClient.php'; $bitcoin = new jsonRPCClient('http://user:password@127.0.0.1:8332/'); echo "<pre>\n"; print_r($bitcoin->getinfo()); echo "\n"; echo "Received: ".$bitcoin->getreceivedbylabel("Your Address")."\n"; echo "</pre>"; 

A good selection of documentation is here .


The examples above are slightly modified versions listed by reference.


Integration with the site


The rather simple part remains - to set up processing of receiving payments and generating addresses for replenishment.


The process of integration of the acceptance of payments by the crypt looks like this:



To generate addresses for receiving, you can use several different approaches - creating a new address for each deposit, or using a permanent address for a user account.


The first option is more secure (since it is more difficult to track repeat payments by the payer) and simple, but it can become a problem when using not very powerful hardware (each generated address increases the load on the node, but this only becomes apparent from several million addresses).


The second option is more convenient if users need to register and pay often, but at the same time, it is less secure (for example, you can track all funds received on a user account).


To generate the top-up address, call the getnewaddress method, which in response will return a new address for the top-up. For convenience, you can transfer the account as a parameter (account) to which the created address will be associated. Sometimes it can be convenient to view transactions for a specific user.


Several methods are suitable for checking the balance. The easiest way is to create a record in the database for each generated replenishment address, then check the receipt of funds for each of the records via the getreceivedbyaddress method (not the most productive option, but suitable for most situations).


Another good option would be to receive information through the listtransactions about the latest operations and for them to already search for the user who receives the balances. What kind of implementation to use - you choose.


An important point in the verification of transactions - correctly specify the number of confirmations to protect against various attacks. For most cryptocurrencies, they can usually be found in White Paper.


For bitcoin, the current recommended value is 6 confirmations for small amounts. Here everything is well described.


I will not write code examples here anymore, because it strongly depends on the technology used.


Conclusion


I also wanted to write about the integration of other wallets, in more detail about server requirements and output, how to accept payments, if the site is not hosted on a VPS with root access, etc., but I understood that the article turned out to be big, so if It will be interesting - it is better to publish it in one of the following articles.


')

Source: https://habr.com/ru/post/350430/


All Articles