Skip to main content

cryptunnel

Python SDK for Cryptunnel - accept crypto payments straight into your own wallets. Async-first for bots on aiogram, with a blocking mirror for scripts.

pip install cryptunnel

httpx is the only dependency. Python 3.10+.

Create a sandbox payment

from cryptunnel import CryptunnelSync

cryptunnel = CryptunnelSync("<merchant id>", "<api key>", sandbox=True)

print(cryptunnel.get_merchant())  # your credentials work if this prints your merchant

payment = cryptunnel.create_widget_payment(
    amount=10,
    currency="USD",
    external_id="order-1",
    success_url="https://example.com/thanks",
)
print(payment["url"])  # send the buyer here

sandbox=True sets is_test on every payment it creates: the payer is offered testnet currencies only, and the payment is excluded from your stats and fees. It is the same host and the same key - the sandbox is a flag, not a second account.

Verify a webhook

from flask import Flask, request

from cryptunnel import verify_webhook

app = Flask(__name__)
WEBHOOK_SECRET = "<whsec_...>"

@app.post("/cryptunnel")
def callback():
    if not verify_webhook(WEBHOOK_SECRET, request.headers, request.get_data()):
        return "", 401
    payment = request.get_json()
    if payment["status"] in ("confirmed", "confirmed_manual"):
        deliver(payment["external_id"])  # deduplicate on (id, status): retries repeat the same pair
    return "", 200

Pass the raw body bytes as received - parsing and re-serialising the JSON changes the signature. A failed delivery is retried 60 times, once a minute, for one hour, each attempt re-signed with a fresh timestamp over the same body.

Async

import asyncio

from cryptunnel import Cryptunnel

async def main():
    async with Cryptunnel("<merchant id>", "<api key>", sandbox=True) as cryptunnel:
        currencies = await cryptunnel.list_currencies()
        payment = await cryptunnel.create_h2h_payment(10, "USD", "order-2", currencies[0]["code"])
        print(payment["wallet_address"], payment["amount"], payment["currency"])

asyncio.run(main())

The whole surface

Method Call
create_widget_payment(amount, currency, external_id, ...) POST /v1/payments/widget
create_h2h_payment(amount, currency, external_id, target_currency, ...) POST /v1/payments/h2h
get_payment(payment_id) GET /v1/payments/{id}
list_payments(limit, offset) GET /v1/payments
list_currencies() GET /v1/currencies
get_merchant() GET /v1/merchants
verify_webhook(secret, headers, raw_body) local, no request
wait_for_payment(payment_id) polls GET /v1/payments/{id}

Every method exists on both Cryptunnel (await it) and CryptunnelSync (call it).

Payment creation is idempotent on external_id: a retry after a network timeout returns the payment you already created instead of a second one. Repeating an external_id with a different amount or currency is rejected with PAYMENT_ALREADY_EXISTS.

get_payment returns two shapes, and the status tells them apart: a created or expired payment carries no amount, currency or wallet_address, because nobody has picked a currency for it yet. Read them with payment.get("amount") rather than payment["amount"] unless you already know the status.

wait_for_payment is for scripts and development - it polls every 5 seconds, backing off to 30, and raises PaymentTimeoutError after 30 minutes. In production the webhook is the guarantee: a buyer who closes the page still produces a callback.

Errors

from cryptunnel import AuthenticationError, RateLimitError, ValidationError

try:
    cryptunnel.create_h2h_payment(10, "USD", "order-3", "DOGE")
except ValidationError as error:
    print(error.code)  # WALLET_NOT_FOUND - you have no active DOGE wallet
except AuthenticationError:
    print("check the merchant id and the api key")
except RateLimitError as error:
    print(error.retry_after)  # seconds to wait; None when the API sends no Retry-After header

CryptunnelError is the base; AuthenticationError (401), NotFoundError (404), ValidationError (400), RateLimitError (429) and ApiError (5xx and transport failures) derive from it. The raw API code is always on the exception.

MIT licensed.

Metadata

Release files for cryptunnel 1.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for cryptunnel 1.0.0
File Size Uploaded
cryptunnel-1.0.0.tar.gz 9.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for cryptunnel 1.0.0
File Interpreter ABI Platform
cryptunnel-1.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 18.8 kB

Release files / cryptunnel-1.0.0.tar.gz

Download URL cryptunnel-1.0.0.tar.gz
Size 9.6 kB
Tags Source
SHA-256 checksum
How to use checksums
7351ad28ccf91e4bd93d6aec1b679c99596e1ccadbed6ac8f101902d9eb0c06d
BLAKE2b-256 checksum
How to use checksums
f0de92afb2a34d1f19e7333c64731080fe01f7c605afec0664540edc1055f18b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 3, 2026.

Transparency log

Release files / cryptunnel-1.0.0-py3-none-any.whl

Download URL cryptunnel-1.0.0-py3-none-any.whl
Size 9.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
7dedd53c0d803373eb843126a1e8eb764258461f42a8ed5235d600c83eeb0db7
BLAKE2b-256 checksum
How to use checksums
b519736c54bb624070b06f9dd2e7bfcc1596d529692eec7c0a10092fb184c311
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 3, 2026.

Transparency log

Release history Release notifications | RSS feed

1.1.0

2 release files

This release

1.0.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page