Fixed client
This commit is contained in:
parent
f9a9ce20d0
commit
33c14a9ef4
@ -1,5 +1,10 @@
|
||||
# btcpay-python
|
||||
|
||||
## Install
|
||||
```shell
|
||||
pip3 install btcpay
|
||||
```
|
||||
|
||||
|
||||
## Pairing
|
||||
* Generate and save private key:
|
||||
|
||||
@ -15,7 +15,7 @@ Classifier: Programming Language :: Python :: 3 :: Only
|
||||
Classifier: Development Status :: 5 - Production/Stable
|
||||
Classifier: Environment :: Web Environment
|
||||
Classifier: Intended Audience :: Developers
|
||||
Classifier: License :: OSI Approved :: Apache2 License
|
||||
Classifier: License :: OSI Approved :: Apache Software License
|
||||
Classifier: Operating System :: OS Independent
|
||||
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
||||
Classifier: Topic :: Office/Business :: Financial
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
README.md
|
||||
setup.py
|
||||
btcpay/__init__.py
|
||||
btcpay/client.py
|
||||
btcpay/crypto.py
|
||||
|
||||
Binary file not shown.
@ -55,9 +55,9 @@ class BTCPayClient:
|
||||
uri = self.host + path
|
||||
if payload:
|
||||
payload = json.dumps(payload)
|
||||
r = self.s.post(uri, headers=headers, data=payload)
|
||||
r = self.s.post(uri, data=payload)
|
||||
else:
|
||||
r = self.s.get(uri, headers=headers)
|
||||
r = self.s.get(uri)
|
||||
r.raise_for_status()
|
||||
return r.json()['data']
|
||||
|
||||
@ -85,10 +85,19 @@ class BTCPayClient:
|
||||
if re.match(r'^\w{7,7}$', code) is None:
|
||||
raise ValueError("pairing code is not legal")
|
||||
payload = {'id': self.client_id, 'pairingCode': code}
|
||||
return self._unsigned_request('/tokens', payload)
|
||||
data = self._unsigned_request('/tokens', payload)
|
||||
data = data[0]
|
||||
return {
|
||||
data['facade']: data['token']
|
||||
}
|
||||
|
||||
def __repr__(self):
|
||||
return '{}({})'.format(
|
||||
type(self).__name__,
|
||||
self.host
|
||||
)
|
||||
|
||||
# from btcpay import BTCPayClient
|
||||
client = BTCPayClient(host=shop.gateway.client.uri, insecure=True, pem=shop.gateway.client.pem, tokens=shop.gateway.client.tokens)
|
||||
|
||||
client = BTCPayClient(host=shop.gateway.client.uri, insecure=True, pem=shop.gateway.client.pem, tokens={'merchant': 'ET9rzVZUJLg9xnWo7pcjw32fPnqLj7KocfP3XyDptrCo'})
|
||||
|
||||
2
build/lib/btcpay/__init__.py
Normal file
2
build/lib/btcpay/__init__.py
Normal file
@ -0,0 +1,2 @@
|
||||
from . import crypto, client
|
||||
from .client import BTCPayClient
|
||||
94
build/lib/btcpay/client.py
Normal file
94
build/lib/btcpay/client.py
Normal file
@ -0,0 +1,94 @@
|
||||
"""btcpay.client
|
||||
|
||||
BTCPay API Client.
|
||||
"""
|
||||
|
||||
import re
|
||||
import json
|
||||
|
||||
import requests
|
||||
|
||||
from . import crypto
|
||||
|
||||
|
||||
class BTCPayClient:
|
||||
def __init__(self, host, pem, insecure=False, tokens=None):
|
||||
self.host = host
|
||||
self.verify = not(insecure)
|
||||
self.pem = pem
|
||||
self.tokens = tokens or dict()
|
||||
self.client_id = crypto.get_sin_from_pem(pem)
|
||||
self.user_agent = 'btcpay-python'
|
||||
self.s = requests.Session()
|
||||
self.s.verify = self.verify
|
||||
self.s.headers.update(
|
||||
{'Content-Type': 'application/json',
|
||||
'accept': 'application/json',
|
||||
'X-accept-version': '2.0.0'})
|
||||
|
||||
def _create_signed_headers(self, uri, payload):
|
||||
return {
|
||||
"X-Identity": crypto.get_compressed_public_key_from_pem(self.pem),
|
||||
"X-Signature": crypto.sign(uri + payload, self.pem)
|
||||
}
|
||||
|
||||
def _signed_get_request(self, path, token=None):
|
||||
token = token or list(self.tokens.values())[0]
|
||||
uri = self.host + path
|
||||
payload = "?token=%s" % token
|
||||
headers = self._create_signed_headers(uri, payload)
|
||||
r = self.s.get(uri + payload, headers=headers)
|
||||
r.raise_for_status()
|
||||
return r.json()['data']
|
||||
|
||||
def _signed_post_request(self, path, payload, token=None):
|
||||
token = token or list(self.tokens.values())[0]
|
||||
uri = self.host + path
|
||||
payload['token'] = token
|
||||
payload = json.dumps(payload)
|
||||
headers = self._create_signed_headers(uri, payload)
|
||||
r = self.s.post(uri, headers=headers, data=payload)
|
||||
r.raise_for_status()
|
||||
return r.json()['data']
|
||||
|
||||
def _unsigned_request(self, path, payload=None):
|
||||
uri = self.host + path
|
||||
if payload:
|
||||
payload = json.dumps(payload)
|
||||
r = self.s.post(uri, headers=headers, data=payload)
|
||||
else:
|
||||
r = self.s.get(uri, headers=headers)
|
||||
r.raise_for_status()
|
||||
return r.json()['data']
|
||||
|
||||
def get_rates(self):
|
||||
return self._signed_get_request('/rates/')
|
||||
|
||||
def get_rate(self, currency):
|
||||
rates = self.get_rates()
|
||||
rate = [rate for rate in rates if rate['code'] == currency.upper()][0]
|
||||
return rate['rate']
|
||||
|
||||
def create_invoice(self, payload, token=None):
|
||||
if re.match(r'^[A-Z]{3,3}$', payload['currency']) is None:
|
||||
raise ValueError('Currency is invalid.')
|
||||
try:
|
||||
float(payload['price'])
|
||||
except ValueError as e:
|
||||
raise ValueError('Price must be a float') from e
|
||||
return self._signed_post_request('/invoices/', payload, token=token)
|
||||
|
||||
def get_invoice(self, invoice_id, token=None):
|
||||
return self._signed_get_request('/invoices/' + invoice_id, token=token)
|
||||
|
||||
def pair_client(self, code):
|
||||
if re.match(r'^\w{7,7}$', code) is None:
|
||||
raise ValueError("pairing code is not legal")
|
||||
payload = {'id': self.client_id, 'pairingCode': code}
|
||||
return self._unsigned_request('/tokens', payload)
|
||||
|
||||
def __repr__(self):
|
||||
return '{}({})'.format(
|
||||
type(self).__name__,
|
||||
self.host
|
||||
)
|
||||
86
build/lib/btcpay/crypto.py
Normal file
86
build/lib/btcpay/crypto.py
Normal file
@ -0,0 +1,86 @@
|
||||
"""btcpay.crypto
|
||||
|
||||
These are various crytography related utility functions borrowed from:
|
||||
bitpay-python: https://github.com/bitpay/bitpay-python
|
||||
"""
|
||||
|
||||
import binascii
|
||||
import hashlib
|
||||
|
||||
from ecdsa import SigningKey, SECP256k1, VerifyingKey
|
||||
from ecdsa import util as ecdsaUtil
|
||||
|
||||
|
||||
def generate_privkey():
|
||||
sk = SigningKey.generate(curve=SECP256k1)
|
||||
pem = sk.to_pem()
|
||||
pem = pem.decode('utf-8')
|
||||
return pem
|
||||
|
||||
|
||||
def get_sin_from_pem(pem):
|
||||
public_key = get_compressed_public_key_from_pem(pem)
|
||||
version = get_version_from_compressed_key(public_key)
|
||||
checksum = get_checksum_from_version(version)
|
||||
return base58encode(version + checksum)
|
||||
|
||||
|
||||
def get_compressed_public_key_from_pem(pem):
|
||||
vks = SigningKey.from_pem(pem).get_verifying_key().to_string()
|
||||
bts = binascii.hexlify(vks)
|
||||
compressed = compress_key(bts)
|
||||
return compressed
|
||||
|
||||
|
||||
def sign(message, pem):
|
||||
message = message.encode()
|
||||
sk = SigningKey.from_pem(pem)
|
||||
signed = sk.sign(message, hashfunc=hashlib.sha256,
|
||||
sigencode=ecdsaUtil.sigencode_der)
|
||||
return binascii.hexlify(signed).decode()
|
||||
|
||||
|
||||
def base58encode(hexastring):
|
||||
chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
|
||||
int_val = int(hexastring, 16)
|
||||
encoded = encode58('', int_val, chars)
|
||||
return encoded
|
||||
|
||||
|
||||
def encode58(string, int_val, chars):
|
||||
if int_val == 0:
|
||||
return string
|
||||
else:
|
||||
(new_val, rem) = divmod(int_val, 58)
|
||||
new_string = chars[rem] + string
|
||||
return encode58(new_string, new_val, chars)
|
||||
|
||||
|
||||
def get_checksum_from_version(version):
|
||||
return sha_digest(sha_digest(version))[0:8]
|
||||
|
||||
|
||||
def get_version_from_compressed_key(key):
|
||||
sh2 = sha_digest(key)
|
||||
rphash = hashlib.new('ripemd160')
|
||||
rphash.update(binascii.unhexlify(sh2))
|
||||
rp1 = rphash.hexdigest()
|
||||
return '0F02' + rp1
|
||||
|
||||
|
||||
def sha_digest(hexastring):
|
||||
return hashlib.sha256(binascii.unhexlify(hexastring)).hexdigest()
|
||||
|
||||
|
||||
def compress_key(bts):
|
||||
intval = int(bts, 16)
|
||||
prefix = find_prefix(intval)
|
||||
return prefix + bts[0:64].decode('utf-8')
|
||||
|
||||
|
||||
def find_prefix(intval):
|
||||
if intval % 2 == 0:
|
||||
prefix = '02'
|
||||
else:
|
||||
prefix = '03'
|
||||
return prefix
|
||||
BIN
dist/btcpay-1.0.0-py3-none-any.whl
vendored
Normal file
BIN
dist/btcpay-1.0.0-py3-none-any.whl
vendored
Normal file
Binary file not shown.
BIN
dist/btcpay-1.0.0.tar.gz
vendored
Normal file
BIN
dist/btcpay-1.0.0.tar.gz
vendored
Normal file
Binary file not shown.
6
setup.py
6
setup.py
@ -4,12 +4,12 @@ from setuptools import setup, find_packages
|
||||
setup(
|
||||
name="btcpay",
|
||||
packages=find_packages(),
|
||||
version="1.0.0",
|
||||
version="1.0.1",
|
||||
description="Accept bitcoin with BTCPay",
|
||||
author="Joe Black",
|
||||
author_email="me@joeblack.nyc",
|
||||
url="https://github.com/joeblackwaslike/btcpay-python",
|
||||
download_url="https://github.com/joeblackwaslike/btcpay-python/tarball/v1.0.0",
|
||||
download_url="https://github.com/joeblackwaslike/btcpay-python/tarball/v1.0.1",
|
||||
license='Apache 2.0',
|
||||
keywords=["bitcoin", "payments", "crypto"],
|
||||
install_requires=[
|
||||
@ -24,7 +24,7 @@ setup(
|
||||
"Development Status :: 5 - Production/Stable",
|
||||
"Environment :: Web Environment",
|
||||
"Intended Audience :: Developers",
|
||||
"License :: OSI Approved :: Apache2 License",
|
||||
"License :: OSI Approved :: Apache Software License",
|
||||
"Operating System :: OS Independent",
|
||||
"Topic :: Software Development :: Libraries :: Python Modules",
|
||||
"Topic :: Office/Business :: Financial"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user