t402 1.6.1__py3-none-any.whl → 1.9.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.
- t402/__init__.py +169 -2
- t402/common.py +37 -8
- t402/encoding.py +298 -1
- t402/facilitator.py +1 -1
- t402/fastapi/__init__.py +79 -0
- t402/fastapi/dependencies.py +398 -0
- t402/fastapi/middleware.py +665 -130
- t402/schemes/__init__.py +164 -0
- t402/schemes/evm/__init__.py +46 -0
- t402/schemes/evm/exact/__init__.py +29 -0
- t402/schemes/evm/exact/client.py +265 -0
- t402/schemes/evm/exact/server.py +181 -0
- t402/schemes/evm/upto/__init__.py +58 -0
- t402/schemes/evm/upto/client.py +240 -0
- t402/schemes/evm/upto/types.py +305 -0
- t402/schemes/interfaces.py +401 -0
- t402/schemes/registry.py +477 -0
- t402/schemes/ton/__init__.py +22 -0
- t402/schemes/ton/exact/__init__.py +27 -0
- t402/schemes/ton/exact/client.py +343 -0
- t402/schemes/ton/exact/server.py +201 -0
- t402/schemes/tron/__init__.py +22 -0
- t402/schemes/tron/exact/__init__.py +27 -0
- t402/schemes/tron/exact/client.py +260 -0
- t402/schemes/tron/exact/server.py +192 -0
- t402/schemes/upto/__init__.py +80 -0
- t402/schemes/upto/types.py +376 -0
- t402/types.py +178 -8
- {t402-1.6.1.dist-info → t402-1.9.0.dist-info}/METADATA +1 -1
- {t402-1.6.1.dist-info → t402-1.9.0.dist-info}/RECORD +32 -11
- {t402-1.6.1.dist-info → t402-1.9.0.dist-info}/WHEEL +0 -0
- {t402-1.6.1.dist-info → t402-1.9.0.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
"""Up-To Scheme Core Types.
|
|
2
|
+
|
|
3
|
+
The upto scheme authorizes transfer of up to a maximum amount,
|
|
4
|
+
enabling usage-based billing where the final settlement amount
|
|
5
|
+
is determined by actual usage.
|
|
6
|
+
|
|
7
|
+
Example:
|
|
8
|
+
```python
|
|
9
|
+
from t402.schemes.upto import (
|
|
10
|
+
UptoPaymentRequirements,
|
|
11
|
+
UptoSettlement,
|
|
12
|
+
UptoUsageDetails,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
# Server specifies requirements
|
|
16
|
+
requirements = UptoPaymentRequirements(
|
|
17
|
+
scheme="upto",
|
|
18
|
+
network="eip155:8453",
|
|
19
|
+
max_amount="1000000", # $1.00 in USDC
|
|
20
|
+
min_amount="10000", # $0.01 minimum
|
|
21
|
+
asset="0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
22
|
+
pay_to="0x...",
|
|
23
|
+
max_timeout_seconds=300,
|
|
24
|
+
extra=UptoExtra(unit="token", unit_price="100"),
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
# Server settles for actual usage
|
|
28
|
+
settlement = UptoSettlement(
|
|
29
|
+
settle_amount="150000", # $0.15
|
|
30
|
+
usage_details=UptoUsageDetails(
|
|
31
|
+
units_consumed=1500,
|
|
32
|
+
unit_price="100",
|
|
33
|
+
unit_type="token",
|
|
34
|
+
),
|
|
35
|
+
)
|
|
36
|
+
```
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
from __future__ import annotations
|
|
40
|
+
|
|
41
|
+
from typing import Any, Dict, List, Optional, Literal
|
|
42
|
+
from pydantic import BaseModel, ConfigDict, Field, field_validator
|
|
43
|
+
from pydantic.alias_generators import to_camel
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
# Constants
|
|
47
|
+
SCHEME_UPTO = "upto"
|
|
48
|
+
DEFAULT_MIN_AMOUNT = "1000"
|
|
49
|
+
DEFAULT_MAX_TIMEOUT_SECONDS = 300
|
|
50
|
+
|
|
51
|
+
# Supported billing units
|
|
52
|
+
SUPPORTED_UNITS: List[str] = [
|
|
53
|
+
"token",
|
|
54
|
+
"request",
|
|
55
|
+
"second",
|
|
56
|
+
"minute",
|
|
57
|
+
"byte",
|
|
58
|
+
"kb",
|
|
59
|
+
"mb",
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class UptoExtra(BaseModel):
|
|
64
|
+
"""Extra fields specific to the upto scheme."""
|
|
65
|
+
|
|
66
|
+
unit: Optional[str] = Field(
|
|
67
|
+
default=None,
|
|
68
|
+
description="Billing unit (e.g., 'token', 'request', 'second', 'byte')",
|
|
69
|
+
)
|
|
70
|
+
unit_price: Optional[str] = Field(
|
|
71
|
+
default=None,
|
|
72
|
+
alias="unitPrice",
|
|
73
|
+
description="Price per unit in smallest denomination",
|
|
74
|
+
)
|
|
75
|
+
name: Optional[str] = Field(
|
|
76
|
+
default=None,
|
|
77
|
+
description="EIP-712 domain name (for EVM)",
|
|
78
|
+
)
|
|
79
|
+
version: Optional[str] = Field(
|
|
80
|
+
default=None,
|
|
81
|
+
description="EIP-712 domain version (for EVM)",
|
|
82
|
+
)
|
|
83
|
+
router_address: Optional[str] = Field(
|
|
84
|
+
default=None,
|
|
85
|
+
alias="routerAddress",
|
|
86
|
+
description="Router contract address (for EVM)",
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
model_config = ConfigDict(
|
|
90
|
+
alias_generator=to_camel,
|
|
91
|
+
populate_by_name=True,
|
|
92
|
+
from_attributes=True,
|
|
93
|
+
extra="allow", # Allow additional fields
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class UptoPaymentRequirements(BaseModel):
|
|
98
|
+
"""Extended payment requirements for the upto scheme.
|
|
99
|
+
|
|
100
|
+
Uses maxAmount instead of amount to indicate a maximum authorization.
|
|
101
|
+
"""
|
|
102
|
+
|
|
103
|
+
scheme: Literal["upto"] = Field(default="upto")
|
|
104
|
+
network: str = Field(description="Network identifier (CAIP-2 format)")
|
|
105
|
+
max_amount: str = Field(
|
|
106
|
+
alias="maxAmount",
|
|
107
|
+
description="Maximum amount the client authorizes (in smallest denomination)",
|
|
108
|
+
)
|
|
109
|
+
min_amount: Optional[str] = Field(
|
|
110
|
+
default=None,
|
|
111
|
+
alias="minAmount",
|
|
112
|
+
description="Minimum settlement amount (prevents dust payments)",
|
|
113
|
+
)
|
|
114
|
+
asset: str = Field(description="Asset contract address or identifier")
|
|
115
|
+
pay_to: str = Field(alias="payTo", description="Recipient address")
|
|
116
|
+
max_timeout_seconds: int = Field(
|
|
117
|
+
alias="maxTimeoutSeconds",
|
|
118
|
+
description="Maximum time in seconds before payment expires",
|
|
119
|
+
)
|
|
120
|
+
extra: UptoExtra = Field(default_factory=UptoExtra)
|
|
121
|
+
|
|
122
|
+
model_config = ConfigDict(
|
|
123
|
+
alias_generator=to_camel,
|
|
124
|
+
populate_by_name=True,
|
|
125
|
+
from_attributes=True,
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
@field_validator("max_amount", "min_amount")
|
|
129
|
+
@classmethod
|
|
130
|
+
def validate_amount(cls, v: Optional[str]) -> Optional[str]:
|
|
131
|
+
if v is not None:
|
|
132
|
+
try:
|
|
133
|
+
int(v)
|
|
134
|
+
except ValueError:
|
|
135
|
+
raise ValueError("Amount must be an integer encoded as a string")
|
|
136
|
+
return v
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
class UptoUsageDetails(BaseModel):
|
|
140
|
+
"""Usage details for settlement auditing."""
|
|
141
|
+
|
|
142
|
+
units_consumed: Optional[int] = Field(
|
|
143
|
+
default=None,
|
|
144
|
+
alias="unitsConsumed",
|
|
145
|
+
description="Number of units consumed",
|
|
146
|
+
)
|
|
147
|
+
unit_price: Optional[str] = Field(
|
|
148
|
+
default=None,
|
|
149
|
+
alias="unitPrice",
|
|
150
|
+
description="Price per unit used",
|
|
151
|
+
)
|
|
152
|
+
unit_type: Optional[str] = Field(
|
|
153
|
+
default=None,
|
|
154
|
+
alias="unitType",
|
|
155
|
+
description="Type of unit",
|
|
156
|
+
)
|
|
157
|
+
start_time: Optional[int] = Field(
|
|
158
|
+
default=None,
|
|
159
|
+
alias="startTime",
|
|
160
|
+
description="Start timestamp of usage period",
|
|
161
|
+
)
|
|
162
|
+
end_time: Optional[int] = Field(
|
|
163
|
+
default=None,
|
|
164
|
+
alias="endTime",
|
|
165
|
+
description="End timestamp of usage period",
|
|
166
|
+
)
|
|
167
|
+
metadata: Optional[Dict[str, Any]] = Field(
|
|
168
|
+
default=None,
|
|
169
|
+
description="Additional metadata",
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
model_config = ConfigDict(
|
|
173
|
+
alias_generator=to_camel,
|
|
174
|
+
populate_by_name=True,
|
|
175
|
+
from_attributes=True,
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
class UptoSettlement(BaseModel):
|
|
180
|
+
"""Settlement request for the upto scheme."""
|
|
181
|
+
|
|
182
|
+
settle_amount: str = Field(
|
|
183
|
+
alias="settleAmount",
|
|
184
|
+
description="Actual amount to settle (must be <= maxAmount)",
|
|
185
|
+
)
|
|
186
|
+
usage_details: Optional[UptoUsageDetails] = Field(
|
|
187
|
+
default=None,
|
|
188
|
+
alias="usageDetails",
|
|
189
|
+
description="Optional usage details for auditing",
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
model_config = ConfigDict(
|
|
193
|
+
alias_generator=to_camel,
|
|
194
|
+
populate_by_name=True,
|
|
195
|
+
from_attributes=True,
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
@field_validator("settle_amount")
|
|
199
|
+
@classmethod
|
|
200
|
+
def validate_settle_amount(cls, v: str) -> str:
|
|
201
|
+
try:
|
|
202
|
+
int(v)
|
|
203
|
+
except ValueError:
|
|
204
|
+
raise ValueError("settle_amount must be an integer encoded as a string")
|
|
205
|
+
return v
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
class UptoSettlementResponse(BaseModel):
|
|
209
|
+
"""Settlement response for the upto scheme."""
|
|
210
|
+
|
|
211
|
+
success: bool = Field(description="Whether settlement was successful")
|
|
212
|
+
transaction_hash: Optional[str] = Field(
|
|
213
|
+
default=None,
|
|
214
|
+
alias="transactionHash",
|
|
215
|
+
description="Transaction hash (if on-chain)",
|
|
216
|
+
)
|
|
217
|
+
settled_amount: str = Field(
|
|
218
|
+
alias="settledAmount",
|
|
219
|
+
description="Actual amount settled",
|
|
220
|
+
)
|
|
221
|
+
max_amount: str = Field(
|
|
222
|
+
alias="maxAmount",
|
|
223
|
+
description="Maximum amount that was authorized",
|
|
224
|
+
)
|
|
225
|
+
block_number: Optional[int] = Field(
|
|
226
|
+
default=None,
|
|
227
|
+
alias="blockNumber",
|
|
228
|
+
description="Block number (if on-chain)",
|
|
229
|
+
)
|
|
230
|
+
gas_used: Optional[str] = Field(
|
|
231
|
+
default=None,
|
|
232
|
+
alias="gasUsed",
|
|
233
|
+
description="Gas used (if on-chain)",
|
|
234
|
+
)
|
|
235
|
+
error: Optional[str] = Field(
|
|
236
|
+
default=None,
|
|
237
|
+
description="Error message if failed",
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
model_config = ConfigDict(
|
|
241
|
+
alias_generator=to_camel,
|
|
242
|
+
populate_by_name=True,
|
|
243
|
+
from_attributes=True,
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
class UptoValidationResult(BaseModel):
|
|
248
|
+
"""Validation result for upto payment."""
|
|
249
|
+
|
|
250
|
+
is_valid: bool = Field(alias="isValid", description="Whether the payment is valid")
|
|
251
|
+
invalid_reason: Optional[str] = Field(
|
|
252
|
+
default=None,
|
|
253
|
+
alias="invalidReason",
|
|
254
|
+
description="Reason if invalid",
|
|
255
|
+
)
|
|
256
|
+
validated_max_amount: Optional[str] = Field(
|
|
257
|
+
default=None,
|
|
258
|
+
alias="validatedMaxAmount",
|
|
259
|
+
description="Validated maximum amount",
|
|
260
|
+
)
|
|
261
|
+
payer: Optional[str] = Field(
|
|
262
|
+
default=None,
|
|
263
|
+
description="Payer address",
|
|
264
|
+
)
|
|
265
|
+
expires_at: Optional[int] = Field(
|
|
266
|
+
default=None,
|
|
267
|
+
alias="expiresAt",
|
|
268
|
+
description="Expiration timestamp",
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
model_config = ConfigDict(
|
|
272
|
+
alias_generator=to_camel,
|
|
273
|
+
populate_by_name=True,
|
|
274
|
+
from_attributes=True,
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
def is_upto_payment_requirements(data: Dict[str, Any]) -> bool:
|
|
279
|
+
"""Check if the given data represents upto payment requirements.
|
|
280
|
+
|
|
281
|
+
Args:
|
|
282
|
+
data: Dictionary to check
|
|
283
|
+
|
|
284
|
+
Returns:
|
|
285
|
+
True if data has scheme="upto" and maxAmount field
|
|
286
|
+
"""
|
|
287
|
+
scheme = data.get("scheme")
|
|
288
|
+
has_max_amount = "maxAmount" in data or "max_amount" in data
|
|
289
|
+
return scheme == SCHEME_UPTO and has_max_amount
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
def is_valid_unit(unit: str) -> bool:
|
|
293
|
+
"""Check if the given unit is a supported billing unit.
|
|
294
|
+
|
|
295
|
+
Args:
|
|
296
|
+
unit: Unit string to check
|
|
297
|
+
|
|
298
|
+
Returns:
|
|
299
|
+
True if unit is in SUPPORTED_UNITS
|
|
300
|
+
"""
|
|
301
|
+
return unit in SUPPORTED_UNITS
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
def create_payment_requirements(
|
|
305
|
+
network: str,
|
|
306
|
+
max_amount: str,
|
|
307
|
+
asset: str,
|
|
308
|
+
pay_to: str,
|
|
309
|
+
min_amount: Optional[str] = None,
|
|
310
|
+
max_timeout_seconds: int = DEFAULT_MAX_TIMEOUT_SECONDS,
|
|
311
|
+
extra: Optional[UptoExtra] = None,
|
|
312
|
+
) -> UptoPaymentRequirements:
|
|
313
|
+
"""Create a new UptoPaymentRequirements with default values.
|
|
314
|
+
|
|
315
|
+
Args:
|
|
316
|
+
network: Network identifier (CAIP-2 format)
|
|
317
|
+
max_amount: Maximum authorization amount
|
|
318
|
+
asset: Asset contract address
|
|
319
|
+
pay_to: Recipient address
|
|
320
|
+
min_amount: Minimum settlement amount (defaults to DEFAULT_MIN_AMOUNT)
|
|
321
|
+
max_timeout_seconds: Timeout in seconds
|
|
322
|
+
extra: Additional scheme-specific data
|
|
323
|
+
|
|
324
|
+
Returns:
|
|
325
|
+
UptoPaymentRequirements instance
|
|
326
|
+
"""
|
|
327
|
+
return UptoPaymentRequirements(
|
|
328
|
+
scheme="upto",
|
|
329
|
+
network=network,
|
|
330
|
+
max_amount=max_amount,
|
|
331
|
+
min_amount=min_amount or DEFAULT_MIN_AMOUNT,
|
|
332
|
+
asset=asset,
|
|
333
|
+
pay_to=pay_to,
|
|
334
|
+
max_timeout_seconds=max_timeout_seconds,
|
|
335
|
+
extra=extra or UptoExtra(),
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
def create_settlement(
|
|
340
|
+
settle_amount: str,
|
|
341
|
+
units_consumed: Optional[int] = None,
|
|
342
|
+
unit_price: Optional[str] = None,
|
|
343
|
+
unit_type: Optional[str] = None,
|
|
344
|
+
start_time: Optional[int] = None,
|
|
345
|
+
end_time: Optional[int] = None,
|
|
346
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
347
|
+
) -> UptoSettlement:
|
|
348
|
+
"""Create a settlement with optional usage details.
|
|
349
|
+
|
|
350
|
+
Args:
|
|
351
|
+
settle_amount: Amount to settle
|
|
352
|
+
units_consumed: Number of units consumed
|
|
353
|
+
unit_price: Price per unit
|
|
354
|
+
unit_type: Type of unit
|
|
355
|
+
start_time: Start timestamp
|
|
356
|
+
end_time: End timestamp
|
|
357
|
+
metadata: Additional metadata
|
|
358
|
+
|
|
359
|
+
Returns:
|
|
360
|
+
UptoSettlement instance
|
|
361
|
+
"""
|
|
362
|
+
usage_details = None
|
|
363
|
+
if any([units_consumed, unit_price, unit_type, start_time, end_time, metadata]):
|
|
364
|
+
usage_details = UptoUsageDetails(
|
|
365
|
+
units_consumed=units_consumed,
|
|
366
|
+
unit_price=unit_price,
|
|
367
|
+
unit_type=unit_type,
|
|
368
|
+
start_time=start_time,
|
|
369
|
+
end_time=end_time,
|
|
370
|
+
metadata=metadata,
|
|
371
|
+
)
|
|
372
|
+
|
|
373
|
+
return UptoSettlement(
|
|
374
|
+
settle_amount=settle_amount,
|
|
375
|
+
usage_details=usage_details,
|
|
376
|
+
)
|
t402/types.py
CHANGED
|
@@ -12,6 +12,14 @@ from pydantic.alias_generators import to_camel
|
|
|
12
12
|
|
|
13
13
|
from t402.networks import SupportedNetworks
|
|
14
14
|
|
|
15
|
+
# Protocol version constants
|
|
16
|
+
T402_VERSION_V1 = 1
|
|
17
|
+
T402_VERSION_V2 = 2
|
|
18
|
+
T402_VERSION = T402_VERSION_V2 # Current default version
|
|
19
|
+
|
|
20
|
+
# Network type alias (CAIP-2 format: "namespace:reference")
|
|
21
|
+
Network = str # e.g., "eip155:1", "solana:mainnet", "ton:mainnet"
|
|
22
|
+
|
|
15
23
|
|
|
16
24
|
# Add HTTP request structure types
|
|
17
25
|
class HTTPVerbs(str, Enum):
|
|
@@ -93,7 +101,14 @@ Money = Union[str, int] # e.g., "$0.01", 0.01, "0.001"
|
|
|
93
101
|
Price = Union[Money, TokenAmount]
|
|
94
102
|
|
|
95
103
|
|
|
96
|
-
|
|
104
|
+
# =============================================================================
|
|
105
|
+
# V1 Types (Legacy - for backward compatibility)
|
|
106
|
+
# =============================================================================
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class PaymentRequirementsV1(BaseModel):
|
|
110
|
+
"""V1 Payment Requirements - Legacy format."""
|
|
111
|
+
|
|
97
112
|
scheme: str
|
|
98
113
|
network: SupportedNetworks
|
|
99
114
|
max_amount_required: str
|
|
@@ -123,10 +138,15 @@ class PaymentRequirements(BaseModel):
|
|
|
123
138
|
return v
|
|
124
139
|
|
|
125
140
|
|
|
126
|
-
#
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
141
|
+
# Alias for backward compatibility
|
|
142
|
+
PaymentRequirements = PaymentRequirementsV1
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
class t402PaymentRequiredResponseV1(BaseModel):
|
|
146
|
+
"""V1 Payment Required Response - Legacy format (returned in response body)."""
|
|
147
|
+
|
|
148
|
+
t402_version: int = Field(default=T402_VERSION_V1, alias="t402Version")
|
|
149
|
+
accepts: list[PaymentRequirementsV1]
|
|
130
150
|
error: str
|
|
131
151
|
|
|
132
152
|
model_config = ConfigDict(
|
|
@@ -136,6 +156,80 @@ class t402PaymentRequiredResponse(BaseModel):
|
|
|
136
156
|
)
|
|
137
157
|
|
|
138
158
|
|
|
159
|
+
# Alias for backward compatibility
|
|
160
|
+
t402PaymentRequiredResponse = t402PaymentRequiredResponseV1
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
# =============================================================================
|
|
164
|
+
# V2 Types (Current Protocol Version)
|
|
165
|
+
# =============================================================================
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
class ResourceInfo(BaseModel):
|
|
169
|
+
"""Resource information for V2 protocol.
|
|
170
|
+
|
|
171
|
+
Contains metadata about the protected resource.
|
|
172
|
+
"""
|
|
173
|
+
|
|
174
|
+
url: str
|
|
175
|
+
description: str = ""
|
|
176
|
+
mime_type: str = Field(default="", alias="mimeType")
|
|
177
|
+
|
|
178
|
+
model_config = ConfigDict(
|
|
179
|
+
alias_generator=to_camel,
|
|
180
|
+
populate_by_name=True,
|
|
181
|
+
from_attributes=True,
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
class PaymentRequirementsV2(BaseModel):
|
|
186
|
+
"""V2 Payment Requirements - Current format.
|
|
187
|
+
|
|
188
|
+
Represents a single payment option that a client can use to pay for access.
|
|
189
|
+
"""
|
|
190
|
+
|
|
191
|
+
scheme: str
|
|
192
|
+
network: Network
|
|
193
|
+
asset: str
|
|
194
|
+
amount: str
|
|
195
|
+
pay_to: str = Field(alias="payTo")
|
|
196
|
+
max_timeout_seconds: int = Field(alias="maxTimeoutSeconds")
|
|
197
|
+
extra: dict[str, Any] = Field(default_factory=dict)
|
|
198
|
+
|
|
199
|
+
model_config = ConfigDict(
|
|
200
|
+
alias_generator=to_camel,
|
|
201
|
+
populate_by_name=True,
|
|
202
|
+
from_attributes=True,
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
@field_validator("amount")
|
|
206
|
+
def validate_amount(cls, v):
|
|
207
|
+
try:
|
|
208
|
+
int(v)
|
|
209
|
+
except ValueError:
|
|
210
|
+
raise ValueError("amount must be an integer encoded as a string")
|
|
211
|
+
return v
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
class PaymentRequiredV2(BaseModel):
|
|
215
|
+
"""V2 Payment Required Response - Current format.
|
|
216
|
+
|
|
217
|
+
Returned in the PAYMENT-REQUIRED header as base64-encoded JSON.
|
|
218
|
+
"""
|
|
219
|
+
|
|
220
|
+
t402_version: int = Field(default=T402_VERSION_V2, alias="t402Version")
|
|
221
|
+
resource: ResourceInfo
|
|
222
|
+
accepts: list[PaymentRequirementsV2]
|
|
223
|
+
error: Optional[str] = None
|
|
224
|
+
extensions: Optional[dict[str, Any]] = None
|
|
225
|
+
|
|
226
|
+
model_config = ConfigDict(
|
|
227
|
+
alias_generator=to_camel,
|
|
228
|
+
populate_by_name=True,
|
|
229
|
+
from_attributes=True,
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
|
|
139
233
|
class ExactPaymentPayload(BaseModel):
|
|
140
234
|
signature: str
|
|
141
235
|
authorization: EIP3009Authorization
|
|
@@ -258,7 +352,7 @@ class VerifyResponse(BaseModel):
|
|
|
258
352
|
|
|
259
353
|
class SettleResponse(BaseModel):
|
|
260
354
|
success: bool
|
|
261
|
-
error_reason: Optional[str] = None
|
|
355
|
+
error_reason: Optional[str] = Field(None, alias="errorReason")
|
|
262
356
|
transaction: Optional[str] = None
|
|
263
357
|
network: Optional[str] = None
|
|
264
358
|
payer: Optional[str] = None
|
|
@@ -270,12 +364,65 @@ class SettleResponse(BaseModel):
|
|
|
270
364
|
)
|
|
271
365
|
|
|
272
366
|
|
|
367
|
+
class PaymentResponseV2(BaseModel):
|
|
368
|
+
"""V2 Payment Response - returned in PAYMENT-RESPONSE header after settlement."""
|
|
369
|
+
|
|
370
|
+
success: bool
|
|
371
|
+
error_reason: Optional[str] = Field(None, alias="errorReason")
|
|
372
|
+
payer: Optional[str] = None
|
|
373
|
+
transaction: str
|
|
374
|
+
network: Network
|
|
375
|
+
requirements: PaymentRequirementsV2
|
|
376
|
+
|
|
377
|
+
model_config = ConfigDict(
|
|
378
|
+
alias_generator=to_camel,
|
|
379
|
+
populate_by_name=True,
|
|
380
|
+
from_attributes=True,
|
|
381
|
+
)
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
# =============================================================================
|
|
385
|
+
# Facilitator Types
|
|
386
|
+
# =============================================================================
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
class SupportedKind(BaseModel):
|
|
390
|
+
"""A single supported scheme/network combination from the facilitator."""
|
|
391
|
+
|
|
392
|
+
t402_version: int = Field(alias="t402Version")
|
|
393
|
+
scheme: str
|
|
394
|
+
network: Network
|
|
395
|
+
extra: Optional[dict[str, Any]] = None
|
|
396
|
+
|
|
397
|
+
model_config = ConfigDict(
|
|
398
|
+
alias_generator=to_camel,
|
|
399
|
+
populate_by_name=True,
|
|
400
|
+
from_attributes=True,
|
|
401
|
+
)
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
class SupportedResponse(BaseModel):
|
|
405
|
+
"""Response from facilitator's /supported endpoint."""
|
|
406
|
+
|
|
407
|
+
kinds: list[SupportedKind]
|
|
408
|
+
extensions: list[str] = Field(default_factory=list)
|
|
409
|
+
signers: dict[str, list[str]] = Field(default_factory=dict) # CAIP family → addresses
|
|
410
|
+
|
|
411
|
+
model_config = ConfigDict(
|
|
412
|
+
alias_generator=to_camel,
|
|
413
|
+
populate_by_name=True,
|
|
414
|
+
from_attributes=True,
|
|
415
|
+
)
|
|
416
|
+
|
|
417
|
+
|
|
273
418
|
# Union of payloads for each scheme
|
|
274
419
|
SchemePayloads = Union[ExactPaymentPayload, TonPaymentPayload, TronPaymentPayload]
|
|
275
420
|
|
|
276
421
|
|
|
277
|
-
class
|
|
278
|
-
|
|
422
|
+
class PaymentPayloadV1(BaseModel):
|
|
423
|
+
"""V1 Payment Payload - Legacy format."""
|
|
424
|
+
|
|
425
|
+
t402_version: int = Field(default=T402_VERSION_V1, alias="t402Version")
|
|
279
426
|
scheme: str
|
|
280
427
|
network: str
|
|
281
428
|
payload: SchemePayloads
|
|
@@ -287,6 +434,29 @@ class PaymentPayload(BaseModel):
|
|
|
287
434
|
)
|
|
288
435
|
|
|
289
436
|
|
|
437
|
+
# Alias for backward compatibility
|
|
438
|
+
PaymentPayload = PaymentPayloadV1
|
|
439
|
+
|
|
440
|
+
|
|
441
|
+
class PaymentPayloadV2(BaseModel):
|
|
442
|
+
"""V2 Payment Payload - Current format.
|
|
443
|
+
|
|
444
|
+
Sent in the PAYMENT-SIGNATURE header as base64-encoded JSON.
|
|
445
|
+
"""
|
|
446
|
+
|
|
447
|
+
t402_version: int = Field(default=T402_VERSION_V2, alias="t402Version")
|
|
448
|
+
resource: Optional[ResourceInfo] = None
|
|
449
|
+
accepted: PaymentRequirementsV2
|
|
450
|
+
payload: dict[str, Any]
|
|
451
|
+
extensions: Optional[dict[str, Any]] = None
|
|
452
|
+
|
|
453
|
+
model_config = ConfigDict(
|
|
454
|
+
alias_generator=to_camel,
|
|
455
|
+
populate_by_name=True,
|
|
456
|
+
from_attributes=True,
|
|
457
|
+
)
|
|
458
|
+
|
|
459
|
+
|
|
290
460
|
class T402Headers(BaseModel):
|
|
291
461
|
x_payment: str
|
|
292
462
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
t402/__init__.py,sha256=
|
|
1
|
+
t402/__init__.py,sha256=fOImK_KcZYKuxqqISMTpH2mn0MxF6BH5OxHfsfhzhEI,14708
|
|
2
2
|
t402/chains.py,sha256=Hyn3bfubNpiWE1fqkyJulkonzMu3lncSt-_RCgagdro,2832
|
|
3
3
|
t402/cli.py,sha256=UiGuXNHUp67P7S2nD2j67JJ1M2KWalXk5qjE3LfXK_4,9732
|
|
4
|
-
t402/common.py,sha256=
|
|
5
|
-
t402/encoding.py,sha256=
|
|
4
|
+
t402/common.py,sha256=nw-PxQxdAmdsRImjYYKHNLPtrtsZ2TS3YGGpD8LCM8Y,6845
|
|
5
|
+
t402/encoding.py,sha256=uOiN5mqADsYJJVcGxL0qe1XSPJSCm9l67ZFFUBs-DZY,9859
|
|
6
6
|
t402/evm_paywall_template.py,sha256=VJp3bntjRmz5ocWFCtzeB9842ds1Kd8as8rP48GhaDI,1928580
|
|
7
7
|
t402/exact.py,sha256=ZC2CqwBYKv5WzhTpNWtcUPeAItDvatyrMNV47nYumE8,4448
|
|
8
|
-
t402/facilitator.py,sha256=
|
|
8
|
+
t402/facilitator.py,sha256=uEFu1BM_pXpT2Zxq8ZLlES-sf5BLOdcgo05sU-EwDm4,4791
|
|
9
9
|
t402/networks.py,sha256=WZzQ_azmlLwD4sp3RCcU0BMMZ26FGyQSiGeNjaxX340,3766
|
|
10
10
|
t402/path.py,sha256=G3oxm12FS1OsxXK_BPopS4bVCFrei5MOMnQAvDbgZsY,1311
|
|
11
11
|
t402/paywall.py,sha256=lSpGlDBC4slPU5rw7aDq5iBrBJXxLHRe3lgddFMKZ-Q,4119
|
|
@@ -15,7 +15,7 @@ t402/svm_paywall_template.py,sha256=pmueO0Qgcwl-uyci8lVI1eiery2vggHGl_b1tnxcB_E,
|
|
|
15
15
|
t402/ton.py,sha256=ggh_6nvZ55c-iYe6bNVejPJIWoFRSf85-DQmsvu-auI,11919
|
|
16
16
|
t402/ton_paywall_template.py,sha256=kRddMyZk51dDd1b4-r_MlSGCc-DLRHskIxA-LN9l9vw,7343
|
|
17
17
|
t402/tron.py,sha256=2e1au4-kqIvlN-gHHCnt8eH1x2LDtl7lnqydQj56J1A,14441
|
|
18
|
-
t402/types.py,sha256=
|
|
18
|
+
t402/types.py,sha256=gE9mOzKCoqiMIe1HUKcef3RnZfs7sPOUHo4wMHGsCEM,14031
|
|
19
19
|
t402/bridge/__init__.py,sha256=S-ZBryTNC7agJmZNuvfxy_icJA4Yf7vwVGbOyMN-1oY,3437
|
|
20
20
|
t402/bridge/client.py,sha256=023ZEfDrBN03K-JlH_Lf-wHFZUL1IwYz78udT6JfW7U,10285
|
|
21
21
|
t402/bridge/constants.py,sha256=7mojUg9v-f-SJxkzPwEo12b_rEgMZzMBeDv4rx-n9WI,7886
|
|
@@ -31,8 +31,9 @@ t402/erc4337/accounts.py,sha256=Q-d4RitjFpjek0vM8jkWl_StajBu9rZeg2jOLCDfmsg,1366
|
|
|
31
31
|
t402/erc4337/bundlers.py,sha256=K_s9sqmMOBhg-t4x_uUsR9jRW0s3m2-J2xpotnJqxyE,16784
|
|
32
32
|
t402/erc4337/paymasters.py,sha256=dGb5LffxMuT_f796ZOSiK3xozRAB7_Q71EwY6eb8t1k,17294
|
|
33
33
|
t402/erc4337/types.py,sha256=gf4FOWwwkH41u6ccXw68Rdd6PgokHMpC0rlYFPkP_2c,9903
|
|
34
|
-
t402/fastapi/__init__.py,sha256=
|
|
35
|
-
t402/fastapi/
|
|
34
|
+
t402/fastapi/__init__.py,sha256=PDtegk_kRcJJngLPRx8CDhcCenLLRnE5CJGkE7SuJBM,2043
|
|
35
|
+
t402/fastapi/dependencies.py,sha256=7fiISDww0NbSzqOkIU2h9EYciHysQlLcABBZKFu3DEw,12830
|
|
36
|
+
t402/fastapi/middleware.py,sha256=w80PNhLJbIl7uDfAU6zVpxz75Cgv7h195OsZ9GxCEKk,26643
|
|
36
37
|
t402/flask/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
37
38
|
t402/flask/middleware.py,sha256=SFvUvRLDHmMHEf8TpnvHDr8bBqwXHNa6SR-4zsBAkh0,14146
|
|
38
39
|
t402/mcp/__init__.py,sha256=H4GObInvcQhretxbesQlm2Gc6nyY4GNwjKJULVSIJUs,2556
|
|
@@ -40,12 +41,32 @@ t402/mcp/constants.py,sha256=H6gD1VKUnq6Bmx0nVXbbAVqeSY9gXpooa9HgzOA0dZw,6296
|
|
|
40
41
|
t402/mcp/server.py,sha256=YVSnSLZu3nNtI_rryVSSsiXZBu-tGikaB3x6ZoIB5rQ,18874
|
|
41
42
|
t402/mcp/tools.py,sha256=w5HQfZW6oGaKd-ncNGPJ6W9BtFIzrg-7vg2UPKLYNWA,6600
|
|
42
43
|
t402/mcp/types.py,sha256=_fOBnWOtH2WdUxjffJilizRp6MkK7QRN-_wV1YzGdhA,4017
|
|
44
|
+
t402/schemes/__init__.py,sha256=zTCbq3Cqu9xiOLuHV3ejRlZcfJnrR0rIouyNBZY6raM,3883
|
|
45
|
+
t402/schemes/interfaces.py,sha256=7wxWx_QhYb5azpIkbIRSk-Y9KwDiXZoyVsYTS3Rn2nE,11981
|
|
46
|
+
t402/schemes/registry.py,sha256=p0HrDrrc0UtyShQwVp34pt-KNXM3LQSpreUM4y1yxqc,14141
|
|
47
|
+
t402/schemes/evm/__init__.py,sha256=FvODTl-e920OMtJjEMFOBJWmw_8bUFpDfymss-dY2Uw,992
|
|
48
|
+
t402/schemes/evm/exact/__init__.py,sha256=PGlfCJTac11_daE7elG2t1p_St8qMobOE3g_8n473Mk,647
|
|
49
|
+
t402/schemes/evm/exact/client.py,sha256=w9Xg5ojKCjeKq8OuEp7OISMapFThjsEwK67qDDCO1yw,7714
|
|
50
|
+
t402/schemes/evm/exact/server.py,sha256=n92CWi_FqB19N93BFaJQuMFXmI1B9w5rIdwm_dz3upw,5547
|
|
51
|
+
t402/schemes/evm/upto/__init__.py,sha256=O2GnKrSgB-tf2jbwCAji-462BEzbnODF9nNyrRDj7XU,1313
|
|
52
|
+
t402/schemes/evm/upto/client.py,sha256=qA1oPiEnPX8bpXfn_3bdZ8veRJdbyA6YBTGnLQ4YDRk,6995
|
|
53
|
+
t402/schemes/evm/upto/types.py,sha256=AcM2H22JBEBNwdlwI_GHJVJddxDhITw5vjC2s8AdKPM,8578
|
|
54
|
+
t402/schemes/ton/__init__.py,sha256=Wb90tspWUVf-GmJkvXySlH0Z1JFmd4O9TwCh4IvZxL8,427
|
|
55
|
+
t402/schemes/ton/exact/__init__.py,sha256=uUFX-KO41ikujER4lUNTd22_BrX8XnWu20LMobyEEQI,606
|
|
56
|
+
t402/schemes/ton/exact/client.py,sha256=h4oZ5-uMR-p_uJYBFajTRtWhcu1nDgsCt0lZ0exxT0o,10493
|
|
57
|
+
t402/schemes/ton/exact/server.py,sha256=KPBOZpPiHHDz8CG6X3s60VGyJl-lkA2xAm8M-oK5_lw,6371
|
|
58
|
+
t402/schemes/tron/__init__.py,sha256=Z4wikC4qTAy2ZJ74lCMPXT1AcjB-6uRVAnGqZxrzhYI,451
|
|
59
|
+
t402/schemes/tron/exact/__init__.py,sha256=FLm62DA2lxk7xmBlQb69-p2clj7QUY5HZ337Whdcn58,620
|
|
60
|
+
t402/schemes/tron/exact/client.py,sha256=l5wLMazpIQ6xLnEozNEe0cJPhPBPthYvuH7GAo3zoY8,7722
|
|
61
|
+
t402/schemes/tron/exact/server.py,sha256=ZRD-ygEvV9B6dwKKnNGijGO_GG_vNQvDOFByKPi0A2w,6171
|
|
62
|
+
t402/schemes/upto/__init__.py,sha256=vAXy3DnU98_jNEdkueeZvAwqinDQTrw6Fg64vwdLKaI,1947
|
|
63
|
+
t402/schemes/upto/types.py,sha256=Lpq5_tLJCNYOFMvYVt33WUxClL_ltC7QZf51iS_u8HI,10544
|
|
43
64
|
t402/wdk/__init__.py,sha256=H75LP3hkvX4kQ8jHRuVISMXBqAUFTOdA9CJbgJnvNc0,2786
|
|
44
65
|
t402/wdk/chains.py,sha256=UlkOjBeV8HhUis61YceMevHgGoPLvDJ9VzL8IskO2V8,6885
|
|
45
66
|
t402/wdk/errors.py,sha256=Sz_qNoO1K8Tx1sGs3KcG9rMoKJXkIvhSkoEZyPaAyd0,6066
|
|
46
67
|
t402/wdk/signer.py,sha256=cEjBTdWNVqrDZCHsuy6IC7fV81o_dX2mpKqLEhxtLGI,20234
|
|
47
68
|
t402/wdk/types.py,sha256=dkuLK7hNNUTrIs15obZTYiniwT0vF87qoEMyOJkOmms,2542
|
|
48
|
-
t402-1.
|
|
49
|
-
t402-1.
|
|
50
|
-
t402-1.
|
|
51
|
-
t402-1.
|
|
69
|
+
t402-1.9.0.dist-info/METADATA,sha256=zRZPR-ma2Kt_l9ujHQLm7c2g-zFQw7mEBfVDtPyNC1Y,11317
|
|
70
|
+
t402-1.9.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
71
|
+
t402-1.9.0.dist-info/entry_points.txt,sha256=Lx-ftLGax72kwtKHBUcyIbwOPT9tjMLc5_bxbRts-G4,39
|
|
72
|
+
t402-1.9.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|