pay-skill 0.1.2__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.
@@ -0,0 +1,28 @@
1
+ # Python
2
+ __pycache__/
3
+ *.pyc
4
+ *.pyo
5
+ *.egg-info/
6
+ dist/
7
+ build/
8
+ .eggs/
9
+ .mypy_cache/
10
+ .ruff_cache/
11
+ .pytest_cache/
12
+ htmlcov/
13
+ .coverage
14
+
15
+ # TypeScript
16
+ node_modules/
17
+ typescript/dist/
18
+ *.tsbuildinfo
19
+
20
+ # IDE
21
+ .vscode/
22
+ .idea/
23
+ *.swp
24
+ *.swo
25
+
26
+ # OS
27
+ .DS_Store
28
+ Thumbs.db
@@ -0,0 +1,182 @@
1
+ Metadata-Version: 2.4
2
+ Name: pay-skill
3
+ Version: 0.1.2
4
+ Summary: Python SDK for pay — payment infrastructure for AI agents
5
+ Project-URL: Homepage, https://pay-skill.com
6
+ Project-URL: Repository, https://github.com/pay-skill/pay-sdk
7
+ Author-email: "pay-skill.com" <hello@pay-skill.com>
8
+ License: MIT
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Topic :: Office/Business :: Financial
13
+ Requires-Python: >=3.10
14
+ Requires-Dist: eth-account>=0.11
15
+ Requires-Dist: httpx>=0.27
16
+ Requires-Dist: pydantic>=2.0
17
+ Provides-Extra: dev
18
+ Requires-Dist: mypy>=1.10; extra == 'dev'
19
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
20
+ Requires-Dist: pytest-cov>=5.0; extra == 'dev'
21
+ Requires-Dist: pytest-httpx>=0.30; extra == 'dev'
22
+ Requires-Dist: pytest-timeout>=2.3; extra == 'dev'
23
+ Requires-Dist: pytest>=8.0; extra == 'dev'
24
+ Requires-Dist: ruff>=0.4; extra == 'dev'
25
+ Provides-Extra: ows
26
+ Requires-Dist: open-wallet-standard>=1.2; extra == 'ows'
27
+ Description-Content-Type: text/markdown
28
+
29
+ # pay-sdk
30
+
31
+ Python SDK for [pay](https://pay-skill.com) — payment infrastructure for AI agents. USDC on Base.
32
+
33
+ Three primitives: direct payments, tabs (pre-funded metered accounts), and x402 HTTP paywalls.
34
+
35
+ ## Install
36
+
37
+ ```bash
38
+ pip install pay-sdk
39
+ ```
40
+
41
+ Requires Python 3.10+.
42
+
43
+ ## Quick Start
44
+
45
+ ```python
46
+ from payskill import PayClient
47
+
48
+ client = PayClient(signer="cli") # uses `pay sign` subprocess
49
+
50
+ # Pay another agent $5
51
+ result = client.pay_direct("0xprovider...", 5_000_000, memo="task-42")
52
+ print(result.tx_hash)
53
+
54
+ # Open a metered tab
55
+ tab = client.open_tab("0xprovider...", 20_000_000, max_charge_per_call=500_000)
56
+
57
+ # x402 request (SDK handles payment automatically)
58
+ response = client.request("https://api.example.com/data")
59
+ ```
60
+
61
+ All amounts are in USDC micro-units (6 decimals). `$1.00 = 1_000_000`.
62
+
63
+ ## API Reference
64
+
65
+ ### PayClient
66
+
67
+ ```python
68
+ from payskill import PayClient
69
+
70
+ client = PayClient(
71
+ api_url="https://pay-skill.com/api/v1", # default
72
+ signer="cli", # "cli", "raw", "custom", or a Signer instance
73
+ )
74
+ ```
75
+
76
+ ### Direct Payment
77
+
78
+ One-shot USDC transfer. $1.00 minimum.
79
+
80
+ ```python
81
+ result = client.pay_direct(to, amount, memo="")
82
+ # Returns: DirectPaymentResult(tx_hash, status, amount, fee)
83
+ ```
84
+
85
+ ### Tab Management
86
+
87
+ Pre-funded metered account. $5.00 minimum to open.
88
+
89
+ ```python
90
+ # Open
91
+ tab = client.open_tab(provider, amount, max_charge_per_call)
92
+
93
+ # Query
94
+ tabs = client.list_tabs()
95
+ tab = client.get_tab(tab_id)
96
+
97
+ # Top up (no extra activation fee)
98
+ tab = client.top_up_tab(tab_id, amount)
99
+
100
+ # Close (either party, unilateral)
101
+ tab = client.close_tab(tab_id)
102
+ ```
103
+
104
+ Returns `Tab(tab_id, provider, amount, balance_remaining, total_charged, charge_count, max_charge_per_call, status)`.
105
+
106
+ ### x402 Requests
107
+
108
+ Transparent HTTP 402 handling. The SDK detects `402 Payment Required`, pays (via direct or tab), and retries.
109
+
110
+ ```python
111
+ response = client.request(url, method="GET", body=None, headers=None)
112
+ # Returns: httpx.Response
113
+ ```
114
+
115
+ If the provider requires tab settlement, the SDK auto-opens a tab at 10x the per-call price (minimum $5).
116
+
117
+ ### Wallet
118
+
119
+ ```python
120
+ status = client.get_status()
121
+ # Returns: StatusResponse(address, balance, open_tabs)
122
+ ```
123
+
124
+ ### Webhooks
125
+
126
+ ```python
127
+ wh = client.register_webhook(url, events=["tab.opened"], secret="whsec_...")
128
+ webhooks = client.list_webhooks()
129
+ client.delete_webhook(webhook_id)
130
+ ```
131
+
132
+ ### Funding
133
+
134
+ ```python
135
+ link = client.create_fund_link(amount=10_000_000) # Coinbase Onramp
136
+ link = client.create_withdraw_link(amount=5_000_000)
137
+ ```
138
+
139
+ ## Signer Modes
140
+
141
+ | Mode | Usage | When |
142
+ |------|-------|------|
143
+ | `"cli"` | Subprocess call to `pay sign` | Default. Key in OS keychain. |
144
+ | `"raw"` | `PAYSKILL_KEY` env var | Dev/testing only. |
145
+ | `"custom"` | Your own callback | Custom key management. |
146
+
147
+ ```python
148
+ # CLI signer (default)
149
+ client = PayClient(signer="cli")
150
+
151
+ # Raw key (dev only)
152
+ client = PayClient(signer="raw", key="0xdead...")
153
+
154
+ # Custom callback
155
+ from payskill.signer import CallbackSigner
156
+ signer = CallbackSigner(callback=lambda hash_bytes: my_sign(hash_bytes))
157
+ client = PayClient(signer=signer)
158
+ ```
159
+
160
+ ## Error Handling
161
+
162
+ ```python
163
+ from payskill.errors import (
164
+ PayError, # Base class
165
+ PayValidationError, # Bad input (has .field)
166
+ PayNetworkError, # Connection failed
167
+ PayServerError, # Server returned error (has .status_code)
168
+ PayInsufficientFundsError, # Not enough USDC
169
+ )
170
+ ```
171
+
172
+ ## Configuration
173
+
174
+ | Env Var | Purpose |
175
+ |---------|---------|
176
+ | `PAYSKILL_KEY` | Private key for raw signer mode |
177
+
178
+ The API URL is configurable via the `api_url` parameter. Default: `https://pay-skill.com/api/v1`.
179
+
180
+ ## License
181
+
182
+ MIT
@@ -0,0 +1,154 @@
1
+ # pay-sdk
2
+
3
+ Python SDK for [pay](https://pay-skill.com) — payment infrastructure for AI agents. USDC on Base.
4
+
5
+ Three primitives: direct payments, tabs (pre-funded metered accounts), and x402 HTTP paywalls.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ pip install pay-sdk
11
+ ```
12
+
13
+ Requires Python 3.10+.
14
+
15
+ ## Quick Start
16
+
17
+ ```python
18
+ from payskill import PayClient
19
+
20
+ client = PayClient(signer="cli") # uses `pay sign` subprocess
21
+
22
+ # Pay another agent $5
23
+ result = client.pay_direct("0xprovider...", 5_000_000, memo="task-42")
24
+ print(result.tx_hash)
25
+
26
+ # Open a metered tab
27
+ tab = client.open_tab("0xprovider...", 20_000_000, max_charge_per_call=500_000)
28
+
29
+ # x402 request (SDK handles payment automatically)
30
+ response = client.request("https://api.example.com/data")
31
+ ```
32
+
33
+ All amounts are in USDC micro-units (6 decimals). `$1.00 = 1_000_000`.
34
+
35
+ ## API Reference
36
+
37
+ ### PayClient
38
+
39
+ ```python
40
+ from payskill import PayClient
41
+
42
+ client = PayClient(
43
+ api_url="https://pay-skill.com/api/v1", # default
44
+ signer="cli", # "cli", "raw", "custom", or a Signer instance
45
+ )
46
+ ```
47
+
48
+ ### Direct Payment
49
+
50
+ One-shot USDC transfer. $1.00 minimum.
51
+
52
+ ```python
53
+ result = client.pay_direct(to, amount, memo="")
54
+ # Returns: DirectPaymentResult(tx_hash, status, amount, fee)
55
+ ```
56
+
57
+ ### Tab Management
58
+
59
+ Pre-funded metered account. $5.00 minimum to open.
60
+
61
+ ```python
62
+ # Open
63
+ tab = client.open_tab(provider, amount, max_charge_per_call)
64
+
65
+ # Query
66
+ tabs = client.list_tabs()
67
+ tab = client.get_tab(tab_id)
68
+
69
+ # Top up (no extra activation fee)
70
+ tab = client.top_up_tab(tab_id, amount)
71
+
72
+ # Close (either party, unilateral)
73
+ tab = client.close_tab(tab_id)
74
+ ```
75
+
76
+ Returns `Tab(tab_id, provider, amount, balance_remaining, total_charged, charge_count, max_charge_per_call, status)`.
77
+
78
+ ### x402 Requests
79
+
80
+ Transparent HTTP 402 handling. The SDK detects `402 Payment Required`, pays (via direct or tab), and retries.
81
+
82
+ ```python
83
+ response = client.request(url, method="GET", body=None, headers=None)
84
+ # Returns: httpx.Response
85
+ ```
86
+
87
+ If the provider requires tab settlement, the SDK auto-opens a tab at 10x the per-call price (minimum $5).
88
+
89
+ ### Wallet
90
+
91
+ ```python
92
+ status = client.get_status()
93
+ # Returns: StatusResponse(address, balance, open_tabs)
94
+ ```
95
+
96
+ ### Webhooks
97
+
98
+ ```python
99
+ wh = client.register_webhook(url, events=["tab.opened"], secret="whsec_...")
100
+ webhooks = client.list_webhooks()
101
+ client.delete_webhook(webhook_id)
102
+ ```
103
+
104
+ ### Funding
105
+
106
+ ```python
107
+ link = client.create_fund_link(amount=10_000_000) # Coinbase Onramp
108
+ link = client.create_withdraw_link(amount=5_000_000)
109
+ ```
110
+
111
+ ## Signer Modes
112
+
113
+ | Mode | Usage | When |
114
+ |------|-------|------|
115
+ | `"cli"` | Subprocess call to `pay sign` | Default. Key in OS keychain. |
116
+ | `"raw"` | `PAYSKILL_KEY` env var | Dev/testing only. |
117
+ | `"custom"` | Your own callback | Custom key management. |
118
+
119
+ ```python
120
+ # CLI signer (default)
121
+ client = PayClient(signer="cli")
122
+
123
+ # Raw key (dev only)
124
+ client = PayClient(signer="raw", key="0xdead...")
125
+
126
+ # Custom callback
127
+ from payskill.signer import CallbackSigner
128
+ signer = CallbackSigner(callback=lambda hash_bytes: my_sign(hash_bytes))
129
+ client = PayClient(signer=signer)
130
+ ```
131
+
132
+ ## Error Handling
133
+
134
+ ```python
135
+ from payskill.errors import (
136
+ PayError, # Base class
137
+ PayValidationError, # Bad input (has .field)
138
+ PayNetworkError, # Connection failed
139
+ PayServerError, # Server returned error (has .status_code)
140
+ PayInsufficientFundsError, # Not enough USDC
141
+ )
142
+ ```
143
+
144
+ ## Configuration
145
+
146
+ | Env Var | Purpose |
147
+ |---------|---------|
148
+ | `PAYSKILL_KEY` | Private key for raw signer mode |
149
+
150
+ The API URL is configurable via the `api_url` parameter. Default: `https://pay-skill.com/api/v1`.
151
+
152
+ ## License
153
+
154
+ MIT
@@ -0,0 +1,66 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "pay-skill"
7
+ version = "0.1.2"
8
+ description = "Python SDK for pay — payment infrastructure for AI agents"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = { text = "MIT" }
12
+ authors = [{name = "pay-skill.com", email = "hello@pay-skill.com"}]
13
+ classifiers = [
14
+ "Development Status :: 3 - Alpha",
15
+ "License :: OSI Approved :: MIT License",
16
+ "Programming Language :: Python :: 3",
17
+ "Topic :: Office/Business :: Financial",
18
+ ]
19
+
20
+ dependencies = [
21
+ "httpx>=0.27",
22
+ "pydantic>=2.0",
23
+ "eth-account>=0.11",
24
+ ]
25
+
26
+ [project.urls]
27
+ Homepage = "https://pay-skill.com"
28
+ Repository = "https://github.com/pay-skill/pay-sdk"
29
+
30
+ [project.optional-dependencies]
31
+ ows = [
32
+ "open-wallet-standard>=1.2",
33
+ ]
34
+ dev = [
35
+ "pytest>=8.0",
36
+ "pytest-asyncio>=0.23",
37
+ "pytest-cov>=5.0",
38
+ "pytest-timeout>=2.3",
39
+ "pytest-httpx>=0.30",
40
+ "ruff>=0.4",
41
+ "mypy>=1.10",
42
+ ]
43
+
44
+ [tool.hatch.build.targets.wheel]
45
+ packages = ["src/payskill"]
46
+
47
+ [tool.ruff]
48
+ target-version = "py310"
49
+ line-length = 100
50
+
51
+ [tool.ruff.lint]
52
+ select = ["E", "W", "F", "I", "N", "UP", "B", "S"]
53
+ ignore = ["S101", "S603"]
54
+
55
+ [tool.mypy]
56
+ python_version = "3.10"
57
+ strict = true
58
+ ignore_missing_imports = true
59
+ warn_unused_ignores = false
60
+
61
+ [tool.pytest.ini_options]
62
+ asyncio_mode = "auto"
63
+ testpaths = ["tests"]
64
+ markers = [
65
+ "e2e: end-to-end tests against live testnet (requires PAYSKILL_TESTNET_KEY)",
66
+ ]
@@ -0,0 +1,30 @@
1
+ """pay SDK — payment infrastructure for AI agents."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ from payskill.auth import build_auth_headers, derive_address
8
+ from payskill.client import PayClient
9
+ from payskill.errors import PayError, PayNetworkError, PayValidationError
10
+ from payskill.models import DirectPaymentResult, Tab, TabStatus
11
+
12
+ # OWS signer is optional — only available if open-wallet-standard is installed.
13
+ OwsSigner: Any
14
+ try:
15
+ from payskill.ows_signer import OwsSigner
16
+ except ImportError:
17
+ OwsSigner = None
18
+
19
+ __all__ = [
20
+ "PayClient",
21
+ "PayError",
22
+ "PayNetworkError",
23
+ "PayValidationError",
24
+ "DirectPaymentResult",
25
+ "Tab",
26
+ "TabStatus",
27
+ "build_auth_headers",
28
+ "derive_address",
29
+ "OwsSigner",
30
+ ]
@@ -0,0 +1,101 @@
1
+ """EIP-712 authentication for pay API requests.
2
+
3
+ Every authenticated request includes four headers:
4
+ X-Pay-Agent — wallet address (0x-prefixed, checksummed)
5
+ X-Pay-Signature — EIP-712 signature (0x-prefixed hex, 65 bytes)
6
+ X-Pay-Timestamp — unix timestamp in seconds
7
+ X-Pay-Nonce — random 32-byte hex (0x-prefixed)
8
+
9
+ The EIP-712 domain:
10
+ name: "pay"
11
+ version: "0.1"
12
+ chainId: <from config>
13
+ verifyingContract: <router address>
14
+
15
+ The typed data:
16
+ APIRequest(string method, string path, uint256 timestamp, bytes32 nonce)
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ import os
22
+ import time
23
+
24
+ from eth_account import Account
25
+ from eth_account.messages import encode_typed_data
26
+
27
+
28
+ def build_auth_headers(
29
+ private_key: str,
30
+ method: str,
31
+ path: str,
32
+ chain_id: int,
33
+ router_address: str,
34
+ ) -> dict[str, str]:
35
+ """Build X-Pay-* auth headers for an API request.
36
+
37
+ Args:
38
+ private_key: Hex-encoded private key (with or without 0x prefix).
39
+ method: HTTP method (GET, POST, DELETE, etc.).
40
+ path: Request path (e.g., /api/v1/direct).
41
+ chain_id: Chain ID for EIP-712 domain.
42
+ router_address: Router contract address for EIP-712 domain.
43
+
44
+ Returns:
45
+ Dict with X-Pay-Agent, X-Pay-Signature, X-Pay-Timestamp, X-Pay-Nonce.
46
+ """
47
+ account = Account.from_key(private_key)
48
+ timestamp = int(time.time())
49
+ nonce = "0x" + os.urandom(32).hex()
50
+
51
+ domain_data = {
52
+ "name": "pay",
53
+ "version": "0.1",
54
+ "chainId": chain_id,
55
+ "verifyingContract": router_address,
56
+ }
57
+
58
+ message_types = {
59
+ "APIRequest": [
60
+ {"name": "method", "type": "string"},
61
+ {"name": "path", "type": "string"},
62
+ {"name": "timestamp", "type": "uint256"},
63
+ {"name": "nonce", "type": "bytes32"},
64
+ ],
65
+ }
66
+
67
+ message_data = {
68
+ "method": method.upper(),
69
+ "path": path,
70
+ "timestamp": timestamp,
71
+ "nonce": bytes.fromhex(nonce[2:]),
72
+ }
73
+
74
+ signable = encode_typed_data(
75
+ domain_data,
76
+ message_types,
77
+ message_data,
78
+ )
79
+ signed = account.sign_message(signable)
80
+
81
+ return {
82
+ "X-Pay-Agent": account.address,
83
+ "X-Pay-Signature": signed.signature.hex()
84
+ if isinstance(signed.signature, bytes)
85
+ else hex(signed.signature),
86
+ "X-Pay-Timestamp": str(timestamp),
87
+ "X-Pay-Nonce": nonce,
88
+ }
89
+
90
+
91
+ def derive_address(private_key: str) -> str:
92
+ """Derive an Ethereum address from a private key.
93
+
94
+ Args:
95
+ private_key: Hex-encoded private key (with or without 0x prefix).
96
+
97
+ Returns:
98
+ Checksummed Ethereum address.
99
+ """
100
+ account = Account.from_key(private_key)
101
+ return str(account.address)