You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

138 lines
4.5 KiB
Python

import string
import random
import requests
import logging
from django.shortcuts import redirect
from django.utils.translation import ugettext_lazy as _
from django.urls import reverse
from django.conf import settings as project_settings
from .base import BackendBase
logger = logging.getLogger(__name__)
prng = random.SystemRandom()
def generate_token(length=16):
charset = string.digits + string.ascii_letters
return ''.join([prng.choice(charset) for _ in range(length)])
class CoinGateBackend(BackendBase):
backend_id = 'coingate'
backend_verbose_name = _("CoinGate")
backend_display_name = _("Cryptocurrencies")
backend_has_recurring = False
def __init__(self, settings):
self.api_token = settings.get('api_token')
if not self.api_token:
return
self.currency = settings.get('currency', 'EUR')
self.title = settings.get('title', 'VPN Payment')
if settings.get('sandbox'):
self.sandbox = True
self.api_base = "https://api-sandbox.coingate.com"
else:
self.sandbox = False
default_base = "https://api.coingate.com"
self.api_base = settings.get('api_base', default_base)
self.backend_enabled = True
def _post(self, endpoint, **kwargs):
headers = {
'Authorization': 'Token ' + self.api_token,
}
url = self.api_base + endpoint
response = requests.post(url, headers=headers, **kwargs)
response.raise_for_status()
j = response.json()
return j
def new_payment(self, payment):
root_url = project_settings.ROOT_URL
assert root_url
token = generate_token()
order = self._post('/v2/orders', data={
'order_id': str(payment.id),
'price_amount': payment.amount / 100,
'price_currency': self.currency,
'receive_currency': self.currency,
'title': self.title,
'callback_url': root_url + reverse('payments:cb_coingate', args=(payment.id,)),
'cancel_url': root_url + reverse('payments:cancel', args=(payment.id,)),
'success_url': root_url + reverse('payments:view', args=(payment.id,)),
'token': token,
})
url = order['payment_url']
payment.backend_extid = order['id']
payment.backend_data['coingate_id'] = order['id']
payment.backend_data['coingate_url'] = url
payment.backend_data['coingate_token'] = token
payment.save()
return redirect(url)
def callback(self, payment, request):
post_data = request.POST
# Verify callback authenticity
saved_token = payment.backend_data.get('coingate_token')
if not saved_token:
logger.debug("payment does not have a coingate_token")
return False
token = post_data.get('token')
if token != saved_token:
logger.debug("unexpected token (%r != %r)", token, saved_token)
return False
order_id = post_data.get('order_id')
if order_id != str(payment.id):
logger.debug("unexpected order_id (%r != %r)", order_id, str(payment.id))
return False
# Handle event
status = post_data.get('status')
if status == 'new' or status == 'pending':
payment.update_status('new')
elif status == 'confirming':
payment.update_status('new', _("Confirming transaction"))
elif status == 'paid':
if payment.status in {'new', 'cancelled', 'error'}:
# we don't have the exact converted amount, but it's not
# important. settings to the requested amount for consistency
payment.paid_amount = payment.amount
payment.confirm()
elif status == 'invalid' or status == 'expired':
if payment.status != 'confirmed':
payment.update_status('cancelled')
elif status == 'refunded':
if payment.status == 'confirmed':
payment.refund()
else:
logger.debug("unexpected payment status: %r", status)
return False
payment.save()
return True
def get_ext_url(self, payment):
if not payment.backend_extid:
return None
if self.sandbox:
return 'https://sandbox.coingate.com/account/orders/%s' % payment.backend_extid
else:
return 'https://coingate.com/account/orders/%s' % payment.backend_extid