t402 1.9.1__py3-none-any.whl → 1.10.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 +1 -1
- t402/a2a/__init__.py +73 -0
- t402/a2a/helpers.py +158 -0
- t402/a2a/types.py +145 -0
- t402/bridge/constants.py +1 -1
- t402/django/__init__.py +42 -0
- t402/django/middleware.py +596 -0
- t402/errors.py +213 -0
- t402/facilitator.py +125 -0
- t402/mcp/constants.py +3 -6
- t402/mcp/server.py +428 -44
- t402/mcp/web3_utils.py +493 -0
- t402/multisig/__init__.py +120 -0
- t402/multisig/constants.py +54 -0
- t402/multisig/safe.py +441 -0
- t402/multisig/signature.py +228 -0
- t402/multisig/transaction.py +238 -0
- t402/multisig/types.py +108 -0
- t402/multisig/utils.py +77 -0
- t402/schemes/__init__.py +19 -0
- t402/schemes/cosmos/__init__.py +114 -0
- t402/schemes/cosmos/constants.py +211 -0
- t402/schemes/cosmos/exact_direct/__init__.py +21 -0
- t402/schemes/cosmos/exact_direct/client.py +198 -0
- t402/schemes/cosmos/exact_direct/facilitator.py +493 -0
- t402/schemes/cosmos/exact_direct/server.py +315 -0
- t402/schemes/cosmos/types.py +501 -0
- t402/schemes/evm/__init__.py +1 -1
- t402/schemes/evm/exact_legacy/server.py +1 -1
- t402/schemes/near/__init__.py +25 -0
- t402/schemes/near/upto/__init__.py +54 -0
- t402/schemes/near/upto/types.py +272 -0
- t402/schemes/svm/__init__.py +15 -0
- t402/schemes/svm/upto/__init__.py +23 -0
- t402/schemes/svm/upto/types.py +193 -0
- t402/schemes/ton/__init__.py +15 -0
- t402/schemes/ton/upto/__init__.py +31 -0
- t402/schemes/ton/upto/types.py +215 -0
- t402/schemes/tron/__init__.py +21 -4
- t402/schemes/tron/upto/__init__.py +30 -0
- t402/schemes/tron/upto/types.py +213 -0
- t402/starlette/__init__.py +38 -0
- t402/starlette/middleware.py +522 -0
- t402/ton.py +1 -1
- t402/ton_paywall_template.py +1 -1
- t402/types.py +100 -2
- t402/wdk/chains.py +1 -1
- {t402-1.9.1.dist-info → t402-1.10.0.dist-info}/METADATA +3 -3
- {t402-1.9.1.dist-info → t402-1.10.0.dist-info}/RECORD +51 -20
- {t402-1.9.1.dist-info → t402-1.10.0.dist-info}/WHEEL +0 -0
- {t402-1.9.1.dist-info → t402-1.10.0.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
"""TON Up-To Scheme Types.
|
|
2
|
+
|
|
3
|
+
TON-specific types for the up-to payment scheme using an escrow pattern.
|
|
4
|
+
The client transfers maxAmount to the facilitator's holding address,
|
|
5
|
+
and the facilitator forwards settleAmount to payTo and refunds the rest.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from typing import Any, Dict, Optional
|
|
11
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
12
|
+
from pydantic.alias_generators import to_camel
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class UptoTonAuthorization(BaseModel):
|
|
16
|
+
"""TON upto authorization metadata.
|
|
17
|
+
|
|
18
|
+
Contains all parameters for verifying the signed transfer message.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from_address: str = Field(
|
|
22
|
+
alias="from",
|
|
23
|
+
description="Sender wallet address (friendly format, bounceable)",
|
|
24
|
+
)
|
|
25
|
+
facilitator: str = Field(
|
|
26
|
+
description="Facilitator holding address that receives the initial transfer",
|
|
27
|
+
)
|
|
28
|
+
jetton_master: str = Field(
|
|
29
|
+
alias="jettonMaster",
|
|
30
|
+
description="Jetton master contract address",
|
|
31
|
+
)
|
|
32
|
+
max_amount: str = Field(
|
|
33
|
+
alias="maxAmount",
|
|
34
|
+
description="Maximum authorized amount in smallest units",
|
|
35
|
+
)
|
|
36
|
+
ton_amount: str = Field(
|
|
37
|
+
alias="tonAmount",
|
|
38
|
+
description="Gas amount in nanoTON",
|
|
39
|
+
)
|
|
40
|
+
valid_until: int = Field(
|
|
41
|
+
alias="validUntil",
|
|
42
|
+
description="Unix timestamp (seconds) until which the message is valid",
|
|
43
|
+
)
|
|
44
|
+
seqno: int = Field(
|
|
45
|
+
description="Wallet sequence number for replay protection",
|
|
46
|
+
)
|
|
47
|
+
query_id: str = Field(
|
|
48
|
+
alias="queryId",
|
|
49
|
+
description="Unique message ID (as string for large numbers)",
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
model_config = ConfigDict(
|
|
53
|
+
alias_generator=to_camel,
|
|
54
|
+
populate_by_name=True,
|
|
55
|
+
from_attributes=True,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class UptoTonPayload(BaseModel):
|
|
60
|
+
"""TON upto payment payload.
|
|
61
|
+
|
|
62
|
+
Contains a signed transfer message to the facilitator's holding address.
|
|
63
|
+
The facilitator broadcasts the transfer, then forwards settleAmount to payTo
|
|
64
|
+
and refunds (maxAmount - settleAmount) back to the client.
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
signed_boc: str = Field(
|
|
68
|
+
alias="signedBoc",
|
|
69
|
+
description="Base64 encoded signed external message (BOC format)",
|
|
70
|
+
)
|
|
71
|
+
authorization: UptoTonAuthorization = Field(
|
|
72
|
+
description="Transfer authorization metadata for verification",
|
|
73
|
+
)
|
|
74
|
+
payment_nonce: str = Field(
|
|
75
|
+
alias="paymentNonce",
|
|
76
|
+
description="Unique nonce for replay protection (hex string)",
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
model_config = ConfigDict(
|
|
80
|
+
alias_generator=to_camel,
|
|
81
|
+
populate_by_name=True,
|
|
82
|
+
from_attributes=True,
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
86
|
+
"""Convert to dictionary for JSON serialization.
|
|
87
|
+
|
|
88
|
+
Returns:
|
|
89
|
+
Dictionary with camelCase keys matching the wire format.
|
|
90
|
+
"""
|
|
91
|
+
return {
|
|
92
|
+
"signedBoc": self.signed_boc,
|
|
93
|
+
"authorization": {
|
|
94
|
+
"from": self.authorization.from_address,
|
|
95
|
+
"facilitator": self.authorization.facilitator,
|
|
96
|
+
"jettonMaster": self.authorization.jetton_master,
|
|
97
|
+
"maxAmount": self.authorization.max_amount,
|
|
98
|
+
"tonAmount": self.authorization.ton_amount,
|
|
99
|
+
"validUntil": self.authorization.valid_until,
|
|
100
|
+
"seqno": self.authorization.seqno,
|
|
101
|
+
"queryId": self.authorization.query_id,
|
|
102
|
+
},
|
|
103
|
+
"paymentNonce": self.payment_nonce,
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class UptoTonExtra(BaseModel):
|
|
108
|
+
"""TON-specific extra fields for the upto scheme.
|
|
109
|
+
|
|
110
|
+
Included in PaymentRequirements.extra to provide upto-specific parameters.
|
|
111
|
+
"""
|
|
112
|
+
|
|
113
|
+
facilitator: Optional[str] = Field(
|
|
114
|
+
default=None,
|
|
115
|
+
description="Facilitator address that will receive the initial transfer",
|
|
116
|
+
)
|
|
117
|
+
max_amount: Optional[str] = Field(
|
|
118
|
+
default=None,
|
|
119
|
+
alias="maxAmount",
|
|
120
|
+
description="Maximum payment amount authorized",
|
|
121
|
+
)
|
|
122
|
+
min_amount: Optional[str] = Field(
|
|
123
|
+
default=None,
|
|
124
|
+
alias="minAmount",
|
|
125
|
+
description="Minimum acceptable settlement amount",
|
|
126
|
+
)
|
|
127
|
+
unit: Optional[str] = Field(
|
|
128
|
+
default=None,
|
|
129
|
+
description="Billing unit (e.g., 'token', 'request', 'second')",
|
|
130
|
+
)
|
|
131
|
+
unit_price: Optional[str] = Field(
|
|
132
|
+
default=None,
|
|
133
|
+
alias="unitPrice",
|
|
134
|
+
description="Price per unit in smallest denomination",
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
model_config = ConfigDict(
|
|
138
|
+
alias_generator=to_camel,
|
|
139
|
+
populate_by_name=True,
|
|
140
|
+
from_attributes=True,
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def is_upto_ton_payload(data: Any) -> bool:
|
|
145
|
+
"""Check if the given data represents a TON upto payload.
|
|
146
|
+
|
|
147
|
+
Validates the presence and types of required fields including signedBoc,
|
|
148
|
+
authorization (with from, facilitator, jettonMaster, maxAmount), and paymentNonce.
|
|
149
|
+
|
|
150
|
+
Args:
|
|
151
|
+
data: Dictionary or object to check.
|
|
152
|
+
|
|
153
|
+
Returns:
|
|
154
|
+
True if data has the correct TON upto payload structure.
|
|
155
|
+
"""
|
|
156
|
+
if not isinstance(data, dict):
|
|
157
|
+
return False
|
|
158
|
+
|
|
159
|
+
signed_boc = data.get("signedBoc")
|
|
160
|
+
payment_nonce = data.get("paymentNonce")
|
|
161
|
+
auth = data.get("authorization")
|
|
162
|
+
|
|
163
|
+
if not signed_boc or not isinstance(signed_boc, str):
|
|
164
|
+
return False
|
|
165
|
+
if not payment_nonce or not isinstance(payment_nonce, str):
|
|
166
|
+
return False
|
|
167
|
+
|
|
168
|
+
if not auth or not isinstance(auth, dict):
|
|
169
|
+
return False
|
|
170
|
+
|
|
171
|
+
# Check required authorization fields
|
|
172
|
+
required_str_fields = ["from", "facilitator", "jettonMaster", "maxAmount", "tonAmount", "queryId"]
|
|
173
|
+
for field in required_str_fields:
|
|
174
|
+
val = auth.get(field)
|
|
175
|
+
if not isinstance(val, str):
|
|
176
|
+
return False
|
|
177
|
+
|
|
178
|
+
# from and facilitator must not be empty
|
|
179
|
+
if not auth.get("from") or not auth.get("facilitator"):
|
|
180
|
+
return False
|
|
181
|
+
|
|
182
|
+
# Check numeric fields
|
|
183
|
+
if not isinstance(auth.get("validUntil"), (int, float)):
|
|
184
|
+
return False
|
|
185
|
+
if not isinstance(auth.get("seqno"), (int, float)):
|
|
186
|
+
return False
|
|
187
|
+
|
|
188
|
+
return True
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def upto_payload_from_dict(data: Dict[str, Any]) -> UptoTonPayload:
|
|
192
|
+
"""Create an UptoTonPayload from a dictionary.
|
|
193
|
+
|
|
194
|
+
Args:
|
|
195
|
+
data: Dictionary containing payload data with camelCase keys.
|
|
196
|
+
|
|
197
|
+
Returns:
|
|
198
|
+
UptoTonPayload instance.
|
|
199
|
+
"""
|
|
200
|
+
auth_data = data.get("authorization", {})
|
|
201
|
+
|
|
202
|
+
return UptoTonPayload(
|
|
203
|
+
signed_boc=data.get("signedBoc", ""),
|
|
204
|
+
authorization=UptoTonAuthorization(
|
|
205
|
+
from_address=auth_data.get("from", ""),
|
|
206
|
+
facilitator=auth_data.get("facilitator", ""),
|
|
207
|
+
jetton_master=auth_data.get("jettonMaster", ""),
|
|
208
|
+
max_amount=auth_data.get("maxAmount", ""),
|
|
209
|
+
ton_amount=auth_data.get("tonAmount", ""),
|
|
210
|
+
valid_until=auth_data.get("validUntil", 0),
|
|
211
|
+
seqno=auth_data.get("seqno", 0),
|
|
212
|
+
query_id=auth_data.get("queryId", ""),
|
|
213
|
+
),
|
|
214
|
+
payment_nonce=data.get("paymentNonce", ""),
|
|
215
|
+
)
|
t402/schemes/tron/__init__.py
CHANGED
|
@@ -4,6 +4,7 @@ This package provides payment scheme implementations for TRON blockchain.
|
|
|
4
4
|
|
|
5
5
|
Supported schemes:
|
|
6
6
|
- exact: TRC-20 token transfers with signed transactions
|
|
7
|
+
- upto: TRC-20 approve + transferFrom for authorized maximum-amount payments
|
|
7
8
|
"""
|
|
8
9
|
|
|
9
10
|
from t402.schemes.tron.exact import (
|
|
@@ -16,16 +17,32 @@ from t402.schemes.tron.exact import (
|
|
|
16
17
|
SCHEME_EXACT,
|
|
17
18
|
)
|
|
18
19
|
|
|
20
|
+
from t402.schemes.tron.upto import (
|
|
21
|
+
UptoTronAuthorization,
|
|
22
|
+
UptoTronPayload,
|
|
23
|
+
UptoTronExtra,
|
|
24
|
+
is_upto_tron_payload,
|
|
25
|
+
upto_payload_from_dict,
|
|
26
|
+
)
|
|
27
|
+
|
|
19
28
|
__all__ = [
|
|
20
|
-
# Client
|
|
29
|
+
# Exact - Client
|
|
21
30
|
"ExactTronClientScheme",
|
|
22
31
|
"TronSigner",
|
|
23
|
-
# Server
|
|
32
|
+
# Exact - Server
|
|
24
33
|
"ExactTronServerScheme",
|
|
25
|
-
# Facilitator
|
|
34
|
+
# Exact - Facilitator
|
|
26
35
|
"ExactTronFacilitatorScheme",
|
|
27
36
|
"ExactTronFacilitatorConfig",
|
|
28
37
|
"FacilitatorTronSigner",
|
|
29
|
-
# Constants
|
|
38
|
+
# Exact - Constants
|
|
30
39
|
"SCHEME_EXACT",
|
|
40
|
+
# Upto - Types
|
|
41
|
+
"UptoTronAuthorization",
|
|
42
|
+
"UptoTronPayload",
|
|
43
|
+
"UptoTronExtra",
|
|
44
|
+
# Upto - Type guards
|
|
45
|
+
"is_upto_tron_payload",
|
|
46
|
+
# Upto - Helpers
|
|
47
|
+
"upto_payload_from_dict",
|
|
31
48
|
]
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""TRON Up-To Payment Scheme.
|
|
2
|
+
|
|
3
|
+
This package provides the upto payment scheme types for TRON networks
|
|
4
|
+
using TRC-20 approve + transferFrom for authorized maximum-amount payments.
|
|
5
|
+
|
|
6
|
+
The upto scheme allows clients to authorize a maximum amount that can be
|
|
7
|
+
settled later based on actual usage.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from t402.schemes.tron.upto.types import (
|
|
11
|
+
# Models
|
|
12
|
+
UptoTronAuthorization,
|
|
13
|
+
UptoTronPayload,
|
|
14
|
+
UptoTronExtra,
|
|
15
|
+
# Type guards
|
|
16
|
+
is_upto_tron_payload,
|
|
17
|
+
# Helper functions
|
|
18
|
+
upto_payload_from_dict,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
__all__ = [
|
|
22
|
+
# Types
|
|
23
|
+
"UptoTronAuthorization",
|
|
24
|
+
"UptoTronPayload",
|
|
25
|
+
"UptoTronExtra",
|
|
26
|
+
# Type guards
|
|
27
|
+
"is_upto_tron_payload",
|
|
28
|
+
# Helper functions
|
|
29
|
+
"upto_payload_from_dict",
|
|
30
|
+
]
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
"""TRON Up-To Scheme Types.
|
|
2
|
+
|
|
3
|
+
TRON-specific types for the up-to payment scheme using TRC-20 approve + transferFrom.
|
|
4
|
+
The approve pattern allows the facilitator to execute a transferFrom up to the
|
|
5
|
+
authorized maximum amount on behalf of the payer.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from typing import Any, Dict, Optional
|
|
11
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
12
|
+
from pydantic.alias_generators import to_camel
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class UptoTronAuthorization(BaseModel):
|
|
16
|
+
"""TRC-20 approve authorization metadata.
|
|
17
|
+
|
|
18
|
+
Contains all information needed to verify the approve transaction
|
|
19
|
+
without parsing the signed transaction.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
owner: str = Field(description="Token owner address (T-prefix base58check)")
|
|
23
|
+
spender: str = Field(description="Approved spender address - facilitator (T-prefix base58check)")
|
|
24
|
+
contract_address: str = Field(
|
|
25
|
+
alias="contractAddress",
|
|
26
|
+
description="TRC-20 contract address (T-prefix base58check)",
|
|
27
|
+
)
|
|
28
|
+
max_amount: str = Field(
|
|
29
|
+
alias="maxAmount",
|
|
30
|
+
description="Maximum approved amount in smallest units (as string)",
|
|
31
|
+
)
|
|
32
|
+
expiration: int = Field(
|
|
33
|
+
description="Transaction expiration timestamp (milliseconds since epoch)",
|
|
34
|
+
)
|
|
35
|
+
ref_block_bytes: str = Field(
|
|
36
|
+
alias="refBlockBytes",
|
|
37
|
+
description="Reference block bytes (hex string)",
|
|
38
|
+
)
|
|
39
|
+
ref_block_hash: str = Field(
|
|
40
|
+
alias="refBlockHash",
|
|
41
|
+
description="Reference block hash (hex string)",
|
|
42
|
+
)
|
|
43
|
+
timestamp: int = Field(
|
|
44
|
+
description="Transaction timestamp (milliseconds since epoch)",
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
model_config = ConfigDict(
|
|
48
|
+
alias_generator=to_camel,
|
|
49
|
+
populate_by_name=True,
|
|
50
|
+
from_attributes=True,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class UptoTronPayload(BaseModel):
|
|
55
|
+
"""TRON upto payment payload.
|
|
56
|
+
|
|
57
|
+
Contains a signed TRC-20 approve transaction that authorizes the
|
|
58
|
+
facilitator to transfer up to maxAmount of tokens on behalf of the payer.
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
signed_transaction: str = Field(
|
|
62
|
+
alias="signedTransaction",
|
|
63
|
+
description="Hex-encoded signed approve transaction",
|
|
64
|
+
)
|
|
65
|
+
authorization: UptoTronAuthorization = Field(
|
|
66
|
+
description="Approve transaction authorization metadata",
|
|
67
|
+
)
|
|
68
|
+
payment_nonce: str = Field(
|
|
69
|
+
alias="paymentNonce",
|
|
70
|
+
description="Unique nonce for replay protection (hex string)",
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
model_config = ConfigDict(
|
|
74
|
+
alias_generator=to_camel,
|
|
75
|
+
populate_by_name=True,
|
|
76
|
+
from_attributes=True,
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
80
|
+
"""Convert to dictionary for JSON serialization.
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
Dictionary with camelCase keys matching the Go/TypeScript wire format.
|
|
84
|
+
"""
|
|
85
|
+
return {
|
|
86
|
+
"signedTransaction": self.signed_transaction,
|
|
87
|
+
"authorization": {
|
|
88
|
+
"owner": self.authorization.owner,
|
|
89
|
+
"spender": self.authorization.spender,
|
|
90
|
+
"contractAddress": self.authorization.contract_address,
|
|
91
|
+
"maxAmount": self.authorization.max_amount,
|
|
92
|
+
"expiration": self.authorization.expiration,
|
|
93
|
+
"refBlockBytes": self.authorization.ref_block_bytes,
|
|
94
|
+
"refBlockHash": self.authorization.ref_block_hash,
|
|
95
|
+
"timestamp": self.authorization.timestamp,
|
|
96
|
+
},
|
|
97
|
+
"paymentNonce": self.payment_nonce,
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class UptoTronExtra(BaseModel):
|
|
102
|
+
"""TRON-specific extra fields for upto payment requirements.
|
|
103
|
+
|
|
104
|
+
Included in the PaymentRequirements.extra field to communicate
|
|
105
|
+
upto-specific parameters to the client.
|
|
106
|
+
"""
|
|
107
|
+
|
|
108
|
+
max_amount: Optional[str] = Field(
|
|
109
|
+
default=None,
|
|
110
|
+
alias="maxAmount",
|
|
111
|
+
description="Maximum payment amount authorized",
|
|
112
|
+
)
|
|
113
|
+
min_amount: Optional[str] = Field(
|
|
114
|
+
default=None,
|
|
115
|
+
alias="minAmount",
|
|
116
|
+
description="Minimum acceptable settlement amount",
|
|
117
|
+
)
|
|
118
|
+
unit: Optional[str] = Field(
|
|
119
|
+
default=None,
|
|
120
|
+
description="Billing unit (e.g., 'token', 'request', 'second')",
|
|
121
|
+
)
|
|
122
|
+
unit_price: Optional[str] = Field(
|
|
123
|
+
default=None,
|
|
124
|
+
alias="unitPrice",
|
|
125
|
+
description="Price per unit in smallest denomination",
|
|
126
|
+
)
|
|
127
|
+
spender_address: Optional[str] = Field(
|
|
128
|
+
default=None,
|
|
129
|
+
alias="spenderAddress",
|
|
130
|
+
description="Facilitator address that will be approved as spender",
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
model_config = ConfigDict(
|
|
134
|
+
alias_generator=to_camel,
|
|
135
|
+
populate_by_name=True,
|
|
136
|
+
from_attributes=True,
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def is_upto_tron_payload(data: Dict[str, Any]) -> bool:
|
|
141
|
+
"""Check if the given data represents an upto TRON payload.
|
|
142
|
+
|
|
143
|
+
Validates that the data has the correct structure for a TRON upto
|
|
144
|
+
approve payload, including nested authorization fields.
|
|
145
|
+
|
|
146
|
+
Args:
|
|
147
|
+
data: Dictionary to check
|
|
148
|
+
|
|
149
|
+
Returns:
|
|
150
|
+
True if data has the correct upto TRON payload structure
|
|
151
|
+
"""
|
|
152
|
+
if not isinstance(data, dict):
|
|
153
|
+
return False
|
|
154
|
+
|
|
155
|
+
# Check top-level fields
|
|
156
|
+
if not isinstance(data.get("signedTransaction"), str) or not data["signedTransaction"]:
|
|
157
|
+
return False
|
|
158
|
+
|
|
159
|
+
if not isinstance(data.get("paymentNonce"), str) or not data["paymentNonce"]:
|
|
160
|
+
return False
|
|
161
|
+
|
|
162
|
+
auth = data.get("authorization")
|
|
163
|
+
if not isinstance(auth, dict):
|
|
164
|
+
return False
|
|
165
|
+
|
|
166
|
+
# Check required authorization fields
|
|
167
|
+
required_string_fields = ["owner", "spender", "contractAddress", "maxAmount"]
|
|
168
|
+
for field in required_string_fields:
|
|
169
|
+
if not isinstance(auth.get(field), str) or not auth[field]:
|
|
170
|
+
return False
|
|
171
|
+
|
|
172
|
+
# Check numeric authorization fields
|
|
173
|
+
if not isinstance(auth.get("expiration"), (int, float)):
|
|
174
|
+
return False
|
|
175
|
+
|
|
176
|
+
if not isinstance(auth.get("timestamp"), (int, float)):
|
|
177
|
+
return False
|
|
178
|
+
|
|
179
|
+
# refBlockBytes and refBlockHash should be strings
|
|
180
|
+
if not isinstance(auth.get("refBlockBytes"), str):
|
|
181
|
+
return False
|
|
182
|
+
|
|
183
|
+
if not isinstance(auth.get("refBlockHash"), str):
|
|
184
|
+
return False
|
|
185
|
+
|
|
186
|
+
return True
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def upto_payload_from_dict(data: Dict[str, Any]) -> UptoTronPayload:
|
|
190
|
+
"""Create an UptoTronPayload from a dictionary.
|
|
191
|
+
|
|
192
|
+
Args:
|
|
193
|
+
data: Dictionary containing payload data with camelCase keys
|
|
194
|
+
|
|
195
|
+
Returns:
|
|
196
|
+
UptoTronPayload instance
|
|
197
|
+
"""
|
|
198
|
+
auth_data = data.get("authorization", {})
|
|
199
|
+
|
|
200
|
+
return UptoTronPayload(
|
|
201
|
+
signed_transaction=data.get("signedTransaction", ""),
|
|
202
|
+
authorization=UptoTronAuthorization(
|
|
203
|
+
owner=auth_data.get("owner", ""),
|
|
204
|
+
spender=auth_data.get("spender", ""),
|
|
205
|
+
contract_address=auth_data.get("contractAddress", ""),
|
|
206
|
+
max_amount=auth_data.get("maxAmount", ""),
|
|
207
|
+
expiration=auth_data.get("expiration", 0),
|
|
208
|
+
ref_block_bytes=auth_data.get("refBlockBytes", ""),
|
|
209
|
+
ref_block_hash=auth_data.get("refBlockHash", ""),
|
|
210
|
+
timestamp=auth_data.get("timestamp", 0),
|
|
211
|
+
),
|
|
212
|
+
payment_nonce=data.get("paymentNonce", ""),
|
|
213
|
+
)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""Starlette Integration for T402 Payment Protocol.
|
|
2
|
+
|
|
3
|
+
This package provides Starlette ASGI middleware for integrating T402 payments
|
|
4
|
+
into Starlette applications.
|
|
5
|
+
|
|
6
|
+
Features:
|
|
7
|
+
- PaymentMiddleware: Class-based middleware for protecting routes
|
|
8
|
+
- V1 and V2 protocol support
|
|
9
|
+
- Automatic settlement on successful responses
|
|
10
|
+
- Browser paywall support
|
|
11
|
+
|
|
12
|
+
Usage:
|
|
13
|
+
```python
|
|
14
|
+
from starlette.applications import Starlette
|
|
15
|
+
from t402.starlette import PaymentMiddleware
|
|
16
|
+
|
|
17
|
+
app = Starlette()
|
|
18
|
+
payment = PaymentMiddleware(app)
|
|
19
|
+
payment.add(
|
|
20
|
+
path="/api/*",
|
|
21
|
+
price="$0.10",
|
|
22
|
+
pay_to_address="0x1234...",
|
|
23
|
+
network="eip155:8453",
|
|
24
|
+
)
|
|
25
|
+
```
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
from t402.starlette.middleware import (
|
|
29
|
+
PaymentMiddleware,
|
|
30
|
+
PaymentConfig,
|
|
31
|
+
PaymentDetails,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
__all__ = [
|
|
35
|
+
"PaymentMiddleware",
|
|
36
|
+
"PaymentConfig",
|
|
37
|
+
"PaymentDetails",
|
|
38
|
+
]
|