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/__init__.py +14 -14
- paymentsgate/cache.py +9 -7
- paymentsgate/client.py +270 -110
- paymentsgate/enums.py +60 -59
- paymentsgate/exceptions.py +29 -12
- paymentsgate/logger.py +0 -1
- paymentsgate/mappers.py +0 -1
- paymentsgate/models.py +222 -197
- paymentsgate/signature.py +91 -0
- paymentsgate/tokens.py +7 -4
- paymentsgate/transport.py +12 -6
- paymentsgate/types.py +3 -5
- {paymentsgate-1.5.0.dist-info → paymentsgate-1.5.1.dist-info}/METADATA +5 -4
- paymentsgate-1.5.1.dist-info/RECORD +16 -0
- paymentsgate-1.5.0.dist-info/RECORD +0 -15
- {paymentsgate-1.5.0.dist-info → paymentsgate-1.5.1.dist-info}/LICENSE +0 -0
- {paymentsgate-1.5.0.dist-info → paymentsgate-1.5.1.dist-info}/WHEEL +0 -0
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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
-
|
|
23
|
+
model_config = ConfigDict(extra="forbid")
|
|
24
|
+
|
|
22
25
|
|
|
23
26
|
class BaseResponseModel(BaseModel):
|
|
24
|
-
|
|
27
|
+
model_config = ConfigDict(extra="ignore")
|
|
25
28
|
|
|
26
29
|
|
|
27
30
|
class Credentials(BaseModel):
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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
|
-
|
|
128
|
-
|
|
136
|
+
id: str
|
|
137
|
+
status: str
|
|
138
|
+
|
|
129
139
|
|
|
130
140
|
class GetQuoteModel(BaseRequestModel):
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
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
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
149
|
+
currencyFrom: Currencies
|
|
150
|
+
currencyTo: Currencies
|
|
151
|
+
pair: str
|
|
152
|
+
rate: float
|
|
153
|
+
|
|
142
154
|
|
|
143
155
|
class GetQuoteResponseModel(BaseResponseModel):
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
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
|
-
|
|
167
|
-
|
|
168
|
-
|
|
178
|
+
currency: Currencies
|
|
179
|
+
address: str
|
|
180
|
+
expiredAt: datetime.datetime
|
|
169
181
|
|
|
170
182
|
|
|
171
183
|
class CurrencyModel(BaseResponseModel):
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
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
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
195
|
+
name: str
|
|
196
|
+
title: str
|
|
197
|
+
currency: Currencies
|
|
198
|
+
fpsId: str
|
|
199
|
+
|
|
186
200
|
|
|
187
201
|
class InvoiceStatusModel(BaseResponseModel):
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
202
|
+
name: Statuses
|
|
203
|
+
createdAt: datetime.datetime
|
|
204
|
+
updatedAt: datetime.datetime
|
|
205
|
+
|
|
191
206
|
|
|
192
207
|
class InvoiceAmountModel(BaseResponseModel):
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
208
|
+
crypto: float
|
|
209
|
+
fiat: float
|
|
210
|
+
fiat_net: float
|
|
211
|
+
|
|
196
212
|
|
|
197
213
|
class InvoiceMetadataModel(BaseResponseModel):
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
214
|
+
invoiceId: Optional[str]
|
|
215
|
+
clientId: Optional[str]
|
|
216
|
+
fiatAmount: Optional[float]
|
|
217
|
+
|
|
201
218
|
|
|
202
219
|
class InvoiceModel(BaseResponseModel):
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
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
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
238
|
+
currency: CurrencyModel
|
|
239
|
+
total: float
|
|
240
|
+
pending: float
|
|
241
|
+
available: float
|
|
242
|
+
|
|
224
243
|
|
|
225
244
|
class AssetsResponseModel(BaseResponseModel):
|
|
226
|
-
|
|
245
|
+
assets: List[AssetsAccountModel]
|
|
246
|
+
|
|
227
247
|
|
|
228
248
|
class PayInMultiWidgetOptions(BaseRequestModel):
|
|
229
|
-
|
|
230
|
-
|
|
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
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
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
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
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
|
-
|
|
268
|
+
data: str
|
|
269
|
+
|
|
246
270
|
|
|
247
271
|
class QuoteTlvResponse(BaseResponseModel):
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
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
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
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[
|
|
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 =
|
|
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(
|
|
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
|
-
|
|
34
|
+
return self.raw_body.decode("utf-8")
|