t402 1.0.0__tar.gz → 1.2.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.
Files changed (46) hide show
  1. {t402-1.0.0 → t402-1.2.0}/PKG-INFO +5 -2
  2. {t402-1.0.0 → t402-1.2.0}/README.md +4 -1
  3. {t402-1.0.0 → t402-1.2.0}/pyproject.toml +1 -1
  4. t402-1.2.0/src/t402/__init__.py +135 -0
  5. {t402-1.0.0 → t402-1.2.0}/src/t402/common.py +60 -5
  6. {t402-1.0.0 → t402-1.2.0}/src/t402/fastapi/middleware.py +3 -3
  7. {t402-1.0.0 → t402-1.2.0}/src/t402/flask/middleware.py +3 -3
  8. t402-1.2.0/src/t402/networks.py +88 -0
  9. {t402-1.0.0 → t402-1.2.0}/src/t402/paywall.py +3 -0
  10. t402-1.2.0/src/t402/ton.py +474 -0
  11. t402-1.2.0/src/t402/ton_paywall_template.py +193 -0
  12. t402-1.2.0/src/t402/tron.py +578 -0
  13. {t402-1.0.0 → t402-1.2.0}/src/t402/types.py +81 -1
  14. t402-1.2.0/tests/test_ton.py +420 -0
  15. t402-1.0.0/src/t402/__init__.py +0 -2
  16. t402-1.0.0/src/t402/networks.py +0 -11
  17. {t402-1.0.0 → t402-1.2.0}/.gitignore +0 -0
  18. {t402-1.0.0 → t402-1.2.0}/.python-version +0 -0
  19. {t402-1.0.0 → t402-1.2.0}/src/t402/chains.py +0 -0
  20. {t402-1.0.0 → t402-1.2.0}/src/t402/clients/__init__.py +0 -0
  21. {t402-1.0.0 → t402-1.2.0}/src/t402/clients/base.py +0 -0
  22. {t402-1.0.0 → t402-1.2.0}/src/t402/clients/httpx.py +0 -0
  23. {t402-1.0.0 → t402-1.2.0}/src/t402/clients/requests.py +0 -0
  24. {t402-1.0.0 → t402-1.2.0}/src/t402/encoding.py +0 -0
  25. {t402-1.0.0 → t402-1.2.0}/src/t402/evm_paywall_template.py +0 -0
  26. {t402-1.0.0 → t402-1.2.0}/src/t402/exact.py +0 -0
  27. {t402-1.0.0 → t402-1.2.0}/src/t402/facilitator.py +0 -0
  28. {t402-1.0.0 → t402-1.2.0}/src/t402/fastapi/__init__.py +0 -0
  29. {t402-1.0.0 → t402-1.2.0}/src/t402/flask/__init__.py +0 -0
  30. {t402-1.0.0 → t402-1.2.0}/src/t402/path.py +0 -0
  31. {t402-1.0.0 → t402-1.2.0}/src/t402/py.typed +0 -0
  32. {t402-1.0.0 → t402-1.2.0}/src/t402/svm_paywall_template.py +0 -0
  33. {t402-1.0.0 → t402-1.2.0}/tests/clients/__init__.py +0 -0
  34. {t402-1.0.0 → t402-1.2.0}/tests/clients/test_base.py +0 -0
  35. {t402-1.0.0 → t402-1.2.0}/tests/clients/test_httpx.py +0 -0
  36. {t402-1.0.0 → t402-1.2.0}/tests/clients/test_requests.py +0 -0
  37. {t402-1.0.0 → t402-1.2.0}/tests/fastapi_tests/__init__.py +0 -0
  38. {t402-1.0.0 → t402-1.2.0}/tests/fastapi_tests/test_middleware.py +0 -0
  39. {t402-1.0.0 → t402-1.2.0}/tests/flask_tests/__init__.py +0 -0
  40. {t402-1.0.0 → t402-1.2.0}/tests/flask_tests/test_middleware.py +0 -0
  41. {t402-1.0.0 → t402-1.2.0}/tests/test_common.py +0 -0
  42. {t402-1.0.0 → t402-1.2.0}/tests/test_encoding.py +0 -0
  43. {t402-1.0.0 → t402-1.2.0}/tests/test_exact.py +0 -0
  44. {t402-1.0.0 → t402-1.2.0}/tests/test_paywall.py +0 -0
  45. {t402-1.0.0 → t402-1.2.0}/tests/test_types.py +0 -0
  46. {t402-1.0.0 → t402-1.2.0}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: t402
3
- Version: 1.0.0
3
+ Version: 1.2.0
4
4
  Summary: t402: An internet native payments protocol
5
5
  Author-email: T402 Team <dev@t402.io>
6
6
  License: Apache-2.0
@@ -25,6 +25,9 @@ Python package for the t402 payments protocol.
25
25
 
26
26
  ```bash
27
27
  pip install t402
28
+
29
+ # or with uv
30
+ uv add t402
28
31
  ```
29
32
 
30
33
  ## Overview
@@ -233,4 +236,4 @@ async def foo(req: request: Request):
233
236
  )
234
237
  ```
235
238
 
236
- For more examples and advanced usage patterns, check out our [examples directory](https://github.com/awesome-doge/t402/tree/main/examples/python).
239
+ For more examples and advanced usage patterns, check out our [examples directory](https://github.com/t402-io/t402/tree/main/examples/python).
@@ -6,6 +6,9 @@ Python package for the t402 payments protocol.
6
6
 
7
7
  ```bash
8
8
  pip install t402
9
+
10
+ # or with uv
11
+ uv add t402
9
12
  ```
10
13
 
11
14
  ## Overview
@@ -214,4 +217,4 @@ async def foo(req: request: Request):
214
217
  )
215
218
  ```
216
219
 
217
- For more examples and advanced usage patterns, check out our [examples directory](https://github.com/awesome-doge/t402/tree/main/examples/python).
220
+ For more examples and advanced usage patterns, check out our [examples directory](https://github.com/t402-io/t402/tree/main/examples/python).
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "t402"
3
- version = "1.0.0"
3
+ version = "1.2.0"
4
4
  description = "t402: An internet native payments protocol"
5
5
  readme = "README.md"
6
6
  license = { text = "Apache-2.0" }
@@ -0,0 +1,135 @@
1
+ # Re-export commonly used items for convenience
2
+ from t402.common import (
3
+ parse_money,
4
+ process_price_to_atomic_amount,
5
+ find_matching_payment_requirements,
6
+ t402_VERSION,
7
+ )
8
+ from t402.networks import (
9
+ is_ton_network,
10
+ is_tron_network,
11
+ is_evm_network,
12
+ get_network_type,
13
+ )
14
+ from t402.types import (
15
+ PaymentRequirements,
16
+ PaymentPayload,
17
+ VerifyResponse,
18
+ SettleResponse,
19
+ TonAuthorization,
20
+ TonPaymentPayload,
21
+ TronAuthorization,
22
+ TronPaymentPayload,
23
+ )
24
+ from t402.facilitator import FacilitatorClient, FacilitatorConfig
25
+ from t402.exact import (
26
+ prepare_payment_header,
27
+ sign_payment_header,
28
+ encode_payment,
29
+ decode_payment,
30
+ )
31
+ from t402.ton import (
32
+ TON_MAINNET,
33
+ TON_TESTNET,
34
+ USDT_MAINNET_ADDRESS,
35
+ USDT_TESTNET_ADDRESS,
36
+ validate_ton_address,
37
+ get_usdt_address,
38
+ get_network_config as get_ton_network_config,
39
+ get_default_asset as get_ton_default_asset,
40
+ prepare_ton_payment_header,
41
+ parse_amount as parse_ton_amount,
42
+ format_amount as format_ton_amount,
43
+ validate_boc,
44
+ is_testnet as is_ton_testnet,
45
+ )
46
+ from t402.tron import (
47
+ TRON_MAINNET,
48
+ TRON_NILE,
49
+ TRON_SHASTA,
50
+ USDT_MAINNET_ADDRESS as TRON_USDT_MAINNET_ADDRESS,
51
+ USDT_NILE_ADDRESS as TRON_USDT_NILE_ADDRESS,
52
+ USDT_SHASTA_ADDRESS as TRON_USDT_SHASTA_ADDRESS,
53
+ validate_tron_address,
54
+ get_usdt_address as get_tron_usdt_address,
55
+ get_network_config as get_tron_network_config,
56
+ get_default_asset as get_tron_default_asset,
57
+ prepare_tron_payment_header,
58
+ parse_amount as parse_tron_amount,
59
+ format_amount as format_tron_amount,
60
+ is_testnet as is_tron_testnet,
61
+ )
62
+ from t402.paywall import (
63
+ get_paywall_html,
64
+ get_paywall_template,
65
+ is_browser_request,
66
+ )
67
+
68
+ def hello() -> str:
69
+ return "Hello from t402!"
70
+
71
+
72
+ __all__ = [
73
+ # Core
74
+ "hello",
75
+ "t402_VERSION",
76
+ # Common utilities
77
+ "parse_money",
78
+ "process_price_to_atomic_amount",
79
+ "find_matching_payment_requirements",
80
+ # Network utilities
81
+ "is_ton_network",
82
+ "is_tron_network",
83
+ "is_evm_network",
84
+ "get_network_type",
85
+ # Types
86
+ "PaymentRequirements",
87
+ "PaymentPayload",
88
+ "VerifyResponse",
89
+ "SettleResponse",
90
+ "TonAuthorization",
91
+ "TonPaymentPayload",
92
+ "TronAuthorization",
93
+ "TronPaymentPayload",
94
+ # Facilitator
95
+ "FacilitatorClient",
96
+ "FacilitatorConfig",
97
+ # EVM payment
98
+ "prepare_payment_header",
99
+ "sign_payment_header",
100
+ "encode_payment",
101
+ "decode_payment",
102
+ # TON utilities
103
+ "TON_MAINNET",
104
+ "TON_TESTNET",
105
+ "USDT_MAINNET_ADDRESS",
106
+ "USDT_TESTNET_ADDRESS",
107
+ "validate_ton_address",
108
+ "get_usdt_address",
109
+ "get_ton_network_config",
110
+ "get_ton_default_asset",
111
+ "prepare_ton_payment_header",
112
+ "parse_ton_amount",
113
+ "format_ton_amount",
114
+ "validate_boc",
115
+ "is_ton_testnet",
116
+ # TRON utilities
117
+ "TRON_MAINNET",
118
+ "TRON_NILE",
119
+ "TRON_SHASTA",
120
+ "TRON_USDT_MAINNET_ADDRESS",
121
+ "TRON_USDT_NILE_ADDRESS",
122
+ "TRON_USDT_SHASTA_ADDRESS",
123
+ "validate_tron_address",
124
+ "get_tron_usdt_address",
125
+ "get_tron_network_config",
126
+ "get_tron_default_asset",
127
+ "prepare_tron_payment_header",
128
+ "parse_tron_amount",
129
+ "format_tron_amount",
130
+ "is_tron_testnet",
131
+ # Paywall
132
+ "get_paywall_html",
133
+ "get_paywall_template",
134
+ "is_browser_request",
135
+ ]
@@ -8,6 +8,7 @@ from t402.chains import (
8
8
  get_token_version,
9
9
  get_default_token_address,
10
10
  )
11
+ from t402.networks import is_ton_network, is_tron_network
11
12
  from t402.types import Price, TokenAmount, PaymentRequirements, PaymentPayload
12
13
 
13
14
 
@@ -22,8 +23,18 @@ def parse_money(amount: str | int, address: str, network: str) -> int:
22
23
  amount = amount[1:]
23
24
  decimal_amount = Decimal(amount)
24
25
 
25
- chain_id = get_chain_id(network)
26
- decimals = get_token_decimals(chain_id, address)
26
+ # Handle TON networks differently
27
+ if is_ton_network(network):
28
+ from t402.ton import DEFAULT_DECIMALS
29
+ decimals = DEFAULT_DECIMALS # USDT on TON uses 6 decimals
30
+ # Handle TRON networks
31
+ elif is_tron_network(network):
32
+ from t402.tron import DEFAULT_DECIMALS
33
+ decimals = DEFAULT_DECIMALS # USDT on TRON uses 6 decimals
34
+ else:
35
+ chain_id = get_chain_id(network)
36
+ decimals = get_token_decimals(chain_id, address)
37
+
27
38
  decimal_amount = decimal_amount * Decimal(10**decimals)
28
39
  return int(decimal_amount)
29
40
  return amount
@@ -39,19 +50,63 @@ def process_price_to_atomic_amount(
39
50
  network: Network identifier
40
51
 
41
52
  Returns:
42
- Tuple of (max_amount_required, asset_address, eip712_domain)
53
+ Tuple of (max_amount_required, asset_address, extra_info)
54
+ For EVM: extra_info contains EIP-712 domain (name, version)
55
+ For TON: extra_info contains Jetton metadata (name, symbol)
43
56
 
44
57
  Raises:
45
58
  ValueError: If price format is invalid
46
59
  """
47
60
  if isinstance(price, (str, int)):
48
- # Money type - convert USD to USDC atomic units
61
+ # Money type - convert USD to atomic units
49
62
  try:
50
63
  if isinstance(price, str) and price.startswith("$"):
51
64
  price = price[1:]
52
65
  amount = Decimal(str(price))
53
66
 
54
- # Get USDC address for the network
67
+ # Handle TON networks
68
+ if is_ton_network(network):
69
+ from t402.ton import (
70
+ get_usdt_address,
71
+ get_default_asset,
72
+ DEFAULT_DECIMALS,
73
+ )
74
+
75
+ asset_address = get_usdt_address(network)
76
+ decimals = DEFAULT_DECIMALS
77
+ atomic_amount = int(amount * Decimal(10**decimals))
78
+
79
+ # For TON, return Jetton metadata instead of EIP-712 domain
80
+ asset_info = get_default_asset(network)
81
+ extra_info = {
82
+ "name": asset_info["name"] if asset_info else "Tether USD",
83
+ "symbol": asset_info["symbol"] if asset_info else "USDT",
84
+ }
85
+
86
+ return str(atomic_amount), asset_address, extra_info
87
+
88
+ # Handle TRON networks
89
+ if is_tron_network(network):
90
+ from t402.tron import (
91
+ get_usdt_address as get_tron_usdt_address,
92
+ get_default_asset as get_tron_default_asset,
93
+ DEFAULT_DECIMALS,
94
+ )
95
+
96
+ asset_address = get_tron_usdt_address(network)
97
+ decimals = DEFAULT_DECIMALS
98
+ atomic_amount = int(amount * Decimal(10**decimals))
99
+
100
+ # For TRON, return TRC20 metadata
101
+ asset_info = get_tron_default_asset(network)
102
+ extra_info = {
103
+ "name": asset_info["name"] if asset_info else "Tether USD",
104
+ "symbol": asset_info["symbol"] if asset_info else "USDT",
105
+ }
106
+
107
+ return str(atomic_amount), asset_address, extra_info
108
+
109
+ # Handle EVM networks
55
110
  chain_id = get_chain_id(network)
56
111
  asset_address = get_usdc_address(chain_id)
57
112
  decimals = get_token_decimals(chain_id, asset_address)
@@ -1,7 +1,7 @@
1
1
  import base64
2
2
  import json
3
3
  import logging
4
- from typing import Any, Callable, Optional, get_args, cast
4
+ from typing import Any, Callable, Optional, cast
5
5
 
6
6
  from fastapi import Request
7
7
  from fastapi.responses import JSONResponse, HTMLResponse
@@ -14,6 +14,7 @@ from t402.common import (
14
14
  )
15
15
  from t402.encoding import safe_base64_decode
16
16
  from t402.facilitator import FacilitatorClient, FacilitatorConfig
17
+ from t402.networks import get_all_supported_networks, SupportedNetworks
17
18
  from t402.path import path_is_match
18
19
  from t402.paywall import is_browser_request, get_paywall_html
19
20
  from t402.types import (
@@ -22,7 +23,6 @@ from t402.types import (
22
23
  Price,
23
24
  t402PaymentRequiredResponse,
24
25
  PaywallConfig,
25
- SupportedNetworks,
26
26
  HTTPInputSchema,
27
27
  )
28
28
 
@@ -73,7 +73,7 @@ def require_payment(
73
73
  """
74
74
 
75
75
  # Validate network is supported
76
- supported_networks = get_args(SupportedNetworks)
76
+ supported_networks = get_all_supported_networks()
77
77
  if network not in supported_networks:
78
78
  raise ValueError(
79
79
  f"Unsupported network: {network}. Must be one of: {supported_networks}"
@@ -1,15 +1,15 @@
1
1
  import base64
2
2
  import json
3
- from typing import Any, Dict, Optional, Union, get_args, cast
3
+ from typing import Any, Dict, Optional, Union, cast
4
4
  from flask import Flask, request, g
5
5
  from t402.path import path_is_match
6
+ from t402.networks import get_all_supported_networks, SupportedNetworks
6
7
  from t402.types import (
7
8
  Price,
8
9
  PaymentPayload,
9
10
  PaymentRequirements,
10
11
  t402PaymentRequiredResponse,
11
12
  PaywallConfig,
12
- SupportedNetworks,
13
13
  HTTPInputSchema,
14
14
  )
15
15
  from t402.common import (
@@ -148,7 +148,7 @@ class PaymentMiddleware:
148
148
  """Create a WSGI middleware function for the given configuration."""
149
149
 
150
150
  # Validate network is supported
151
- supported_networks = get_args(SupportedNetworks)
151
+ supported_networks = get_all_supported_networks()
152
152
  if config["network"] not in supported_networks:
153
153
  raise ValueError(
154
154
  f"Unsupported network: {config['network']}. Must be one of: {supported_networks}"
@@ -0,0 +1,88 @@
1
+ from typing import Literal, Union, get_args
2
+
3
+
4
+ # EVM Networks
5
+ EVMNetworks = Literal["base", "base-sepolia", "avalanche-fuji", "avalanche"]
6
+
7
+ # TON Networks (CAIP-2 format)
8
+ TONNetworks = Literal["ton:mainnet", "ton:testnet"]
9
+
10
+ # TRON Networks (CAIP-2 format)
11
+ TRONNetworks = Literal["tron:mainnet", "tron:nile", "tron:shasta"]
12
+
13
+ # All supported networks
14
+ SupportedNetworks = Union[EVMNetworks, TONNetworks, TRONNetworks]
15
+
16
+
17
+ def get_all_supported_networks() -> tuple[str, ...]:
18
+ """Get all supported network identifiers as a flat tuple of strings."""
19
+ evm = get_args(EVMNetworks)
20
+ ton = get_args(TONNetworks)
21
+ tron = get_args(TRONNetworks)
22
+ return evm + ton + tron
23
+
24
+ EVM_NETWORK_TO_CHAIN_ID = {
25
+ "base-sepolia": 84532,
26
+ "base": 8453,
27
+ "avalanche-fuji": 43113,
28
+ "avalanche": 43114,
29
+ }
30
+
31
+ # TON Network configurations
32
+ TON_NETWORKS = {
33
+ "ton:mainnet": {
34
+ "name": "TON Mainnet",
35
+ "endpoint": "https://toncenter.com/api/v2/jsonRPC",
36
+ "is_testnet": False,
37
+ },
38
+ "ton:testnet": {
39
+ "name": "TON Testnet",
40
+ "endpoint": "https://testnet.toncenter.com/api/v2/jsonRPC",
41
+ "is_testnet": True,
42
+ },
43
+ }
44
+
45
+ # TRON Network configurations
46
+ TRON_NETWORKS = {
47
+ "tron:mainnet": {
48
+ "name": "TRON Mainnet",
49
+ "endpoint": "https://api.trongrid.io",
50
+ "is_testnet": False,
51
+ },
52
+ "tron:nile": {
53
+ "name": "TRON Nile Testnet",
54
+ "endpoint": "https://api.nileex.io",
55
+ "is_testnet": True,
56
+ },
57
+ "tron:shasta": {
58
+ "name": "TRON Shasta Testnet",
59
+ "endpoint": "https://api.shasta.trongrid.io",
60
+ "is_testnet": True,
61
+ },
62
+ }
63
+
64
+
65
+ def is_ton_network(network: str) -> bool:
66
+ """Check if a network is a TON network."""
67
+ return network.startswith("ton:")
68
+
69
+
70
+ def is_tron_network(network: str) -> bool:
71
+ """Check if a network is a TRON network."""
72
+ return network.startswith("tron:")
73
+
74
+
75
+ def is_evm_network(network: str) -> bool:
76
+ """Check if a network is an EVM network."""
77
+ return network in EVM_NETWORK_TO_CHAIN_ID
78
+
79
+
80
+ def get_network_type(network: str) -> str:
81
+ """Get the network type (ton, tron, evm, or unknown)."""
82
+ if is_ton_network(network):
83
+ return "ton"
84
+ if is_tron_network(network):
85
+ return "tron"
86
+ if is_evm_network(network):
87
+ return "evm"
88
+ return "unknown"
@@ -5,12 +5,15 @@ from t402.types import PaymentRequirements, PaywallConfig
5
5
  from t402.common import t402_VERSION
6
6
  from t402.evm_paywall_template import EVM_PAYWALL_TEMPLATE
7
7
  from t402.svm_paywall_template import SVM_PAYWALL_TEMPLATE
8
+ from t402.ton_paywall_template import TON_PAYWALL_TEMPLATE
8
9
 
9
10
 
10
11
  def get_paywall_template(network: str) -> str:
11
12
  """Get the appropriate paywall template for the given network."""
12
13
  if network.startswith("solana:"):
13
14
  return SVM_PAYWALL_TEMPLATE
15
+ if network.startswith("ton:"):
16
+ return TON_PAYWALL_TEMPLATE
14
17
  return EVM_PAYWALL_TEMPLATE
15
18
 
16
19