fractalpay 1.0.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.
- fractalpay-1.0.0/.gitignore +5 -0
- fractalpay-1.0.0/CHANGELOG.md +39 -0
- fractalpay-1.0.0/LICENSE +17 -0
- fractalpay-1.0.0/PKG-INFO +188 -0
- fractalpay-1.0.0/README.md +154 -0
- fractalpay-1.0.0/examples/quickstart.py +36 -0
- fractalpay-1.0.0/examples/webhook_handler.py +64 -0
- fractalpay-1.0.0/pyproject.toml +62 -0
- fractalpay-1.0.0/src/fractalpay/__init__.py +73 -0
- fractalpay-1.0.0/src/fractalpay/client.py +296 -0
- fractalpay-1.0.0/src/fractalpay/errors.py +107 -0
- fractalpay-1.0.0/src/fractalpay/types.py +135 -0
- fractalpay-1.0.0/src/fractalpay/webhook.py +140 -0
- fractalpay-1.0.0/tests/__init__.py +0 -0
- fractalpay-1.0.0/tests/test_client.py +218 -0
- fractalpay-1.0.0/tests/test_types.py +90 -0
- fractalpay-1.0.0/tests/test_webhook.py +132 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to the `fractalpay` Python SDK are documented here. Format
|
|
4
|
+
follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). Versioning
|
|
5
|
+
follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
## [1.0.0] — 2026-05-25
|
|
8
|
+
|
|
9
|
+
Initial public release. Stripe-shaped API on top of the existing FractalPay
|
|
10
|
+
intent infrastructure (`/api/pay`, `/api/pay/webhook`).
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- `FractalPay` client class with `httpx` under the hood
|
|
15
|
+
- `FractalPay.intents.create()` — wraps `POST /api/pay` with snake_case→camelCase
|
|
16
|
+
- `FractalPay.intents.retrieve(id)` — wraps `GET /api/pay?id=X`
|
|
17
|
+
- `FractalPay.intents.list(status?, limit?)` — wraps `GET /api/pay?list=true`
|
|
18
|
+
- `FractalPay.intents.stream(id)` — SSE iterator for real-time updates
|
|
19
|
+
- `verify_webhook(payload, signature, secret)` — HMAC-SHA256 verification
|
|
20
|
+
with constant-time comparison; accepts both `sha256=<hex>` and bare hex
|
|
21
|
+
- `WebhookEvent` dataclass with typed `event.type` and `event.intent`
|
|
22
|
+
- `PaymentIntent` dataclass mirroring API shape (snake_case Pythonic fields)
|
|
23
|
+
- Typed exception hierarchy: `FractalPayError` base + `AuthenticationError`,
|
|
24
|
+
`InvalidRequestError`, `RateLimitError`, `APIConnectionError`, `APIError`,
|
|
25
|
+
`SignatureVerificationError`
|
|
26
|
+
- Full pytest suite covering serialization, error mapping, signature
|
|
27
|
+
verification (positive + adversarial), context-manager lifecycle
|
|
28
|
+
- `examples/quickstart.py` — create + display intent in 20 lines
|
|
29
|
+
- `examples/webhook_handler.py` — Flask example for merchant integration
|
|
30
|
+
|
|
31
|
+
### Spec compliance
|
|
32
|
+
|
|
33
|
+
- [x] Mirrors the server-side `PaymentIntent` type from
|
|
34
|
+
`frontend/lib/types/payment.ts`
|
|
35
|
+
- [x] camelCase wire format (server) ↔ snake_case Pythonic API (SDK)
|
|
36
|
+
- [x] Constant-time HMAC comparison for webhook verification
|
|
37
|
+
(`hmac.compare_digest`)
|
|
38
|
+
- [x] Bearer token Authorization header when `api_key` is provided
|
|
39
|
+
- [x] Apache-2.0 license; zero non-permissive dependencies
|
fractalpay-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
Copyright 2026 FractalAI Foundation and contributors
|
|
6
|
+
|
|
7
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
8
|
+
you may not use this file except in compliance with the License.
|
|
9
|
+
You may obtain a copy of the License at
|
|
10
|
+
|
|
11
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
+
|
|
13
|
+
Unless required by applicable law or agreed to in writing, software
|
|
14
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
+
See the License for the specific language governing permissions and
|
|
17
|
+
limitations under the License.
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fractalpay
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: FractalPay AaaS — Python SDK. The post-quantum, AI-verified, multi-chain payment gateway as a service. Stripe-shaped API on top of 9 blockchains (8 EVM + Stellar) with VAID-1 cryptographic attestations.
|
|
5
|
+
Project-URL: Homepage, https://fractalai.net.co/standards/fractalpay
|
|
6
|
+
Project-URL: Documentation, https://fractalai.net.co/docs/fractalpay
|
|
7
|
+
Project-URL: Repository, https://github.com/johnInarti/FRACTAL-AI
|
|
8
|
+
Project-URL: Issues, https://github.com/johnInarti/FRACTAL-AI/issues
|
|
9
|
+
Project-URL: Changelog, https://github.com/johnInarti/FRACTAL-AI/blob/main/sdk/python-fractalpay/CHANGELOG.md
|
|
10
|
+
Author-email: FractalAI Foundation <developers@fractalai.net.co>
|
|
11
|
+
License: Apache-2.0
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Keywords: aaas,base,crypto-payments,ethereum,fractalai,fractalpay,multi-chain,payments,post-quantum,stablecoin,stellar,stripe-alternative
|
|
14
|
+
Classifier: Development Status :: 4 - Beta
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: Intended Audience :: Financial and Insurance Industry
|
|
17
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
18
|
+
Classifier: Operating System :: OS Independent
|
|
19
|
+
Classifier: Programming Language :: Python :: 3
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
24
|
+
Classifier: Topic :: Office/Business :: Financial
|
|
25
|
+
Classifier: Topic :: Security :: Cryptography
|
|
26
|
+
Requires-Python: >=3.9
|
|
27
|
+
Requires-Dist: httpx>=0.25.0
|
|
28
|
+
Provides-Extra: dev
|
|
29
|
+
Requires-Dist: pytest-cov>=4.1; extra == 'dev'
|
|
30
|
+
Requires-Dist: pytest>=7.4; extra == 'dev'
|
|
31
|
+
Requires-Dist: respx>=0.20; extra == 'dev'
|
|
32
|
+
Requires-Dist: ruff>=0.1.0; extra == 'dev'
|
|
33
|
+
Description-Content-Type: text/markdown
|
|
34
|
+
|
|
35
|
+
# fractalpay
|
|
36
|
+
|
|
37
|
+
**FractalPay AaaS** — Python SDK for the post-quantum, AI-verified, multi-chain
|
|
38
|
+
payment gateway as a service.
|
|
39
|
+
|
|
40
|
+
Stripe-shaped API. Nine blockchains (8 EVM + Stellar) native. Cryptographic
|
|
41
|
+
[VAID-1](https://github.com/johnInarti/FRACTAL-AI/blob/main/VAID_1_SPEC.md)
|
|
42
|
+
attestation on every payment. 0.618% fee instead of 2.9% + 30¢.
|
|
43
|
+
|
|
44
|
+
## Install
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
pip install fractalpay
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Requires Python 3.9+. Only dependency is `httpx`.
|
|
51
|
+
|
|
52
|
+
## Quickstart — receive a payment in 5 lines
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
from fractalpay import FractalPay
|
|
56
|
+
|
|
57
|
+
fp = FractalPay() # base_url defaults to https://fractalai.net.co
|
|
58
|
+
|
|
59
|
+
intent = fp.intents.create(
|
|
60
|
+
amount="100.00",
|
|
61
|
+
currency="USDC",
|
|
62
|
+
recipient_address="0xYourWalletOnBase",
|
|
63
|
+
recipient_chain="base",
|
|
64
|
+
description="Pro plan — monthly",
|
|
65
|
+
callback_url="https://your-app.com/webhooks/fractalpay",
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
print(f"Send your customer to: {intent.web_url}")
|
|
69
|
+
# → https://fractalai.net.co/pay/{intent.id}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
That's it. The customer lands on a hosted checkout page, pays in their wallet,
|
|
73
|
+
your webhook fires when the payment is confirmed on-chain.
|
|
74
|
+
|
|
75
|
+
## Why this exists
|
|
76
|
+
|
|
77
|
+
| | Stripe | Coinbase Commerce | FractalPay |
|
|
78
|
+
|---|---|---|---|
|
|
79
|
+
| Fee per tx | 2.9% + 30¢ | 1.0% | **0.618%** (φ⁻¹) |
|
|
80
|
+
| Chains supported | 0 (card only) | 4 | **9** (8 EVM + Stellar) |
|
|
81
|
+
| Post-quantum signatures | ❌ | ❌ | ✅ **CRYSTALS-Dilithium** |
|
|
82
|
+
| Cryptographic proof per payment | ❌ (PDF receipt) | ❌ | ✅ **VAID-1 attestation** |
|
|
83
|
+
| Open source | ❌ | ❌ | ✅ Apache-2.0 |
|
|
84
|
+
| Settlement | T+2 to bank | T+0 to crypto | T+0 to crypto, fiat off-ramp roadmap |
|
|
85
|
+
|
|
86
|
+
## Verify an incoming webhook
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
from fractalpay import verify_webhook
|
|
90
|
+
|
|
91
|
+
@app.route("/webhooks/fractalpay", methods=["POST"])
|
|
92
|
+
def handle_webhook():
|
|
93
|
+
event = verify_webhook(
|
|
94
|
+
payload=request.get_data(),
|
|
95
|
+
signature=request.headers["X-FractalPay-Signature"],
|
|
96
|
+
secret=os.environ["FRACTALPAY_WEBHOOK_SECRET"],
|
|
97
|
+
)
|
|
98
|
+
if event.type == "payment.completed":
|
|
99
|
+
# event.intent.id, event.intent.tx_hash, event.intent.payer_address
|
|
100
|
+
fulfill_order(event.intent.metadata["order_id"])
|
|
101
|
+
return "ok", 200
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Query an intent
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
intent = fp.intents.retrieve("intent_abc123")
|
|
108
|
+
print(intent.status)
|
|
109
|
+
# 'created' | 'detecting' | 'confirming' | 'bridging' | 'completed' | 'expired' | 'failed' | 'refunded'
|
|
110
|
+
|
|
111
|
+
if intent.status == "completed":
|
|
112
|
+
print(f"Settled: {intent.settled_amount} {intent.currency} on {intent.recipient_chain}")
|
|
113
|
+
print(f"Payer: {intent.payer_address}")
|
|
114
|
+
print(f"Tx hash: {intent.tx_hash}")
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## List intents
|
|
118
|
+
|
|
119
|
+
```python
|
|
120
|
+
# All recent
|
|
121
|
+
for intent in fp.intents.list(limit=100):
|
|
122
|
+
print(intent.id, intent.amount, intent.status)
|
|
123
|
+
|
|
124
|
+
# Only completed
|
|
125
|
+
for intent in fp.intents.list(status="completed", limit=50):
|
|
126
|
+
print(intent.id, intent.completed_at)
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Multi-chain payment routing
|
|
130
|
+
|
|
131
|
+
Your customer can pay from ANY of the 9 supported chains; FractalPay handles the
|
|
132
|
+
routing and bridges to your settlement chain.
|
|
133
|
+
|
|
134
|
+
```python
|
|
135
|
+
intent = fp.intents.create(
|
|
136
|
+
amount="500.00",
|
|
137
|
+
currency="USDC",
|
|
138
|
+
recipient_address="0xMyBaseWallet",
|
|
139
|
+
recipient_chain="base", # I want USDC on Base
|
|
140
|
+
# Customer can pay from: ethereum, polygon, arbitrum, stellar, etc.
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
# `intent.suggested_chains` lists the chains the customer can use.
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Hosted vs. embedded checkout
|
|
147
|
+
|
|
148
|
+
**Hosted (recommended):** redirect the customer to `intent.web_url`. Zero
|
|
149
|
+
frontend work, mobile-optimized, supports every wallet (MetaMask, Coinbase,
|
|
150
|
+
Lobstr, hardware wallets, etc.).
|
|
151
|
+
|
|
152
|
+
**Embedded:** use the JS widget — see `@fractalai/pay` (TypeScript SDK) and
|
|
153
|
+
`/api/pay/widget` endpoint.
|
|
154
|
+
|
|
155
|
+
## Real-time updates (SSE)
|
|
156
|
+
|
|
157
|
+
```python
|
|
158
|
+
for event in fp.intents.stream(intent.id):
|
|
159
|
+
print(f"{event.timestamp}: {event.status}")
|
|
160
|
+
if event.status in ("completed", "failed", "expired"):
|
|
161
|
+
break
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## What's a payment intent?
|
|
165
|
+
|
|
166
|
+
Same concept as Stripe's `PaymentIntent`: a server-side object representing
|
|
167
|
+
your intent to collect payment from a customer. It's created with an amount,
|
|
168
|
+
currency, and recipient; it expires after a TTL (default 30 min); it tracks
|
|
169
|
+
status through its lifecycle from `created` → `detecting` → `confirming` →
|
|
170
|
+
`completed`.
|
|
171
|
+
|
|
172
|
+
The big difference: FractalPay's intent is **multi-chain native** (the
|
|
173
|
+
customer chooses where to pay from) and **HMAC-signed** (the recipient address
|
|
174
|
+
can't be tampered with in transit).
|
|
175
|
+
|
|
176
|
+
## License
|
|
177
|
+
|
|
178
|
+
- This SDK: Apache-2.0 (see `LICENSE`)
|
|
179
|
+
- The FractalPay API and protocol: same license, plus open-source server
|
|
180
|
+
implementation at github.com/johnInarti/FRACTAL-AI
|
|
181
|
+
|
|
182
|
+
## Resources
|
|
183
|
+
|
|
184
|
+
- [API reference](https://fractalai.net.co/docs/fractalpay)
|
|
185
|
+
- [VAID-1 spec](https://github.com/johnInarti/FRACTAL-AI/blob/main/VAID_1_SPEC.md)
|
|
186
|
+
(the attestation standard every payment carries)
|
|
187
|
+
- [TypeScript SDK](https://www.npmjs.com/package/@fractalai/pay) (coming)
|
|
188
|
+
- [Issues](https://github.com/johnInarti/FRACTAL-AI/issues) (label `fractalpay`)
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# fractalpay
|
|
2
|
+
|
|
3
|
+
**FractalPay AaaS** — Python SDK for the post-quantum, AI-verified, multi-chain
|
|
4
|
+
payment gateway as a service.
|
|
5
|
+
|
|
6
|
+
Stripe-shaped API. Nine blockchains (8 EVM + Stellar) native. Cryptographic
|
|
7
|
+
[VAID-1](https://github.com/johnInarti/FRACTAL-AI/blob/main/VAID_1_SPEC.md)
|
|
8
|
+
attestation on every payment. 0.618% fee instead of 2.9% + 30¢.
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
pip install fractalpay
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Requires Python 3.9+. Only dependency is `httpx`.
|
|
17
|
+
|
|
18
|
+
## Quickstart — receive a payment in 5 lines
|
|
19
|
+
|
|
20
|
+
```python
|
|
21
|
+
from fractalpay import FractalPay
|
|
22
|
+
|
|
23
|
+
fp = FractalPay() # base_url defaults to https://fractalai.net.co
|
|
24
|
+
|
|
25
|
+
intent = fp.intents.create(
|
|
26
|
+
amount="100.00",
|
|
27
|
+
currency="USDC",
|
|
28
|
+
recipient_address="0xYourWalletOnBase",
|
|
29
|
+
recipient_chain="base",
|
|
30
|
+
description="Pro plan — monthly",
|
|
31
|
+
callback_url="https://your-app.com/webhooks/fractalpay",
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
print(f"Send your customer to: {intent.web_url}")
|
|
35
|
+
# → https://fractalai.net.co/pay/{intent.id}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
That's it. The customer lands on a hosted checkout page, pays in their wallet,
|
|
39
|
+
your webhook fires when the payment is confirmed on-chain.
|
|
40
|
+
|
|
41
|
+
## Why this exists
|
|
42
|
+
|
|
43
|
+
| | Stripe | Coinbase Commerce | FractalPay |
|
|
44
|
+
|---|---|---|---|
|
|
45
|
+
| Fee per tx | 2.9% + 30¢ | 1.0% | **0.618%** (φ⁻¹) |
|
|
46
|
+
| Chains supported | 0 (card only) | 4 | **9** (8 EVM + Stellar) |
|
|
47
|
+
| Post-quantum signatures | ❌ | ❌ | ✅ **CRYSTALS-Dilithium** |
|
|
48
|
+
| Cryptographic proof per payment | ❌ (PDF receipt) | ❌ | ✅ **VAID-1 attestation** |
|
|
49
|
+
| Open source | ❌ | ❌ | ✅ Apache-2.0 |
|
|
50
|
+
| Settlement | T+2 to bank | T+0 to crypto | T+0 to crypto, fiat off-ramp roadmap |
|
|
51
|
+
|
|
52
|
+
## Verify an incoming webhook
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
from fractalpay import verify_webhook
|
|
56
|
+
|
|
57
|
+
@app.route("/webhooks/fractalpay", methods=["POST"])
|
|
58
|
+
def handle_webhook():
|
|
59
|
+
event = verify_webhook(
|
|
60
|
+
payload=request.get_data(),
|
|
61
|
+
signature=request.headers["X-FractalPay-Signature"],
|
|
62
|
+
secret=os.environ["FRACTALPAY_WEBHOOK_SECRET"],
|
|
63
|
+
)
|
|
64
|
+
if event.type == "payment.completed":
|
|
65
|
+
# event.intent.id, event.intent.tx_hash, event.intent.payer_address
|
|
66
|
+
fulfill_order(event.intent.metadata["order_id"])
|
|
67
|
+
return "ok", 200
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Query an intent
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
intent = fp.intents.retrieve("intent_abc123")
|
|
74
|
+
print(intent.status)
|
|
75
|
+
# 'created' | 'detecting' | 'confirming' | 'bridging' | 'completed' | 'expired' | 'failed' | 'refunded'
|
|
76
|
+
|
|
77
|
+
if intent.status == "completed":
|
|
78
|
+
print(f"Settled: {intent.settled_amount} {intent.currency} on {intent.recipient_chain}")
|
|
79
|
+
print(f"Payer: {intent.payer_address}")
|
|
80
|
+
print(f"Tx hash: {intent.tx_hash}")
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## List intents
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
# All recent
|
|
87
|
+
for intent in fp.intents.list(limit=100):
|
|
88
|
+
print(intent.id, intent.amount, intent.status)
|
|
89
|
+
|
|
90
|
+
# Only completed
|
|
91
|
+
for intent in fp.intents.list(status="completed", limit=50):
|
|
92
|
+
print(intent.id, intent.completed_at)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Multi-chain payment routing
|
|
96
|
+
|
|
97
|
+
Your customer can pay from ANY of the 9 supported chains; FractalPay handles the
|
|
98
|
+
routing and bridges to your settlement chain.
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
intent = fp.intents.create(
|
|
102
|
+
amount="500.00",
|
|
103
|
+
currency="USDC",
|
|
104
|
+
recipient_address="0xMyBaseWallet",
|
|
105
|
+
recipient_chain="base", # I want USDC on Base
|
|
106
|
+
# Customer can pay from: ethereum, polygon, arbitrum, stellar, etc.
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
# `intent.suggested_chains` lists the chains the customer can use.
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Hosted vs. embedded checkout
|
|
113
|
+
|
|
114
|
+
**Hosted (recommended):** redirect the customer to `intent.web_url`. Zero
|
|
115
|
+
frontend work, mobile-optimized, supports every wallet (MetaMask, Coinbase,
|
|
116
|
+
Lobstr, hardware wallets, etc.).
|
|
117
|
+
|
|
118
|
+
**Embedded:** use the JS widget — see `@fractalai/pay` (TypeScript SDK) and
|
|
119
|
+
`/api/pay/widget` endpoint.
|
|
120
|
+
|
|
121
|
+
## Real-time updates (SSE)
|
|
122
|
+
|
|
123
|
+
```python
|
|
124
|
+
for event in fp.intents.stream(intent.id):
|
|
125
|
+
print(f"{event.timestamp}: {event.status}")
|
|
126
|
+
if event.status in ("completed", "failed", "expired"):
|
|
127
|
+
break
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## What's a payment intent?
|
|
131
|
+
|
|
132
|
+
Same concept as Stripe's `PaymentIntent`: a server-side object representing
|
|
133
|
+
your intent to collect payment from a customer. It's created with an amount,
|
|
134
|
+
currency, and recipient; it expires after a TTL (default 30 min); it tracks
|
|
135
|
+
status through its lifecycle from `created` → `detecting` → `confirming` →
|
|
136
|
+
`completed`.
|
|
137
|
+
|
|
138
|
+
The big difference: FractalPay's intent is **multi-chain native** (the
|
|
139
|
+
customer chooses where to pay from) and **HMAC-signed** (the recipient address
|
|
140
|
+
can't be tampered with in transit).
|
|
141
|
+
|
|
142
|
+
## License
|
|
143
|
+
|
|
144
|
+
- This SDK: Apache-2.0 (see `LICENSE`)
|
|
145
|
+
- The FractalPay API and protocol: same license, plus open-source server
|
|
146
|
+
implementation at github.com/johnInarti/FRACTAL-AI
|
|
147
|
+
|
|
148
|
+
## Resources
|
|
149
|
+
|
|
150
|
+
- [API reference](https://fractalai.net.co/docs/fractalpay)
|
|
151
|
+
- [VAID-1 spec](https://github.com/johnInarti/FRACTAL-AI/blob/main/VAID_1_SPEC.md)
|
|
152
|
+
(the attestation standard every payment carries)
|
|
153
|
+
- [TypeScript SDK](https://www.npmjs.com/package/@fractalai/pay) (coming)
|
|
154
|
+
- [Issues](https://github.com/johnInarti/FRACTAL-AI/issues) (label `fractalpay`)
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""
|
|
2
|
+
FractalPay SDK quickstart — create a payment intent in under 20 lines.
|
|
3
|
+
|
|
4
|
+
Run with:
|
|
5
|
+
pip install -e ..
|
|
6
|
+
python quickstart.py
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from fractalpay import FractalPay
|
|
10
|
+
|
|
11
|
+
# Point at staging or your own deployment with base_url=...; defaults to prod.
|
|
12
|
+
fp = FractalPay()
|
|
13
|
+
|
|
14
|
+
# Create a $10 USDC payment intent settling on Base.
|
|
15
|
+
intent = fp.intents.create(
|
|
16
|
+
amount="10.00",
|
|
17
|
+
currency="USDC",
|
|
18
|
+
recipient_address="0xC13789e82661635d9Cea38a53A0390CF9939ef4f",
|
|
19
|
+
recipient_chain="base",
|
|
20
|
+
merchant_name="FractalPay SDK Demo",
|
|
21
|
+
description="Pro plan — monthly",
|
|
22
|
+
metadata={"order_id": "DEMO-001"},
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
print(f"intent id: {intent.id}")
|
|
26
|
+
print(f"status: {intent.status}")
|
|
27
|
+
print(f"amount: {intent.amount} {intent.currency}")
|
|
28
|
+
print(f"recipient chain: {intent.recipient_chain}")
|
|
29
|
+
print(f"suggested chains: {', '.join(intent.suggested_chains)}")
|
|
30
|
+
print(f"expires (unix ms): {intent.expires_at}")
|
|
31
|
+
print()
|
|
32
|
+
print(f"Send your customer to:")
|
|
33
|
+
print(f" {intent.web_url}")
|
|
34
|
+
print()
|
|
35
|
+
print(f"Mobile deep link:")
|
|
36
|
+
print(f" {intent.qr_url}")
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Example merchant webhook handler.
|
|
3
|
+
|
|
4
|
+
Flask-style — adapt to FastAPI / Django / whatever framework you use. The
|
|
5
|
+
critical part is calling `verify_webhook` with the RAW request body (not a
|
|
6
|
+
re-serialized JSON dict) and the signature header.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import os
|
|
10
|
+
|
|
11
|
+
from flask import Flask, request
|
|
12
|
+
|
|
13
|
+
from fractalpay import SignatureVerificationError, verify_webhook
|
|
14
|
+
|
|
15
|
+
app = Flask(__name__)
|
|
16
|
+
|
|
17
|
+
# The merchant's webhook signing secret. Get this from your FractalPay dashboard.
|
|
18
|
+
WEBHOOK_SECRET = os.environ["FRACTALPAY_WEBHOOK_SECRET"]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@app.post("/webhooks/fractalpay")
|
|
22
|
+
def handle_fractalpay_webhook():
|
|
23
|
+
# IMPORTANT: pass the raw body bytes (not request.json) — re-serializing
|
|
24
|
+
# changes the bytes and invalidates the signature.
|
|
25
|
+
try:
|
|
26
|
+
event = verify_webhook(
|
|
27
|
+
payload=request.get_data(),
|
|
28
|
+
signature=request.headers.get("X-FractalPay-Signature"),
|
|
29
|
+
secret=WEBHOOK_SECRET,
|
|
30
|
+
)
|
|
31
|
+
except SignatureVerificationError:
|
|
32
|
+
# Hostile or misconfigured caller — reject without revealing why.
|
|
33
|
+
return "", 401
|
|
34
|
+
|
|
35
|
+
if event.type == "payment.completed":
|
|
36
|
+
order_id = (event.intent.metadata or {}).get("order_id")
|
|
37
|
+
if order_id:
|
|
38
|
+
mark_order_paid(
|
|
39
|
+
order_id,
|
|
40
|
+
amount=event.intent.settled_amount or event.intent.amount,
|
|
41
|
+
currency=event.intent.currency,
|
|
42
|
+
tx_hash=event.intent.tx_hash,
|
|
43
|
+
)
|
|
44
|
+
elif event.type == "payment.failed":
|
|
45
|
+
order_id = (event.intent.metadata or {}).get("order_id")
|
|
46
|
+
if order_id:
|
|
47
|
+
mark_order_failed(order_id)
|
|
48
|
+
|
|
49
|
+
# Always return 2xx to acknowledge — FractalPay won't retry on 2xx.
|
|
50
|
+
return "", 200
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def mark_order_paid(order_id: str, *, amount: str, currency: str, tx_hash: str | None) -> None:
|
|
54
|
+
"""Stub — replace with your real order-fulfillment logic."""
|
|
55
|
+
print(f"[paid] order={order_id} amount={amount} {currency} tx={tx_hash}")
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def mark_order_failed(order_id: str) -> None:
|
|
59
|
+
"""Stub — replace with your real failure-handling logic."""
|
|
60
|
+
print(f"[failed] order={order_id}")
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
if __name__ == "__main__":
|
|
64
|
+
app.run(port=5000, debug=False)
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "fractalpay"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "FractalPay AaaS — Python SDK. The post-quantum, AI-verified, multi-chain payment gateway as a service. Stripe-shaped API on top of 9 blockchains (8 EVM + Stellar) with VAID-1 cryptographic attestations."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { text = "Apache-2.0" }
|
|
11
|
+
requires-python = ">=3.9"
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "FractalAI Foundation", email = "developers@fractalai.net.co" },
|
|
14
|
+
]
|
|
15
|
+
keywords = [
|
|
16
|
+
"payments", "stripe-alternative", "crypto-payments", "stablecoin",
|
|
17
|
+
"post-quantum", "multi-chain", "stellar", "base", "ethereum",
|
|
18
|
+
"fractalai", "fractalpay", "aaas",
|
|
19
|
+
]
|
|
20
|
+
classifiers = [
|
|
21
|
+
"Development Status :: 4 - Beta",
|
|
22
|
+
"Intended Audience :: Developers",
|
|
23
|
+
"Intended Audience :: Financial and Insurance Industry",
|
|
24
|
+
"License :: OSI Approved :: Apache Software License",
|
|
25
|
+
"Operating System :: OS Independent",
|
|
26
|
+
"Programming Language :: Python :: 3",
|
|
27
|
+
"Programming Language :: Python :: 3.9",
|
|
28
|
+
"Programming Language :: Python :: 3.10",
|
|
29
|
+
"Programming Language :: Python :: 3.11",
|
|
30
|
+
"Programming Language :: Python :: 3.12",
|
|
31
|
+
"Topic :: Office/Business :: Financial",
|
|
32
|
+
"Topic :: Security :: Cryptography",
|
|
33
|
+
]
|
|
34
|
+
dependencies = [
|
|
35
|
+
"httpx>=0.25.0",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
[project.optional-dependencies]
|
|
39
|
+
dev = [
|
|
40
|
+
"pytest>=7.4",
|
|
41
|
+
"pytest-cov>=4.1",
|
|
42
|
+
"ruff>=0.1.0",
|
|
43
|
+
"respx>=0.20",
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
[project.urls]
|
|
47
|
+
Homepage = "https://fractalai.net.co/standards/fractalpay"
|
|
48
|
+
Documentation = "https://fractalai.net.co/docs/fractalpay"
|
|
49
|
+
Repository = "https://github.com/johnInarti/FRACTAL-AI"
|
|
50
|
+
Issues = "https://github.com/johnInarti/FRACTAL-AI/issues"
|
|
51
|
+
Changelog = "https://github.com/johnInarti/FRACTAL-AI/blob/main/sdk/python-fractalpay/CHANGELOG.md"
|
|
52
|
+
|
|
53
|
+
[tool.hatch.build.targets.wheel]
|
|
54
|
+
packages = ["src/fractalpay"]
|
|
55
|
+
|
|
56
|
+
[tool.pytest.ini_options]
|
|
57
|
+
testpaths = ["tests"]
|
|
58
|
+
python_files = ["test_*.py"]
|
|
59
|
+
|
|
60
|
+
[tool.ruff]
|
|
61
|
+
line-length = 100
|
|
62
|
+
target-version = "py39"
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"""
|
|
2
|
+
fractalpay — Python SDK for FractalPay AaaS.
|
|
3
|
+
|
|
4
|
+
Quick reference for the public API:
|
|
5
|
+
|
|
6
|
+
Client:
|
|
7
|
+
FractalPay — main entry point
|
|
8
|
+
FractalPay.intents — payment intent operations
|
|
9
|
+
|
|
10
|
+
Webhook:
|
|
11
|
+
verify_webhook — validate + parse a merchant webhook
|
|
12
|
+
WebhookEvent — typed event from verify_webhook
|
|
13
|
+
|
|
14
|
+
Types:
|
|
15
|
+
PaymentIntent — a created/retrieved payment intent
|
|
16
|
+
PaymentChain — Literal of supported chains
|
|
17
|
+
PaymentCurrency — Literal of supported tokens
|
|
18
|
+
PaymentStatus — Literal of intent lifecycle states
|
|
19
|
+
|
|
20
|
+
Errors:
|
|
21
|
+
FractalPayError — base class
|
|
22
|
+
AuthenticationError — bad/missing credentials
|
|
23
|
+
InvalidRequestError — 4xx from API
|
|
24
|
+
RateLimitError — 429
|
|
25
|
+
APIConnectionError — network problem
|
|
26
|
+
APIError — 5xx
|
|
27
|
+
SignatureVerificationError — webhook verification failed
|
|
28
|
+
|
|
29
|
+
API docs:
|
|
30
|
+
https://fractalai.net.co/docs/fractalpay
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
from fractalpay.client import FractalPay
|
|
34
|
+
from fractalpay.errors import (
|
|
35
|
+
APIConnectionError,
|
|
36
|
+
APIError,
|
|
37
|
+
AuthenticationError,
|
|
38
|
+
FractalPayError,
|
|
39
|
+
InvalidRequestError,
|
|
40
|
+
RateLimitError,
|
|
41
|
+
SignatureVerificationError,
|
|
42
|
+
)
|
|
43
|
+
from fractalpay.types import (
|
|
44
|
+
PaymentChain,
|
|
45
|
+
PaymentCurrency,
|
|
46
|
+
PaymentIntent,
|
|
47
|
+
PaymentStatus,
|
|
48
|
+
)
|
|
49
|
+
from fractalpay.webhook import WebhookEvent, verify_webhook
|
|
50
|
+
|
|
51
|
+
__version__ = "1.0.0"
|
|
52
|
+
|
|
53
|
+
__all__ = [
|
|
54
|
+
# Client
|
|
55
|
+
"FractalPay",
|
|
56
|
+
# Webhook
|
|
57
|
+
"verify_webhook",
|
|
58
|
+
"WebhookEvent",
|
|
59
|
+
# Types
|
|
60
|
+
"PaymentIntent",
|
|
61
|
+
"PaymentChain",
|
|
62
|
+
"PaymentCurrency",
|
|
63
|
+
"PaymentStatus",
|
|
64
|
+
# Errors
|
|
65
|
+
"FractalPayError",
|
|
66
|
+
"AuthenticationError",
|
|
67
|
+
"InvalidRequestError",
|
|
68
|
+
"RateLimitError",
|
|
69
|
+
"APIConnectionError",
|
|
70
|
+
"APIError",
|
|
71
|
+
"SignatureVerificationError",
|
|
72
|
+
"__version__",
|
|
73
|
+
]
|