1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
|
from django.conf import settings
import datetime
from decimal import Decimal
import requests
from requests.auth import HTTPBasicAuth
from .models import StripeCheckout, StripeRefund
class StripeException(Exception):
pass
class StripeApi(object):
APIBASE = "https://api.stripe.com/v1/"
def __init__(self, pm):
self.published_key = pm.config('published_key')
self.secret_key = pm.config('secret_key')
def _api_encode(self, params):
for key, value in params.items():
if isinstance(value, list) or isinstance(value, tuple):
for i, subval in enumerate(value):
if isinstance(subval, dict):
subdict = self._encode_nested_dict("%s[%d]" % (key, i), subval)
yield from self._api_encode(subdict)
else:
yield ("%s[%d]" % (key, i), subval)
elif isinstance(value, dict):
subdict = self._encode_nested_dict(key, value)
yield from self._api_encode(subdict)
elif isinstance(value, datetime.datetime):
yield (key, self._encode_datetime(value))
else:
yield (key, value)
def _encode_nested_dict(self, key, data, fmt="%s[%s]"):
d = {}
for subkey, subvalue in data.items():
d[fmt % (key, subkey)] = subvalue
return d
def secret(self, suburl, params=None, raise_for_status=True):
if params:
r = requests.post(self.APIBASE + suburl,
list(self._api_encode(params)),
auth=HTTPBasicAuth(self.secret_key, ''),
)
else:
r = requests.get(self.APIBASE + suburl,
auth=HTTPBasicAuth(self.secret_key, ''),
)
if raise_for_status:
r.raise_for_status()
return r
def get_balance(self):
r = self.secret('balance').json()
print(r)
balance = Decimal(0)
for a in r['available']:
if a['currency'].lower() == settings.CURRENCY_ISO.lower():
balance += Decimal(a['amount']) / 100
break
else:
raise StripeException("No available balance entry found for currency {}".format(settings.CURRENCY_ISO))
for p in r['pending']:
if p['currency'].lower() == settings.CURRENCY_ISO.lower():
balance += Decimal(p['amount']) / 100
break
else:
raise StripeException("No pending balance entry found for currency {}".format(settings.CURRENCY_ISO))
return balance
def update_checkout_status(self, co):
# Update the status of a payment. If it switched from unpaid to paid,
# return True, otherwise False.
if co.completedat:
# Already completed!
return False
# We have to check the payment intent to get all the data that we
# need, so we don't bother checking the co itself.
r = self.secret('payment_intents/{}'.format(co.paymentintent)).json()
if r['status'] == 'succeeded':
# Before we flag it as done, we need to calculate the fees. Those we
# can only find by loking at the charges, and from there finding the
# corresponding balance transaction.
if len(r['charges']['data']) != 1:
raise StripeException("More than one charge found, not supported!")
c = r['charges']['data'][0]
if not c['paid']:
return False
if c['currency'].lower() != settings.CURRENCY_ISO.lower():
raise StripeException("Found payment charge in currency {0}, expected {1}".format(c['currency'], settings.CURRENCY_ISO))
txid = c['balance_transaction']
t = self.secret('balance/history/{}'.format(txid)).json()
if t['currency'].lower() != settings.CURRENCY_ISO.lower():
raise StripeException("Found balance transaction in currency {0}, expected {1}".format(t['currency'], settings.CURRENCY_ISO))
if t['exchange_rate']:
raise StripeException("Found balance transaction with exchange rate set!")
co.fee = Decimal(t['fee']) / 100
co.completedat = datetime.datetime.now()
co.save()
return True
# Still nothing
return False
def refund_transaction(self, co, amount, refundid):
# To refund we need to find the charge id.
r = self.secret('payment_intents/{}'.format(co.paymentintent)).json()
if len(r['charges']['data']) != 1:
raise StripeException("Number of charges is {}, not 1, don't know how to refund".format(len(r['charges']['data'])))
chargeid = r['charges']['data'][0]['id']
r = self.secret('refunds', {
'charge': chargeid,
'amount': int(amount * 100),
'metadata': {
'refundid': refundid,
},
}).json()
refund = StripeRefund(paymentmethod=co.paymentmethod,
chargeid=chargeid,
invoicerefundid=refundid,
amount=amount,
refundid=r['id'])
refund.save()
return r['id']
|