Pyxpub is a simple webapp that generates unique payment requests for Bitcoin Cash. It exposes the needed features of a receive-only wallet through a JSON API to enable quick development of Point-of-Sale systems. This without the developer having to worry to much about the sensitive bits. Pyxpub makes it easy to follow Bitcoin best practices and future-proofs your application in order to scale towards future needs.
Pyxpub also includes a Point-of-Sale app by default.
Check out the live demo at demo.pyxpub.io (github repo/screenshots: acidsploit/react-pos)
As per Bitcoin best practices it is preferred to use a new receiving address for each payment. This for security and privacy implications for both you and your customers or donators. Also, we do not want any private keys on the server generating the receiving address, nor would we want them on any PoS system. This can easily be achieved by using an hd-wallet as described in BIP32. This is the default wallet type when you create a new wallet with Electron Cash.
Pyxpub exposes the needed features of that wallet through an easy to use JSON API, this to enable quick Point-of-Sale developement, without having to worry about the sensitive bits. Pyxpub handles the generation of new unique addresses for payment requests and keeps track of those, monitor payments requests for incoming transactions, and keeps a sales ledger. All you need to do is call the right api endpoints to get it all in your app.
All generated Bitcoin Cash addresses are derived from a pre-defined (Electron Cash) xpub key. Address re-use is prevented by keeping track of used addresses.
First, when you create a new wallet with Electron Cash, it is very important to properly backup and safely store your mnemonic seed. This Electron wallet is your full (send & receive) wallet, make sure it is password protected and also stored safely. You will use it later to retrieve payments. The webapp will use the xpub key to generate receive only addresses on the server, corresponding to the addresses from your Electron wallet. This way we don't need private keys on the server. You can find your wallet's master public key or xpub key through the 'Wallet -> Information' menu.
clone repo
git clone https://github.com/acidsploit/pyxpub.git pyxpub
setup environment
cd pyxpub/
bash setup.sh
set xpub key
Copy your xpub key from an Electron Cash wallet and paste it in the key.list file.
echo 'xpub...' > key.list
run
cd pyxpub/
bash pyxpub.sh
access locally
Browse to http://localhost:8080
A simple script (raspbian.sh) to deploy pyxpub on a fresh Raspbian installation. Gets you up and running in no time on a Raspberry Pi.
sudo apt-get update
sudo apt-get upgrade
sudo apt-get install git
sudo reboot
sudo mkdir /srv/wsgi
cd /srv/wsgi
sudo git clone https://github.com/acidsploit/pyxpub.git
cd pyxpub/
echo 'xpub...' > key.list
sudo bash raspbian.sh
Access pyxpub through your raspberry pi ip address. Example: http://192.168.1.15
Monitor the process:
sudo tail -f /var/log/uwsgi/app/pyxpub.log
install nginx, uwsgi and uwsgi python plugin
Follow your distribution's directions to install these packages.
insall pyxpub in appropriate location
cd /srv/http/
git clone https://github.com/acidsploit/pyxpub.git pyxpub
mkdir /srv/http/public_html/
mv pyxpub/react/ public_html/
cd pyxpub/
bash setup.sh
echo 'xpub...' > key.list
sudo chown http:http -R /srv/http/pyxpub
uWSGI config
Create: /etc/uwsgi/pyxpub.ini
[uwsgi]
socket = /run/uwsgi/%n.sock
processes = 4
chdir = /srv/http/pyxpub/
master = true
plugins = python
file = xpub.py
uid = http
gid = http
virtualenv = /srv/http/pyxpub/env/
uWSGI start & enable at startup
sudo systemctl start uwsgi@pyxpub.service
sudo systemctl enable uwsgi@pyxpub.service
setup nginx vhost to reverse proxy uWSGI
Set up a vhost (preferably with ssl) with following locations and uwsgi_pass:
upstream _pyxpub {
server unix:/run/uwsgi/pyxpub.sock;
}
server {
server_name yourserver.com;
listen 80;
root /srv/http/pyxpub/public_html;
rewrite ^/$ /react permanent;
location /react {
index index.html;
}
location /qr {
try_files $uri @uwsgi;
}
location /api {
try_files $uri @uwsgi;
}
location /embed {
return 403;
}
location @uwsgi {
include uwsgi_params;
uwsgi_pass _pyxpub;
}
}
NGINX start & enable at startup
sudo systemctl start nginx.service
sudo systemctl enable nginx.service
http://localhost:8080 This will open the React Point-of-Sale app.
payment request /api/payment
Generate new payment request.
Options:
-
amount
-
label
curl 'http://localhost:8080/api/payment?amount=0.0023&label=SHOP:1Wed2B44' { "payment": { "amount": "0.0023", "addr": "bitcoincash:qpej4uw429m9m0wawcphw9v4sch2ymd6qsqh7jx9gl", "legacy_addr": "1BVx9uf5UGJDt1eMqjut8qh1K4mmEeDSFQ", "label": "SHOP:1Wed2B44", "qr_img": "/qr?addr=bitcoincash:qpej4uw429m9m0wawcphw9v4sch2ymd6qsqh7jx9gl&amount=0.0023&label=SHOP:1Wed2B44", "payment_uri": "bitcoincash:qpej4uw429m9m0wawcphw9v4sch2ymd6qsqh7jx9gl?amount=0.0023&message=SHOP:1Wed2B44" } }
payment verification request /api/verify
Verify payment of payment request
Options:
- addr
- amount
or
-
label
curl 'http://localhost:8080/api/verify?addr=bitcoincash:qpemxfnepk9f0g2yzgsyk4ynnklaaunr7g99rrwas9&amount=0.0023' {"received": 0} or {"received": 1}
sales ledger request /api/ledger
Fetch sales ledger.
curl 'http://localhost:8080/api/ledger'
{
"1":{
"id":1,
"timestamp":1519152761.0008476,
"addr":"bitcoincash:qzwdulf49wfmalj6a36gn2h5ncvrxmw98ydzhxe7gz",
"amount":"0.00040650",
"label":"DEVZERO.BE:5cc4f403-9351-42f1-8850-a50735f921fd",
"received":1,
"confirmations":0,
"txid":"5bae0304eb76e78167af30fe6b98f83462828ae974b9c7bc236ebb5b7a9d9e26"
},
"2":{
"id":2,
"timestamp":1519159704.388939,
"addr":"bitcoincash:qpej4uw429m9m0wawcphw9v4sch2ymd6qsqh7jx9gl",
"amount":"0.00976920",
"label":"DEVZERO.BE:7d2cc3a5-9a6b-4112-b8ac-c60f89d28408",
"received":0,
"confirmations":0,
"txid":"NoTX"
},
"3":{
"id":3,
"timestamp":1519220076.8360977,
"addr":"bitcoincash:qpemxfnepk9f0g2yzgsyk4ynnklaaunr7g99rrwas9",
"amount":"0.0023",
"label":"SHOP:1Wed2B44",
"received":0,
"confirmations":0,
"txid":"NoTX"
}
}
exchange rate request /api/rate
Fetch exchange rate or list supported currencies for specified source.
Options:
- source (cryptocompate, kraken, coinbase)
- currency
=> Returns exchange rate for requested source and currency
or
- source (cryptocompate, kraken, coinbase)
=> Returns array of supported currencies for specified source
curl 'http://localhost:8080/api/rate?source=cryptocompare'
{
"currencies": ["EUR",
"USD",
"GBP",
"AUD",
"BRL",
"CAD",
"CHF",
"CLP",
"CNY",
"CZK",
"DKK",
"HKD",
"HUF",
"IDR",
"ILS",
"INR",
"JPY",
"KRW",
"MXN",
"MYR",
"NOK",
"NZD",
"PHP",
"PKR",
"PLN",
"RUB",
"SEK",
"SGD",
"THB",
"TRY",
"TWD",
"ZAR"]
}
curl 'http://localhost:8080/api/rate?source=kraken'
{
"currencies": ["EUR",
"USD"]
}
curl 'http://localhost:8080/api/rate?source=kraken¤cy=EUR'
{
"currency": "EUR",
"price": 953.8
}
http://localhost:8080/qr?addr=bitcoincash:qpej4uw429m9m0wawcphw9v4sch2ymd6qsqh7jx9gl&amount=0.0023&label=SHOP:PAYM123