Skip to main content

Typed Python client for the Revolut Merchant API (sync + async).

Project description

revolut-merchant-py

CI PyPI Python License: MIT

A typed Python client for the Revolut Merchant API — online payments (orders, payments, refunds, customers) and webhooks — with both synchronous and asynchronous clients.

Status: early development (v0.1.0). See the ROADMAP.

Features

  • Sync (RevolutMerchantClient) and async (AsyncRevolutMerchantClient) clients
  • Typed request/response models (Pydantic v2)
  • Amounts handled safely in minor units (integers)
  • Sandbox and production environments
  • Webhook signature verification (HMAC-SHA256)
  • mypy --strict clean, fully type-annotated

Installation

pip install revolut-merchant-py

The distribution is published as revolut-merchant-py; the import package is revolut:

import revolut

Latest from source:

pip install git+https://github.com/robertruben98/revolut-merchant-py.git

Quick start

from revolut import RevolutMerchantClient

with RevolutMerchantClient(secret_key="sk_...", environment="sandbox") as client:
    order = client.orders.create(amount=1000, currency="GBP")  # 1000 = £10.00
    # `token` is the public id used by the Web SDK / hosted checkout page.
    print(order.id, order.state, order.public_token)

Async — the same API, awaited:

import asyncio
from revolut import AsyncRevolutMerchantClient

async def main():
    async with AsyncRevolutMerchantClient(secret_key="sk_...") as client:
        order = await client.orders.create(amount=1000, currency="GBP")
        print(order.id)

asyncio.run(main())

Working with money

Amounts are integers in minor units (e.g. 1099 = £10.99). The Money helper converts to/from major units, accounting for zero- and three-decimal currencies:

from revolut import Money

Money.from_major("10.99", "GBP").amount   # 1099
Money.from_major("1000", "JPY").amount    # 1000  (JPY has no minor unit)
Money(1234, "BHD").to_major()             # Decimal("1.234")

Orders

order = client.orders.create(
    amount=5000, currency="EUR",
    capture_mode="manual",                      # authorise now, capture later
    merchant_order_data={"reference": "INV-42"},
    idempotency_key="INV-42",                   # safe to retry
)
client.orders.capture(order.id, amount=5000)    # capture authorised funds
client.orders.cancel(order.id)                  # or cancel before capture
client.orders.list(limit=50, state="completed") # paginated listing

Refunds

# Refunds create a new order of type "refund" linked to the original.
refund = client.refunds.create(order.id, amount=2500, idempotency_key="INV-42-r1")
assert refund.type == "refund"

Webhooks

Verify the authenticity of incoming webhook deliveries (HMAC-SHA256, replay protected). Pass the raw request body — do not re-serialize it:

from revolut import verify_signature, SignatureVerificationError

try:
    event = verify_signature(
        raw_body=request.body,                                  # bytes or str
        signature_header=request.headers["Revolut-Signature"],
        timestamp_header=request.headers["Revolut-Request-Timestamp"],
        signing_secret="wsk_...",                               # from webhook creation
    )
    print(event.event, event.order_id)
except SignatureVerificationError:
    ...  # reject the request (401)

Manage webhook endpoints via the API:

wh = client.webhooks.create(url="https://example.com/cb", events=["ORDER_COMPLETED"])
print(wh.signing_secret)  # store this to verify future deliveries

Error handling

Non-2xx responses raise a typed subclass of RevolutError:

from revolut import NotFoundError, RateLimitError, RevolutError

try:
    client.orders.retrieve("missing")
except NotFoundError as exc:
    print(exc.status_code, exc.code, exc.message)
except RateLimitError as exc:
    print("retry after", exc.retry_after)
except RevolutError:
    ...  # base class for everything this library raises

Transient failures (429, 5xx) are retried automatically with exponential backoff; tune via RetryConfig.

Development

pip install -e ".[dev]"
ruff check . && ruff format --check .
mypy
pytest

License

pip install -e ".[dev]"
ruff check . && ruff format --check .
mypy
pytest

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

revolut_merchant_py-0.1.0a1.tar.gz (24.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

revolut_merchant_py-0.1.0a1-py3-none-any.whl (25.0 kB view details)

Uploaded Python 3

File details

Details for the file revolut_merchant_py-0.1.0a1.tar.gz.

File metadata

  • Download URL: revolut_merchant_py-0.1.0a1.tar.gz
  • Upload date:
  • Size: 24.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.2

File hashes

Hashes for revolut_merchant_py-0.1.0a1.tar.gz
Algorithm Hash digest
SHA256 6af5f460bf5a0708a8bf64348e2f0e440bb8c96885ae21a0f2387e73fdc70fbb
MD5 01bc44a2f561f9f6ae4faf1ecffc7b71
BLAKE2b-256 8c2abc06e764e0fe25b25cca5e6ff4dd2e8df867e313e94d9c64511cf28ebf8e

See more details on using hashes here.

File details

Details for the file revolut_merchant_py-0.1.0a1-py3-none-any.whl.

File metadata

File hashes

Hashes for revolut_merchant_py-0.1.0a1-py3-none-any.whl
Algorithm Hash digest
SHA256 04616f5140a8aa9ceb25e6893c843f45090b7bc807da61d09005a846a021f648
MD5 e1a0004e2d7412c44e1ab3f8d36844d2
BLAKE2b-256 6706f43913641ab65b19676e510214f5aa69c1a8310781c2f136066a5157e853

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page