paymentsgate 1.4.8__tar.gz → 1.5.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.

Potentially problematic release.


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

@@ -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
@@ -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
+
@@ -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}")
@@ -0,0 +1,264 @@
1
+ from __future__ import annotations
2
+ import datetime
3
+ import json
4
+ from typing import Optional, List
5
+ from pydantic import BaseModel, ConfigDict, Field
6
+
7
+
8
+ from paymentsgate.enums import (
9
+ Currencies,
10
+ InvoiceTypes,
11
+ Languages,
12
+ Statuses,
13
+ TTLUnits,
14
+ CurrencyTypes,
15
+ FeesStrategy,
16
+ InvoiceDirection,
17
+ CredentialsTypes
18
+ )
19
+
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)
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')
40
+
41
+
42
+ 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
53
+
54
+ 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]
62
+
63
+ 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
+
83
+ 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]
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):
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
+
126
+ class PayOutResponseModel(BaseResponseModel):
127
+ id: str
128
+ status: Statuses
129
+
130
+ class GetQuoteModel(BaseRequestModel):
131
+ currency_from: Currencies
132
+ currency_to: Currencies
133
+ amount: float
134
+ subtype: Optional[InvoiceTypes]
135
+ currency_original: Optional[Currencies]
136
+
137
+ class QuoteEntity(BaseResponseModel):
138
+ currencyFrom: Currencies
139
+ currencyTo: Currencies
140
+ pair: str
141
+ rate: float
142
+
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)
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)
163
+
164
+
165
+ class DepositAddressResponseModel(BaseResponseModel):
166
+ currency: Currencies
167
+ address: str
168
+ expiredAt: datetime.datetime
169
+
170
+
171
+ 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)
180
+
181
+ class BankModel(BaseResponseModel):
182
+ name: str
183
+ title: str
184
+ currency: Currencies
185
+ fpsId: str
186
+
187
+ class InvoiceStatusModel(BaseResponseModel):
188
+ name: Statuses
189
+ createdAt: datetime.datetime
190
+ updatedAt: datetime.datetime
191
+
192
+ class InvoiceAmountModel(BaseResponseModel):
193
+ crypto: float
194
+ fiat: float
195
+ fiat_net: float
196
+
197
+ class InvoiceMetadataModel(BaseResponseModel):
198
+ invoiceId: Optional[str]
199
+ clientId: Optional[str]
200
+ fiatAmount: Optional[float]
201
+
202
+ 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
218
+
219
+ class AssetsAccountModel(BaseResponseModel):
220
+ currency: CurrencyModel;
221
+ total: float
222
+ pending: float
223
+ available: float
224
+
225
+ class AssetsResponseModel(BaseResponseModel):
226
+ assets: List[AssetsAccountModel]
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)
@@ -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")
@@ -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
+
@@ -14,7 +14,7 @@ name = "paymentsgate"
14
14
  packages = [{include = "paymentsgate"}]
15
15
  readme = "README.md"
16
16
  repository = "https://github.com/paymentsgate/python-secure-api"
17
- version = "1.4.8"
17
+ version = "1.5.0"
18
18
 
19
19
  [tool.poetry.dependencies]
20
20
  pydantic = "^2.8.2"
@@ -15,7 +15,7 @@ install_requires = \
15
15
 
16
16
  setup_kwargs = {
17
17
  'name': 'paymentsgate',
18
- 'version': '1.4.8',
18
+ 'version': '1.5.0',
19
19
  'description': "PaymentsGate's Python SDK for REST API",
20
20
  'long_description': '\n# Paymentsgate Python SDK for Payments REST API\n\n\n## Requirements\n\n- Python >= 3.8.1\n- dependencies:\n - [`requests`](https://github.com/kennethreitz/requests)\n - [`pydantic`](https://docs.pydantic.dev/latest/)\n - [`jwt`](https://pyjwt.readthedocs.io/en/stable/)\n \n## Installation\n\nThe simplest way to install SDK is to use [PIP](https://docs.python.org/3/installing/):\n\n```bash\npip install paymentsgate\n```\n\n## Basic usage\n\n```python\nfrom paymentsgate import ApiClient, Credentials, Currencies\n\n\n# minimal configuration\nconfig = Credentials().fromFile(\'/path/to/credentials.json\');\n\n# create ApiClient\nclient = ApiClient(config, baseUrl=\'https://api.example.com\');\n\n# request quote\nres = cli.Quote(\n {\n "amount": 10.10,\n "currency_from": Currencies.EUR,\n "currency_to": Currencies.AZN,\n }\n)\nprint(res);\n```\n\nThe `credentials.json` file is used to connect to the client and contains all necessary data to use the API. This file can be obtained in your personal cabinet, in the service accounts section. Follow the instructions in the documentation to issue new keys. If you already have keys, but you don\'t feel comfortable storing them in a file, you can use client initialization via variables. In this case, the key data can be stored in external storage instead of on the file system:\n\n```python\nfrom paymentsgate import ApiClient, Credentials\n\nconfig = Credentials(\n account_id="00000000-4000-4000-0000-00000000000a" \n public_key="LS0tLS1CRUdJTiBSU0EgUFJJVkFUNSUlFb3dJQk..."\n)\n\nclient = ApiClient(config, baseUrl=\'https://api.example.com\');\n\n...\n```\n*It is important to note that the data format for key transfer is base46.\n\n## Examples\n\n### create PayIn\n\n```python\nres = cli.PayIn(\n {\n "amount": 10.10,\n "currency": Currencies.AZN,\n "invoiceId": "INVOICE-112123124",\n "clientId": "",\n "successUrl": "https://example.com/success",\n "failUrl": "https://example.com/fail",\n "type": InvoiceTypes.m10\n }\n)\nprint(res);\n```\n\n### create PayOut\n\n```python\nres = cli.PayOut(\n {\n "amount": 5.12,\n "currencyTo": Currencies.EUR,\n "invoiceId": "INVOICE-112123124",\n "clientId": "CLIENT-003010023004",\n "baseCurrency": CurrencyTypes.fiat,\n "feesStrategy": FeesStrategy.add,\n "recipient": {\n "account_number": "4000000000000012",\n "account_owner": "CARD HOLDER",\n "type": CredentialsTypes.card\n }\n }\n)\nprint(res);\n```\n\n### Error handling\n\n```python\ntry:\n res = cli.PayOut(\n {\n "amount": 5.12,\n "currencyTo": Currencies.EUR,\n "invoiceId": "INVOICE-112123124",\n "clientId": "CLIENT-003010023004",\n "baseCurrency": CurrencyTypes.fiat,\n "feesStrategy": FeesStrategy.add,\n "recipient": {\n "account_number": "4000000000000012",\n "account_owner": "CARD HOLDER",\n "type": CredentialsTypes.card\n }\n }\n )\n print(res);\nexcept APIAuthenticationError as err:\n print(f"Authentication fail: {err.message}")\nexcept APIResponseError as err:\n print(f"Exception: {err.error}; Message: {err.message}")\n```',
21
21
  'author': 'PaymentsGate',
@@ -1,233 +0,0 @@
1
- from __future__ import annotations
2
- from dataclasses import dataclass
3
- import datetime
4
- import json
5
- from typing import Optional, List
6
-
7
- from paymentsgate.enums import (
8
- Currencies,
9
- InvoiceTypes,
10
- Languages,
11
- Statuses,
12
- TTLUnits,
13
- CurrencyTypes,
14
- FeesStrategy,
15
- InvoiceDirection,
16
- CredentialsTypes
17
- )
18
-
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
36
-
37
- @classmethod
38
- def fromFile(cls, filename):
39
- 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'))
45
-
46
-
47
- @dataclass
48
- class PayInFingerprintBrowserModel:
49
- acceptHeader: str
50
- colorDepth: int
51
- language: str
52
- screenHeight: int
53
- screenWidth: int
54
- timezone: str
55
- userAgent: str
56
- javaEnabled: bool
57
- windowHeight: int
58
- windowWidth: int
59
-
60
- @dataclass
61
- class PayInFingerprintModel:
62
- fingerprint: str
63
- ip: str
64
- country: str
65
- city: str
66
- state: str
67
- zip: str
68
- browser: Optional[PayInFingerprintBrowserModel]
69
-
70
- @dataclass
71
- class PayInModel:
72
- amount: float
73
- currency: Currencies
74
- invoiceId: Optional[str] # idempotent key
75
- clientId: Optional[str]
76
- type: InvoiceTypes
77
- bankId: Optional[str]
78
- trusted: Optional[bool]
79
- successUrl: Optional[str]
80
- failUrl: Optional[str]
81
- backUrl: Optional[str]
82
- clientCard: Optional[str]
83
- fingerprint: Optional[PayInFingerprintModel]
84
- lang: Optional[Languages]
85
-
86
- @dataclass
87
- class PayInResponseModel:
88
- id: str
89
- status: Statuses
90
- 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:
108
- currency: Optional[Currencies] # currency from, by default = usdt
109
- currencyTo:Currencies
110
- amount: float
111
- invoiceId: Optional[str] # idempotent key
112
- clientId: Optional[str]
113
- ttl: Optional[int]
114
- ttl_unit: Optional[TTLUnits]
115
- finalAmount: Optional[float]
116
- sender_name: Optional[str]
117
- baseCurrency: Optional[CurrencyTypes]
118
- feesStrategy: Optional[FeesStrategy]
119
- recipient: PayOutRecipientModel
120
- quoteId: Optional[str]
121
-
122
- @dataclass
123
- class PayOutResponseModel:
124
- id: str
125
- status: Statuses
126
-
127
- @dataclass
128
- class GetQuoteModel:
129
- currency_from: Currencies
130
- currency_to: Currencies
131
- amount: float
132
- subtype: InvoiceTypes
133
-
134
- @dataclass
135
- class QuoteEntity:
136
- currency_from: CurrencyModel
137
- currency_to: CurrencyModel
138
- pair: str
139
- rate: float
140
-
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
152
-
153
- #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:
165
- currency: Currencies
166
- address: str
167
- expiredAt: datetime
168
-
169
- @dataclass
170
- class CurrencyModel:
171
- _id: str
172
- type: CurrencyTypes
173
- code: Currencies
174
- symbol: str
175
- label: Optional[str]
176
- decimal: int
177
- countryCode: Optional[str]
178
- countryName: Optional[str]
179
-
180
- @dataclass
181
- class BankModel:
182
- name: str
183
- title: str
184
- currency: Currencies
185
- fpsId: str
186
-
187
- @dataclass
188
- class InvoiceStatusModel:
189
- name: Statuses
190
- createdAt: datetime
191
- updatedAt: datetime
192
-
193
- @dataclass
194
- class InvoiceAmountModel:
195
- crypto: float
196
- fiat: float
197
- fiat_net: float
198
-
199
- @dataclass
200
- class InvoiceMetadataModel:
201
- invoiceId: Optional[str]
202
- clientId: Optional[str]
203
- fiatAmount: Optional[float]
204
-
205
- @dataclass
206
- class InvoiceModel:
207
- _id: str
208
- orderId: str
209
- projectId: str
210
- currencyFrom: CurrencyModel
211
- currencyTo: CurrencyModel
212
- direction: InvoiceDirection
213
- amount: float
214
- status: InvoiceStatusModel
215
- amounts: InvoiceAmountModel
216
- metadata: InvoiceMetadataModel
217
- receiptUrls: List[str]
218
- isExpired: bool
219
- createdAt: datetime
220
- updatedAt: datetime
221
- expiredAt: datetime
222
-
223
- @dataclass
224
- class AssetsAccountModel:
225
- currency: CurrencyModel;
226
- total: float
227
- pending: float
228
- available: float
229
-
230
- @dataclass
231
- class AssetsResponseModel:
232
- assets: List[AssetsAccountModel]
233
-
File without changes
File without changes