og-test-x402 0.0.5__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.
- og_test_x402-0.0.5/.gitignore +9 -0
- og_test_x402-0.0.5/.python-version +1 -0
- og_test_x402-0.0.5/PKG-INFO +236 -0
- og_test_x402-0.0.5/README.md +217 -0
- og_test_x402-0.0.5/pyproject.toml +45 -0
- og_test_x402-0.0.5/src/x402/__init__.py +2 -0
- og_test_x402-0.0.5/src/x402/chains.py +62 -0
- og_test_x402-0.0.5/src/x402/clients/__init__.py +20 -0
- og_test_x402-0.0.5/src/x402/clients/base.py +199 -0
- og_test_x402-0.0.5/src/x402/clients/httpx.py +136 -0
- og_test_x402-0.0.5/src/x402/clients/requests.py +145 -0
- og_test_x402-0.0.5/src/x402/common.py +114 -0
- og_test_x402-0.0.5/src/x402/encoding.py +28 -0
- og_test_x402-0.0.5/src/x402/exact.py +130 -0
- og_test_x402-0.0.5/src/x402/facilitator.py +139 -0
- og_test_x402-0.0.5/src/x402/fastapi/__init__.py +0 -0
- og_test_x402-0.0.5/src/x402/fastapi/middleware.py +219 -0
- og_test_x402-0.0.5/src/x402/flask/__init__.py +0 -0
- og_test_x402-0.0.5/src/x402/flask/middleware.py +322 -0
- og_test_x402-0.0.5/src/x402/networks.py +10 -0
- og_test_x402-0.0.5/src/x402/path.py +43 -0
- og_test_x402-0.0.5/src/x402/paywall.py +119 -0
- og_test_x402-0.0.5/src/x402/py.typed +0 -0
- og_test_x402-0.0.5/src/x402/static/paywall.html +1355 -0
- og_test_x402-0.0.5/src/x402/template.py +1 -0
- og_test_x402-0.0.5/src/x402/types.py +287 -0
- og_test_x402-0.0.5/tests/clients/__init__.py +0 -0
- og_test_x402-0.0.5/tests/clients/test_base.py +194 -0
- og_test_x402-0.0.5/tests/clients/test_httpx.py +239 -0
- og_test_x402-0.0.5/tests/clients/test_requests.py +310 -0
- og_test_x402-0.0.5/tests/fastapi_tests/__init__.py +0 -0
- og_test_x402-0.0.5/tests/fastapi_tests/test_middleware.py +531 -0
- og_test_x402-0.0.5/tests/flask_tests/__init__.py +0 -0
- og_test_x402-0.0.5/tests/flask_tests/test_middleware.py +360 -0
- og_test_x402-0.0.5/tests/test_common.py +175 -0
- og_test_x402-0.0.5/tests/test_encoding.py +81 -0
- og_test_x402-0.0.5/tests/test_exact.py +198 -0
- og_test_x402-0.0.5/tests/test_paywall.py +262 -0
- og_test_x402-0.0.5/tests/test_types.py +162 -0
- og_test_x402-0.0.5/uv.lock +2084 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.10
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: og-test-x402
|
|
3
|
+
Version: 0.0.5
|
|
4
|
+
Summary: x402: An internet native payments protocol
|
|
5
|
+
Author-email: erik <erik.reppel@coinbase.com>
|
|
6
|
+
License: Apache-2.0
|
|
7
|
+
Keywords: cdp,crypto,payments,sdk,web3,x402
|
|
8
|
+
Requires-Python: >=3.10
|
|
9
|
+
Requires-Dist: eth-account>=0.13.7
|
|
10
|
+
Requires-Dist: eth-typing>=4.0.0
|
|
11
|
+
Requires-Dist: eth-utils>=3.0.0
|
|
12
|
+
Requires-Dist: fastapi[standard]>=0.115.12
|
|
13
|
+
Requires-Dist: flask>=3.0.0
|
|
14
|
+
Requires-Dist: pydantic-settings>=2.2.1
|
|
15
|
+
Requires-Dist: pydantic>=2.10.3
|
|
16
|
+
Requires-Dist: python-dotenv>=1.0.1
|
|
17
|
+
Requires-Dist: web3>=6.0.0
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
|
|
20
|
+
# x402 Python
|
|
21
|
+
|
|
22
|
+
Python package for the x402 payments protocol.
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
pip install x402
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Overview
|
|
31
|
+
|
|
32
|
+
The x402 package provides the core building blocks for implementing the x402 Payment Protocol in Python. It's designed to be used by:
|
|
33
|
+
|
|
34
|
+
- FastAPI middleware for accepting payments
|
|
35
|
+
- Flask middleware for accepting payments
|
|
36
|
+
- httpx client for paying resources
|
|
37
|
+
- requests client for paying resources
|
|
38
|
+
|
|
39
|
+
## FastAPI Integration
|
|
40
|
+
|
|
41
|
+
The simplest way to add x402 payment protection to your FastAPI application:
|
|
42
|
+
|
|
43
|
+
```py
|
|
44
|
+
from fastapi import FastAPI
|
|
45
|
+
from x402.fastapi.middleware import require_payment
|
|
46
|
+
|
|
47
|
+
app = FastAPI()
|
|
48
|
+
app.middleware("http")(
|
|
49
|
+
require_payment(price="0.01", pay_to_address="0x209693Bc6afc0C5328bA36FaF03C514EF312287C")
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
@app.get("/")
|
|
53
|
+
async def root():
|
|
54
|
+
return {"message": "Hello World"}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
To protect specific routes:
|
|
58
|
+
|
|
59
|
+
```py
|
|
60
|
+
app.middleware("http")(
|
|
61
|
+
require_payment(price="0.01",
|
|
62
|
+
pay_to_address="0x209693Bc6afc0C5328bA36FaF03C514EF312287C"),
|
|
63
|
+
path="/foo" # <-- this can also be a list ex: ["/foo", "/bar"]
|
|
64
|
+
)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Flask Integration
|
|
68
|
+
|
|
69
|
+
The simplest way to add x402 payment protection to your Flask application:
|
|
70
|
+
|
|
71
|
+
```py
|
|
72
|
+
from flask import Flask
|
|
73
|
+
from x402.flask.middleware import PaymentMiddleware
|
|
74
|
+
|
|
75
|
+
app = Flask(__name__)
|
|
76
|
+
|
|
77
|
+
# Initialize payment middleware
|
|
78
|
+
payment_middleware = PaymentMiddleware(app)
|
|
79
|
+
|
|
80
|
+
# Add payment protection for all routes
|
|
81
|
+
payment_middleware.add(
|
|
82
|
+
price="$0.01",
|
|
83
|
+
pay_to_address="0x209693Bc6afc0C5328bA36FaF03C514EF312287C",
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
@app.route("/")
|
|
87
|
+
def root():
|
|
88
|
+
return {"message": "Hello World"}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
To protect specific routes:
|
|
92
|
+
|
|
93
|
+
```py
|
|
94
|
+
# Protect specific endpoint
|
|
95
|
+
payment_middleware.add(
|
|
96
|
+
path="/foo",
|
|
97
|
+
price="$0.001",
|
|
98
|
+
pay_to_address="0x209693Bc6afc0C5328bA36FaF03C514EF312287C",
|
|
99
|
+
)
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Client Integration
|
|
103
|
+
|
|
104
|
+
### Simple Usage
|
|
105
|
+
|
|
106
|
+
#### Httpx Client
|
|
107
|
+
```py
|
|
108
|
+
from eth_account import Account
|
|
109
|
+
from x402.clients.httpx import x402HttpxClient
|
|
110
|
+
|
|
111
|
+
# Initialize account
|
|
112
|
+
account = Account.from_key("your_private_key")
|
|
113
|
+
|
|
114
|
+
# Create client and make request
|
|
115
|
+
async with x402HttpxClient(account=account, base_url="https://api.example.com") as client:
|
|
116
|
+
response = await client.get("/protected-endpoint")
|
|
117
|
+
print(await response.aread())
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
#### Requests Session Client
|
|
121
|
+
```py
|
|
122
|
+
from eth_account import Account
|
|
123
|
+
from x402.clients.requests import x402_requests
|
|
124
|
+
|
|
125
|
+
# Initialize account
|
|
126
|
+
account = Account.from_key("your_private_key")
|
|
127
|
+
|
|
128
|
+
# Create session and make request
|
|
129
|
+
session = x402_requests(account)
|
|
130
|
+
response = session.get("https://api.example.com/protected-endpoint")
|
|
131
|
+
print(response.content)
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Advanced Usage
|
|
135
|
+
|
|
136
|
+
#### Httpx Extensible Example
|
|
137
|
+
```py
|
|
138
|
+
import httpx
|
|
139
|
+
from eth_account import Account
|
|
140
|
+
from x402.clients.httpx import x402_payment_hooks
|
|
141
|
+
|
|
142
|
+
# Initialize account
|
|
143
|
+
account = Account.from_key("your_private_key")
|
|
144
|
+
|
|
145
|
+
# Create httpx client with x402 payment hooks
|
|
146
|
+
async with httpx.AsyncClient(base_url="https://api.example.com") as client:
|
|
147
|
+
# Add payment hooks directly to client
|
|
148
|
+
client.event_hooks = x402_payment_hooks(account)
|
|
149
|
+
|
|
150
|
+
# Make request - payment handling is automatic
|
|
151
|
+
response = await client.get("/protected-endpoint")
|
|
152
|
+
print(await response.aread())
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
#### Requests Session Extensible Example
|
|
156
|
+
```py
|
|
157
|
+
import requests
|
|
158
|
+
from eth_account import Account
|
|
159
|
+
from x402.clients.requests import x402_http_adapter
|
|
160
|
+
|
|
161
|
+
# Initialize account
|
|
162
|
+
account = Account.from_key("your_private_key")
|
|
163
|
+
|
|
164
|
+
# Create session and mount the x402 adapter
|
|
165
|
+
session = requests.Session()
|
|
166
|
+
adapter = x402_http_adapter(account)
|
|
167
|
+
|
|
168
|
+
# Mount the adapter for both HTTP and HTTPS
|
|
169
|
+
session.mount("http://", adapter)
|
|
170
|
+
session.mount("https://", adapter)
|
|
171
|
+
|
|
172
|
+
# Make request - payment handling is automatic
|
|
173
|
+
response = session.get("https://api.example.com/protected-endpoint")
|
|
174
|
+
print(response.content)
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## Manual Server Integration
|
|
178
|
+
|
|
179
|
+
If you're not using the FastAPI middleware, you can implement the x402 protocol manually. Here's what you'll need to handle:
|
|
180
|
+
|
|
181
|
+
1. Return 402 error responses with the appropriate response body
|
|
182
|
+
2. Use the facilitator to validate payments
|
|
183
|
+
3. Use the facilitator to settle payments
|
|
184
|
+
4. Return the appropriate response header to the caller
|
|
185
|
+
|
|
186
|
+
Here's an example of manual integration:
|
|
187
|
+
|
|
188
|
+
```py
|
|
189
|
+
from typing import Annotated
|
|
190
|
+
from fastapi import FastAPI, Request
|
|
191
|
+
from x402.types import PaymentRequiredResponse, PaymentRequirements
|
|
192
|
+
from x402.encoding import safe_base64_decode
|
|
193
|
+
|
|
194
|
+
payment_requirements = PaymentRequirements(...)
|
|
195
|
+
facilitator = FacilitatorClient(facilitator_url)
|
|
196
|
+
|
|
197
|
+
@app.get("/foo")
|
|
198
|
+
async def foo(req: request: Request):
|
|
199
|
+
payment_required = PaymentRequiredResponse(
|
|
200
|
+
x402_version: 1,
|
|
201
|
+
accepts=[payment_requirements],
|
|
202
|
+
error="",
|
|
203
|
+
)
|
|
204
|
+
payment_header = req.headers.get("X-PAYMENT", "")
|
|
205
|
+
|
|
206
|
+
if payment_header == "":
|
|
207
|
+
payment_required.error = "X-PAYMENT header not set"
|
|
208
|
+
return JSONResponse(
|
|
209
|
+
content=payment_required.model_dump(by_alias=True),
|
|
210
|
+
status_code=402,
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
payment = PaymentPayload(**json.loads(safe_base64_decode(payment_header)))
|
|
214
|
+
|
|
215
|
+
verify_response = await facilitator.verify(payment, payment_requirements)
|
|
216
|
+
if not verify_response.is_valid:
|
|
217
|
+
payment_required.error = "Invalid payment"
|
|
218
|
+
return JSONResponse(
|
|
219
|
+
content=payment_required.model_dump(by_alias=True),
|
|
220
|
+
status_code=402,
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
settle_response = await facilitator.settle(payment, payment_requirements)
|
|
224
|
+
if settle_response.success:
|
|
225
|
+
response.headers["X-PAYMENT-RESPONSE"] = base64.b64encode(
|
|
226
|
+
settle_response.model_dump_json().encode("utf-8")
|
|
227
|
+
).decode("utf-8")
|
|
228
|
+
else:
|
|
229
|
+
payment_required.error = "Settle failed: " + settle_response.error
|
|
230
|
+
return JSONResponse(
|
|
231
|
+
content=payment_required.model_dump(by_alias=True),
|
|
232
|
+
status_code=402,
|
|
233
|
+
)
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
For more examples and advanced usage patterns, check out our [examples directory](https://github.com/coinbase/x402/tree/main/examples/python).
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
# x402 Python
|
|
2
|
+
|
|
3
|
+
Python package for the x402 payments protocol.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install x402
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Overview
|
|
12
|
+
|
|
13
|
+
The x402 package provides the core building blocks for implementing the x402 Payment Protocol in Python. It's designed to be used by:
|
|
14
|
+
|
|
15
|
+
- FastAPI middleware for accepting payments
|
|
16
|
+
- Flask middleware for accepting payments
|
|
17
|
+
- httpx client for paying resources
|
|
18
|
+
- requests client for paying resources
|
|
19
|
+
|
|
20
|
+
## FastAPI Integration
|
|
21
|
+
|
|
22
|
+
The simplest way to add x402 payment protection to your FastAPI application:
|
|
23
|
+
|
|
24
|
+
```py
|
|
25
|
+
from fastapi import FastAPI
|
|
26
|
+
from x402.fastapi.middleware import require_payment
|
|
27
|
+
|
|
28
|
+
app = FastAPI()
|
|
29
|
+
app.middleware("http")(
|
|
30
|
+
require_payment(price="0.01", pay_to_address="0x209693Bc6afc0C5328bA36FaF03C514EF312287C")
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
@app.get("/")
|
|
34
|
+
async def root():
|
|
35
|
+
return {"message": "Hello World"}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
To protect specific routes:
|
|
39
|
+
|
|
40
|
+
```py
|
|
41
|
+
app.middleware("http")(
|
|
42
|
+
require_payment(price="0.01",
|
|
43
|
+
pay_to_address="0x209693Bc6afc0C5328bA36FaF03C514EF312287C"),
|
|
44
|
+
path="/foo" # <-- this can also be a list ex: ["/foo", "/bar"]
|
|
45
|
+
)
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Flask Integration
|
|
49
|
+
|
|
50
|
+
The simplest way to add x402 payment protection to your Flask application:
|
|
51
|
+
|
|
52
|
+
```py
|
|
53
|
+
from flask import Flask
|
|
54
|
+
from x402.flask.middleware import PaymentMiddleware
|
|
55
|
+
|
|
56
|
+
app = Flask(__name__)
|
|
57
|
+
|
|
58
|
+
# Initialize payment middleware
|
|
59
|
+
payment_middleware = PaymentMiddleware(app)
|
|
60
|
+
|
|
61
|
+
# Add payment protection for all routes
|
|
62
|
+
payment_middleware.add(
|
|
63
|
+
price="$0.01",
|
|
64
|
+
pay_to_address="0x209693Bc6afc0C5328bA36FaF03C514EF312287C",
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
@app.route("/")
|
|
68
|
+
def root():
|
|
69
|
+
return {"message": "Hello World"}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
To protect specific routes:
|
|
73
|
+
|
|
74
|
+
```py
|
|
75
|
+
# Protect specific endpoint
|
|
76
|
+
payment_middleware.add(
|
|
77
|
+
path="/foo",
|
|
78
|
+
price="$0.001",
|
|
79
|
+
pay_to_address="0x209693Bc6afc0C5328bA36FaF03C514EF312287C",
|
|
80
|
+
)
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Client Integration
|
|
84
|
+
|
|
85
|
+
### Simple Usage
|
|
86
|
+
|
|
87
|
+
#### Httpx Client
|
|
88
|
+
```py
|
|
89
|
+
from eth_account import Account
|
|
90
|
+
from x402.clients.httpx import x402HttpxClient
|
|
91
|
+
|
|
92
|
+
# Initialize account
|
|
93
|
+
account = Account.from_key("your_private_key")
|
|
94
|
+
|
|
95
|
+
# Create client and make request
|
|
96
|
+
async with x402HttpxClient(account=account, base_url="https://api.example.com") as client:
|
|
97
|
+
response = await client.get("/protected-endpoint")
|
|
98
|
+
print(await response.aread())
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
#### Requests Session Client
|
|
102
|
+
```py
|
|
103
|
+
from eth_account import Account
|
|
104
|
+
from x402.clients.requests import x402_requests
|
|
105
|
+
|
|
106
|
+
# Initialize account
|
|
107
|
+
account = Account.from_key("your_private_key")
|
|
108
|
+
|
|
109
|
+
# Create session and make request
|
|
110
|
+
session = x402_requests(account)
|
|
111
|
+
response = session.get("https://api.example.com/protected-endpoint")
|
|
112
|
+
print(response.content)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Advanced Usage
|
|
116
|
+
|
|
117
|
+
#### Httpx Extensible Example
|
|
118
|
+
```py
|
|
119
|
+
import httpx
|
|
120
|
+
from eth_account import Account
|
|
121
|
+
from x402.clients.httpx import x402_payment_hooks
|
|
122
|
+
|
|
123
|
+
# Initialize account
|
|
124
|
+
account = Account.from_key("your_private_key")
|
|
125
|
+
|
|
126
|
+
# Create httpx client with x402 payment hooks
|
|
127
|
+
async with httpx.AsyncClient(base_url="https://api.example.com") as client:
|
|
128
|
+
# Add payment hooks directly to client
|
|
129
|
+
client.event_hooks = x402_payment_hooks(account)
|
|
130
|
+
|
|
131
|
+
# Make request - payment handling is automatic
|
|
132
|
+
response = await client.get("/protected-endpoint")
|
|
133
|
+
print(await response.aread())
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
#### Requests Session Extensible Example
|
|
137
|
+
```py
|
|
138
|
+
import requests
|
|
139
|
+
from eth_account import Account
|
|
140
|
+
from x402.clients.requests import x402_http_adapter
|
|
141
|
+
|
|
142
|
+
# Initialize account
|
|
143
|
+
account = Account.from_key("your_private_key")
|
|
144
|
+
|
|
145
|
+
# Create session and mount the x402 adapter
|
|
146
|
+
session = requests.Session()
|
|
147
|
+
adapter = x402_http_adapter(account)
|
|
148
|
+
|
|
149
|
+
# Mount the adapter for both HTTP and HTTPS
|
|
150
|
+
session.mount("http://", adapter)
|
|
151
|
+
session.mount("https://", adapter)
|
|
152
|
+
|
|
153
|
+
# Make request - payment handling is automatic
|
|
154
|
+
response = session.get("https://api.example.com/protected-endpoint")
|
|
155
|
+
print(response.content)
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Manual Server Integration
|
|
159
|
+
|
|
160
|
+
If you're not using the FastAPI middleware, you can implement the x402 protocol manually. Here's what you'll need to handle:
|
|
161
|
+
|
|
162
|
+
1. Return 402 error responses with the appropriate response body
|
|
163
|
+
2. Use the facilitator to validate payments
|
|
164
|
+
3. Use the facilitator to settle payments
|
|
165
|
+
4. Return the appropriate response header to the caller
|
|
166
|
+
|
|
167
|
+
Here's an example of manual integration:
|
|
168
|
+
|
|
169
|
+
```py
|
|
170
|
+
from typing import Annotated
|
|
171
|
+
from fastapi import FastAPI, Request
|
|
172
|
+
from x402.types import PaymentRequiredResponse, PaymentRequirements
|
|
173
|
+
from x402.encoding import safe_base64_decode
|
|
174
|
+
|
|
175
|
+
payment_requirements = PaymentRequirements(...)
|
|
176
|
+
facilitator = FacilitatorClient(facilitator_url)
|
|
177
|
+
|
|
178
|
+
@app.get("/foo")
|
|
179
|
+
async def foo(req: request: Request):
|
|
180
|
+
payment_required = PaymentRequiredResponse(
|
|
181
|
+
x402_version: 1,
|
|
182
|
+
accepts=[payment_requirements],
|
|
183
|
+
error="",
|
|
184
|
+
)
|
|
185
|
+
payment_header = req.headers.get("X-PAYMENT", "")
|
|
186
|
+
|
|
187
|
+
if payment_header == "":
|
|
188
|
+
payment_required.error = "X-PAYMENT header not set"
|
|
189
|
+
return JSONResponse(
|
|
190
|
+
content=payment_required.model_dump(by_alias=True),
|
|
191
|
+
status_code=402,
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
payment = PaymentPayload(**json.loads(safe_base64_decode(payment_header)))
|
|
195
|
+
|
|
196
|
+
verify_response = await facilitator.verify(payment, payment_requirements)
|
|
197
|
+
if not verify_response.is_valid:
|
|
198
|
+
payment_required.error = "Invalid payment"
|
|
199
|
+
return JSONResponse(
|
|
200
|
+
content=payment_required.model_dump(by_alias=True),
|
|
201
|
+
status_code=402,
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
settle_response = await facilitator.settle(payment, payment_requirements)
|
|
205
|
+
if settle_response.success:
|
|
206
|
+
response.headers["X-PAYMENT-RESPONSE"] = base64.b64encode(
|
|
207
|
+
settle_response.model_dump_json().encode("utf-8")
|
|
208
|
+
).decode("utf-8")
|
|
209
|
+
else:
|
|
210
|
+
payment_required.error = "Settle failed: " + settle_response.error
|
|
211
|
+
return JSONResponse(
|
|
212
|
+
content=payment_required.model_dump(by_alias=True),
|
|
213
|
+
status_code=402,
|
|
214
|
+
)
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
For more examples and advanced usage patterns, check out our [examples directory](https://github.com/coinbase/x402/tree/main/examples/python).
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "og-test-x402"
|
|
3
|
+
version = "0.0.5"
|
|
4
|
+
description = "x402: An internet native payments protocol"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = { text = "Apache-2.0" }
|
|
7
|
+
authors = [
|
|
8
|
+
{ name = "erik", email = "erik.reppel@coinbase.com" }
|
|
9
|
+
]
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
keywords = ["x402", "sdk", "crypto", "cdp", "payments", "web3"]
|
|
12
|
+
dependencies = [
|
|
13
|
+
"eth-account>=0.13.7",
|
|
14
|
+
"eth-typing>=4.0.0",
|
|
15
|
+
"eth-utils>=3.0.0",
|
|
16
|
+
"fastapi[standard]>=0.115.12",
|
|
17
|
+
"flask>=3.0.0",
|
|
18
|
+
"pydantic>=2.10.3",
|
|
19
|
+
"pydantic-settings>=2.2.1",
|
|
20
|
+
"python-dotenv>=1.0.1",
|
|
21
|
+
"web3>=6.0.0",
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
[project.scripts]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
[build-system]
|
|
28
|
+
requires = ["hatchling"]
|
|
29
|
+
build-backend = "hatchling.build"
|
|
30
|
+
|
|
31
|
+
[dependency-groups]
|
|
32
|
+
dev = [
|
|
33
|
+
"pytest>=8.3.5",
|
|
34
|
+
"pytest-asyncio>=1.0.0",
|
|
35
|
+
"ruff>=0.11.9",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
[tool.pytest.ini_options]
|
|
39
|
+
asyncio_mode = "auto"
|
|
40
|
+
|
|
41
|
+
[tool.hatch.build.targets.wheel]
|
|
42
|
+
packages = ["src/x402"]
|
|
43
|
+
|
|
44
|
+
[tool.hatch.metadata]
|
|
45
|
+
allow-direct-references = true
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
NETWORK_TO_ID = {
|
|
2
|
+
"og-devnet": "10744",
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def get_chain_id(network: str) -> str:
|
|
7
|
+
"""Get the chain ID for a given network
|
|
8
|
+
Supports string encoded chain IDs and human readable networks
|
|
9
|
+
"""
|
|
10
|
+
try:
|
|
11
|
+
int(network)
|
|
12
|
+
return network
|
|
13
|
+
except ValueError:
|
|
14
|
+
pass
|
|
15
|
+
if network not in NETWORK_TO_ID:
|
|
16
|
+
raise ValueError(f"Unsupported network: {network}")
|
|
17
|
+
return NETWORK_TO_ID[network]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
KNOWN_TOKENS = {
|
|
21
|
+
"10744": [
|
|
22
|
+
{
|
|
23
|
+
"human_name": "ousdc",
|
|
24
|
+
"address": "0x48515A4b24f17cadcD6109a9D85a57ba55a619a6",
|
|
25
|
+
"name": "OUSDC",
|
|
26
|
+
"decimals": 6,
|
|
27
|
+
"version": "2",
|
|
28
|
+
}
|
|
29
|
+
],
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def get_token_name(chain_id: str, address: str) -> str:
|
|
34
|
+
"""Get the token name for a given chain and address"""
|
|
35
|
+
for token in KNOWN_TOKENS[chain_id]:
|
|
36
|
+
if token["address"] == address:
|
|
37
|
+
return token["name"]
|
|
38
|
+
raise ValueError(f"Token not found for chain {chain_id} and address {address}")
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def get_token_version(chain_id: str, address: str) -> str:
|
|
42
|
+
"""Get the token version for a given chain and address"""
|
|
43
|
+
for token in KNOWN_TOKENS[chain_id]:
|
|
44
|
+
if token["address"] == address:
|
|
45
|
+
return token["version"]
|
|
46
|
+
raise ValueError(f"Token not found for chain {chain_id} and address {address}")
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def get_token_decimals(chain_id: str, address: str) -> int:
|
|
50
|
+
"""Get the token decimals for a given chain and address"""
|
|
51
|
+
for token in KNOWN_TOKENS[chain_id]:
|
|
52
|
+
if token["address"] == address:
|
|
53
|
+
return token["decimals"]
|
|
54
|
+
raise ValueError(f"Token not found for chain {chain_id} and address {address}")
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def get_default_token_address(chain_id: str, token_type: str = "usdc") -> str:
|
|
58
|
+
"""Get the default token address for a given chain and token type"""
|
|
59
|
+
for token in KNOWN_TOKENS[chain_id]:
|
|
60
|
+
if token["human_name"] == token_type:
|
|
61
|
+
return token["address"]
|
|
62
|
+
raise ValueError(f"Token type '{token_type}' not found for chain {chain_id}")
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from x402.clients.base import x402Client, decode_x_payment_response
|
|
2
|
+
from x402.clients.httpx import (
|
|
3
|
+
x402_payment_hooks,
|
|
4
|
+
x402HttpxClient,
|
|
5
|
+
)
|
|
6
|
+
from x402.clients.requests import (
|
|
7
|
+
x402HTTPAdapter,
|
|
8
|
+
x402_http_adapter,
|
|
9
|
+
x402_requests,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"x402Client",
|
|
14
|
+
"decode_x_payment_response",
|
|
15
|
+
"x402_payment_hooks",
|
|
16
|
+
"x402HttpxClient",
|
|
17
|
+
"x402HTTPAdapter",
|
|
18
|
+
"x402_http_adapter",
|
|
19
|
+
"x402_requests",
|
|
20
|
+
]
|