paymentsgate 1.5.0__py3-none-any.whl → 1.5.1__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.
paymentsgate/models.py CHANGED
@@ -1,264 +1,289 @@
1
1
  from __future__ import annotations
2
2
  import datetime
3
+ from decimal import Decimal
3
4
  import json
4
5
  from typing import Optional, List
5
6
  from pydantic import BaseModel, ConfigDict, Field
6
7
 
7
8
 
8
9
  from paymentsgate.enums import (
9
- Currencies,
10
- InvoiceTypes,
11
- Languages,
12
- Statuses,
13
- TTLUnits,
14
- CurrencyTypes,
15
- FeesStrategy,
16
- InvoiceDirection,
17
- CredentialsTypes
10
+ Currencies,
11
+ InvoiceTypes,
12
+ Languages,
13
+ Statuses,
14
+ TTLUnits,
15
+ CurrencyTypes,
16
+ FeesStrategy,
17
+ InvoiceDirection,
18
+ CredentialsTypes,
18
19
  )
19
20
 
21
+
20
22
  class BaseRequestModel(BaseModel):
21
- model_config = ConfigDict(extra='forbid')
23
+ model_config = ConfigDict(extra="forbid")
24
+
22
25
 
23
26
  class BaseResponseModel(BaseModel):
24
- model_config = ConfigDict(extra='ignore')
27
+ model_config = ConfigDict(extra="ignore")
25
28
 
26
29
 
27
30
  class Credentials(BaseModel):
28
- account_id: str
29
- public_key: str
30
- private_key: Optional[str] = Field(default=None)
31
- merchant_id: Optional[str] = Field(default=None)
32
- project_id: Optional[str] = Field(default=None)
33
-
34
- @classmethod
35
- def fromFile(cls, filename):
36
- data = json.load(open(filename))
37
- return cls(**data)
38
-
39
- model_config = ConfigDict(extra='ignore')
31
+ account_id: str
32
+ public_key: str
33
+ private_key: Optional[str] = Field(default=None)
34
+ merchant_id: Optional[str] = Field(default=None)
35
+ project_id: Optional[str] = Field(default=None)
36
+
37
+ @classmethod
38
+ def fromFile(cls, filename):
39
+ data = json.load(open(filename))
40
+ return cls(**data)
41
+
42
+ model_config = ConfigDict(extra="ignore")
40
43
 
41
44
 
42
45
  class PayInFingerprintBrowserModel(BaseRequestModel):
43
- acceptHeader: str
44
- colorDepth: int
45
- language: str
46
- screenHeight: int
47
- screenWidth: int
48
- timezone: str
49
- userAgent: str
50
- javaEnabled: bool
51
- windowHeight: int
52
- windowWidth: int
46
+ acceptHeader: str
47
+ colorDepth: int
48
+ language: str
49
+ screenHeight: int
50
+ screenWidth: int
51
+ timezone: str
52
+ userAgent: str
53
+ javaEnabled: bool
54
+ windowHeight: int
55
+ windowWidth: int
56
+
53
57
 
54
58
  class PayInFingerprintModel(BaseRequestModel):
55
- fingerprint: str
56
- ip: str
57
- country: str
58
- city: str
59
- state: str
60
- zip: str
61
- browser: Optional[PayInFingerprintBrowserModel]
59
+ fingerprint: str
60
+ ip: str
61
+ country: str
62
+ city: str
63
+ state: str
64
+ zip: str
65
+ browser: Optional[PayInFingerprintBrowserModel]
66
+
62
67
 
63
68
  class PayInModel(BaseRequestModel):
64
- amount: float # decimals: 2
65
- currency: Currencies
66
- country: Optional[str] # Country iso code
67
- invoiceId: Optional[str] # idempotent key
68
- clientId: Optional[str] # uniq client ref
69
- type: InvoiceTypes # Invoice subtype, see documentation
70
- bankId: Optional[str] # ID from bank list or NSPK id
71
- trusted: Optional[bool]
72
- successUrl: Optional[str]
73
- failUrl: Optional[str]
74
- backUrl: Optional[str]
75
- clientCard: Optional[str]
76
- clientName: Optional[str]
77
- fingerprint: Optional[PayInFingerprintModel]
78
- lang: Optional[Languages]
79
- sync: Optional[bool] # sync h2h scheme, see documentation
80
- multiWidgetOptions: Optional[PayInMultiWidgetOptions]
81
- theme: Optional[str] # personalized widget theme
82
-
69
+ amount: float # decimals: 2
70
+ currency: Currencies
71
+ country: Optional[str] # Country iso code
72
+ invoiceId: Optional[str] # idempotent key
73
+ clientId: Optional[str] # uniq client ref
74
+ type: InvoiceTypes # Invoice subtype, see documentation
75
+ bankId: Optional[str] # ID from bank list or NSPK id
76
+ trusted: Optional[bool]
77
+ successUrl: Optional[str]
78
+ failUrl: Optional[str]
79
+ backUrl: Optional[str]
80
+ clientCard: Optional[str]
81
+ clientName: Optional[str]
82
+ fingerprint: Optional[PayInFingerprintModel]
83
+ lang: Optional[Languages]
84
+ sync: Optional[bool] # sync h2h scheme, see documentation
85
+ multiWidgetOptions: Optional[PayInMultiWidgetOptions]
86
+ theme: Optional[str] # personalized widget theme
87
+
88
+
83
89
  class PayInResponseModel(BaseResponseModel):
84
- id: str
85
- status: Statuses
86
- type: InvoiceTypes
87
- url: Optional[str]
88
- deeplink: Optional[str]
89
- m10: Optional[str]
90
- cardholder: Optional[str]
91
- account: Optional[str]
92
- bankId: Optional[str]
93
- accountSubType: Optional[str]
90
+ id: str
91
+ status: Statuses
92
+ type: InvoiceTypes
93
+ url: Optional[str]
94
+ deeplink: Optional[str]
95
+ m10: Optional[str]
96
+ cardholder: Optional[str]
97
+ account: Optional[str]
98
+ bankId: Optional[str]
99
+ accountSubType: Optional[str]
100
+
94
101
 
95
102
  class PayOutRecipientModel(BaseRequestModel):
96
- account_number: str # IBAN, Phone, Card, local bank account number, wallet number, etc'
97
- account_owner: Optional[str] # FirstName LastName or FirstName MiddleName LastName
98
- account_iban: Optional[str] # use only cases where iban is't primary account id
99
- account_swift: Optional[str] # for swift transfers only
100
- account_phone: Optional[str] # additional recipient phone number, use only cases where phone is't primary account id
101
- account_bic: Optional[str] # recipient bank id
102
- account_ewallet_name: Optional[str] # additional recipient wallet provider info
103
- account_email: Optional[str] # additional recipient email, use only cases where email is't primary account id
104
- account_bank_id: Optional[str] # recipient bankId (from API banks or RU NSPK id)
105
- account_internal_client_number: Optional[str] # Bank internal identifier used for method banktransferphp (Philippines)
106
- type: Optional[CredentialsTypes] # primary credential type
103
+ account_number: str | None = None # IBAN, Phone, Card, local bank account number, wallet number, etc'
104
+ account_owner: str | None = None # FirstName LastName or FirstName MiddleName LastName
105
+ account_iban: str | None = None # use only cases where iban is't primary account id
106
+ account_swift: str | None = None # for swift transfers only
107
+ account_phone: str | None = None # additional recipient phone number, use only cases where phone is't primary account id
108
+ account_bic: str | None = None # recipient bank id
109
+ account_ewallet_name: str | None = None # additional recipient wallet provider info
110
+ account_email: str | None = None # additional recipient email, use only cases where email is't primary account id
111
+ account_bank_id: str | None = None # recipient bankId (from API banks or RU NSPK id)
112
+ account_internal_client_number: str | None = None # Bank internal identifier used for method banktransferphp (Philippines)
113
+ type: CredentialsTypes | None = None # primary credential type
114
+
107
115
 
108
116
  class PayOutModel(BaseRequestModel):
109
- currency: Optional[Currencies] # currency from, by default = usdt
110
- currencyTo: Optional[Currencies] # currency to, fiat only, if use quoteId - not required
111
- amount: Optional[float] # decimals: 2, if use quoteId - not required
112
- invoiceId: Optional[str] # idempotent key
113
- clientId: Optional[str] # uniq client ref
114
- ttl: Optional[int]
115
- ttl_unit: Optional[TTLUnits]
116
- finalAmount: Optional[float] # Optional, for pre-charge rate lock
117
- sender_name: Optional[str] # sender personal short data
118
- sender_personal: Optional[PayOutSenderModel]
119
- baseCurrency: Optional[CurrencyTypes]
120
- feesStrategy: Optional[FeesStrategy]
121
- recipient: PayOutRecipientModel
122
- quoteId: Optional[str]
123
- src_amount: Optional[str] # Optional, source amount in local currency for 2phase payout
124
- type: Optional[InvoiceTypes] # payout transaction scheme hint
125
-
117
+ currency: Currencies | None = None # currency from, by default = usdt
118
+ currencyTo: Currencies | None = None # currency to, fiat only, if use quoteId - not required
119
+ amount: Decimal | None = None # decimals: 2, if use quoteId - not required
120
+ invoiceId: str | None = None # idempotent key
121
+ clientId: str | None = None # uniq client ref
122
+ ttl: int | None = None
123
+ ttl_unit: TTLUnits | None = None
124
+ finalAmount: Decimal | None = None # Optional, for pre-charge rate lock
125
+ sender_name: str | None = None # sender personal short data
126
+ sender_personal: PayOutSenderModel | None = None
127
+ baseCurrency: CurrencyTypes | None = None
128
+ feesStrategy: FeesStrategy | None = None
129
+ recipient: PayOutRecipientModel
130
+ quoteId: str | None = None
131
+ src_amount: str | None = None # Optional, source amount in local currency for 2phase payout
132
+ type: InvoiceTypes | None = None # payout transaction scheme hint
133
+
134
+
126
135
  class PayOutResponseModel(BaseResponseModel):
127
- id: str
128
- status: Statuses
136
+ id: str
137
+ status: str
138
+
129
139
 
130
140
  class GetQuoteModel(BaseRequestModel):
131
- currency_from: Currencies
132
- currency_to: Currencies
133
- amount: float
134
- subtype: Optional[InvoiceTypes]
135
- currency_original: Optional[Currencies]
141
+ currency_from: Currencies
142
+ currency_to: Currencies
143
+ amount: Decimal
144
+ subtype: InvoiceTypes | None = None
145
+ currency_original: Currencies | None = None
146
+
136
147
 
137
148
  class QuoteEntity(BaseResponseModel):
138
- currencyFrom: Currencies
139
- currencyTo: Currencies
140
- pair: str
141
- rate: float
149
+ currencyFrom: Currencies
150
+ currencyTo: Currencies
151
+ pair: str
152
+ rate: float
153
+
142
154
 
143
155
  class GetQuoteResponseModel(BaseResponseModel):
144
- id: str
145
- finalAmount: float
146
- direction: InvoiceDirection
147
- fullRate: float
148
- fullRateReverse: float
149
- fees: float
150
- fees_percent: float
151
- quotes: List[QuoteEntity]
152
- expiredAt: Optional[datetime.datetime] = Field(default=None)
153
-
154
- #deprecated
155
- currency_from: Optional[CurrencyModel] = Field(default=None)
156
- currency_to: Optional[CurrencyModel] = Field(default=None)
157
- currency_middle: Optional[CurrencyModel] = Field(default=None)
158
- rate1: Optional[float] = Field(default=None)
159
- rate2: Optional[float] = Field(default=None)
160
- rate3: Optional[float] = Field(default=None)
161
- net_amount: Optional[float] = Field(default=None)
162
- metadata: Optional[object] = Field(default=None)
156
+ id: str
157
+ finalAmount: Decimal
158
+ direction: InvoiceDirection
159
+ fullRate: Decimal
160
+ fullRateReverse: Decimal
161
+ fees: Decimal
162
+ fees_percent: Decimal
163
+ quotes: List[QuoteEntity]
164
+ expiredAt: Optional[datetime.datetime] | None = None
165
+
166
+ # deprecated
167
+ currency_from: Optional[CurrencyModel] = Field(default=None)
168
+ currency_to: Optional[CurrencyModel] = Field(default=None)
169
+ currency_middle: Optional[CurrencyModel] = Field(default=None)
170
+ rate1: Optional[float] = Field(default=None)
171
+ rate2: Optional[float] = Field(default=None)
172
+ rate3: Optional[float] = Field(default=None)
173
+ net_amount: Optional[float] = Field(default=None)
174
+ metadata: Optional[object] = Field(default=None)
163
175
 
164
176
 
165
177
  class DepositAddressResponseModel(BaseResponseModel):
166
- currency: Currencies
167
- address: str
168
- expiredAt: datetime.datetime
178
+ currency: Currencies
179
+ address: str
180
+ expiredAt: datetime.datetime
169
181
 
170
182
 
171
183
  class CurrencyModel(BaseResponseModel):
172
- _id: str
173
- type: CurrencyTypes
174
- code: Currencies
175
- symbol: str
176
- label: Optional[str] = Field(default=None)
177
- decimal: int
178
- countryCode: Optional[str] = Field(default=None)
179
- countryName: Optional[str] = Field(default=None)
184
+ _id: str
185
+ type: CurrencyTypes
186
+ code: Currencies
187
+ symbol: str
188
+ label: Optional[str] = Field(default=None)
189
+ decimal: int
190
+ countryCode: Optional[str] = Field(default=None)
191
+ countryName: Optional[str] = Field(default=None)
192
+
180
193
 
181
194
  class BankModel(BaseResponseModel):
182
- name: str
183
- title: str
184
- currency: Currencies
185
- fpsId: str
195
+ name: str
196
+ title: str
197
+ currency: Currencies
198
+ fpsId: str
199
+
186
200
 
187
201
  class InvoiceStatusModel(BaseResponseModel):
188
- name: Statuses
189
- createdAt: datetime.datetime
190
- updatedAt: datetime.datetime
202
+ name: Statuses
203
+ createdAt: datetime.datetime
204
+ updatedAt: datetime.datetime
205
+
191
206
 
192
207
  class InvoiceAmountModel(BaseResponseModel):
193
- crypto: float
194
- fiat: float
195
- fiat_net: float
208
+ crypto: float
209
+ fiat: float
210
+ fiat_net: float
211
+
196
212
 
197
213
  class InvoiceMetadataModel(BaseResponseModel):
198
- invoiceId: Optional[str]
199
- clientId: Optional[str]
200
- fiatAmount: Optional[float]
214
+ invoiceId: Optional[str]
215
+ clientId: Optional[str]
216
+ fiatAmount: Optional[float]
217
+
201
218
 
202
219
  class InvoiceModel(BaseResponseModel):
203
- _id: str
204
- orderId: str
205
- projectId: str
206
- currencyFrom: CurrencyModel
207
- currencyTo: CurrencyModel
208
- direction: InvoiceDirection
209
- amount: float
210
- status: InvoiceStatusModel
211
- amounts: InvoiceAmountModel
212
- metadata: InvoiceMetadataModel
213
- receiptUrls: List[str]
214
- isExpired: bool
215
- createdAt: datetime.datetime
216
- updatedAt: datetime.datetime
217
- expiredAt: datetime.datetime
220
+ id: str | None = Field(..., alias='_id')
221
+ orderId: str
222
+ projectId: str
223
+ currencyFrom: CurrencyModel
224
+ currencyTo: CurrencyModel
225
+ direction: InvoiceDirection
226
+ amount: float
227
+ status: InvoiceStatusModel
228
+ amounts: InvoiceAmountModel
229
+ metadata: InvoiceMetadataModel
230
+ receiptUrls: List[str]
231
+ isExpired: bool
232
+ createdAt: datetime.datetime | None = None
233
+ updatedAt: datetime.datetime | None = None
234
+ expiredAt: datetime.datetime | None = None
235
+
218
236
 
219
237
  class AssetsAccountModel(BaseResponseModel):
220
- currency: CurrencyModel;
221
- total: float
222
- pending: float
223
- available: float
238
+ currency: CurrencyModel
239
+ total: float
240
+ pending: float
241
+ available: float
242
+
224
243
 
225
244
  class AssetsResponseModel(BaseResponseModel):
226
- assets: List[AssetsAccountModel]
245
+ assets: List[AssetsAccountModel]
246
+
227
247
 
228
248
  class PayInMultiWidgetOptions(BaseRequestModel):
229
- offerAmount: Optional[bool] # show amount select from best offers
230
- elqrBanks: Optional[str] # elqr bank list
249
+ offerAmount: Optional[bool] # show amount select from best offers
250
+ elqrBanks: Optional[str] # elqr bank list
251
+
231
252
 
232
253
  class PayOutSenderModel(BaseRequestModel):
233
- name: Optional[str]
234
- birthday: Optional[str]
235
- phone: Optional[str]
236
- passport: Optional[str]
254
+ name: Optional[str]
255
+ birthday: Optional[str]
256
+ phone: Optional[str]
257
+ passport: Optional[str]
258
+
237
259
 
238
260
  class PayOutTlvRequestModel(BaseRequestModel):
239
- quoteId: str # ID from /fx/tlv response
240
- invoiceId: Optional[str]
241
- clientId: Optional[str]
242
- sender_personal: Optional[PayOutSenderModel]
261
+ quoteId: str # ID from /fx/tlv response
262
+ invoiceId: Optional[str]
263
+ clientId: Optional[str]
264
+ sender_personal: Optional[PayOutSenderModel]
265
+
243
266
 
244
267
  class GetQuoteTlv(BaseRequestModel):
245
- data: str
268
+ data: str
269
+
246
270
 
247
271
  class QuoteTlvResponse(BaseResponseModel):
248
- id: str
249
- amount: float # fiat local amount
250
- amountCrypto: float # total crypto amount inc. fees
251
- currencyCode: Currencies # local currency
252
- feeInCrypto: float # total fee in crypto
253
- feePercent: float # fee percent
254
- qrVersion: int # qr code version, 1 - nspk, 2 - tlv encoded, 3 - tlv plain
255
- rate: float # exchange rate
256
- merchant: Optional[str] = Field(default=None) # merchant title
257
- logo: Optional[str] = Field(default=None) # merchant logo
272
+ id: str
273
+ amount: float # fiat local amount
274
+ amountCrypto: float # total crypto amount inc. fees
275
+ currencyCode: Currencies # local currency
276
+ feeInCrypto: float # total fee in crypto
277
+ feePercent: float # fee percent
278
+ qrVersion: int # qr code version, 1 - nspk, 2 - tlv encoded, 3 - tlv plain
279
+ rate: float # exchange rate
280
+ merchant: Optional[str] = Field(default=None) # merchant title
281
+ logo: Optional[str] = Field(default=None) # merchant logo
282
+
258
283
 
259
284
  class PayOutTlvRequest(BaseRequestModel):
260
- quoteId: str # quote.id ref
261
- invoiceId: Optional[str] = Field(default=None)
262
- clientId: Optional[str] = Field(default=None)
263
- src_amount: Optional[float] = Field(default=None)
264
- sender_personal: Optional[PayOutSenderModel] = Field(default=None)
285
+ quoteId: str # quote.id ref
286
+ invoiceId: Optional[str] = Field(default=None)
287
+ clientId: Optional[str] = Field(default=None)
288
+ src_amount: Optional[float] = Field(default=None)
289
+ sender_personal: Optional[PayOutSenderModel] = Field(default=None)
@@ -0,0 +1,91 @@
1
+ import enum
2
+ import base64
3
+ import hashlib
4
+ from cryptography.hazmat.primitives.asymmetric import padding
5
+ from cryptography.hazmat.primitives import hashes, serialization
6
+
7
+
8
+ class SignatureCheckMode(enum.StrEnum):
9
+ none = enum.auto()
10
+ decrypt_only = enum.auto()
11
+ full = enum.auto()
12
+
13
+
14
+ def flatten_stringify(d: dict, count: int = 1) -> dict:
15
+ def inner(d, count):
16
+ res = {}
17
+ for k, v in d.items():
18
+ if type(v) is list:
19
+ count += 1
20
+ elif type(v) is dict:
21
+ new_d, count = inner(v, count)
22
+ res = res | new_d
23
+ else:
24
+ res[(k.lower(), count)] = str(v).lower() if type(v) is bool else str(v)
25
+ count += 1
26
+ return res, count
27
+
28
+ (d, _) = inner(d, count)
29
+ return d
30
+
31
+
32
+ class SignatureHelper:
33
+ def __init__(
34
+ self,
35
+ private_key_data: str,
36
+ password: str = None,
37
+ mode: SignatureCheckMode = SignatureCheckMode.full,
38
+ ) -> None:
39
+ decoded_key = base64.decodebytes(private_key_data.encode("utf-8"))
40
+ self.private_key = serialization.load_pem_private_key(
41
+ decoded_key, password=password
42
+ )
43
+ self.mode = mode
44
+
45
+ def check(self, api_signature: str, json_value: dict) -> bool:
46
+ try:
47
+ self._check_impl(api_signature, json_value)
48
+ except ValueError:
49
+ return False
50
+ except:
51
+ raise
52
+ return True
53
+
54
+ def sign(self, json_value: dict) -> str:
55
+ digest = self.encode(json_value)
56
+ return base64.encodebytes(
57
+ self.private_key.sign(
58
+ digest,
59
+ padding.PSS(
60
+ mgf=padding.MGF1(hashes.SHA256()),
61
+ salt_length=padding.PSS.MAX_LENGTH,
62
+ ),
63
+ hashes.SHA256(),
64
+ )
65
+ ).decode("utf-8")
66
+
67
+ def encode(self, json_value: dict) -> bytes:
68
+ d = flatten_stringify(json_value)
69
+ val = "".join(d[k] for k in sorted(d.keys()))
70
+ return hashlib.sha256(val.encode("utf-8")).hexdigest().encode("utf-8")
71
+
72
+ def check_raise(self, api_signatre: str, json_value: dict) -> None:
73
+ self._check_impl(api_signatre, json_value)
74
+
75
+ def _check_impl(self, api_signature: str, json_value: dict) -> None:
76
+ if self.mode == SignatureCheckMode.none:
77
+ return
78
+ if len(api_signature) < 256:
79
+ raise ValueError(
80
+ "wrong api_signature length, check Webhook version.in project settings, should be 3!"
81
+ )
82
+ decoded_signature = base64.decodebytes(api_signature.encode("utf-8"))
83
+ alg = hashes.SHA256()
84
+ digest = self.private_key.decrypt(
85
+ decoded_signature,
86
+ padding.OAEP(mgf=padding.MGF1(algorithm=alg), algorithm=alg, label=None),
87
+ )
88
+ if self.mode == SignatureCheckMode.decrypt_only:
89
+ return
90
+ if self.encode(json_value) != digest:
91
+ raise ValueError("Error while checking signature")
paymentsgate/tokens.py CHANGED
@@ -2,6 +2,7 @@ from dataclasses import dataclass
2
2
  from jwt import JWT
3
3
  import time
4
4
 
5
+
5
6
  @dataclass
6
7
  class AccessToken:
7
8
  token: str
@@ -11,16 +12,18 @@ class AccessToken:
11
12
  self.token = token
12
13
  jwdInstance = JWT()
13
14
  parsed = jwdInstance.decode(token, do_verify=False, do_time_check=False)
14
- self.expiredAt = int(parsed['exp'])
15
+ self.expiredAt = int(parsed["exp"])
16
+
15
17
  @property
16
18
  def is_expired(self):
17
19
  if self.expiredAt:
18
- return int(time.time()) >= self.expiredAt;
20
+ return int(time.time()) >= self.expiredAt
19
21
  return True
20
22
 
21
23
  def __str__(self) -> str:
22
24
  return self.token
23
25
 
26
+
24
27
  @dataclass
25
28
  class RefreshToken:
26
29
  token: str
@@ -29,12 +32,12 @@ class RefreshToken:
29
32
  def __init__(self, token, expiredAt):
30
33
  self.token = token
31
34
  self.expiredAt = expiredAt
35
+
32
36
  @property
33
37
  def is_expired(self):
34
38
  if self.expiredAt:
35
- return int(time.time()) >= self.expiredAt;
39
+ return int(time.time()) >= self.expiredAt
36
40
  return True
37
41
 
38
42
  def __str__(self) -> str:
39
43
  return self.token
40
-
paymentsgate/transport.py CHANGED
@@ -1,19 +1,20 @@
1
- from dataclasses import dataclass
2
1
  from pydantic import BaseModel
3
2
 
3
+
4
4
  class Request(BaseModel):
5
5
  method: str
6
6
  path: str
7
- content_type: str = 'application/json'
7
+ content_type: str = "application/json"
8
8
  headers: dict[str, str] | None = None
9
9
  body: dict | None = None
10
10
  noAuth: bool | None = False
11
11
  signature: bool | None = False
12
12
 
13
+
13
14
  class Response(BaseModel):
14
15
  raw_body: bytes
15
- json_body: dict
16
16
  status_code: int
17
+ json_body: dict | None = None
17
18
 
18
19
  @property
19
20
  def success(self) -> bool:
@@ -22,7 +23,12 @@ class Response(BaseModel):
22
23
  def cast(self, model: BaseModel, error: dict):
23
24
  if self.success:
24
25
  return model(**self.json_body)
25
- return error(self.json_body.get('error'), self.json_body.get('message'), self.json_body.get('data'), self.json_body.get('status'));
26
-
26
+ return error(
27
+ self.json_body.get("error"),
28
+ self.json_body.get("message"),
29
+ self.json_body.get("data"),
30
+ self.json_body.get("status"),
31
+ )
32
+
27
33
  def __str__(self) -> str:
28
- return self.raw_body.decode("utf-8")
34
+ return self.raw_body.decode("utf-8")