cryptocloud-sdk 0.1.0__py3-none-any.whl

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.
@@ -0,0 +1,5 @@
1
+ from .merchant import CryptoCloud
2
+
3
+ __all__ = [
4
+ "CryptoCloud",
5
+ ]
@@ -0,0 +1,25 @@
1
+ from .balance import CoinBalance
2
+ from .currency import InvoiceCurrency, BalanceCurrency, SupportedCryptoCurrency, SupportedFiatCurrency
3
+ from .invoice import TimeToPay, AddedInvoiceParamsInput, InvoiceInput, CreatedInvoice, InvoiceInfo
4
+ from .network import CurrencyNetwork
5
+ from .postback import InvoicePostback
6
+ from .project import Project
7
+ from .static_wallet import StaticWallet
8
+ from .stats import Stats
9
+
10
+ __all__ = [
11
+ "InvoiceCurrency",
12
+ "BalanceCurrency",
13
+ "SupportedCryptoCurrency",
14
+ "SupportedFiatCurrency",
15
+ "TimeToPay",
16
+ "AddedInvoiceParamsInput",
17
+ "InvoiceInput",
18
+ "CreatedInvoice",
19
+ "InvoiceInfo",
20
+ "Project",
21
+ "CoinBalance",
22
+ "StaticWallet",
23
+ "InvoicePostback",
24
+ "Stats"
25
+ ]
@@ -0,0 +1,11 @@
1
+ from pydantic import BaseModel, confloat
2
+
3
+ from .currency import BalanceCurrency
4
+
5
+
6
+ class CoinBalance(BaseModel):
7
+ currency: BalanceCurrency
8
+ balance_crypto: confloat(ge=0)
9
+ balance_usd: confloat(ge=0)
10
+ available_balance: confloat(ge=0)
11
+ available_balance_usd: confloat(ge=0)
@@ -0,0 +1,82 @@
1
+ from dataclasses import dataclass
2
+
3
+ from pydantic import BaseModel, conint
4
+
5
+ from .network import CurrencyNetwork
6
+
7
+
8
+ @dataclass(slots=True, frozen=True)
9
+ class SupportedCryptoCurrency:
10
+ """ Supported Crypto Currencies
11
+ https://docs.cryptocloud.plus/ru/api-reference-v2/create-invoice#opisanie-dopolnitelnykh-parametrov
12
+ """
13
+ USDT_TRC20 = "USDT_TRC20"
14
+ USDC_TRC20 = "USDC_TRC20"
15
+ TUSD_TRC20 = "TUSD_TRC20"
16
+ USDT_ERC20 = "USDT_ERC20"
17
+ USDC_ERC20 = "USDC_ERC20"
18
+ TUSD_ERC20 = "TUSD_ERC20"
19
+ USDD_TRC20 = "USDD_TRC20"
20
+ SHIB_ERC20 = "SHIB_ERC20"
21
+ USDT_BSC = "USDT_BSC"
22
+ USDC_BSC = "USDC_BSC"
23
+ TUSD_BSC = "TUSD_BSC"
24
+ USDT_TON = "USDT_TON"
25
+ BTC = "BTC"
26
+ LTC = "LTC"
27
+ ETH = "ETH"
28
+ TRX = "TRX"
29
+ BNB = "BNB"
30
+ TON = "TON"
31
+
32
+
33
+ @dataclass(slots=True, frozen=True)
34
+ class SupportedFiatCurrency:
35
+ USD = "USD"
36
+ UZS = "UZS"
37
+ KGS = "KGS"
38
+ KZT = "KZT"
39
+ AMD = "AMD"
40
+ AZN = "AZN"
41
+ BYN = "BYN"
42
+ AUD = "AUD"
43
+ TRY = "TRY"
44
+ AED = "AED"
45
+ CAD = "CAD"
46
+ CNY = "CNY"
47
+ HKD = "HKD"
48
+ IDR = "IDR"
49
+ INR = "INR"
50
+ JPY = "JPY"
51
+ PHP = "PHP"
52
+ SGD = "SGD"
53
+ THB = "THB"
54
+ VND = "VND"
55
+ MYR = "MYR"
56
+ RUB = "RUB"
57
+ UAH = "UAH"
58
+ EUR = "EUR"
59
+ GBP = "GBP"
60
+
61
+
62
+ class _BaseCurrency(BaseModel):
63
+ id: conint(ge=0)
64
+ code: str
65
+ name: str
66
+ is_email_required: bool
67
+ stablecoin: bool
68
+ icon_base: str
69
+ icon_network: str
70
+ icon_qr: str
71
+ order: conint(ge=0)
72
+
73
+
74
+ class InvoiceCurrency(_BaseCurrency):
75
+ network: CurrencyNetwork
76
+ fullcode: str
77
+
78
+
79
+ class BalanceCurrency(_BaseCurrency):
80
+ enable: bool | None = None
81
+ obj_network: CurrencyNetwork
82
+ short_code: str
@@ -0,0 +1,63 @@
1
+ from datetime import datetime
2
+ from typing import Literal
3
+
4
+ from pydantic import BaseModel, confloat
5
+
6
+ from .currency import InvoiceCurrency
7
+ from .project import Project
8
+
9
+
10
+ class _BasePostInvoice(BaseModel):
11
+ uuid: str
12
+ expiry_date: datetime
13
+ address: str
14
+ side_commission: str
15
+ amount: confloat(gt=0)
16
+ amount_usd: confloat(gt=0)
17
+ fee: confloat(gt=0)
18
+ fee_usd: confloat(gt=0)
19
+ service_fee: confloat(gt=0)
20
+ service_fee_usd: confloat(gt=0)
21
+ status: str
22
+ currency: InvoiceCurrency
23
+ project: Project
24
+ test_mode: bool
25
+
26
+
27
+ class TimeToPay(BaseModel):
28
+ minutes: int | None = 00
29
+ hours: int | None = 2
30
+
31
+
32
+ class AddedInvoiceParamsInput(BaseModel):
33
+ time_to_pay: TimeToPay | None = None
34
+ email_to_send: str | None = None
35
+ available_currencies: list[str] | None = None
36
+ cryptocurrency: str | None = None
37
+ period: Literal["month", "week", "day"] | None = None
38
+
39
+
40
+ class InvoiceInput(BaseModel):
41
+ amount: confloat(gt=0)
42
+
43
+ currency: str | None = "USD"
44
+ order_id: str | None = None
45
+ email: str | None = None
46
+
47
+ add_fields: AddedInvoiceParamsInput | None = None
48
+
49
+
50
+ class CreatedInvoice(_BasePostInvoice):
51
+ created: datetime
52
+ amount_in_fiat: confloat(gt=0)
53
+ fiat_currency: str
54
+ side_commission_service: str
55
+ is_email_required: bool
56
+ link: str
57
+
58
+
59
+ class InvoiceInfo(_BasePostInvoice):
60
+ received: confloat(ge=0)
61
+ received_usd: confloat(ge=0)
62
+ order_id: str | None = None
63
+ side_commission_cc: str
@@ -0,0 +1,8 @@
1
+ from pydantic import BaseModel, conint
2
+
3
+
4
+ class CurrencyNetwork(BaseModel):
5
+ code: str
6
+ id: conint(ge=0)
7
+ icon: str
8
+ fullname: str
@@ -0,0 +1,15 @@
1
+ from typing import Literal
2
+
3
+ from pydantic import BaseModel, constr, confloat
4
+
5
+
6
+ class InvoicePostback(BaseModel):
7
+ """ Invoice postback
8
+ https://docs.cryptocloud.plus/ru/api-reference-v2/postback
9
+ """
10
+ status: Literal["success"]
11
+ invoice_id: constr(max_length=12)
12
+ amount_crypto: confloat(ge=0)
13
+ currency: constr(max_length=12)
14
+ amount_usdt: confloat(ge=0)
15
+ order_id: int | None = None
@@ -0,0 +1,9 @@
1
+ from pydantic import BaseModel, conint
2
+
3
+
4
+ class Project(BaseModel):
5
+ id: conint(ge=0)
6
+ name: str
7
+ fail: str
8
+ success: str
9
+ logo: str | None = None
@@ -0,0 +1,9 @@
1
+ from pydantic import BaseModel
2
+
3
+ from .currency import BalanceCurrency
4
+
5
+
6
+ class StaticWallet(BaseModel):
7
+ uuid: str
8
+ address: str
9
+ currency: BalanceCurrency
@@ -0,0 +1,24 @@
1
+ from pydantic import BaseModel, conint, confloat
2
+
3
+
4
+ class StatsCount(BaseModel):
5
+ all: conint(ge=0)
6
+ created: conint(ge=0)
7
+ paid: conint(ge=0)
8
+ overpaid: conint(ge=0)
9
+ partial: conint(ge=0)
10
+ canceled: conint(ge=0)
11
+
12
+
13
+ class StatsAmount(BaseModel):
14
+ all: confloat(ge=0)
15
+ created: confloat(ge=0)
16
+ paid: confloat(ge=0)
17
+ overpaid: confloat(ge=0)
18
+ partial: confloat(ge=0)
19
+ canceled: confloat(ge=0)
20
+
21
+
22
+ class Stats(BaseModel):
23
+ count: StatsCount
24
+ amount: StatsAmount
@@ -0,0 +1,10 @@
1
+ class BadRequestError(Exception):
2
+ ...
3
+
4
+
5
+ class UnauthorizedError(Exception):
6
+ ...
7
+
8
+
9
+ class ForbiddenError(Exception):
10
+ ...
@@ -0,0 +1,60 @@
1
+ from abc import ABC, abstractmethod
2
+ from typing import Literal
3
+
4
+ import aiohttp
5
+
6
+ from .errors import BadRequestError, UnauthorizedError, ForbiddenError
7
+
8
+
9
+ class BaseHttpExecutor(ABC):
10
+ def __init__(
11
+ self,
12
+ headers: dict
13
+ ):
14
+ self.__headers = headers
15
+
16
+ async def make_request(
17
+ self,
18
+ url: str,
19
+ method: Literal["get", "post"] = "post",
20
+ load_response_body: bool = True,
21
+ **kwargs
22
+ ) -> (aiohttp.ClientResponse, dict):
23
+ async with aiohttp.ClientSession() as session:
24
+ async with getattr(session, method)(
25
+ url=url,
26
+ headers=self.__headers,
27
+ **kwargs
28
+ ) as resp:
29
+ if resp.status == 404:
30
+ raise BadRequestError(f"The requested {url=} not found. Check API updates.")
31
+
32
+ resp_body = await resp.json()
33
+
34
+ self._check_response_status(resp, resp_body)
35
+
36
+ return resp, resp_body if load_response_body else {}
37
+
38
+ @abstractmethod
39
+ def _check_response_status(self, response: aiohttp.ClientResponse, response_body: dict) -> None:
40
+ raise NotImplementedError
41
+
42
+
43
+ class CryptoCloudHttpExecutor(BaseHttpExecutor):
44
+ def _check_response_status(self, response: aiohttp.ClientResponse, response_body: dict) -> None:
45
+ resp_code = response.status
46
+
47
+ match resp_code:
48
+ case 400:
49
+ raise BadRequestError(self._format_error(resp_code, response_body.get("result")))
50
+ case 401:
51
+ raise UnauthorizedError(self._format_error(resp_code, response_body.get("detail")))
52
+ case 403:
53
+ raise ForbiddenError(self._format_error(resp_code, response_body.get("detail")))
54
+
55
+ @staticmethod
56
+ def _format_error(code: int, tip: str) -> str:
57
+ text = f"Returned [{code}] error because - '{tip}'. " \
58
+ "Make sure that creds and params was correctly passed."
59
+
60
+ return text
@@ -0,0 +1,114 @@
1
+ from datetime import date
2
+
3
+ from . import dto
4
+ from .http_executor import CryptoCloudHttpExecutor
5
+
6
+
7
+ class CryptoCloud:
8
+ def __init__(
9
+ self,
10
+ api_token: str,
11
+ shop_id: str,
12
+ host="https://api.cryptocloud.plus",
13
+ version: str = "/v2"
14
+ ):
15
+ self._http = CryptoCloudHttpExecutor(
16
+ headers={
17
+ "Content-Type": "application/json",
18
+ "Authorization": f"Token {api_token}"
19
+ }
20
+ )
21
+ self._base_url = f"{host}{version}"
22
+ self._shop_id = shop_id
23
+
24
+ async def create_invoice(self, invoice: dto.InvoiceInput) -> dto.CreatedInvoice:
25
+ """
26
+ https://docs.cryptocloud.plus/ru/api-reference-v2/create-invoice#request-body
27
+ """
28
+ url = self._base_url + "/invoice/create"
29
+ payload = invoice.model_dump(exclude_none=True) | {"shop_id": self._shop_id}
30
+
31
+ _, body = await self._http.make_request(
32
+ url=url,
33
+ json=payload,
34
+ )
35
+
36
+ return dto.CreatedInvoice(**body.get("result"))
37
+
38
+ async def get_invoices(self, uuids: list[str]) -> list[dto.InvoiceInfo]:
39
+ """
40
+ https://docs.cryptocloud.plus/ru/api-reference-v2/invoice-list#request-body
41
+ """
42
+ url = self._base_url + "/invoice/merchant/info"
43
+
44
+ _, body = await self._http.make_request(
45
+ url=url,
46
+ json={"uuids": uuids},
47
+ )
48
+
49
+ return [dto.InvoiceInfo(**i) for i in body.get("result")]
50
+
51
+ async def cancel_invoice(self, uuid: str) -> None:
52
+ """ https://docs.cryptocloud.plus/ru/api-reference-v2/cancel-invoice#request-body
53
+
54
+ Request will be executed successfully only when invoice has 'created' status
55
+ :return None -> if canceled without any problem
56
+ :return Exception -> when some param is not correct """
57
+
58
+ url = self._base_url + "/invoice/merchant/canceled"
59
+
60
+ _, body = await self._http.make_request(
61
+ url=url,
62
+ json={"uuid": uuid},
63
+ )
64
+
65
+ async def get_balance(
66
+ self, in_currency: dto.SupportedCryptoCurrency | None = None
67
+ ) -> list[dto.CoinBalance] | dto.CoinBalance:
68
+ """
69
+ https://docs.cryptocloud.plus/ru/api-reference-v2/balance#poluchit-balans
70
+ """
71
+ url = self._base_url + "/merchant/wallet/balance/all"
72
+
73
+ _, body = await self._http.make_request(url=url)
74
+
75
+ if in_currency:
76
+ in_curr = [b for b in body.get("result") if b.get("currency").get("code") == in_currency]
77
+ if in_curr:
78
+ return dto.CoinBalance(**in_curr.pop())
79
+
80
+ return [dto.CoinBalance(**i) for i in body.get("result")]
81
+
82
+ async def get_stats(self, start: date, end: date) -> dto.Stats:
83
+ """
84
+ https://docs.cryptocloud.plus/ru/api-reference-v2/statistics#request-body
85
+ """
86
+ url = self._base_url + "/invoice/merchant/statistics"
87
+ payload = {
88
+ "start": start.strftime("%d.%m.%Y"),
89
+ "end": end.strftime("%d.%m.%Y")
90
+ }
91
+
92
+ _, body = await self._http.make_request(
93
+ url=url,
94
+ json=payload,
95
+ )
96
+
97
+ return dto.Stats(**body.get("result"))
98
+
99
+ async def create_static_wallet(self, currency: str, identify: str) -> dto.StaticWallet:
100
+ """
101
+ https://docs.cryptocloud.plus/ru/api-reference-v2/static-wallet#request-body
102
+ """
103
+ url = self._base_url + "/invoice/static/create"
104
+
105
+ _, body = await self._http.make_request(
106
+ url=url,
107
+ json={
108
+ "shop_id": self._shop_id,
109
+ "currency": currency,
110
+ "identify": identify,
111
+ },
112
+ )
113
+
114
+ return dto.StaticWallet(**body.get("result"))
@@ -0,0 +1,83 @@
1
+ Metadata-Version: 2.3
2
+ Name: cryptocloud-sdk
3
+ Version: 0.1.0
4
+ Summary:
5
+ Keywords: async,crypto cloud,python,sdk
6
+ Author: Никита Прожога
7
+ Author-email: fofmow@gmail.com
8
+ Requires-Python: >=3.10
9
+ Classifier: Programming Language :: Python :: 3.10
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Operating System :: OS Independent
12
+ Requires-Dist: aiohttp (>=3.11.11,<4.0.0)
13
+ Requires-Dist: pydantic (>=2.10.5,<3.0.0)
14
+ Project-URL: Homepage, https://github.com/fofmow/crypto-cloud-sdk
15
+ Project-URL: Repository, https://github.com/fofmow/crypto-cloud-sdk
16
+ Description-Content-Type: text/markdown
17
+
18
+ # Crypto Cloud SDK — простая асинхронная библиотека для работы с API [CryptoCloud](https://cryptocloud.plus/)
19
+
20
+ ### 💡 Регистрация мерчанта и получение API ключей [описаны в документации](https://docs.cryptocloud.plus/ru/start/get-api-keys)
21
+
22
+ ## Примеры использования / Use cases
23
+
24
+ ```python
25
+ import asyncio
26
+ from datetime import date, timedelta
27
+
28
+ from cryptocloud_sdk import CryptoCloud, dto, errors
29
+
30
+
31
+ async def main():
32
+ merchant = CryptoCloud(
33
+ api_token="YOUR_API_TOKEN",
34
+ shop_id="YOUR_SHOP_ID"
35
+ )
36
+
37
+ invoice: dto.CreatedInvoice = await merchant.create_invoice(
38
+ invoice=dto.InvoiceInput(
39
+ amount=250,
40
+ currency="USD" # or dto.currency.SupportedFiatCurrency.USD
41
+ )
42
+ )
43
+ print(f"Invoice url is {invoice.link}")
44
+
45
+ invoices: list[dto.InvoiceInfo] = await merchant.get_invoices(uuids=[invoice.uuid])
46
+
47
+ canceled = await merchant.cancel_invoice(invoice.uuid)
48
+
49
+ balances: list[dto.CoinBalance] = await merchant.get_balance()
50
+
51
+ statictics: dto.Stats = await merchant.get_stats(
52
+ start=date.today() - timedelta(days=3),
53
+ end=date.today()
54
+ )
55
+
56
+ static_wallet: dto.StaticWallet = await merchant.create_static_wallet(
57
+ currency="BTC", # or dto.currency.SupportedCryptoCurrency.BTC ,
58
+ identify="my-new-user-7"
59
+ )
60
+
61
+ # Handling errors
62
+ try:
63
+ await merchant.get_balance()
64
+ except errors.UnauthorizedError:
65
+ ... # your code
66
+ except errors.ForbiddenError:
67
+ ... # your code
68
+ except errors.BadRequestError:
69
+ ... # your code
70
+
71
+
72
+ if __name__ == "__main__":
73
+ asyncio.run(main())
74
+ ```
75
+
76
+ ####
77
+
78
+ ## [Want to donate? Look at real app used CryptoCloud 😎](https://t.me/todonators_bot)
79
+
80
+ ##
81
+
82
+
83
+
@@ -0,0 +1,16 @@
1
+ cryptocloud_sdk/__init__.py,sha256=n0aN5xtKtjxsB6UiPZqlPwiA-RfR3emgKgx0fJAeSP8,68
2
+ cryptocloud_sdk/dto/__init__.py,sha256=JEh8-t3d-545xqEf5WRkvQr-uu53fibHViisx06n45U,723
3
+ cryptocloud_sdk/dto/balance.py,sha256=QJTdXVSgHf5nsEYj1n_GoqS6_OIHzAvzbeGDApbJECQ,289
4
+ cryptocloud_sdk/dto/currency.py,sha256=H8xYKRthaXxvEdTBYWEZys4TFt-o_aG5Gs3yZbT-mMk,1661
5
+ cryptocloud_sdk/dto/invoice.py,sha256=RSSM837x2TBTEsVFrnH8gqL6ZAStTVGa_fNbXnQX5qs,1480
6
+ cryptocloud_sdk/dto/network.py,sha256=M4QylRtR_mIguNw9r80LGeCyciu2PH3QXq6_2sxdaC0,142
7
+ cryptocloud_sdk/dto/postback.py,sha256=dAtU_1GEZRce8ggwPAqquQlTkovoPwMIHptOW0T-zEo,412
8
+ cryptocloud_sdk/dto/project.py,sha256=vbpHZV6zHwknU_oSKNAfC6CWuiBlMrJvNXh7XUg4xK4,161
9
+ cryptocloud_sdk/dto/static_wallet.py,sha256=MtI5IiN9oVwXlm9T7VT-yH5HP553vZzZ_IcSED99q1A,164
10
+ cryptocloud_sdk/dto/stats.py,sha256=4g6qIxWcqJHTQ0ugmDD7AT7GG0Pq6Jsp6ZO-kC6T7Gk,498
11
+ cryptocloud_sdk/errors.py,sha256=zi9zNqHrBaapyU9jKegen2j9CnSDXmkvTVv5Z04mzkE,131
12
+ cryptocloud_sdk/http_executor.py,sha256=V1WYIIYPZEbznfjk_lQjjqQT9M6kRH1Fq8af7b0YO14,2050
13
+ cryptocloud_sdk/merchant.py,sha256=n0ykTR_eBYAwitfjl4X1D4OjmD0Gam7L0cHjpHEsrfU,3840
14
+ cryptocloud_sdk-0.1.0.dist-info/METADATA,sha256=jx5hhjNVEFF16dYa8tutrPS2M0r1VTh9kky61m4FveI,2475
15
+ cryptocloud_sdk-0.1.0.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
16
+ cryptocloud_sdk-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: poetry-core 2.0.1
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any