paymentsgate 1.4.8__py3-none-any.whl → 1.5.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.

Potentially problematic release.


This version of paymentsgate might be problematic. Click here for more details.

paymentsgate/client.py CHANGED
@@ -1,18 +1,20 @@
1
1
  from __future__ import annotations
2
2
  import logging
3
- from dataclasses import dataclass, is_dataclass, field, asdict
3
+ # from dataclasses import dataclass, is_dataclass, field, asdict
4
4
  import json
5
5
  from urllib.parse import urlencode
6
+ from pydantic import Field, BaseModel
6
7
 
7
- from paymentsgate.tokens import (
8
+ from .types import TokenResponse
9
+ from .tokens import (
8
10
  AccessToken,
9
11
  RefreshToken
10
12
  )
11
- from paymentsgate.exceptions import (
13
+ from .exceptions import (
12
14
  APIResponseError,
13
15
  APIAuthenticationError
14
16
  )
15
- from paymentsgate.models import (
17
+ from .models import (
16
18
  Credentials,
17
19
  GetQuoteModel,
18
20
  GetQuoteResponseModel,
@@ -20,36 +22,35 @@ from paymentsgate.models import (
20
22
  PayInResponseModel,
21
23
  PayOutModel,
22
24
  PayOutResponseModel,
23
- InvoiceModel
25
+ InvoiceModel,
26
+ GetQuoteTlv,
27
+ PayOutTlvRequest,
28
+ QuoteTlvResponse,
24
29
  )
25
- from paymentsgate.enums import ApiPaths
26
- from paymentsgate.transport import (
30
+ from .enums import ApiPaths
31
+ from .transport import (
27
32
  Request,
28
33
  Response
29
34
  )
30
- from paymentsgate.logger import Logger
31
- from paymentsgate.cache import (
35
+ from .logger import Logger
36
+ from .cache import (
32
37
  AbstractCache,
33
38
  DefaultCache
34
39
  )
35
40
 
36
41
  import requests
37
42
 
38
- @dataclass
39
- class ApiClient:
40
- baseUrl: str = field(default="", init=False)
41
- timeout: int = field(default=180, init=True)
42
- logger: Logger = Logger
43
- cache: AbstractCache = field(default_factory=DefaultCache)
44
- config: Credentials = field(default_factory=dict, init=False)
45
-
46
- REQUEST_DEBUG: bool = False
47
- RESPONSE_DEBUG: bool = False
48
43
 
44
+ class ApiClient:
49
45
  def __init__(self, config: Credentials, baseUrl: str, debug: bool=False):
50
46
  self.config = config
51
47
  self.cache = DefaultCache()
52
48
  self.baseUrl = baseUrl
49
+
50
+ self.REQUEST_DEBUG = False
51
+ self.RESPONSE_DEBUG = False
52
+ self.timeout = 180
53
+
53
54
  if debug:
54
55
  logging.basicConfig(level=logging.DEBUG)
55
56
 
@@ -65,7 +66,7 @@ class ApiClient:
65
66
 
66
67
  # Handle response
67
68
  response = self._send_request(request)
68
- self.logger(request, response)
69
+ self.log(request, response)
69
70
  if (response.success):
70
71
  return response.cast(PayInResponseModel, APIResponseError)
71
72
  else:
@@ -84,45 +85,80 @@ class ApiClient:
84
85
 
85
86
  # Handle response
86
87
  response = self._send_request(request)
87
- self.logger(request, response)
88
+ self.log(request, response)
88
89
  if (response.success):
89
90
  return response.cast(PayOutResponseModel, APIResponseError)
90
91
  else:
91
92
  raise APIResponseError(response)
92
93
 
93
- def Quote(self, request: GetQuoteModel) -> GetQuoteResponseModel:
94
+ def PayOutTlv(self, request: PayOutTlvRequest) -> PayOutResponseModel:
95
+ request = Request(
96
+ method="post",
97
+ path=ApiPaths.invoices_payout_tlv,
98
+ content_type='application/json',
99
+ noAuth=False,
100
+ signature=False,
101
+ body=request
102
+ )
103
+
104
+ # Handle response
105
+ response = self._send_request(request)
106
+ self.log(request, response)
107
+ if not response.success:
108
+ raise APIResponseError(response)
109
+
110
+ return response.cast(PayOutResponseModel, APIResponseError)
111
+
112
+ def Quote(self, params: GetQuoteModel) -> GetQuoteResponseModel:
94
113
  # Prepare request
95
114
  request = Request(
96
- method="get",
115
+ method="post",
97
116
  path=ApiPaths.fx_quote,
98
117
  content_type='application/json',
99
118
  noAuth=False,
100
119
  signature=False,
101
- body=request
120
+ body=params
102
121
  )
103
122
 
104
123
  # Handle response
105
124
  response = self._send_request(request)
106
- self.logger(request, response)
125
+ self.log(request, response)
107
126
  if not response.success:
108
127
  raise APIResponseError(response)
109
128
 
110
129
  return response.cast(GetQuoteResponseModel, APIResponseError)
111
130
 
112
- def Status(self, query: str) -> InvoiceModel:
131
+ def QuoteQr(self, params: GetQuoteTlv) -> QuoteTlvResponse:
132
+ request = Request(
133
+ method="post",
134
+ path=ApiPaths.fx_quote_tlv,
135
+ content_type='application/json',
136
+ noAuth=False,
137
+ signature=False,
138
+ body=params
139
+ )
140
+
141
+ # Handle response
142
+ response = self._send_request(request)
143
+ self.log(request, response)
144
+ if not response.success:
145
+ raise APIResponseError(response)
146
+
147
+ return response.cast(QuoteTlvResponse, APIResponseError)
148
+
149
+ def Status(self, id: str) -> InvoiceModel:
113
150
  # Prepare request
114
151
  request = Request(
115
152
  method="get",
116
- path=ApiPaths.invoices_info,
153
+ path=ApiPaths.invoices_info.replace(':id', id),
117
154
  content_type='application/json',
118
155
  noAuth=False,
119
156
  signature=False,
120
- body=request
121
157
  )
122
158
 
123
159
  # Handle response
124
160
  response = self._send_request(request)
125
- self.logger(request, response)
161
+ self.log(request, response)
126
162
  if not response.success:
127
163
  raise APIResponseError(response)
128
164
 
@@ -131,23 +167,25 @@ class ApiClient:
131
167
  @property
132
168
  def token(self) -> AccessToken | None:
133
169
  # First check if valid token is cached
134
- token = self.cache.get_token('access')
135
- refresh = self.cache.get_token('refresh')
170
+ token = self.cache.get_token('AccessToken')
171
+ refresh = self.cache.get_token('RefreshToken')
172
+
136
173
  if token is not None and not token.is_expired:
137
174
  return token
138
175
  else:
139
176
  # try to refresh token
140
177
  if refresh is not None and not refresh.is_expired:
141
- refreshed = self._refresh_token()
178
+ refreshed = self._refresh_token(token, refresh)
142
179
 
143
180
  if (refreshed.success):
144
181
  access = AccessToken(
145
- response.json["access_token"]
182
+ refreshed.json_body["access_token"]
146
183
  )
147
184
  refresh = RefreshToken(
148
- response.json["refresh_token"],
149
- int(response.json["expires_in"]),
185
+ refreshed.json_body["refresh_token"],
186
+ int(refreshed.json_body["expires_in"]),
150
187
  )
188
+
151
189
  self.cache.set_token(access)
152
190
  self.cache.set_token(refresh)
153
191
 
@@ -158,12 +196,13 @@ class ApiClient:
158
196
  if response.success:
159
197
 
160
198
  access = AccessToken(
161
- response.json["access_token"]
199
+ response.json_body["access_token"]
162
200
  )
163
201
  refresh = RefreshToken(
164
- response.json["refresh_token"],
165
- int(response.json["expires_in"]),
202
+ response.json_body["refresh_token"],
203
+ int(response.json_body["expires_in"]),
166
204
  )
205
+
167
206
  self.cache.set_token(access)
168
207
  self.cache.set_token(refresh)
169
208
 
@@ -175,7 +214,8 @@ class ApiClient:
175
214
  """
176
215
  Send a specified Request to the GoPay REST API and process the response
177
216
  """
178
- body = asdict(request.body) if is_dataclass(request.body) else request.body
217
+ # body = asdict(request.body) if is_dataclass(request.body) else request.body
218
+ body = request.body
179
219
  # Add Bearer authentication to headers if needed
180
220
  headers = request.headers or {}
181
221
  if not request.noAuth:
@@ -185,35 +225,48 @@ class ApiClient:
185
225
 
186
226
  if (request.method == 'get'):
187
227
  params = urlencode(body)
188
- r = requests.request(
189
- method=request.method,
190
- url=f"{self.baseUrl}{request.path}?{params}",
191
- headers=headers,
192
- timeout=self.timeout
193
- )
228
+ try:
229
+ r = requests.request(
230
+ method=request.method,
231
+ url=f"{self.baseUrl}{request.path}?{params}",
232
+ headers=headers,
233
+ timeout=self.timeout
234
+ )
235
+ except:
236
+ print('Error')
237
+ pass
194
238
  else:
195
- r = requests.request(
196
- method=request.method,
197
- url=f"{self.baseUrl}{request.path}",
198
- headers=headers,
199
- json=body,
200
- timeout=self.timeout
201
- )
239
+ try:
240
+ r = requests.request(
241
+ method=request.method,
242
+ url=f"{self.baseUrl}{request.path}",
243
+ headers=headers,
244
+ json=body,
245
+ timeout=self.timeout
246
+ )
247
+ except KeyError:
248
+ print('Error')
249
+ pass
250
+
251
+ # if r == None:
202
252
 
203
253
  # Build Response instance, try to decode body as JSON
204
- response = Response(raw_body=r.content, json={}, status_code=r.status_code)
254
+ response = Response(raw_body=r.content, json_body={}, status_code=r.status_code)
205
255
 
206
256
  if (self.REQUEST_DEBUG):
207
257
  print(f"{request.method} => {self.baseUrl}{request.path} => {response.status_code}")
208
258
 
209
259
  try:
210
- response.json = r.json()
260
+ response.json_body = r.json()
211
261
  except json.JSONDecodeError:
212
262
  pass
213
263
 
214
- self.logger(request, response)
264
+ self.log(request, response)
215
265
  return response
216
266
 
267
+ def log(self, request: Request, response: Response):
268
+ Logger(self, request, response)
269
+
217
270
  def _get_token(self) -> Response:
218
271
  # Prepare request
219
272
  request = Request(
@@ -225,18 +278,27 @@ class ApiClient:
225
278
  )
226
279
  # Handle response
227
280
  response = self._send_request(request)
228
- self.logger(request, response)
281
+ self.log(request, response)
229
282
  return response
230
283
 
231
- def _refresh_token(self) -> Response:
284
+ def _refresh_token(self, access: AccessToken, refresh: RefreshToken) -> Response:
232
285
  # Prepare request
233
286
  request = Request(
234
287
  method="post",
235
288
  path=ApiPaths.token_refresh,
236
289
  content_type='application/json',
237
- body={"refresh_token": self.refreshToken},
290
+ noAuth=True,
291
+ headers={"Authorization": f"Bearer {access.token}" },
292
+ body={"refresh_token": refresh.token},
238
293
  )
239
294
  # Handle response
240
295
  response = self._send_request(request)
241
- self.logger(request, response)
296
+ self.log(request, response)
242
297
  return response
298
+
299
+ def loadToken(self, params: TokenResponse):
300
+ access = AccessToken(params.access_token)
301
+ refresh = RefreshToken(params.refresh_token, int(params.expires_in))
302
+ self.cache.set_token(access)
303
+ self.cache.set_token(refresh)
304
+
paymentsgate/enums.py CHANGED
@@ -16,6 +16,7 @@ class ApiPaths(StrEnum):
16
16
  token_validate = "/auth/token/validate"
17
17
  invoices_payin = "/deals/payin"
18
18
  invoices_payout = "/deals/payout"
19
+ invoices_payout_tlv = "/deals/tlv"
19
20
  invoices_info = "/deals/:id"
20
21
  invoices_credentials = "/deals/:id/credentials"
21
22
  assets_list = "/wallet"
@@ -25,6 +26,7 @@ class ApiPaths(StrEnum):
25
26
  appel_list = "/support/list"
26
27
  appel_stat = "/support/statistic"
27
28
  fx_quote = "/fx/calculatenew"
29
+ fx_quote_tlv = "/fx/tlv"
28
30
 
29
31
  class Currencies(StrEnum):
30
32
  USDT = "USDT"
@@ -57,14 +59,16 @@ class Currencies(StrEnum):
57
59
  AMD = "AMD"
58
60
 
59
61
  class Languages(StrEnum):
60
- EN = "EN"
61
- IN = "IN"
62
- AE = "AE"
63
- TR = "TR"
64
- GE = "GE"
65
- RU = "RU"
66
- UZ = "UZ"
67
- AZ = "AZ"
62
+ EN = "EN",
63
+ AZ = "AZ",
64
+ UZ = "UZ",
65
+ GE = "GE",
66
+ TR = "TR",
67
+ AE = "AE",
68
+ RU = "RU",
69
+ IN = "IN",
70
+ AR = "AR",
71
+ KG = "KG"
68
72
 
69
73
 
70
74
  class Statuses(StrEnum):
@@ -118,14 +122,42 @@ class InvoiceTypes(StrEnum):
118
122
  vodafonecash = "vodafonecash"
119
123
  razn = "razn"
120
124
  rtjs = "rtjs"
121
-
125
+ sberpay = "sberpay",
126
+ tpay = "tpay",
127
+ opay = "opay",
128
+ moniepoint = "moniepoint",
129
+ palmpay = "palmpay",
130
+ wave = "wave",
131
+ orangemoney = "orangemoney",
132
+ moovmoney = "moovmoney",
133
+ rtjscard = "rtjscard",
134
+ ruzs = "ruzs",
135
+ amobile = "amobile",
136
+ payid = "payid",
137
+ baridi = "baridi",
138
+ multiwidget = "multiwidget",
139
+ attijari = "attijari",
140
+ cih = "cih",
141
+ cashplus = "cashplus",
142
+ elqr = "elqr",
143
+ odengi = "odengi"
144
+
145
+ class EELQRBankALias(StrEnum):
146
+ bakai = 'bakai',
147
+ mbank = 'mbank',
148
+ optima = 'optima',
149
+ kicb = 'kicb',
150
+ odengi = 'odengi',
151
+ demir = 'demir',
152
+ megapay = 'megapay',
122
153
 
123
154
  class CredentialsTypes(StrEnum):
124
- iban = "iban"
125
- phone = "phone"
126
- card = "card"
127
- fps = "fps"
128
- account = "account"
155
+ iban = "iban",
156
+ phone = "phone",
157
+ card = "card",
158
+ fps = "fps",
159
+ qr = "qr",
160
+ account = "account",
129
161
  custom = "custom"
130
162
 
131
163
 
@@ -137,12 +169,13 @@ class RiskScoreLevels(StrEnum):
137
169
 
138
170
 
139
171
  class CancellationReason(StrEnum):
140
- NO_MONEY = "NO_MONEY"
141
- CREDENTIALS_INVALID = "CREDENTIALS_INVALID"
142
- EXPIRED = "EXPIRED"
143
- PRECHARGE_GAP_UPPER_LIMIT = "PRECHARGE_GAP_UPPER_LIMIT"
144
- CROSS_BANK_TFF_LESS_THAN_3K = "CROSS_BANK_TFF_LESS_THAN_3K"
145
- CROSS_BANK_UNSUPPORTED = "CROSS_BANK_UNSUPPORTED"
172
+ NO_MONEY = "NO_MONEY",
173
+ CREDENTIALS_INVALID = "CREDENTIALS_INVALID",
174
+ EXPIRED = "EXPIRED",
175
+ PRECHARGE_GAP_UPPER_LIMIT = "PRECHARGE_GAP_UPPER_LIMIT",
176
+ CROSS_BANK_TFF_LESS_THAN_3K = "CROSS_BANK_TFF_LESS_THAN_3K",
177
+ CROSS_BANK_UNSUPPORTED = "CROSS_BANK_UNSUPPORTED",
178
+ ACCOUNT_NUMBER_BLACKLISTED = "ACCOUNT_NUMBER_BLACKLISTED"
146
179
 
147
180
 
148
181
  class FeesStrategy(StrEnum):
@@ -151,8 +184,10 @@ class FeesStrategy(StrEnum):
151
184
 
152
185
 
153
186
  class InvoiceDirection(StrEnum):
154
- F2C = "F2C"
155
- C2F = "C2F"
187
+ F2C = "F2C",
188
+ C2F = "C2F",
189
+ FIAT_IN = "FIAT_IN",
190
+ FIAT_OUT = "FIAT_OUT"
156
191
 
157
192
 
158
193
  class TTLUnits(StrEnum):
@@ -31,8 +31,8 @@ class APIError(PaymentsgateError):
31
31
 
32
32
  class APIResponseError(APIError):
33
33
  def __init__(self, response: Response) -> None:
34
- super().__init__(response.json.get('error'), response.json.get('message'), response.json.get('data'), response.json.get('details'), response.status_code)
34
+ super().__init__(response.json_body.get('error'), response.json_body.get('message'), response.json_body.get('data'), response.json_body.get('details'), response.status_code)
35
35
 
36
36
  class APIAuthenticationError(APIError):
37
37
  def __init__(self, response: Response) -> None:
38
- super().__init__(response.json.get('error'), response.json.get('message'), response.json.get('data'), response.json.get('details'), response.status_code)
38
+ super().__init__(response.json_body.get('error'), response.json_body.get('message'), response.json_body.get('data'), response.json_body.get('details'), response.status_code)
@@ -0,0 +1,4 @@
1
+
2
+ # def QuoteResponseMapper(self, request: , response: Response):
3
+ # logging.debug(f"HTTP Request: {request}")
4
+ # logging.debug(f"HTTP Response: {response}")
paymentsgate/models.py CHANGED
@@ -1,8 +1,9 @@
1
1
  from __future__ import annotations
2
- from dataclasses import dataclass
3
2
  import datetime
4
3
  import json
5
4
  from typing import Optional, List
5
+ from pydantic import BaseModel, ConfigDict, Field
6
+
6
7
 
7
8
  from paymentsgate.enums import (
8
9
  Currencies,
@@ -16,36 +17,29 @@ from paymentsgate.enums import (
16
17
  CredentialsTypes
17
18
  )
18
19
 
19
- from pydantic import BaseModel, ConfigDict
20
-
21
- @dataclass
22
- class Credentials:
23
- def __init__(
24
- self,
25
- account_id: str,
26
- merchant_id: str,
27
- project_id: str,
28
- private_key: str,
29
- public_key: str
30
- ):
31
- self.account_id = account_id
32
- self.merchant_id = merchant_id
33
- self.project_id = project_id
34
- self.private_key = private_key
35
- self.public_key = public_key
20
+ class BaseRequestModel(BaseModel):
21
+ model_config = ConfigDict(extra='forbid')
22
+
23
+ class BaseResponseModel(BaseModel):
24
+ model_config = ConfigDict(extra='ignore')
25
+
26
+
27
+ 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)
36
33
 
37
34
  @classmethod
38
35
  def fromFile(cls, filename):
39
36
  data = json.load(open(filename))
40
- return cls(data.get('account_id'),
41
- data.get('merchant_id'),
42
- data.get('project_id'),
43
- data.get('private_key'),
44
- data.get('public_key'))
37
+ return cls(**data)
38
+
39
+ model_config = ConfigDict(extra='ignore')
45
40
 
46
41
 
47
- @dataclass
48
- class PayInFingerprintBrowserModel:
42
+ class PayInFingerprintBrowserModel(BaseRequestModel):
49
43
  acceptHeader: str
50
44
  colorDepth: int
51
45
  language: str
@@ -57,8 +51,7 @@ class PayInFingerprintBrowserModel:
57
51
  windowHeight: int
58
52
  windowWidth: int
59
53
 
60
- @dataclass
61
- class PayInFingerprintModel:
54
+ class PayInFingerprintModel(BaseRequestModel):
62
55
  fingerprint: str
63
56
  ip: str
64
57
  country: str
@@ -67,143 +60,146 @@ class PayInFingerprintModel:
67
60
  zip: str
68
61
  browser: Optional[PayInFingerprintBrowserModel]
69
62
 
70
- @dataclass
71
- class PayInModel:
72
- amount: float
63
+ class PayInModel(BaseRequestModel):
64
+ amount: float # decimals: 2
73
65
  currency: Currencies
66
+ country: Optional[str] # Country iso code
74
67
  invoiceId: Optional[str] # idempotent key
75
- clientId: Optional[str]
76
- type: InvoiceTypes
77
- bankId: Optional[str]
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
78
71
  trusted: Optional[bool]
79
72
  successUrl: Optional[str]
80
73
  failUrl: Optional[str]
81
74
  backUrl: Optional[str]
82
75
  clientCard: Optional[str]
76
+ clientName: Optional[str]
83
77
  fingerprint: Optional[PayInFingerprintModel]
84
78
  lang: Optional[Languages]
79
+ sync: Optional[bool] # sync h2h scheme, see documentation
80
+ multiWidgetOptions: Optional[PayInMultiWidgetOptions]
81
+ theme: Optional[str] # personalized widget theme
85
82
 
86
- @dataclass
87
- class PayInResponseModel:
83
+ class PayInResponseModel(BaseResponseModel):
88
84
  id: str
89
85
  status: Statuses
90
86
  type: InvoiceTypes
91
- url: str
92
-
93
- @dataclass
94
- class PayOutRecipientModel:
95
- account_number: str
96
- account_owner: Optional[str]
97
- account_iban: Optional[str]
98
- account_swift: Optional[str]
99
- account_phone: Optional[str]
100
- account_bic: Optional[str]
101
- account_ewallet_name: Optional[str]
102
- account_email: Optional[str]
103
- account_bank_id: Optional[str]
104
- type: Optional[CredentialsTypes]
105
-
106
- @dataclass
107
- class PayOutModel:
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]
94
+
95
+ 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
107
+
108
+ class PayOutModel(BaseRequestModel):
108
109
  currency: Optional[Currencies] # currency from, by default = usdt
109
- currencyTo:Currencies
110
- amount: float
110
+ currencyTo: Optional[Currencies] # currency to, fiat only, if use quoteId - not required
111
+ amount: Optional[float] # decimals: 2, if use quoteId - not required
111
112
  invoiceId: Optional[str] # idempotent key
112
- clientId: Optional[str]
113
+ clientId: Optional[str] # uniq client ref
113
114
  ttl: Optional[int]
114
115
  ttl_unit: Optional[TTLUnits]
115
- finalAmount: Optional[float]
116
- sender_name: Optional[str]
116
+ finalAmount: Optional[float] # Optional, for pre-charge rate lock
117
+ sender_name: Optional[str] # sender personal short data
118
+ sender_personal: Optional[PayOutSenderModel]
117
119
  baseCurrency: Optional[CurrencyTypes]
118
120
  feesStrategy: Optional[FeesStrategy]
119
121
  recipient: PayOutRecipientModel
120
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
121
125
 
122
- @dataclass
123
- class PayOutResponseModel:
126
+ class PayOutResponseModel(BaseResponseModel):
124
127
  id: str
125
128
  status: Statuses
126
129
 
127
- @dataclass
128
- class GetQuoteModel:
130
+ class GetQuoteModel(BaseRequestModel):
129
131
  currency_from: Currencies
130
132
  currency_to: Currencies
131
133
  amount: float
132
- subtype: InvoiceTypes
134
+ subtype: Optional[InvoiceTypes]
135
+ currency_original: Optional[Currencies]
133
136
 
134
- @dataclass
135
- class QuoteEntity:
136
- currency_from: CurrencyModel
137
- currency_to: CurrencyModel
137
+ class QuoteEntity(BaseResponseModel):
138
+ currencyFrom: Currencies
139
+ currencyTo: Currencies
138
140
  pair: str
139
141
  rate: float
140
142
 
141
- @dataclass
142
- class GetQuoteResponseModel:
143
- id: Optional[str] = None
144
- finalAmount: Optional[float] = None
145
- direction: Optional[InvoiceDirection] = None
146
- fullRate: Optional[float] = None
147
- fullRateReverse: Optional[float] = None
148
- fees: Optional[float] = None
149
- fees_percent: Optional[float] = None
150
- quotes: Optional[List[QuoteEntity]] = None
151
- expiredAt: Optional[datetime.datetime] = None
143
+ 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)
152
153
 
153
154
  #deprecated
154
- currency_from: Optional[CurrencyModel] = None
155
- currency_to: Optional[CurrencyModel] = None
156
- currency_middle: Optional[CurrencyModel] = None
157
- rate1: Optional[float] = None
158
- rate2: Optional[float] = None
159
- rate3: Optional[float] = None
160
- net_amount: Optional[float] = None
161
- metadata: Optional[object] = None
162
-
163
- @dataclass
164
- class DepositAddressResponseModel:
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)
163
+
164
+
165
+ class DepositAddressResponseModel(BaseResponseModel):
165
166
  currency: Currencies
166
167
  address: str
167
- expiredAt: datetime
168
+ expiredAt: datetime.datetime
168
169
 
169
- @dataclass
170
- class CurrencyModel:
170
+
171
+ class CurrencyModel(BaseResponseModel):
171
172
  _id: str
172
173
  type: CurrencyTypes
173
174
  code: Currencies
174
175
  symbol: str
175
- label: Optional[str]
176
+ label: Optional[str] = Field(default=None)
176
177
  decimal: int
177
- countryCode: Optional[str]
178
- countryName: Optional[str]
178
+ countryCode: Optional[str] = Field(default=None)
179
+ countryName: Optional[str] = Field(default=None)
179
180
 
180
- @dataclass
181
- class BankModel:
181
+ class BankModel(BaseResponseModel):
182
182
  name: str
183
183
  title: str
184
184
  currency: Currencies
185
185
  fpsId: str
186
186
 
187
- @dataclass
188
- class InvoiceStatusModel:
187
+ class InvoiceStatusModel(BaseResponseModel):
189
188
  name: Statuses
190
- createdAt: datetime
191
- updatedAt: datetime
189
+ createdAt: datetime.datetime
190
+ updatedAt: datetime.datetime
192
191
 
193
- @dataclass
194
- class InvoiceAmountModel:
192
+ class InvoiceAmountModel(BaseResponseModel):
195
193
  crypto: float
196
194
  fiat: float
197
195
  fiat_net: float
198
196
 
199
- @dataclass
200
- class InvoiceMetadataModel:
197
+ class InvoiceMetadataModel(BaseResponseModel):
201
198
  invoiceId: Optional[str]
202
199
  clientId: Optional[str]
203
200
  fiatAmount: Optional[float]
204
201
 
205
- @dataclass
206
- class InvoiceModel:
202
+ class InvoiceModel(BaseResponseModel):
207
203
  _id: str
208
204
  orderId: str
209
205
  projectId: str
@@ -216,18 +212,53 @@ class InvoiceModel:
216
212
  metadata: InvoiceMetadataModel
217
213
  receiptUrls: List[str]
218
214
  isExpired: bool
219
- createdAt: datetime
220
- updatedAt: datetime
221
- expiredAt: datetime
215
+ createdAt: datetime.datetime
216
+ updatedAt: datetime.datetime
217
+ expiredAt: datetime.datetime
222
218
 
223
- @dataclass
224
- class AssetsAccountModel:
219
+ class AssetsAccountModel(BaseResponseModel):
225
220
  currency: CurrencyModel;
226
221
  total: float
227
222
  pending: float
228
223
  available: float
229
224
 
230
- @dataclass
231
- class AssetsResponseModel:
225
+ class AssetsResponseModel(BaseResponseModel):
232
226
  assets: List[AssetsAccountModel]
233
227
 
228
+ class PayInMultiWidgetOptions(BaseRequestModel):
229
+ offerAmount: Optional[bool] # show amount select from best offers
230
+ elqrBanks: Optional[str] # elqr bank list
231
+
232
+ class PayOutSenderModel(BaseRequestModel):
233
+ name: Optional[str]
234
+ birthday: Optional[str]
235
+ phone: Optional[str]
236
+ passport: Optional[str]
237
+
238
+ class PayOutTlvRequestModel(BaseRequestModel):
239
+ quoteId: str # ID from /fx/tlv response
240
+ invoiceId: Optional[str]
241
+ clientId: Optional[str]
242
+ sender_personal: Optional[PayOutSenderModel]
243
+
244
+ class GetQuoteTlv(BaseRequestModel):
245
+ data: str
246
+
247
+ 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
258
+
259
+ 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)
paymentsgate/transport.py CHANGED
@@ -1,8 +1,7 @@
1
1
  from dataclasses import dataclass
2
2
  from pydantic import BaseModel
3
3
 
4
- @dataclass
5
- class Request:
4
+ class Request(BaseModel):
6
5
  method: str
7
6
  path: str
8
7
  content_type: str = 'application/json'
@@ -11,11 +10,9 @@ class Request:
11
10
  noAuth: bool | None = False
12
11
  signature: bool | None = False
13
12
 
14
-
15
- @dataclass
16
- class Response:
13
+ class Response(BaseModel):
17
14
  raw_body: bytes
18
- json: dict
15
+ json_body: dict
19
16
  status_code: int
20
17
 
21
18
  @property
@@ -24,8 +21,8 @@ class Response:
24
21
 
25
22
  def cast(self, model: BaseModel, error: dict):
26
23
  if self.success:
27
- return model(**self.json)
28
- return error(self.json.get('error'), self.json.get('message'), self.json.get('data'), self.json.get('status'));
24
+ 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'));
29
26
 
30
27
  def __str__(self) -> str:
31
28
  return self.raw_body.decode("utf-8")
paymentsgate/types.py ADDED
@@ -0,0 +1,9 @@
1
+ from pydantic import BaseModel
2
+
3
+ from .tokens import AccessToken, RefreshToken
4
+
5
+ class TokenResponse(BaseModel):
6
+ access_token: str
7
+ refresh_token: str
8
+ expires_in: int
9
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: paymentsgate
3
- Version: 1.4.8
3
+ Version: 1.5.0
4
4
  Summary: PaymentsGate's Python SDK for REST API
5
5
  Home-page: https://github.com/paymentsgate/python-secure-api
6
6
  License: MIT
@@ -0,0 +1,15 @@
1
+ paymentsgate/__init__.py,sha256=53DrE7IRD2NFQshE2EGYQ28jLr-ro45iFei28ZMOezI,336
2
+ paymentsgate/cache.py,sha256=w3xB3iaYPxVEZbeWxpWM1PQT-JctspoI5qC9bv6Xmts,1002
3
+ paymentsgate/client.py,sha256=0brT3_ZTt52lBIKHoBIPzybGJ_uv_taAelxmdKLkeC0,9338
4
+ paymentsgate/enums.py,sha256=kLK3xfw8zckTK6wbEA7RH9R98VaD2eF7asT9mzyLDBI,4201
5
+ paymentsgate/exceptions.py,sha256=-ZzMvzyViBQCf4LsbAFXwizDHF_0Axyn705n2FBJ17I,1346
6
+ paymentsgate/logger.py,sha256=QY6upavgb2y9dRQG05NwF_OTxP7bPspZR5QdpCjJULI,221
7
+ paymentsgate/mappers.py,sha256=ruXJ3I5VY89wbvJP7aJY8xws2LRXFj3eOMreXkrJ7g8,163
8
+ paymentsgate/models.py,sha256=kRTWo_velRP8pQ76-yc5vZp5OJP2pw-K8mMn3dB06Qg,8072
9
+ paymentsgate/tokens.py,sha256=qdvCQJ9jYIRKSxlmm5gip-nMAQWH8_EHys9zwel4oaU,916
10
+ paymentsgate/transport.py,sha256=VgVkWBCphrUtHeA79ExhlWo-FyidzHeUkxKIx8nnsh4,814
11
+ paymentsgate/types.py,sha256=UQ6RRcTcK-TXYPjwDP6DnSSYtHsw4Fmt2f34q6O_msg,172
12
+ paymentsgate-1.5.0.dist-info/LICENSE,sha256=4xWMZLmqNJ6602DZLEg0A9v03uT4xMq_-XSIxvXvfYM,1075
13
+ paymentsgate-1.5.0.dist-info/WHEEL,sha256=bbU3AyvhQ312rVm7zzRQjs6axI1UYWC3nmFA2E6FFSI,88
14
+ paymentsgate-1.5.0.dist-info/METADATA,sha256=5DVm2gm1jSZXaLNnLpDDXukEPgB7j-dI6ef97PX1dgU,4020
15
+ paymentsgate-1.5.0.dist-info/RECORD,,
@@ -1,13 +0,0 @@
1
- paymentsgate/__init__.py,sha256=53DrE7IRD2NFQshE2EGYQ28jLr-ro45iFei28ZMOezI,336
2
- paymentsgate/cache.py,sha256=w3xB3iaYPxVEZbeWxpWM1PQT-JctspoI5qC9bv6Xmts,1002
3
- paymentsgate/client.py,sha256=OM-kNkSKARc4_Nub9Vy0HEswQfaaYVvK7P3bbxh0Lag,7577
4
- paymentsgate/enums.py,sha256=wvDeVQvSO5WPPSWd1XKOh-8vgI1gsK2wyfMi7P8vay0,3311
5
- paymentsgate/exceptions.py,sha256=fQniUSQp8XkWXPCITHDUbVNcDwSPA8rJpwbbhJ8wfNQ,1306
6
- paymentsgate/logger.py,sha256=QY6upavgb2y9dRQG05NwF_OTxP7bPspZR5QdpCjJULI,221
7
- paymentsgate/models.py,sha256=LEwaoy2rkdA-2BLJoV85aKBlDMDu4NETZ0thCrBIV1g,5017
8
- paymentsgate/tokens.py,sha256=qdvCQJ9jYIRKSxlmm5gip-nMAQWH8_EHys9zwel4oaU,916
9
- paymentsgate/transport.py,sha256=pOmvDZh06uIGIUWTFi0vgf8pAgQT1f96ArcJNycU2Ks,785
10
- paymentsgate-1.4.8.dist-info/LICENSE,sha256=4xWMZLmqNJ6602DZLEg0A9v03uT4xMq_-XSIxvXvfYM,1075
11
- paymentsgate-1.4.8.dist-info/WHEEL,sha256=bbU3AyvhQ312rVm7zzRQjs6axI1UYWC3nmFA2E6FFSI,88
12
- paymentsgate-1.4.8.dist-info/METADATA,sha256=iQOvqOiCD_fnXJhEGnS3lOJId4rZJkAM1S9Tqh7qkWA,4020
13
- paymentsgate-1.4.8.dist-info/RECORD,,