pyCryptomusAPI 0.0.7__tar.gz → 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pyCryptomusAPI-0.0.7/pyCryptomusAPI.egg-info → pyCryptomusAPI-0.1.0}/PKG-INFO +1 -1
- pyCryptomusAPI-0.1.0/pyCryptomusAPI/api.py +624 -0
- {pyCryptomusAPI-0.0.7 → pyCryptomusAPI-0.1.0}/pyCryptomusAPI/cryto_types.py +60 -7
- {pyCryptomusAPI-0.0.7 → pyCryptomusAPI-0.1.0}/pyCryptomusAPI/tests.py +1 -0
- {pyCryptomusAPI-0.0.7 → pyCryptomusAPI-0.1.0}/pyCryptomusAPI/version.py +1 -1
- {pyCryptomusAPI-0.0.7 → pyCryptomusAPI-0.1.0/pyCryptomusAPI.egg-info}/PKG-INFO +1 -1
- pyCryptomusAPI-0.0.7/pyCryptomusAPI/api.py +0 -415
- {pyCryptomusAPI-0.0.7 → pyCryptomusAPI-0.1.0}/LICENSE +0 -0
- {pyCryptomusAPI-0.0.7 → pyCryptomusAPI-0.1.0}/MANIFEST.in +0 -0
- {pyCryptomusAPI-0.0.7 → pyCryptomusAPI-0.1.0}/README.md +0 -0
- {pyCryptomusAPI-0.0.7 → pyCryptomusAPI-0.1.0}/pyCryptomusAPI/__init__.py +0 -0
- {pyCryptomusAPI-0.0.7 → pyCryptomusAPI-0.1.0}/pyCryptomusAPI.egg-info/SOURCES.txt +0 -0
- {pyCryptomusAPI-0.0.7 → pyCryptomusAPI-0.1.0}/pyCryptomusAPI.egg-info/dependency_links.txt +0 -0
- {pyCryptomusAPI-0.0.7 → pyCryptomusAPI-0.1.0}/pyCryptomusAPI.egg-info/top_level.txt +0 -0
- {pyCryptomusAPI-0.0.7 → pyCryptomusAPI-0.1.0}/setup.cfg +0 -0
- {pyCryptomusAPI-0.0.7 → pyCryptomusAPI-0.1.0}/setup.py +0 -0
|
@@ -0,0 +1,624 @@
|
|
|
1
|
+
from hashlib import md5
|
|
2
|
+
from time import sleep
|
|
3
|
+
import base64
|
|
4
|
+
import requests
|
|
5
|
+
|
|
6
|
+
from .cryto_types import *
|
|
7
|
+
|
|
8
|
+
API_URL = "https://api.cryptomus.com/v1/"
|
|
9
|
+
|
|
10
|
+
# noinspection PyPep8Naming
|
|
11
|
+
class pyCryptomusAPIException(Exception):
|
|
12
|
+
def __init__(self, code, message, full_error = ""):
|
|
13
|
+
self.code = code
|
|
14
|
+
self.message = message
|
|
15
|
+
self.full_error = full_error
|
|
16
|
+
super().__init__(self.message)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# noinspection PyPep8Naming
|
|
20
|
+
class pyCryptomusAPI:
|
|
21
|
+
"""
|
|
22
|
+
Cryptomus API Client
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def __init__(self,
|
|
26
|
+
merchant_uuid, payment_api_key = None, payout_api_key = None,
|
|
27
|
+
print_errors = False, timeout = None, add_request_params = None):
|
|
28
|
+
"""
|
|
29
|
+
Create the pyCryptomusAPI instance.
|
|
30
|
+
|
|
31
|
+
:param merchant_uuid: The merchant's uuid, which you can find in the merchant's personal account in the settings section.
|
|
32
|
+
:param payment_api_key: API key for processing payments
|
|
33
|
+
:param payout_api_key: API key for accepting payment and making payouts
|
|
34
|
+
:param print_errors: (Optional) Print dumps on request errors
|
|
35
|
+
:param timeout: (Optional) Request timeout
|
|
36
|
+
:param add_request_params: (List, Optional) Additional request parameters to pass with API calls
|
|
37
|
+
"""
|
|
38
|
+
self.merchant_uuid = merchant_uuid
|
|
39
|
+
self.payment_api_key = payment_api_key
|
|
40
|
+
self.payout_api_key = payout_api_key
|
|
41
|
+
self.print_errors = print_errors
|
|
42
|
+
self.timeout = timeout
|
|
43
|
+
self.add_request_params = add_request_params
|
|
44
|
+
if (not self.payment_api_key) and (not self.payout_api_key):
|
|
45
|
+
raise Exception("You must specify at least one API key.")
|
|
46
|
+
|
|
47
|
+
def __request(self, method_url, mode, **kwargs):
|
|
48
|
+
"""
|
|
49
|
+
Send request to API
|
|
50
|
+
|
|
51
|
+
:param method_url: (String) API method url (part)
|
|
52
|
+
:param mode: (Int) Method mode (1: payment, 2: payout)
|
|
53
|
+
:param kwargs: request data
|
|
54
|
+
"""
|
|
55
|
+
if kwargs:
|
|
56
|
+
data = dict(kwargs)
|
|
57
|
+
else:
|
|
58
|
+
data = {}
|
|
59
|
+
|
|
60
|
+
if self.add_request_params:
|
|
61
|
+
data.update(self.add_request_params)
|
|
62
|
+
|
|
63
|
+
base_resp = None
|
|
64
|
+
try:
|
|
65
|
+
key = self.payment_api_key if (mode == 1) else self.payout_api_key
|
|
66
|
+
if not key:
|
|
67
|
+
raise pyCryptomusAPIException(-6, "Key is empty")
|
|
68
|
+
if not(key.isascii()):
|
|
69
|
+
raise pyCryptomusAPIException(-6, "Key contains non-ascii characters")
|
|
70
|
+
if not self.merchant_uuid:
|
|
71
|
+
raise pyCryptomusAPIException(-6, "Merchant UUID is empty")
|
|
72
|
+
if not(self.merchant_uuid.isascii()):
|
|
73
|
+
raise pyCryptomusAPIException(-6, "Merchant UUID contains non-ascii characters")
|
|
74
|
+
json_dumps = json.dumps(data)
|
|
75
|
+
# json_dumps = json.dumps(data, ensure_ascii=False, separators=(',', ':'))
|
|
76
|
+
pre_sign = json_dumps if data else ""
|
|
77
|
+
if pre_sign and not(pre_sign.isascii()):
|
|
78
|
+
raise pyCryptomusAPIException(-6, "Data dump contains non-ascii characters")
|
|
79
|
+
sign = md5(base64.b64encode(pre_sign.encode('ascii')) + key.encode('ascii')).hexdigest()
|
|
80
|
+
headers = {
|
|
81
|
+
"merchant": self.merchant_uuid,
|
|
82
|
+
"sign": sign,
|
|
83
|
+
"Content-Type": "application/json",
|
|
84
|
+
}
|
|
85
|
+
base_resp = requests.post(API_URL + method_url, data=pre_sign, headers=headers, timeout=self.timeout)
|
|
86
|
+
resp = base_resp.json()
|
|
87
|
+
except ValueError as ve:
|
|
88
|
+
code = base_resp.status_code if base_resp else -2
|
|
89
|
+
message = "Response decode failed: {}".format(ve)
|
|
90
|
+
if self.print_errors:
|
|
91
|
+
print(message)
|
|
92
|
+
raise pyCryptomusAPIException(code, message)
|
|
93
|
+
except pyCryptomusAPIException as pe:
|
|
94
|
+
raise pe
|
|
95
|
+
except Exception as e:
|
|
96
|
+
code = base_resp.status_code if base_resp else -3
|
|
97
|
+
message = "Request unknown exception: {}".format(e)
|
|
98
|
+
if self.print_errors:
|
|
99
|
+
print(message)
|
|
100
|
+
raise pyCryptomusAPIException(code, message)
|
|
101
|
+
if not resp:
|
|
102
|
+
code = base_resp.status_code if base_resp else -4
|
|
103
|
+
message = "None request response"
|
|
104
|
+
if self.print_errors:
|
|
105
|
+
print(message)
|
|
106
|
+
raise pyCryptomusAPIException(code, message)
|
|
107
|
+
elif not resp.get("result"):
|
|
108
|
+
code = base_resp.status_code if base_resp else -5
|
|
109
|
+
if resp.get("message"):
|
|
110
|
+
message = resp["message"]
|
|
111
|
+
elif resp.get("errors"):
|
|
112
|
+
message = resp["errors"]
|
|
113
|
+
else:
|
|
114
|
+
message = "No error info provided"
|
|
115
|
+
if self.print_errors:
|
|
116
|
+
print("Response: {}".format(resp))
|
|
117
|
+
raise pyCryptomusAPIException(code, message)
|
|
118
|
+
# code -6 is used above
|
|
119
|
+
else:
|
|
120
|
+
return resp
|
|
121
|
+
|
|
122
|
+
def create_invoice(self,
|
|
123
|
+
amount, currency, order_id, network = None,
|
|
124
|
+
url_return = None, url_success = None, url_callback = None,
|
|
125
|
+
is_payment_multiple = None, lifetime = None, to_currency = None, subtract = None,
|
|
126
|
+
accuracy_payment_percent = None, additional_data = None, currencies = None,
|
|
127
|
+
except_currencies = None, course_source = None, from_referral_code = None,
|
|
128
|
+
discount_percent = None, is_refresh = None):
|
|
129
|
+
"""
|
|
130
|
+
Creating an invoice
|
|
131
|
+
https://doc.cryptomus.com/payments/creating-invoice
|
|
132
|
+
Requires PAYMENT API key
|
|
133
|
+
|
|
134
|
+
amount: (Float) Amount to be paid. If there are pennies in the amount, then send them with a separator '.' Example: 10.28
|
|
135
|
+
currency: (String) Currency code (https://doc.cryptomus.com/reference)
|
|
136
|
+
order_id: (String[1..128]) Order ID in your system. The parameter should be a string consisting of alphabetic characters, numbers, underscores, and dashes. It should not contain any spaces or special characters.
|
|
137
|
+
network: (String, Optional) Blockchain network code (https://doc.cryptomus.com/reference)
|
|
138
|
+
url_return: (String[6..255], Optional) Before paying, the user can click on the button on the payment form and return to the store page at this URL.
|
|
139
|
+
url_success: (String[6..255], Optional) After successful payment, the user can click on the button on the payment form and return to this URL.
|
|
140
|
+
url_callback: (String[6..255], Optional) Url to which webhooks with payment status will be sent.
|
|
141
|
+
is_payment_multiple: (Bool, Optional) Whether the user is allowed to pay the remaining amount. This is useful when the user has not paid the entire amount of the invoice for one transaction, and you want to allow him to pay up to the full amount. If you disable this feature, the invoice will finalize after receiving the first payment and you will receive funds to your balance.
|
|
142
|
+
lifetime: (Int[300..43200], Optional) The lifespan of the issued invoice (?in seconds?)
|
|
143
|
+
to_currency: (String, Optional) The parameter is used to specify the target currency for converting the invoice amount. When creating an invoice, you provide an amount and currency, and the API will convert that amount to the equivalent value in the to_currency. For example, to create an invoice for 20 USD in bitcoin: amount: 20, currency: USD, to_currency: BTC. The API will convert 20 USD amount to its equivalent in BTC based on the current exchange rate and the user will pay in BTC.
|
|
144
|
+
subtract: (Int[0..100], Optional) Percentage of the payment commission charged to the client. If you have a rate of 1%, then if you create an invoice for 100 USDT with subtract = 100 (the client pays 100% commission), the client will have to pay 101 USDT.
|
|
145
|
+
accuracy_payment_percent: (Float[0..5], Optional) Acceptable inaccuracy in payment. For example, if you pass the value 5, the invoice will be marked as Paid even if the client has paid only 95% of the amount. The actual payment amount will be credited to the balance.
|
|
146
|
+
additional_data: (?String?, Optional) Additional information for you (not shown to the client).
|
|
147
|
+
currencies: (List[Currency][1..255], Optional) List of allowed currencies for payment. This is useful if you want to limit the list of coins that your customers can use to pay invoices.
|
|
148
|
+
except_currencies: (List[Currency], Optional) List of excluded currencies for payment.
|
|
149
|
+
course_source: (String[4..20], Optional) The service from which the exchange rates are taken for conversion in the invoice. If not passed, Cryptomus exchange rates are used. Available values: https://doc.cryptomus.com/payments/creating-invoice
|
|
150
|
+
from_referral_code: (String, Optional) The merchant who makes the request connects to a referrer by code. For example, you are an application that generates invoices via the Cryptomus API and your customers are other stores. They enter their api key and merchant id in your application, and you send requests with their credentials and passing your referral code. Thus, your clients become referrals on your Cryptomus account and you will receive income from their turnover.
|
|
151
|
+
discount_percent: (Int[-99..100], Optional) Positive numbers: allows you to set a discount. To set a 5% discount for the payment, you should pass a value: 5. Negative numbers: allows you to set custom additional commission. To set an additional commission of 10% for the payment, you should pass a value: -10.
|
|
152
|
+
is_refresh: (Bool, Optional) Using this parameter, you can update the lifetime and get a new address for the invoice if the lifetime has expired. To do that, you need to pass all required parameters, and the invoice with passed order_id will be refreshed.
|
|
153
|
+
|
|
154
|
+
* The order_id must be unique within the merchant invoices/static wallets/recurrence payments
|
|
155
|
+
* When we find an existing invoice with order_id, we return its details, a new invoice will not be created.
|
|
156
|
+
* The to_currency should always be the cryptocurrency code, not a fiat currency code.
|
|
157
|
+
* The discount percentage when creating an invoice is taken into account only if the invoice has a specific cryptocurrency.
|
|
158
|
+
* Only address, payment_status and expired_at are changed. No other fields are changed, regardless of the parameters passed.
|
|
159
|
+
"""
|
|
160
|
+
method = "payment"
|
|
161
|
+
params = {
|
|
162
|
+
"amount": str(amount),
|
|
163
|
+
"currency": currency,
|
|
164
|
+
"order_id": str(order_id),
|
|
165
|
+
}
|
|
166
|
+
if network:
|
|
167
|
+
params["network"] = network
|
|
168
|
+
if url_return:
|
|
169
|
+
params["url_return"] = url_return
|
|
170
|
+
if url_success:
|
|
171
|
+
params["url_success"] = url_success
|
|
172
|
+
if url_callback:
|
|
173
|
+
params["url_callback"] = url_callback
|
|
174
|
+
if is_payment_multiple is not None:
|
|
175
|
+
params["is_payment_multiple"] = is_payment_multiple
|
|
176
|
+
if lifetime is not None:
|
|
177
|
+
params["lifetime"] = str(lifetime)
|
|
178
|
+
if to_currency:
|
|
179
|
+
params["to_currency"] = to_currency
|
|
180
|
+
if subtract is not None:
|
|
181
|
+
params["subtract"] = str(subtract)
|
|
182
|
+
if accuracy_payment_percent is not None:
|
|
183
|
+
params["accuracy_payment_percent"] = str(accuracy_payment_percent)
|
|
184
|
+
if additional_data:
|
|
185
|
+
params["additional_data"] = additional_data
|
|
186
|
+
if currencies:
|
|
187
|
+
params["currencies"] = [i.to_dict() for i in currencies]
|
|
188
|
+
if except_currencies:
|
|
189
|
+
params["except_currencies"] = [i.to_dict() for i in except_currencies]
|
|
190
|
+
if course_source:
|
|
191
|
+
params["course_source"] = course_source
|
|
192
|
+
if from_referral_code:
|
|
193
|
+
params["from_referral_code"] = from_referral_code
|
|
194
|
+
if discount_percent is not None:
|
|
195
|
+
params["discount_percent"] = str(discount_percent)
|
|
196
|
+
if is_refresh is not None:
|
|
197
|
+
params["is_refresh"] = is_refresh
|
|
198
|
+
resp = self.__request(method, 1, **params).get("result")
|
|
199
|
+
return Invoice.de_json(resp)
|
|
200
|
+
|
|
201
|
+
def create_wallet(self,
|
|
202
|
+
network, currency, order_id, url_callback = None, from_referral_code = None):
|
|
203
|
+
"""
|
|
204
|
+
Creating a Static wallet
|
|
205
|
+
https://doc.cryptomus.com/payments/creating-static
|
|
206
|
+
Requires PAYMENT API key
|
|
207
|
+
|
|
208
|
+
network: (String) Blockchain network code (https://doc.cryptomus.com/reference)
|
|
209
|
+
currency: (String) Currency code (https://doc.cryptomus.com/reference)
|
|
210
|
+
order_id: (String[1..100]) Order ID in your system. The parameter should be a string consisting of alphabetic characters, numbers, underscores, and dashes. It should not contain any spaces or special characters.
|
|
211
|
+
url_callback: (String[6..255], Optional) URL, to which the webhook will be sent after each top-up of the wallet.
|
|
212
|
+
from_referral_code: (String, Optional) The merchant who makes the request connects to a referrer by code. For example, you are an application that generates invoices via the Cryptomus API and your customers are other stores. They enter their api key and merchant id in your application, and you send requests with their credentials and passing your referral code. Thus, your clients become referrals on your Cryptomus account and you will receive income from their turnover.
|
|
213
|
+
|
|
214
|
+
* The order_id must be unique within the merchant invoices/static wallets/recurrence payments
|
|
215
|
+
* When we find an existing invoice with order_id, we return its details, a new invoice will not be created.
|
|
216
|
+
"""
|
|
217
|
+
method = "wallet"
|
|
218
|
+
params = {
|
|
219
|
+
"network": network,
|
|
220
|
+
"currency": currency,
|
|
221
|
+
"order_id": str(order_id),
|
|
222
|
+
}
|
|
223
|
+
if url_callback:
|
|
224
|
+
params["url_callback"] = url_callback
|
|
225
|
+
if from_referral_code:
|
|
226
|
+
params["from_referral_code"] = from_referral_code
|
|
227
|
+
resp = self.__request(method, 1, **params).get("result")
|
|
228
|
+
return Wallet.de_json(resp)
|
|
229
|
+
|
|
230
|
+
def block_wallet(self,
|
|
231
|
+
wallet_uuid = None, order_id = None, is_force_refund = None):
|
|
232
|
+
"""
|
|
233
|
+
Block static wallet
|
|
234
|
+
https://doc.cryptomus.com/payments/block-wallet
|
|
235
|
+
You need to pass one of the required parameters, if you pass both, the account will be identified by order_id
|
|
236
|
+
Requires PAYMENT API key
|
|
237
|
+
|
|
238
|
+
wallet_uuid: (String, Optional if order_id set) UUID of a static wallet
|
|
239
|
+
order_id: (String[1..32], Optional if wallet_uuid set) Order ID of a static wallet
|
|
240
|
+
is_force_refund: (Bool, Optional) Refund all incoming payments to sender’s address
|
|
241
|
+
|
|
242
|
+
* You need to pass one of the required parameters, if you pass both, the account will be identified by order_id
|
|
243
|
+
"""
|
|
244
|
+
method = "wallet/block-address"
|
|
245
|
+
params = {
|
|
246
|
+
}
|
|
247
|
+
if (not wallet_uuid) and (not order_id):
|
|
248
|
+
raise pyCryptomusAPIException(0, "You need to pass one of the required parameters")
|
|
249
|
+
if wallet_uuid:
|
|
250
|
+
params["uuid"] = wallet_uuid
|
|
251
|
+
if order_id:
|
|
252
|
+
params["order_id"] = order_id
|
|
253
|
+
if is_force_refund is not None:
|
|
254
|
+
params["is_force_refund"] = is_force_refund
|
|
255
|
+
resp = self.__request(method, 1, **params).get("result")
|
|
256
|
+
return resp
|
|
257
|
+
|
|
258
|
+
def block_wallet_refund(self,
|
|
259
|
+
address, wallet_uuid = None, order_id = None):
|
|
260
|
+
"""
|
|
261
|
+
Refund payments on blocked address
|
|
262
|
+
https://doc.cryptomus.com/payments/refundblocked
|
|
263
|
+
You need to pass one of the required parameters, if you pass both, the account will be identified by order_id
|
|
264
|
+
Requires PAYMENT API key
|
|
265
|
+
|
|
266
|
+
address: (String[10..128]) Refund all blocked funds to this address
|
|
267
|
+
wallet_uuid: (String, Optional if order_id set) UUID of a static wallet
|
|
268
|
+
order_id: (String[1..32], Optional if wallet_uuid set) Order ID of a static wallet
|
|
269
|
+
|
|
270
|
+
* To refund payments you need to pass either uuid or order_id, if you pass both, the static wallet will be identified by uuid
|
|
271
|
+
"""
|
|
272
|
+
method = "wallet/blocked-address-refund"
|
|
273
|
+
params = {
|
|
274
|
+
"address": address,
|
|
275
|
+
}
|
|
276
|
+
if (not wallet_uuid) and (not order_id):
|
|
277
|
+
raise pyCryptomusAPIException(0, "You need to pass one of the required parameters")
|
|
278
|
+
if wallet_uuid:
|
|
279
|
+
params["uuid"] = wallet_uuid
|
|
280
|
+
if order_id:
|
|
281
|
+
params["order_id"] = order_id
|
|
282
|
+
resp = self.__request(method, 1, **params).get("result")
|
|
283
|
+
return resp
|
|
284
|
+
|
|
285
|
+
def payment_information(self,
|
|
286
|
+
invoice_uuid = None, order_id = None):
|
|
287
|
+
"""
|
|
288
|
+
Payment information
|
|
289
|
+
https://doc.cryptomus.com/payments/payment-information
|
|
290
|
+
You need to pass one of the required parameters, if you pass both, the account will be identified by order_id
|
|
291
|
+
Requires PAYMENT API key
|
|
292
|
+
|
|
293
|
+
invoice_uuid: (String, Optional if order_id set) Invoice UUID
|
|
294
|
+
order_id: (String[1..128], Optional if wallet_uuid set) Invoice order ID
|
|
295
|
+
|
|
296
|
+
* To get the invoice status you need to pass one of the required parameters, if you pass both, the account will be identified by order_id
|
|
297
|
+
"""
|
|
298
|
+
method = "payment/info"
|
|
299
|
+
params = {
|
|
300
|
+
}
|
|
301
|
+
if (not invoice_uuid) and (not order_id):
|
|
302
|
+
raise pyCryptomusAPIException(0, "You need to pass one of the required parameters")
|
|
303
|
+
if invoice_uuid:
|
|
304
|
+
params["uuid"] = invoice_uuid
|
|
305
|
+
if order_id:
|
|
306
|
+
params["order_id"] = order_id
|
|
307
|
+
resp = self.__request(method, 1, **params).get("result")
|
|
308
|
+
return Invoice.de_json(resp)
|
|
309
|
+
|
|
310
|
+
def refund(self,
|
|
311
|
+
address, is_subtract, invoice_uuid = None, order_id = None):
|
|
312
|
+
"""
|
|
313
|
+
Refund
|
|
314
|
+
https://doc.cryptomus.com/payments/refund
|
|
315
|
+
You need to pass one of the required parameters, if you pass both, the account will be identified by invoice_uuid
|
|
316
|
+
Requires PAYMENT API key
|
|
317
|
+
|
|
318
|
+
address: (String) The address to which the refund should be made
|
|
319
|
+
is_subtract: (Bool) Whether to take a commission from the merchant's balance or from the refund amount. true - take the commission from merchant balance. false - reduce the refundable amount by the commission amount
|
|
320
|
+
invoice_uuid: (String, Optional if order_id set) Invoice UUID
|
|
321
|
+
order_id: (String[1..128], Optional if invoice_uuid set) Invoice order ID
|
|
322
|
+
|
|
323
|
+
* Invoice is identified by order_id or uuid, if you pass both, the account will be identified by uuid
|
|
324
|
+
"""
|
|
325
|
+
method = "payment/refund"
|
|
326
|
+
params = {
|
|
327
|
+
"address": address,
|
|
328
|
+
"is_subtract": is_subtract,
|
|
329
|
+
}
|
|
330
|
+
if (not invoice_uuid) and (not order_id):
|
|
331
|
+
raise pyCryptomusAPIException(0, "You need to pass one of the required parameters")
|
|
332
|
+
if invoice_uuid:
|
|
333
|
+
params["uuid"] = invoice_uuid
|
|
334
|
+
if order_id:
|
|
335
|
+
params["order_id"] = order_id
|
|
336
|
+
resp = self.__request(method, 1, **params).get("result")
|
|
337
|
+
return Invoice.de_json(resp)
|
|
338
|
+
|
|
339
|
+
def payment_history(self, date_from = None, date_to = None, cursor = None):
|
|
340
|
+
"""
|
|
341
|
+
Payment history
|
|
342
|
+
https://doc.cryptomus.com/payments/payment-history
|
|
343
|
+
Requires PAYMENT API key
|
|
344
|
+
|
|
345
|
+
date_from: (String, Optional) Filtering by creation date, from
|
|
346
|
+
date_to: (String, Optional) Filtering by creation date, to
|
|
347
|
+
cursor: (String, Optional) Page cursor (hash)
|
|
348
|
+
"""
|
|
349
|
+
params = {
|
|
350
|
+
}
|
|
351
|
+
if date_from:
|
|
352
|
+
params["date_from"] = date_from.strftime(CryptomusDateFormat)
|
|
353
|
+
if date_to:
|
|
354
|
+
params["date_to"] = date_to.strftime(CryptomusDateFormat)
|
|
355
|
+
if cursor:
|
|
356
|
+
params["cursor"] = cursor
|
|
357
|
+
method = "payment/list"
|
|
358
|
+
if params:
|
|
359
|
+
resp = self.__request(method, 1, **params).get("result")
|
|
360
|
+
else:
|
|
361
|
+
resp = self.__request(method, 1).get("result")
|
|
362
|
+
return PaymentsHistory.de_json(resp)
|
|
363
|
+
|
|
364
|
+
def payment_history_filtered(
|
|
365
|
+
self,
|
|
366
|
+
date_from = None, date_to = None,
|
|
367
|
+
max_results = 15, max_pages = 10,
|
|
368
|
+
currencies = None, networks = None, addresses = None,
|
|
369
|
+
statuses = None, is_final = None, page_delay = 1):
|
|
370
|
+
"""
|
|
371
|
+
Payment history (advanced mode)
|
|
372
|
+
|
|
373
|
+
Based on: payment_history
|
|
374
|
+
https://doc.cryptomus.com/payments/payment-history
|
|
375
|
+
Requires PAYMENT API key
|
|
376
|
+
|
|
377
|
+
Collects only results under filters.
|
|
378
|
+
Process as many pages as needed to collect max_results, but not more than max_pages.
|
|
379
|
+
|
|
380
|
+
date_from: (String, Optional) Filtering by creation date, from
|
|
381
|
+
date_to: (String, Optional) Filtering by creation date, to
|
|
382
|
+
max_results: (Int, Optional, default=15) Max number of results to collect
|
|
383
|
+
max_pages: (Int, Optional, default=10) Max number of pages to process
|
|
384
|
+
currencies: (List of Strings, Optional) List of accepted currencies. Codes: https://doc.cryptomus.com/reference
|
|
385
|
+
networks: (List of Strings, Optional) List of accepted networks. Codes: https://doc.cryptomus.com/reference
|
|
386
|
+
addresses: (List of Strings, Optional) List of accepted addresses
|
|
387
|
+
statuses: (List of Strings, Optional) List of accepted statuses. Codes: https://doc.cryptomus.com/payments/payment-statuses
|
|
388
|
+
is_final: (Bool, Optional) If True, only final payments will be collected, if False - only non-final
|
|
389
|
+
page_delay: (Int, Optional, default=1) Delay between pages (in seconds)
|
|
390
|
+
"""
|
|
391
|
+
|
|
392
|
+
result = PaymentsHistory()
|
|
393
|
+
|
|
394
|
+
page_number = 0
|
|
395
|
+
cursor = None
|
|
396
|
+
while page_number < max_pages:
|
|
397
|
+
if page_number > 0: sleep(page_delay)
|
|
398
|
+
resp = self.payment_history(date_from = date_from, date_to = date_to, cursor = cursor)
|
|
399
|
+
|
|
400
|
+
if not resp.items:
|
|
401
|
+
# No (more) payments
|
|
402
|
+
break
|
|
403
|
+
|
|
404
|
+
for payment in resp.items:
|
|
405
|
+
if currencies and not(payment.currency in currencies):
|
|
406
|
+
continue
|
|
407
|
+
if networks and not(payment.network in networks):
|
|
408
|
+
continue
|
|
409
|
+
if addresses and not(payment.address in addresses):
|
|
410
|
+
continue
|
|
411
|
+
if statuses and not(payment.status in statuses):
|
|
412
|
+
continue
|
|
413
|
+
if (is_final is not None) and payment.is_final != is_final:
|
|
414
|
+
continue
|
|
415
|
+
result.items.append(payment)
|
|
416
|
+
|
|
417
|
+
if len(result.items) >= max_results:
|
|
418
|
+
# Enough results collected
|
|
419
|
+
break
|
|
420
|
+
|
|
421
|
+
if len(result.items) >= max_results:
|
|
422
|
+
# Enough results collected
|
|
423
|
+
break
|
|
424
|
+
|
|
425
|
+
cursor = resp.paginate.nextCursor
|
|
426
|
+
if not cursor:
|
|
427
|
+
# No more pages
|
|
428
|
+
break
|
|
429
|
+
page_number += 1
|
|
430
|
+
|
|
431
|
+
return result
|
|
432
|
+
|
|
433
|
+
def payment_services(self):
|
|
434
|
+
"""
|
|
435
|
+
Get collection of all available payment services
|
|
436
|
+
https://doc.cryptomus.com/payments/list-of-services
|
|
437
|
+
Requires PAYMENT API key
|
|
438
|
+
"""
|
|
439
|
+
method = "payment/services"
|
|
440
|
+
resp = self.__request(method, 1).get("result")
|
|
441
|
+
return [Service.de_json(i) for i in resp]
|
|
442
|
+
|
|
443
|
+
"""
|
|
444
|
+
Request
|
|
445
|
+
Query parameters
|
|
446
|
+
NAME PARAMETER TYPE DEFAULT VALUE DEFINITION
|
|
447
|
+
amount* string Payout amount
|
|
448
|
+
currency* string
|
|
449
|
+
Currency code for the payout
|
|
450
|
+
If Currency if fiat, the to_currency parameter is required.
|
|
451
|
+
order_id*
|
|
452
|
+
string
|
|
453
|
+
min:1
|
|
454
|
+
max:100
|
|
455
|
+
alpha_dash
|
|
456
|
+
Order ID in your system
|
|
457
|
+
The parameter should be a string consisting of alphabetic characters, numbers, underscores, and dashes. It should not contain any spaces or special characters.
|
|
458
|
+
The order_id must be unique within the merchant payouts
|
|
459
|
+
When we find an existing payout with order_id, we return its details, a new payout will not be created.
|
|
460
|
+
address* string The address of the wallet to which the withdrawal will be made
|
|
461
|
+
is_subtract* boolean
|
|
462
|
+
Defines where the withdrawal fee will be deducted
|
|
463
|
+
true - from your balance
|
|
464
|
+
false - from payout amount, the payout amount will be decreased
|
|
465
|
+
network* string
|
|
466
|
+
Blockchain network code
|
|
467
|
+
Not required when the currency/to_currency is a cryptocurrency and has only one network, for example BTC
|
|
468
|
+
url_callback URL to which webhooks with payout status will be sent
|
|
469
|
+
to_currency Cryptocurrency code in which the payout will be made. It is used when the currency parameter is fiat. See examples below
|
|
470
|
+
course_source string
|
|
471
|
+
Available values
|
|
472
|
+
-
|
|
473
|
+
Binance
|
|
474
|
+
-
|
|
475
|
+
BinanceP2p
|
|
476
|
+
-
|
|
477
|
+
Exmo
|
|
478
|
+
-
|
|
479
|
+
Kucoin
|
|
480
|
+
-
|
|
481
|
+
Garantexio
|
|
482
|
+
Value from merchant's settings
|
|
483
|
+
The service from which the exchange rates are taken for conversion in the invoice.
|
|
484
|
+
The parameter is applied only if the currency is fiat, otherwise the default value is taken from the merchant's settings.
|
|
485
|
+
from_currency string null Allows to automatically convert the withdrawal amount and use the from_currency balance. Only USDT is available.
|
|
486
|
+
priority
|
|
487
|
+
string
|
|
488
|
+
min: 4
|
|
489
|
+
max: 11
|
|
490
|
+
Available values
|
|
491
|
+
-
|
|
492
|
+
recommended
|
|
493
|
+
-
|
|
494
|
+
economy
|
|
495
|
+
-
|
|
496
|
+
high
|
|
497
|
+
-
|
|
498
|
+
highest
|
|
499
|
+
recommended
|
|
500
|
+
The parameter for selecting the withdrawal priority. The cost of the withdrawal fee depends on the selected parameter.
|
|
501
|
+
This parameter is applied only in case of using the BTC, ETH, POLYGON, and BSC networks.
|
|
502
|
+
memo
|
|
503
|
+
string
|
|
504
|
+
min: 1
|
|
505
|
+
max: 30
|
|
506
|
+
Additional identifier for TON, used to specify a particular recipient or target
|
|
507
|
+
* - mandatory parameter
|
|
508
|
+
"""
|
|
509
|
+
|
|
510
|
+
def create_payout(self,
|
|
511
|
+
amount, currency, order_id, address, is_subtract, network,
|
|
512
|
+
url_callback = None, to_currency = None, course_source = None,
|
|
513
|
+
from_currency = None, priority = None, memo = None):
|
|
514
|
+
"""
|
|
515
|
+
Creating a payout
|
|
516
|
+
https://doc.cryptomus.com/payouts/creating-payout
|
|
517
|
+
Requires PAYOUT API key
|
|
518
|
+
|
|
519
|
+
amount: (String) Payout amount
|
|
520
|
+
currency: (String) Currency code for the payout. If Currency if fiat, the to_currency parameter is required.
|
|
521
|
+
order_id: (String[1..100]) Order ID in your system. The parameter should be a string consisting of alphabetic characters, numbers, underscores, and dashes. It should not contain any spaces or special characters. The order_id must be unique within the merchant payouts. When we find an existing payout with order_id, we return its details, a new payout will not be created.
|
|
522
|
+
address: (String) The address of the wallet to which the withdrawal will be made
|
|
523
|
+
is_subtract: (Bool) Defines where the withdrawal fee will be deducted. true - from your balance. false - from payout amount, the payout amount will be decreased.
|
|
524
|
+
network: (String) Blockchain network code.Not required when the currency/to_currency is a cryptocurrency and has only one network, for example BTC
|
|
525
|
+
url_callback: (String, Optional) URL to which webhooks with payout status will be sent
|
|
526
|
+
to_currency: (String, Optional) Cryptocurrency code in which the payout will be made. It is used when the currency parameter is fiat.
|
|
527
|
+
course_source: (String, Optional) The service from which the exchange rates are taken for conversion in the invoice. The parameter is applied only if the currency is fiat, otherwise the default value is taken from the merchant's settings.
|
|
528
|
+
from_currency: (String, Optional) Allows to automatically convert the withdrawal amount and use the from_currency balance. Only USDT is available.
|
|
529
|
+
priority: (String, Optional) The parameter for selecting the withdrawal priority. The cost of the withdrawal fee depends on the selected parameter. This parameter is applied only in case of using the BTC, ETH, POLYGON, and BSC networks. Available values: recommended, economy, high, highest
|
|
530
|
+
memo: (String, Optional) Additional identifier for TON, used to specify a particular recipient or target
|
|
531
|
+
"""
|
|
532
|
+
method = "payout"
|
|
533
|
+
params = {
|
|
534
|
+
"amount": str(amount),
|
|
535
|
+
"currency": currency,
|
|
536
|
+
"order_id": str(order_id),
|
|
537
|
+
"address": address,
|
|
538
|
+
"is_subtract": is_subtract,
|
|
539
|
+
"network": network,
|
|
540
|
+
}
|
|
541
|
+
if url_callback:
|
|
542
|
+
params["url_callback"] = url_callback
|
|
543
|
+
if to_currency:
|
|
544
|
+
params["to_currency"] = to_currency
|
|
545
|
+
if course_source:
|
|
546
|
+
params["course_source"] = course_source
|
|
547
|
+
if from_currency:
|
|
548
|
+
params["from_currency"] = from_currency
|
|
549
|
+
if priority:
|
|
550
|
+
params["priority"] = priority
|
|
551
|
+
if memo:
|
|
552
|
+
params["memo"] = memo
|
|
553
|
+
resp = self.__request(method, 2, **params).get("result")
|
|
554
|
+
return Payout.de_json(resp)
|
|
555
|
+
|
|
556
|
+
def payout_information(self,
|
|
557
|
+
payout_uuid = None, order_id = None):
|
|
558
|
+
"""
|
|
559
|
+
Payout information
|
|
560
|
+
https://doc.cryptomus.com/payouts/payout-information
|
|
561
|
+
You need to pass one of the required parameters, if you pass both, the account will be identified by order_id
|
|
562
|
+
Requires PAYOUT API key
|
|
563
|
+
|
|
564
|
+
payout_uuid: (String, Optional if order_id set) Payout UUID
|
|
565
|
+
order_id: (String[1..128], Optional if wallet_uuid set) Payout order ID
|
|
566
|
+
|
|
567
|
+
* To get the payout information you need to pass one of the parameters, if you pass both, the payout will be identified by order_id
|
|
568
|
+
"""
|
|
569
|
+
method = "payout/info"
|
|
570
|
+
params = {
|
|
571
|
+
}
|
|
572
|
+
if (not payout_uuid) and (not order_id):
|
|
573
|
+
raise pyCryptomusAPIException(0, "You need to pass one of the required parameters")
|
|
574
|
+
if payout_uuid:
|
|
575
|
+
params["uuid"] = payout_uuid
|
|
576
|
+
if order_id:
|
|
577
|
+
params["order_id"] = order_id
|
|
578
|
+
resp = self.__request(method, 1, **params).get("result")
|
|
579
|
+
return Payout.de_json(resp)
|
|
580
|
+
|
|
581
|
+
def payout_history(self, date_from = None, date_to = None, cursor = None):
|
|
582
|
+
"""
|
|
583
|
+
Payout history
|
|
584
|
+
https://doc.cryptomus.com/payments/payment-history
|
|
585
|
+
Requires PAYOUT API key
|
|
586
|
+
|
|
587
|
+
date_from: (String, Optional) Filtering by creation date, from
|
|
588
|
+
date_to: (String, Optional) Filtering by creation date, to
|
|
589
|
+
cursor: (String, Optional) Page cursor (hash)
|
|
590
|
+
"""
|
|
591
|
+
params = {
|
|
592
|
+
}
|
|
593
|
+
if date_from:
|
|
594
|
+
params["date_from"] = date_from.strftime(CryptomusDateFormat)
|
|
595
|
+
if date_to:
|
|
596
|
+
params["date_to"] = date_to.strftime(CryptomusDateFormat)
|
|
597
|
+
if cursor:
|
|
598
|
+
params["cursor"] = cursor
|
|
599
|
+
method = "payment/list"
|
|
600
|
+
if params:
|
|
601
|
+
resp = self.__request(method, 1, **params).get("result")
|
|
602
|
+
else:
|
|
603
|
+
resp = self.__request(method, 1).get("result")
|
|
604
|
+
return PayoutHistory.de_json(resp)
|
|
605
|
+
|
|
606
|
+
def payout_services(self):
|
|
607
|
+
"""
|
|
608
|
+
Get collection of all available payout services
|
|
609
|
+
https://doc.cryptomus.com/payouts/list-of-services
|
|
610
|
+
Requires PAYMOUT API key
|
|
611
|
+
"""
|
|
612
|
+
method = "payout/services"
|
|
613
|
+
resp = self.__request(method, 2).get("result")
|
|
614
|
+
return [Service.de_json(i) for i in resp]
|
|
615
|
+
|
|
616
|
+
def balance(self):
|
|
617
|
+
"""
|
|
618
|
+
Get balance of merchant(account) or user(wallet)
|
|
619
|
+
https://doc.cryptomus.com/balance
|
|
620
|
+
Requires PAYMENT API key
|
|
621
|
+
"""
|
|
622
|
+
method = "balance"
|
|
623
|
+
resp = self.__request(method, 1).get("result")
|
|
624
|
+
return Balance.de_json(resp[0])
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import json
|
|
2
2
|
from abc import ABC
|
|
3
3
|
|
|
4
|
+
CryptomusDateFormat = "%Y-%m-%d %H:%M:%S"
|
|
4
5
|
|
|
5
6
|
class Dictionaryable(ABC):
|
|
6
7
|
"""
|
|
@@ -128,9 +129,11 @@ class Balance(JsonDeserializable):
|
|
|
128
129
|
raise ValueError("Not a balance")
|
|
129
130
|
if "merchant" in data:
|
|
130
131
|
for item in data["merchant"]:
|
|
132
|
+
# noinspection PyUnresolvedReferences
|
|
131
133
|
instance.merchant.append(BalanceItem.de_json(item))
|
|
132
134
|
if "user" in data:
|
|
133
135
|
for item in data["user"]:
|
|
136
|
+
# noinspection PyUnresolvedReferences
|
|
134
137
|
instance.user.append(BalanceItem.de_json(item))
|
|
135
138
|
return instance
|
|
136
139
|
|
|
@@ -218,19 +221,22 @@ class Invoice(JsonDeserializable):
|
|
|
218
221
|
self.amount = None
|
|
219
222
|
self.payment_amount = None
|
|
220
223
|
self.payer_amount = None
|
|
224
|
+
self.discount_percent = None
|
|
225
|
+
self.discount = None
|
|
221
226
|
self.payer_currency = None
|
|
222
227
|
self.currency = None
|
|
223
|
-
self.
|
|
228
|
+
self.merchant_amount = None
|
|
224
229
|
self.network = None
|
|
225
230
|
self.address = None
|
|
226
231
|
self.from_ = None
|
|
227
232
|
self.txid = None
|
|
233
|
+
self.payment_status = None
|
|
228
234
|
self.url = None
|
|
229
235
|
self.expired_at = None
|
|
230
|
-
self.payment_status = None
|
|
231
236
|
self.is_final = None
|
|
232
237
|
self.additional_data = None
|
|
233
|
-
self.
|
|
238
|
+
self.created_at = None
|
|
239
|
+
self.updated_at = None
|
|
234
240
|
|
|
235
241
|
@classmethod
|
|
236
242
|
def de_json(cls, json_dict):
|
|
@@ -242,10 +248,16 @@ class Invoice(JsonDeserializable):
|
|
|
242
248
|
instance.payment_amount = float(instance.payment_amount)
|
|
243
249
|
if instance.payer_amount is not None:
|
|
244
250
|
instance.payer_amount = float(instance.payer_amount)
|
|
245
|
-
if instance.
|
|
246
|
-
instance.
|
|
247
|
-
if instance.
|
|
248
|
-
instance.
|
|
251
|
+
if instance.discount_percent is not None:
|
|
252
|
+
instance.discount_percent = float(instance.discount_percent)
|
|
253
|
+
if instance.discount is not None:
|
|
254
|
+
instance.discount = float(instance.discount)
|
|
255
|
+
if instance.merchant_amount is not None:
|
|
256
|
+
instance.merchant_amount = float(instance.merchant_amount)
|
|
257
|
+
# if instance.created_at is not None:
|
|
258
|
+
# instance.created_at = datetime.datetime.strptime(instance.created_at, CryptomusDateFormat)
|
|
259
|
+
# if instance.updated_at is not None:
|
|
260
|
+
# instance.updated_at = datetime.datetime.strptime(instance.updated_at, CryptomusDateFormat)
|
|
249
261
|
return instance
|
|
250
262
|
|
|
251
263
|
# noinspection PyMethodOverriding
|
|
@@ -293,3 +305,44 @@ class PaymentsHistory(JsonDeserializable):
|
|
|
293
305
|
instance.items = [Invoice.de_json(i) for i in instance.items]
|
|
294
306
|
instance.paginate = PaymentPaginate.de_json(instance.paginate)
|
|
295
307
|
return instance
|
|
308
|
+
|
|
309
|
+
# noinspection PyMethodOverriding
|
|
310
|
+
class Payout(JsonDeserializable):
|
|
311
|
+
def __init__(self):
|
|
312
|
+
self.uuid = None
|
|
313
|
+
self.amount = None
|
|
314
|
+
self.currency = None
|
|
315
|
+
self.network = None
|
|
316
|
+
self.address = None
|
|
317
|
+
self.txid = None
|
|
318
|
+
self.status = None
|
|
319
|
+
self.is_final = None
|
|
320
|
+
self.balance = None
|
|
321
|
+
self.payer_currency = None
|
|
322
|
+
self.payer_amount = None
|
|
323
|
+
|
|
324
|
+
@classmethod
|
|
325
|
+
def de_json(cls, json_dict):
|
|
326
|
+
data = cls.check_json(json_dict)
|
|
327
|
+
instance = super(Payout, cls).de_json(data, process_mode=2)
|
|
328
|
+
if instance.amount is not None:
|
|
329
|
+
instance.amount = float(instance.amount)
|
|
330
|
+
if instance.payer_amount is not None:
|
|
331
|
+
instance.payer_amount = float(instance.payer_amount)
|
|
332
|
+
if instance.balance is not None:
|
|
333
|
+
instance.balance = float(instance.balance)
|
|
334
|
+
return instance
|
|
335
|
+
|
|
336
|
+
# noinspection PyMethodOverriding
|
|
337
|
+
class PayoutHistory(JsonDeserializable):
|
|
338
|
+
def __init__(self):
|
|
339
|
+
self.items = []
|
|
340
|
+
self.paginate = PaymentPaginate()
|
|
341
|
+
|
|
342
|
+
@classmethod
|
|
343
|
+
def de_json(cls, json_dict):
|
|
344
|
+
data = cls.check_json(json_dict)
|
|
345
|
+
instance = super(PayoutHistory, cls).de_json(data, process_mode=2)
|
|
346
|
+
instance.items = [Payout.de_json(i) for i in instance.items]
|
|
347
|
+
instance.paginate = PaymentPaginate.de_json(instance.paginate)
|
|
348
|
+
return instance
|
|
@@ -50,6 +50,7 @@ def test_api_functions():
|
|
|
50
50
|
run_and_print(lambda: client.payment_history())
|
|
51
51
|
run_and_print(lambda: client.payment_history_filtered(is_final=True))
|
|
52
52
|
run_and_print(lambda: client.payout_services())
|
|
53
|
+
run_and_print(lambda: client.payout_history())
|
|
53
54
|
run_and_print(lambda: client.balance())
|
|
54
55
|
|
|
55
56
|
test_api_functions()
|
|
@@ -1,415 +0,0 @@
|
|
|
1
|
-
from hashlib import md5
|
|
2
|
-
from time import sleep
|
|
3
|
-
import base64
|
|
4
|
-
import requests
|
|
5
|
-
|
|
6
|
-
from .cryto_types import *
|
|
7
|
-
|
|
8
|
-
API_URL = "https://api.cryptomus.com/v1/"
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
# noinspection PyPep8Naming
|
|
12
|
-
class pyCryptomusAPIException(Exception):
|
|
13
|
-
def __init__(self, code, message, full_error = ""):
|
|
14
|
-
self.code = code
|
|
15
|
-
self.message = message
|
|
16
|
-
self.full_error = full_error
|
|
17
|
-
super().__init__(self.message)
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
# noinspection PyPep8Naming
|
|
21
|
-
class pyCryptomusAPI:
|
|
22
|
-
"""
|
|
23
|
-
Cryptomus API Client
|
|
24
|
-
"""
|
|
25
|
-
|
|
26
|
-
def __init__(self,
|
|
27
|
-
merchant_uuid, payment_api_key = None, payout_api_key = None,
|
|
28
|
-
print_errors = False, timeout = None, add_request_params = None):
|
|
29
|
-
"""
|
|
30
|
-
Create the pyCryptomusAPI instance.
|
|
31
|
-
|
|
32
|
-
:param merchant_uuid: The merchant's uuid, which you can find in the merchant's personal account in the settings section.
|
|
33
|
-
:param payment_api_key: API key for processing payments
|
|
34
|
-
:param payout_api_key: API key for accepting payment and making payouts
|
|
35
|
-
:param print_errors: (Optional) Print dumps on request errors
|
|
36
|
-
:param timeout: (Optional) Request timeout
|
|
37
|
-
:param add_request_params: (List, Optional) Additional request parameters to pass with API calls
|
|
38
|
-
"""
|
|
39
|
-
self.merchant_uuid = merchant_uuid
|
|
40
|
-
self.payment_api_key = payment_api_key
|
|
41
|
-
self.payout_api_key = payout_api_key
|
|
42
|
-
self.print_errors = print_errors
|
|
43
|
-
self.timeout = timeout
|
|
44
|
-
self.add_request_params = add_request_params
|
|
45
|
-
if not(self.payment_api_key) and not(self.payout_api_key):
|
|
46
|
-
raise Exception("You must specify at least one API key.")
|
|
47
|
-
|
|
48
|
-
def __request(self, method_url, mode, **kwargs):
|
|
49
|
-
"""
|
|
50
|
-
Send request to API
|
|
51
|
-
|
|
52
|
-
:param method_url: (String) API method url (part)
|
|
53
|
-
:param mode: (Int) Method mode (1: payment, 2: payout)
|
|
54
|
-
:param kwargs: request data
|
|
55
|
-
"""
|
|
56
|
-
if kwargs:
|
|
57
|
-
data = dict(kwargs)
|
|
58
|
-
else:
|
|
59
|
-
data = {}
|
|
60
|
-
|
|
61
|
-
if self.add_request_params:
|
|
62
|
-
data.update(self.add_request_params)
|
|
63
|
-
|
|
64
|
-
base_resp = None
|
|
65
|
-
try:
|
|
66
|
-
key = self.payment_api_key if (mode == 1) else self.payout_api_key
|
|
67
|
-
if not(key):
|
|
68
|
-
raise pyCryptomusAPIException(-6, "Key is empty")
|
|
69
|
-
if not(key.isascii()):
|
|
70
|
-
raise pyCryptomusAPIException(-6, "Key contains non-ascii characters")
|
|
71
|
-
if not(self.merchant_uuid):
|
|
72
|
-
raise pyCryptomusAPIException(-6, "Merchant UUID is empty")
|
|
73
|
-
if not(self.merchant_uuid.isascii()):
|
|
74
|
-
raise pyCryptomusAPIException(-6, "Merchant UUID contains non-ascii characters")
|
|
75
|
-
json_dumps = json.dumps(data)
|
|
76
|
-
# json_dumps = json.dumps(data, ensure_ascii=False, separators=(',', ':'))
|
|
77
|
-
pre_sign = json_dumps if data else ""
|
|
78
|
-
if pre_sign and not(pre_sign.isascii()):
|
|
79
|
-
raise pyCryptomusAPIException(-6, "Data dump contains non-ascii characters")
|
|
80
|
-
sign = md5(base64.b64encode(pre_sign.encode('ascii')) + key.encode('ascii')).hexdigest()
|
|
81
|
-
headers = {
|
|
82
|
-
"merchant": self.merchant_uuid,
|
|
83
|
-
"sign": sign,
|
|
84
|
-
"Content-Type": "application/json",
|
|
85
|
-
}
|
|
86
|
-
base_resp = requests.post(API_URL + method_url, data=pre_sign, headers=headers, timeout=self.timeout)
|
|
87
|
-
resp = base_resp.json()
|
|
88
|
-
except ValueError as ve:
|
|
89
|
-
code = base_resp.status_code if base_resp else -2
|
|
90
|
-
message = "Response decode failed: {}".format(ve)
|
|
91
|
-
if self.print_errors:
|
|
92
|
-
print(message)
|
|
93
|
-
raise pyCryptomusAPIException(code, message)
|
|
94
|
-
except pyCryptomusAPIException as pe:
|
|
95
|
-
raise pe
|
|
96
|
-
except Exception as e:
|
|
97
|
-
code = base_resp.status_code if base_resp else -3
|
|
98
|
-
message = "Request unknown exception: {}".format(e)
|
|
99
|
-
if self.print_errors:
|
|
100
|
-
print(message)
|
|
101
|
-
raise pyCryptomusAPIException(code, message)
|
|
102
|
-
if not resp:
|
|
103
|
-
code = base_resp.status_code if base_resp else -4
|
|
104
|
-
message = "None request response"
|
|
105
|
-
if self.print_errors:
|
|
106
|
-
print(message)
|
|
107
|
-
raise pyCryptomusAPIException(code, message)
|
|
108
|
-
elif not resp.get("result"):
|
|
109
|
-
code = base_resp.status_code if base_resp else -5
|
|
110
|
-
if resp.get("message"):
|
|
111
|
-
message = resp["message"]
|
|
112
|
-
elif resp.get("errors"):
|
|
113
|
-
message = resp["errors"]
|
|
114
|
-
else:
|
|
115
|
-
message = "No error info provided"
|
|
116
|
-
if self.print_errors:
|
|
117
|
-
print("Response: {}".format(resp))
|
|
118
|
-
raise pyCryptomusAPIException(code, message)
|
|
119
|
-
# code -6 is used above
|
|
120
|
-
else:
|
|
121
|
-
return resp
|
|
122
|
-
|
|
123
|
-
def create_invoice(self,
|
|
124
|
-
amount, currency, order_id, network = None, url_return = None, url_callback = None,
|
|
125
|
-
is_payment_multiple = None, lifetime = None, to_currency = None, subtract = None,
|
|
126
|
-
accuracy_payment_percent = None, additional_data = None, currencies = None,
|
|
127
|
-
except_currencies = None):
|
|
128
|
-
"""
|
|
129
|
-
Creating an invoice
|
|
130
|
-
https://doc.cryptomus.com/payments/creating-invoice
|
|
131
|
-
Requires PAYMENT API key
|
|
132
|
-
|
|
133
|
-
amount: (Float) The amount of the invoice
|
|
134
|
-
currency: (String) Currency code (https://doc.cryptomus.com/reference)
|
|
135
|
-
order_id: (String) Order ID in your system
|
|
136
|
-
network: (String, Optional) Blockchain network code (https://doc.cryptomus.com/reference)
|
|
137
|
-
url_return: (String, Optional) Url to which the user will return after payment
|
|
138
|
-
url_callback: (String, Optional) Url to which webhooks with payment status will be sent
|
|
139
|
-
is_payment_multiple: (Bool, Optional) Whether payment of the remaining amount is possible (true/false)
|
|
140
|
-
lifetime: (?Int?, Optional) The lifespan of the issued invoice (?in seconds?)
|
|
141
|
-
to_currency: (String, Optional) Currency code for accepting payments
|
|
142
|
-
subtract: (?Float?, Optional) Percentage of the payment commission charged to the client. The subtract parameter allows you to specify what percentage of the payment acceptance will be paid by the client. If you have a payment commission 1%, then if you create an invoice for 100 USDT with subtract=100 (the client pays 100% commission), the client will have to pay 101 USDT.
|
|
143
|
-
accuracy_payment_percent: (?Float?, Optional) Acceptable inaccuracy in payment (min 0, max 5.00)
|
|
144
|
-
additional_data: (?String?, Optional) Additional information
|
|
145
|
-
currencies: (List[Currency], Optional) List of allowed currencies for payment Structure
|
|
146
|
-
except_currencies: (List[Currency], Optional) List of excluded currencies for payment Structure
|
|
147
|
-
"""
|
|
148
|
-
method = "payment"
|
|
149
|
-
params = {
|
|
150
|
-
"amount": str(amount),
|
|
151
|
-
"currency": currency,
|
|
152
|
-
"order_id": str(order_id),
|
|
153
|
-
}
|
|
154
|
-
if network:
|
|
155
|
-
params["network"] = network
|
|
156
|
-
if url_return:
|
|
157
|
-
params["url_return"] = url_return
|
|
158
|
-
if url_callback:
|
|
159
|
-
params["url_callback"] = url_callback
|
|
160
|
-
if is_payment_multiple is not None:
|
|
161
|
-
params["is_payment_multiple"] = is_payment_multiple
|
|
162
|
-
if lifetime is not None:
|
|
163
|
-
params["lifetime"] = str(lifetime)
|
|
164
|
-
if to_currency:
|
|
165
|
-
params["to_currency"] = to_currency
|
|
166
|
-
if subtract is not None:
|
|
167
|
-
params["subtract"] = str(subtract)
|
|
168
|
-
if accuracy_payment_percent is not None:
|
|
169
|
-
params["accuracy_payment_percent"] = str(accuracy_payment_percent)
|
|
170
|
-
if additional_data:
|
|
171
|
-
params["additional_data"] = additional_data
|
|
172
|
-
if currencies:
|
|
173
|
-
params["currencies"] = [i.to_dict() for i in currencies]
|
|
174
|
-
if except_currencies:
|
|
175
|
-
params["except_currencies"] = [i.to_dict() for i in except_currencies]
|
|
176
|
-
resp = self.__request(method, 1, **params).get("result")
|
|
177
|
-
return Invoice.de_json(resp)
|
|
178
|
-
|
|
179
|
-
def create_wallet(self,
|
|
180
|
-
network, currency, order_id, url_callback = None):
|
|
181
|
-
"""
|
|
182
|
-
Creating a Static wallet
|
|
183
|
-
https://doc.cryptomus.com/payments/creating-static
|
|
184
|
-
Requires PAYMENT API key
|
|
185
|
-
|
|
186
|
-
network: (String) Blockchain network code (https://doc.cryptomus.com/reference)
|
|
187
|
-
currency: (String) Currency code (https://doc.cryptomus.com/reference)
|
|
188
|
-
order_id: (String) Order ID in your system
|
|
189
|
-
url_callback: (String, Optional) Url to which webhooks with payment status will be sent
|
|
190
|
-
"""
|
|
191
|
-
method = "wallet"
|
|
192
|
-
params = {
|
|
193
|
-
"network": network,
|
|
194
|
-
"currency": currency,
|
|
195
|
-
"order_id": str(order_id),
|
|
196
|
-
}
|
|
197
|
-
if url_callback:
|
|
198
|
-
params["url_callback"] = url_callback
|
|
199
|
-
resp = self.__request(method, 1, **params).get("result")
|
|
200
|
-
return Wallet.de_json(resp)
|
|
201
|
-
|
|
202
|
-
def block_wallet(self,
|
|
203
|
-
wallet_uuid = None, order_id = None, is_force_refund = None):
|
|
204
|
-
"""
|
|
205
|
-
Block static wallet
|
|
206
|
-
https://doc.cryptomus.com/payments/block-wallet
|
|
207
|
-
You need to pass one of the required parameters, if you pass both, the account will be identified by order_id
|
|
208
|
-
Requires PAYMENT API key
|
|
209
|
-
|
|
210
|
-
wallet_uuid: (String, Optional if order_id set) Wallet UUID
|
|
211
|
-
order_id: (String, Optional if wallet_uuid set) Order ID in your system
|
|
212
|
-
is_force_refund: (Bool, Optional) Refund all incoming payments to sender’s address
|
|
213
|
-
"""
|
|
214
|
-
method = "wallet/block-address"
|
|
215
|
-
params = {
|
|
216
|
-
}
|
|
217
|
-
if not(wallet_uuid) and not(order_id):
|
|
218
|
-
raise pyCryptomusAPIException(0, "You need to pass one of the required parameters")
|
|
219
|
-
if wallet_uuid:
|
|
220
|
-
params["uuid"] = wallet_uuid
|
|
221
|
-
if order_id:
|
|
222
|
-
params["order_id"] = order_id
|
|
223
|
-
if is_force_refund is not None:
|
|
224
|
-
params["is_force_refund"] = is_force_refund
|
|
225
|
-
resp = self.__request(method, 1, **params).get("result")
|
|
226
|
-
return resp
|
|
227
|
-
|
|
228
|
-
def block_wallet_refund(self,
|
|
229
|
-
address, wallet_uuid = None, order_id = None):
|
|
230
|
-
"""
|
|
231
|
-
Refund payments on blocked address
|
|
232
|
-
https://doc.cryptomus.com/payments/refundblocked
|
|
233
|
-
You need to pass one of the required parameters, if you pass both, the account will be identified by order_id
|
|
234
|
-
Requires PAYMENT API key
|
|
235
|
-
|
|
236
|
-
address: (String) Address (wallet addres? refund address?)
|
|
237
|
-
wallet_uuid: (String, Optional if order_id set) Wallet UUID
|
|
238
|
-
order_id: (String, Optional if wallet_uuid set) Order ID in your system
|
|
239
|
-
"""
|
|
240
|
-
method = "wallet/blocked-address-refund"
|
|
241
|
-
params = {
|
|
242
|
-
"address": address,
|
|
243
|
-
}
|
|
244
|
-
if not(wallet_uuid) and not(order_id):
|
|
245
|
-
raise pyCryptomusAPIException(0, "You need to pass one of the required parameters")
|
|
246
|
-
if wallet_uuid:
|
|
247
|
-
params["uuid"] = wallet_uuid
|
|
248
|
-
if order_id:
|
|
249
|
-
params["order_id"] = order_id
|
|
250
|
-
resp = self.__request(method, 1, **params).get("result")
|
|
251
|
-
return resp
|
|
252
|
-
|
|
253
|
-
def payment_information(self,
|
|
254
|
-
invoice_uuid = None, order_id = None):
|
|
255
|
-
"""
|
|
256
|
-
Payment information
|
|
257
|
-
https://doc.cryptomus.com/payments/payment-information
|
|
258
|
-
You need to pass one of the required parameters, if you pass both, the account will be identified by order_id
|
|
259
|
-
Requires PAYMENT API key
|
|
260
|
-
|
|
261
|
-
invoice_uuid: (String, Optional if order_id set) Invoice UUID
|
|
262
|
-
order_id: (String, Optional if wallet_uuid set) Order ID in your system
|
|
263
|
-
"""
|
|
264
|
-
method = "payment/info"
|
|
265
|
-
params = {
|
|
266
|
-
}
|
|
267
|
-
if not(invoice_uuid) and not(order_id):
|
|
268
|
-
raise pyCryptomusAPIException(0, "You need to pass one of the required parameters")
|
|
269
|
-
if invoice_uuid:
|
|
270
|
-
params["uuid"] = invoice_uuid
|
|
271
|
-
if order_id:
|
|
272
|
-
params["order_id"] = order_id
|
|
273
|
-
resp = self.__request(method, 1, **params).get("result")
|
|
274
|
-
return Invoice.de_json(resp)
|
|
275
|
-
|
|
276
|
-
def refund(self,
|
|
277
|
-
address, is_subtract, invoice_uuid = None, order_id = None):
|
|
278
|
-
"""
|
|
279
|
-
Refund
|
|
280
|
-
https://doc.cryptomus.com/payments/refund
|
|
281
|
-
You need to pass one of the required parameters, if you pass both, the account will be identified by invoice_uuid
|
|
282
|
-
Requires PAYMENT API key
|
|
283
|
-
|
|
284
|
-
address: (String) Refund address
|
|
285
|
-
is_subtract: (Bool) Determines whether the commission is to be charged to the merchant or to the client (True - to the merchant, False - to the client)
|
|
286
|
-
invoice_uuid: (String, Optional if order_id set) Invoice UUID
|
|
287
|
-
order_id: (String, Optional if wallet_uuid set) Order ID in your system
|
|
288
|
-
"""
|
|
289
|
-
method = "payment/refund"
|
|
290
|
-
params = {
|
|
291
|
-
"address": address,
|
|
292
|
-
"is_subtract": is_subtract,
|
|
293
|
-
}
|
|
294
|
-
if not(invoice_uuid) and not(order_id):
|
|
295
|
-
raise pyCryptomusAPIException(0, "You need to pass one of the required parameters")
|
|
296
|
-
if invoice_uuid:
|
|
297
|
-
params["uuid"] = invoice_uuid
|
|
298
|
-
if order_id:
|
|
299
|
-
params["order_id"] = order_id
|
|
300
|
-
resp = self.__request(method, 1, **params).get("result")
|
|
301
|
-
return Invoice.de_json(resp)
|
|
302
|
-
|
|
303
|
-
def payment_history(self, cursor = None):
|
|
304
|
-
"""
|
|
305
|
-
Payment history
|
|
306
|
-
https://doc.cryptomus.com/payments/payment-history
|
|
307
|
-
Requires PAYMENT API key
|
|
308
|
-
|
|
309
|
-
cursor: (String, Optional) Page cursor (hash)
|
|
310
|
-
"""
|
|
311
|
-
params = {
|
|
312
|
-
}
|
|
313
|
-
if cursor:
|
|
314
|
-
params["cursor"] = cursor
|
|
315
|
-
method = "payment/list"
|
|
316
|
-
if params:
|
|
317
|
-
resp = self.__request(method, 1, **params).get("result")
|
|
318
|
-
else:
|
|
319
|
-
resp = self.__request(method, 1).get("result")
|
|
320
|
-
return PaymentsHistory.de_json(resp)
|
|
321
|
-
|
|
322
|
-
def payment_history_filtered(
|
|
323
|
-
self, max_results = 15, max_pages = 10,
|
|
324
|
-
currencies = None, networks = None, addresses = None,
|
|
325
|
-
statuses = None, is_final = None, page_delay = 1):
|
|
326
|
-
"""
|
|
327
|
-
Payment history (advanced mode)
|
|
328
|
-
|
|
329
|
-
Based on: payment_history
|
|
330
|
-
https://doc.cryptomus.com/payments/payment-history
|
|
331
|
-
Requires PAYMENT API key
|
|
332
|
-
|
|
333
|
-
Collects only results under filters.
|
|
334
|
-
Process as many pages as needed to collect max_results, but not more than max_pages.
|
|
335
|
-
|
|
336
|
-
max_results: (Int, Optional, default=15) Max number of results to collect
|
|
337
|
-
max_pages: (Int, Optional, default=10) Max number of pages to process
|
|
338
|
-
currencies: (List of Strings, Optional) List of accepted currencies. Codes: https://doc.cryptomus.com/reference
|
|
339
|
-
networks: (List of Strings, Optional) List of accepted networks. Codes: https://doc.cryptomus.com/reference
|
|
340
|
-
addresses: (List of Strings, Optional) List of accepted addresses
|
|
341
|
-
statuses: (List of Strings, Optional) List of accepted statuses. Codes: https://doc.cryptomus.com/payments/payment-statuses
|
|
342
|
-
is_final: (Bool, Optional) If True, only final payments will be collected, if False - only non-final
|
|
343
|
-
page_delay: (Int, Optional, default=1) Delay between pages (in seconds)
|
|
344
|
-
"""
|
|
345
|
-
|
|
346
|
-
result = PaymentsHistory()
|
|
347
|
-
|
|
348
|
-
page_number = 0
|
|
349
|
-
cursor = None
|
|
350
|
-
while page_number < max_pages:
|
|
351
|
-
if page_number > 0: sleep(page_delay)
|
|
352
|
-
resp = self.payment_history(cursor = cursor)
|
|
353
|
-
|
|
354
|
-
if not resp.items:
|
|
355
|
-
# No (more) payments
|
|
356
|
-
break
|
|
357
|
-
|
|
358
|
-
for payment in resp.items:
|
|
359
|
-
if currencies and not(payment.currency in currencies):
|
|
360
|
-
continue
|
|
361
|
-
if networks and not(payment.network in networks):
|
|
362
|
-
continue
|
|
363
|
-
if addresses and not(payment.address in addresses):
|
|
364
|
-
continue
|
|
365
|
-
if statuses and not(payment.status in statuses):
|
|
366
|
-
continue
|
|
367
|
-
if (is_final is not None) and payment.is_final != is_final:
|
|
368
|
-
continue
|
|
369
|
-
result.items.append(payment)
|
|
370
|
-
|
|
371
|
-
if len(result.items) >= max_results:
|
|
372
|
-
# Enough results collected
|
|
373
|
-
break
|
|
374
|
-
|
|
375
|
-
if len(result.items) >= max_results:
|
|
376
|
-
# Enough results collected
|
|
377
|
-
break
|
|
378
|
-
|
|
379
|
-
cursor = resp.paginate.nextCursor
|
|
380
|
-
if not cursor:
|
|
381
|
-
# No more pages
|
|
382
|
-
break
|
|
383
|
-
page_number += 1
|
|
384
|
-
|
|
385
|
-
return result
|
|
386
|
-
|
|
387
|
-
def payment_services(self):
|
|
388
|
-
"""
|
|
389
|
-
Get collection of all available payment services
|
|
390
|
-
https://doc.cryptomus.com/payments/list-of-services
|
|
391
|
-
Requires PAYMENT API key
|
|
392
|
-
"""
|
|
393
|
-
method = "payment/services"
|
|
394
|
-
resp = self.__request(method, 1).get("result")
|
|
395
|
-
return [Service.de_json(i) for i in resp]
|
|
396
|
-
|
|
397
|
-
def payout_services(self):
|
|
398
|
-
"""
|
|
399
|
-
Get collection of all available payout services
|
|
400
|
-
https://doc.cryptomus.com/payouts/list-of-services
|
|
401
|
-
Requires PAYMOUT API key
|
|
402
|
-
"""
|
|
403
|
-
method = "payout/services"
|
|
404
|
-
resp = self.__request(method, 2).get("result")
|
|
405
|
-
return [Service.de_json(i) for i in resp]
|
|
406
|
-
|
|
407
|
-
def balance(self):
|
|
408
|
-
"""
|
|
409
|
-
Get balance of merchant(account) or user(wallet)
|
|
410
|
-
https://doc.cryptomus.com/balance
|
|
411
|
-
Requires PAYMENT API key
|
|
412
|
-
"""
|
|
413
|
-
method = "balance"
|
|
414
|
-
resp = self.__request(method, 1).get("result")
|
|
415
|
-
return Balance.de_json(resp[0])
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|