134 lines
5.8 KiB
Markdown
134 lines
5.8 KiB
Markdown
# cardano-checkout
|
|
|
|
Python SDK for merchant-side Cardano payments + NFT certificate-of-authenticity minting.
|
|
|
|
**Zero-custody by design:** the merchant provides a wallet xpub. The SDK derives
|
|
unique receive addresses per invoice, polls the chain for payment, and optionally
|
|
mints a CIP-25 NFT cert on confirmation. The platform never holds or moves funds.
|
|
|
|
Extracted from [the host app](https://git.sulkta.com/example/host-app)'s
|
|
`services/cardano_*.py` modules (2,400+ lines of production code running on the
|
|
Cardano mainnet) and packaged for reuse across the Sulkta Coop product family.
|
|
|
|
## Status
|
|
|
|
**v0.1.0-dev — alpha extraction.** Pure modules lifted verbatim from the host app.
|
|
DB-coupled modules (monitor, scheduler) ship with a `TODO: refactor to Store
|
|
protocol` marker — they work as-is when paired with the host app's SQLAlchemy models
|
|
but will be refactored to the generic `InvoiceStore` Protocol in v0.2.
|
|
|
|
| Module | Status | Notes |
|
|
|---|---|---|
|
|
| `addresses` | ✅ stable | CIP-1852 HD derivation; pure pycardano |
|
|
| `oracles` | ✅ stable | ADA/USD price via Koios with 5-min cache |
|
|
| `invoice` + `store` | ✅ new | Framework-agnostic invoice + persistence Protocol |
|
|
| `mint` | ⏳ stub | CIP-25 v2 metadata builder works; tx submission in v0.2 |
|
|
| `ipfs` | ✅ working | kubo HTTP API client w/ optional mirror-pin |
|
|
| `monitor` | 🟡 SQLAlchemy-coupled | v0.2 target: refactor around `InvoiceStore` |
|
|
| `scheduler` | 🟡 SQLAlchemy-coupled | v0.2 target: same |
|
|
| `txbuild` | ❌ v0.2 | Full PyCardano tx construction via Ogmios |
|
|
|
|
## Design
|
|
|
|
```
|
|
┌────────────────────────────────────────────────────────┐
|
|
│ Merchant App │
|
|
│ (the host app / example-studio / your-product) │
|
|
└──────────────┬───────────────────────┬─────────────────┘
|
|
│ │
|
|
uses │ implements │ imports
|
|
▼ ▼
|
|
┌──────────────┐ ┌────────────────────────┐
|
|
│ InvoiceStore │ ◄────── │ cardano_checkout SDK │
|
|
│ (your DB) │ │ │
|
|
└──────────────┘ │ addresses ← pure │
|
|
│ oracles ← pure │
|
|
│ invoice ← dataclass │
|
|
│ monitor ← polls chain │
|
|
│ scheduler ← bg loop │
|
|
│ mint ← NFT cert │
|
|
│ ipfs ← upload │
|
|
│ txbuild ← PyCardano wrappers │
|
|
└────────────────────────┘
|
|
│
|
|
talks to │
|
|
▼
|
|
┌────────────────────────┐
|
|
│ Koios + Ogmios + kubo │
|
|
└────────────────────────┘
|
|
```
|
|
|
|
The merchant app provides:
|
|
1. A wallet xpub (account-level extended public key).
|
|
2. An `InvoiceStore` implementation (SQLAlchemy, Postgres, SQLite, in-memory — whatever).
|
|
|
|
The SDK provides:
|
|
1. Address derivation from the xpub.
|
|
2. Per-invoice payment monitoring against Koios.
|
|
3. ADA ↔ USD price conversion.
|
|
4. CIP-25 v2 NFT cert minting (v0.2).
|
|
5. IPFS upload + pinning for NFT image metadata.
|
|
|
|
## Quick start
|
|
|
|
```python
|
|
import asyncio
|
|
from cardano_checkout import addresses, oracles
|
|
|
|
# Derive a receive address for invoice #42
|
|
addr = addresses.derive_address(
|
|
xpub_hex="<your wallet xpub>",
|
|
index=42,
|
|
network="mainnet",
|
|
)
|
|
|
|
# Convert a USD price to lovelace at current market
|
|
async def main() -> None:
|
|
lovelace = await oracles.convert_usd_to_lovelace(99.00)
|
|
ada = lovelace / 1_000_000
|
|
print(f"Customer owes {ada:.4f} ADA for $99")
|
|
|
|
asyncio.run(main())
|
|
```
|
|
|
|
## IPFS: bake-then-mirror pattern
|
|
|
|
The SDK's `IPFSClient` expects a local kubo daemon (typically in the same
|
|
Docker image as the web app) for upload and primary pin, and takes an
|
|
optional list of mirror endpoints to `pin add` the CID on a second node
|
|
for archival redundancy.
|
|
|
|
Typical example-studio deployment:
|
|
|
|
```python
|
|
from cardano_checkout import ipfs
|
|
|
|
client = ipfs.IPFSClient(
|
|
api_url="http://127.0.0.1:5001", # local kubo in the same container
|
|
mirror_api_urls=["http://mirror-node.example:5001"], # the cold host's kubo over the LAN/VPN
|
|
)
|
|
|
|
cid = await client.add(photo_bytes, filename="order-0001.jpg")
|
|
# Image now served by the hot host (low latency) AND pinned on the cold host (durability)
|
|
```
|
|
|
|
## NFT cert-of-authenticity design
|
|
|
|
One minting policy per merchant studio. Policy is a native script (no Plutus
|
|
required), optionally time-locked to make "no more editions after X" a
|
|
cryptographically verifiable claim.
|
|
|
|
CIP-25 v2 metadata. Single NFT per order. Policy skey never leaves the custody
|
|
host (the cold host in Sulkta's pattern). The SDK builds the metadata envelope + tx;
|
|
external signer does the signature.
|
|
|
|
## Installation
|
|
|
|
```
|
|
pip install 'cardano-checkout[sqlalchemy]' # if you're using SQLAlchemy
|
|
pip install cardano-checkout # core only
|
|
```
|
|
|
|
## License
|
|
|
|
Apache-2.0 — matches upstream Cardano tooling.
|