move coinify vendor stuff to a submodule
This commit is contained in:
parent
8956c5300f
commit
5ef381f973
|
@ -35,8 +35,8 @@ from shop.models import (
|
||||||
from .forms import AddToOrderForm
|
from .forms import AddToOrderForm
|
||||||
from .epay import calculate_epay_hash, validate_epay_callback
|
from .epay import calculate_epay_hash, validate_epay_callback
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from vendor.coinify_api import CoinifyAPI
|
from vendor.coinify.coinify_api import CoinifyAPI
|
||||||
from vendor.coinify_callback import CoinifyCallback
|
from vendor.coinify.coinify_callback import CoinifyCallback
|
||||||
import json, time
|
import json, time
|
||||||
import logging
|
import logging
|
||||||
logger = logging.getLogger("bornhack.%s" % __name__)
|
logger = logging.getLogger("bornhack.%s" % __name__)
|
||||||
|
|
0
src/vendor/__init__.py
vendored
0
src/vendor/__init__.py
vendored
1
src/vendor/coinify
vendored
1
src/vendor/coinify
vendored
|
@ -1 +0,0 @@
|
||||||
Subproject commit abc76101756a95e59abc8dd13e50a45d26855ccb
|
|
268
src/vendor/coinify_api.py
vendored
268
src/vendor/coinify_api.py
vendored
|
@ -1,268 +0,0 @@
|
||||||
import requests, json, time, hashlib, hmac
|
|
||||||
|
|
||||||
|
|
||||||
class CoinifyAPI:
|
|
||||||
api_key = None
|
|
||||||
"""Coinify API key. Get yours at https://www.coinify.com/merchant/api"""
|
|
||||||
|
|
||||||
api_secret = None
|
|
||||||
"""Coinify API secret. Get yours at https://www.coinify.com/merchant/api"""
|
|
||||||
|
|
||||||
api_base_url = None
|
|
||||||
"""Base URL to the Coinify API"""
|
|
||||||
|
|
||||||
API_DEFAULT_BASE_URL = "https://api.coinify.com"
|
|
||||||
|
|
||||||
def __init__(self, api_key=None, api_secret=None, api_base_url=None):
|
|
||||||
"""
|
|
||||||
Create an instance of the CoinifyAPI class.
|
|
||||||
Provide your API key and API secret.
|
|
||||||
|
|
||||||
Set api_base_url to None to use default
|
|
||||||
"""
|
|
||||||
self.api_key = api_key
|
|
||||||
self.api_secret = api_secret
|
|
||||||
self.api_base_url = api_base_url or self.API_DEFAULT_BASE_URL
|
|
||||||
|
|
||||||
def invoices_list(self, limit=None, offset=None, include_expired=None):
|
|
||||||
"""
|
|
||||||
Returns an array of your Coinify invoices
|
|
||||||
|
|
||||||
See https://www.coinify.com/docs/api/#list-all-invoices
|
|
||||||
"""
|
|
||||||
query_params = {}
|
|
||||||
|
|
||||||
if limit is not None:
|
|
||||||
query_params['limit'] = limit
|
|
||||||
if offset is not None:
|
|
||||||
query_params['offset'] = offset
|
|
||||||
if include_expired is not None:
|
|
||||||
query_params['include_expired'] = include_expired
|
|
||||||
|
|
||||||
return self.call_api_authenticated('/v3/invoices', query_params=query_params)
|
|
||||||
|
|
||||||
def invoice_create(self, amount, currency, plugin_name, plugin_version,
|
|
||||||
description=None, custom=None, callback_url=None, callback_email=None,
|
|
||||||
return_url=None, cancel_url=None, input_currency=None, input_return_address=None):
|
|
||||||
|
|
||||||
"""
|
|
||||||
Create a new invoice.
|
|
||||||
|
|
||||||
See https://www.coinify.com/docs/api/#create-an-invoice
|
|
||||||
"""
|
|
||||||
params = {
|
|
||||||
'amount': amount,
|
|
||||||
'currency': currency,
|
|
||||||
'plugin_name': plugin_name,
|
|
||||||
'plugin_version': plugin_version
|
|
||||||
}
|
|
||||||
|
|
||||||
if description is not None:
|
|
||||||
params['description'] = description
|
|
||||||
if custom is not None:
|
|
||||||
params['custom'] = custom
|
|
||||||
if callback_url is not None:
|
|
||||||
params['callback_url'] = callback_url
|
|
||||||
if callback_email is not None:
|
|
||||||
params['callback_email'] = callback_email
|
|
||||||
if return_url is not None:
|
|
||||||
params['return_url'] = return_url
|
|
||||||
if cancel_url is not None:
|
|
||||||
params['cancel_url'] = cancel_url
|
|
||||||
if input_currency is not None:
|
|
||||||
params['input_currency'] = input_currency
|
|
||||||
if input_return_address is not None:
|
|
||||||
params['input_return_address'] = input_return_address
|
|
||||||
|
|
||||||
return self.call_api_authenticated('/v3/invoices', 'POST', params)
|
|
||||||
|
|
||||||
def invoice_get(self, invoice_id):
|
|
||||||
"""
|
|
||||||
Get a specific invoice
|
|
||||||
|
|
||||||
See https://www.coinify.com/docs/api/#get-a-specific-invoice
|
|
||||||
"""
|
|
||||||
path = '/v3/invoices/%d' % (invoice_id,)
|
|
||||||
|
|
||||||
return self.call_api_authenticated(path)
|
|
||||||
|
|
||||||
def invoice_update(self, invoice_id, description=None, custom=None):
|
|
||||||
"""
|
|
||||||
Update the description and custom data of an invoice
|
|
||||||
|
|
||||||
See https://www.coinify.com/docs/api/#update-an-invoice
|
|
||||||
"""
|
|
||||||
params = {}
|
|
||||||
|
|
||||||
if description is not None:
|
|
||||||
params['description'] = description
|
|
||||||
if custom is not None:
|
|
||||||
params['custom'] = custom
|
|
||||||
|
|
||||||
path = '/v3/invoices/%d' % (invoice_id,)
|
|
||||||
|
|
||||||
return self.call_api_authenticated(path, 'PUT', params)
|
|
||||||
|
|
||||||
def invoice_input_create(self, invoice_id, currency, return_address):
|
|
||||||
"""
|
|
||||||
Request for an invoice to be paid with another input currency.
|
|
||||||
|
|
||||||
See https://www.coinify.com/docs/api/#pay-with-another-input-currency
|
|
||||||
"""
|
|
||||||
params = {
|
|
||||||
'currency': currency,
|
|
||||||
'return_address': return_address
|
|
||||||
}
|
|
||||||
|
|
||||||
path = '/v3/invoices/%d/inputs' % (invoice_id,)
|
|
||||||
|
|
||||||
return self.call_api_authenticated(path, 'POST', params)
|
|
||||||
|
|
||||||
def buy_orders_list(self, limit=None, offset=None, include_cancelled=None):
|
|
||||||
"""
|
|
||||||
Returns an array of your Coinify buy orders
|
|
||||||
|
|
||||||
See https://www.coinify.com/docs/api/#list-all-buy-orders
|
|
||||||
"""
|
|
||||||
|
|
||||||
query_params = {}
|
|
||||||
|
|
||||||
if limit is not None:
|
|
||||||
query_params['limit'] = limit
|
|
||||||
if offset is not None:
|
|
||||||
query_params['offset'] = offset
|
|
||||||
if include_cancelled is not None:
|
|
||||||
query_params['include_cancelled'] = include_cancelled
|
|
||||||
|
|
||||||
return self.call_api_authenticated('/v3/buys', query_params=query_params)
|
|
||||||
|
|
||||||
def buy_order_create(self, amount, currency, btc_address, instant_order=None,
|
|
||||||
callback_url=None, callback_email=None):
|
|
||||||
"""
|
|
||||||
Create a new buy order
|
|
||||||
|
|
||||||
See https://www.coinify.com/docs/api/#create-a-buy-order
|
|
||||||
"""
|
|
||||||
params = {
|
|
||||||
'amount': amount,
|
|
||||||
'currency': currency,
|
|
||||||
'btc_address': btc_address
|
|
||||||
}
|
|
||||||
|
|
||||||
if instant_order is not None:
|
|
||||||
params['instant_order'] = instant_order
|
|
||||||
if callback_url is not None:
|
|
||||||
params['callback_url'] = callback_url
|
|
||||||
if callback_email is not None:
|
|
||||||
params['callback_email'] = callback_email
|
|
||||||
|
|
||||||
return self.call_api_authenticated('/v3/buys', 'POST', params)
|
|
||||||
|
|
||||||
def buy_order_confirm(self, buy_order_id):
|
|
||||||
"""
|
|
||||||
Confirm a buy order
|
|
||||||
|
|
||||||
See https://www.coinify.com/docs/api/#buy-order-confirm
|
|
||||||
"""
|
|
||||||
path = '/v3/buys/%d/actions/confirm' % (buy_order_id,)
|
|
||||||
|
|
||||||
return self.call_api_authenticated(path, 'PUT')
|
|
||||||
|
|
||||||
def buy_order_get(self, buy_order_id):
|
|
||||||
"""
|
|
||||||
Get a specific buy order
|
|
||||||
|
|
||||||
See https://www.coinify.com/docs/api/#get-a-specific-buy-order
|
|
||||||
"""
|
|
||||||
path = '/v3/buys/%d' % (buy_order_id,)
|
|
||||||
|
|
||||||
return self.call_api_authenticated(path)
|
|
||||||
|
|
||||||
def rates_get(self, currency=None):
|
|
||||||
"""
|
|
||||||
Return buy and sell rates for all available currencies or for the specified currency.
|
|
||||||
:param self:
|
|
||||||
:param currency|None: A 3-char currency code
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
if currency is None:
|
|
||||||
path = '/v3/rates'
|
|
||||||
else:
|
|
||||||
path = '/v3/rates/%s' % (currency,)
|
|
||||||
|
|
||||||
return self.call_api_authenticated(path)
|
|
||||||
|
|
||||||
def balance_get(self):
|
|
||||||
"""
|
|
||||||
Get the balance of a merchant
|
|
||||||
:param path:
|
|
||||||
:return: An array as described in https://www.coinify.com/docs/api/#check-account-balance . If success,
|
|
||||||
then the 'data' value contains the balance in BTC and fiat currency and also the base currency
|
|
||||||
of the merchant that requests it.
|
|
||||||
"""
|
|
||||||
path = '/v3/balance'
|
|
||||||
return self.call_api_authenticated(path)
|
|
||||||
|
|
||||||
def input_currencies_list(self):
|
|
||||||
"""
|
|
||||||
Receive a list of supported input currencies
|
|
||||||
"""
|
|
||||||
|
|
||||||
return self.call_api('/v3/input-currencies')
|
|
||||||
|
|
||||||
def call_api_authenticated(self, path, method='GET', params={}, query_params={}):
|
|
||||||
"""
|
|
||||||
Perform an authenticated API call, using the
|
|
||||||
API key and secret provided in the constructor.
|
|
||||||
|
|
||||||
path: API path, WITH a leading slash, e.g. '/v3/invoices'
|
|
||||||
params: dict with parameters to send with the API call
|
|
||||||
|
|
||||||
Returns a dict as described in https://www.coinify.com/docs/api/#response-format,
|
|
||||||
or None if the HTTP call couldn't be performed correctly.
|
|
||||||
"""
|
|
||||||
extra_headers = {
|
|
||||||
'Authorization': self.generate_authorization_header()
|
|
||||||
}
|
|
||||||
|
|
||||||
return self.call_api(path, method, params, query_params, extra_headers)
|
|
||||||
|
|
||||||
def call_api(self, path, method='GET', params={}, query_params={}, headers={}):
|
|
||||||
"""
|
|
||||||
Perform an API call
|
|
||||||
|
|
||||||
path: API path, WITH a leading slash, e.g. '/v3/invoices'
|
|
||||||
params: dict with parameters to send with the API call
|
|
||||||
|
|
||||||
Returns a dict as described in https://www.coinify.com/docs/api/#response-format,
|
|
||||||
or None if the HTTP call couldn't be performed correctly.
|
|
||||||
"""
|
|
||||||
url = self.api_base_url + path
|
|
||||||
|
|
||||||
headers['Content-Type'] = 'application/json'
|
|
||||||
|
|
||||||
if self.api_key is not None:
|
|
||||||
headers['Authorization'] = self.generate_authorization_header()
|
|
||||||
|
|
||||||
r = requests.request(method, url, json=params, headers=headers, params=query_params)
|
|
||||||
return r.json()
|
|
||||||
|
|
||||||
def generate_authorization_header(self):
|
|
||||||
"""
|
|
||||||
Generate a nonce and a signature for an API call
|
|
||||||
and wrap those in a HTTP header
|
|
||||||
"""
|
|
||||||
# Generate nonce, based on the current Unix timestamp
|
|
||||||
nonce = str(int(time.time() * 1000000))
|
|
||||||
|
|
||||||
# Concatenate nonce and API key
|
|
||||||
message = nonce + self.api_key
|
|
||||||
|
|
||||||
# Compute signature
|
|
||||||
signature = hmac.new(self.api_secret, msg=message, digestmod=hashlib.sha256).hexdigest()
|
|
||||||
|
|
||||||
# Construct the header value
|
|
||||||
header_value = 'Coinify apikey="%s", nonce="%s", signature="%s"' % (self.api_key, nonce, signature)
|
|
||||||
|
|
||||||
return header_value
|
|
||||||
|
|
23
src/vendor/coinify_callback.py
vendored
23
src/vendor/coinify_callback.py
vendored
|
@ -1,23 +0,0 @@
|
||||||
import hashlib, hmac
|
|
||||||
|
|
||||||
class CoinifyCallback:
|
|
||||||
"""
|
|
||||||
Class to validate callbacks from Coinfy
|
|
||||||
"""
|
|
||||||
|
|
||||||
ipn_secret = None
|
|
||||||
"""Coinify IPN callback secret. Get yours at https://www.coinify.com/merchant/ipn"""
|
|
||||||
|
|
||||||
def __init__( self, ipn_secret ):
|
|
||||||
self.ipn_secret = ipn_secret
|
|
||||||
|
|
||||||
def validate_callback( self, callback_raw, signature ):
|
|
||||||
"""
|
|
||||||
Validates a callback and it's signature based on the IPN secret given in the constructor.
|
|
||||||
|
|
||||||
callback_raw must contain the raw JSON POST data sent with the callback (before any JSON decoding)
|
|
||||||
signature must contain the signature as extracted from the 'X-Coinify-Callback-Signature' header,
|
|
||||||
which should be a 64-byte hexadecimal string
|
|
||||||
"""
|
|
||||||
return signature == hmac.new(self.ipn_secret, msg=callback_raw, digestmod=hashlib.sha256).hexdigest()
|
|
||||||
|
|
Loading…
Reference in a new issue