A typed sync and async Python client for SHKeeper, the self-hosted crypto payment processor.
- Covers the full SHKeeper v1 API: invoices, payouts, multipayouts, task status, addresses, transactions, wallet decryption and metrics
SHKeeper(sync) andAsyncSHKeeper(asyncio) with the same methods- Typed dataclass responses with
Decimalamounts - Webhook signature verification (HMAC-SHA256) and notification parsing
- Clear exception hierarchy
pip install git+https://github.com/botsgalaxy/shkeeper-python.gitRequires Python 3.9+.
from shkeeper import SHKeeper
with SHKeeper(
"https://pay.example.com",
api_key="your-api-key",
callback_url="https://shop.example.com/shkeeper/webhook",
) as shkeeper:
for crypto in shkeeper.get_cryptos():
print(crypto.name, crypto.display_name)
invoice = shkeeper.create_invoice("ETH-USDT", external_id="order_123", amount="100")
print(invoice.wallet, invoice.amount, invoice.exchange_rate)Async:
from shkeeper import AsyncSHKeeper
async with AsyncSHKeeper("https://pay.example.com", api_key="your-api-key") as shkeeper:
invoice = await shkeeper.create_invoice(
"BTC", "order_123", "100", callback_url="https://shop.example.com/webhook"
)base_url is the root of your SHKeeper instance. A trailing /api/v1 is accepted and stripped.
SHKeeper uses two kinds of credentials:
| Credential | Client argument | Used by |
|---|---|---|
| API key (Wallets → Manage → API key) | api_key |
invoices, addresses, transactions, payment info, wallet decryption |
| SHKeeper UI login (HTTP Basic) | username, password |
payouts, multipayouts, task status |
| Metrics login | metrics_username, metrics_password |
get_metrics() |
get_cryptos() needs no credentials.
| Method | Endpoint | Returns |
|---|---|---|
get_cryptos() |
GET /api/v1/crypto |
list[CryptoCurrency] |
create_invoice(crypto, external_id, amount, *, callback_url=None, fiat="USD") |
POST /api/v1/{crypto}/payment_request |
Invoice |
get_invoices(external_id) |
GET /api/v1/invoices/{external_id} |
list[InvoiceInfo] |
get_addresses(crypto) |
GET /api/v1/{crypto}/addresses |
list[str] |
get_transactions(crypto, addr) |
GET /api/v1/transactions/{crypto}/{addr} |
list[Transaction] |
get_payment_info(txid, external_id) |
GET /api/v1/tx-info/{txid}/{external_id} |
PaymentInfo |
decrypt_wallets(key) |
POST /api/v1/decryption-key |
bool |
create_payout(crypto, destination, amount, fee, *, callback_url=None, external_id=None) |
POST /api/v1/{crypto}/payout |
PayoutTask |
create_multipayout(crypto, recipients) |
POST /api/v1/{crypto}/multipayout |
PayoutTask |
get_task(crypto, task_id) |
GET /api/v1/{crypto}/task/{task_id} |
TaskStatus |
wait_for_task(crypto, task_id, *, interval=5, timeout=300) |
polls get_task |
TaskStatus |
get_metrics() |
GET /metrics |
str (Prometheus format) |
Calling create_invoice again with the same external_id updates the existing invoice. Amounts can be str, int, float or Decimal; they are sent as plain decimal strings.
from shkeeper import SHKeeper, PayoutRecipient
shkeeper = SHKeeper("https://pay.example.com", username="admin", password="secret")
task = shkeeper.create_payout("BTC", "bc1q...", amount="0.01", fee=12)
status = shkeeper.wait_for_task("BTC", task.task_id)
if status.is_success:
for result in status.result:
print(result.dest, result.txids)
task = shkeeper.create_multipayout(
"ETH-USDT",
[PayoutRecipient("0xAAA...", "100"), PayoutRecipient("0xBBB...", "9.99")],
)fee is required by SHKeeper. It is sat/vByte for BTC, sat/Byte for LTC and DOGE, 1–4 priority for XMR, and ignored for other coins. Multipayout is not supported for BTC, LTC, DOGE and XMR. For XRP pass dest_tag on PayoutRecipient or use an X-address.
SHKeeper POSTs a JSON notification to your callback_url when an invoice receives a payment, and when a payout completes (if payout callbacks are enabled). Requests are signed with your API key. Respond with HTTP 202; anything else makes SHKeeper retry every 60 seconds.
from flask import Flask, request
from shkeeper import PaymentNotification, WebhookVerificationError, webhooks
app = Flask(__name__)
@app.post("/shkeeper/webhook")
def shkeeper_webhook():
try:
event = webhooks.construct_event("your-api-key", request.get_data(), request.headers)
except WebhookVerificationError:
return "", 400
if isinstance(event, PaymentNotification):
if event.paid:
mark_order_paid(event.external_id, event.balance_fiat)
else:
mark_payout_done(event.external_id, event.tx_hash)
return "", 202construct_event checks X-Shkeeper-Signature and X-Shkeeper-Timestamp (rejecting timestamps older than 5 minutes by default, tolerance=None disables this) and returns a PaymentNotification or PayoutNotification. Always pass the raw request body, not re-serialized JSON. verify_signature and parse_notification are available separately.
PaymentNotification.status is PARTIAL, PAID or OVERPAID, and trigger_transaction gives the transaction that caused the notification.
All exceptions inherit from SHKeeperError:
| Exception | Raised when |
|---|---|
AuthenticationError |
bad or missing API key / credentials (400, 401, 403) |
NotFoundError |
404 |
ServerError |
5xx |
APIError |
any other error response, including {"status": "error"} bodies |
NetworkError |
connection failures and timeouts |
WebhookVerificationError |
invalid webhook signature, timestamp or body |
API errors carry message, status_code and the raw body.
import httpx
from shkeeper import SHKeeper
shkeeper = SHKeeper(
"https://pay.example.com",
api_key="...",
timeout=30,
http_client=httpx.Client(proxy="http://proxy:8080"),
)Pass your own httpx.Client / httpx.AsyncClient to control proxies, retries or TLS. A client you pass in is not closed by SHKeeper.
pip install -e ".[dev]"
pytestMIT. Author: BotsGalaxy · Telegram primeakash