Skip to main content

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"

Pagination

List endpoints accept paging params, or use iter() to stream across all pages transparently (sync yields, async is an async iterator):

for order in client.orders.iter(state="completed", limit=100):
    ...

for customer in client.customers.iter(max_items=500):
    ...

# async
async for order in async_client.orders.iter():
    ...

Saved payment methods

Tokenized methods live under a customer; charge one via payments.pay:

methods = client.payment_methods.list(customer_id, only_merchant=True)
client.payment_methods.retrieve(customer_id, methods[0].id)
client.payment_methods.delete(customer_id, methods[0].id)

# charge a saved method against an existing order
client.payments.pay(order.id, payment_method_id=methods[0].id)

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

More resources

The client also exposes subscriptions, payouts and locations, each with the same sync/async parity:

client.subscriptions.list()
client.payouts.retrieve("po_...")
client.locations.create(name="Web", type="online")

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 (including optional jitter). Enable request tracing with the revolut logger:

import logging
logging.getLogger("revolut").setLevel(logging.DEBUG)

from revolut import RetryConfig
client = RevolutMerchantClient(secret_key="sk_...", retry=RetryConfig(max_retries=4, jitter=0.3))

Stability and versioning

This project follows SemVer. From 1.0.0 the public API is the set of names exported from the top-level revolut package (i.e. revolut.__all__) plus the documented resource attributes on the clients.

  • Patch (1.0.x): bug fixes, no API changes.
  • Minor (1.x.0): backwards-compatible additions (new resources, fields, optional arguments).
  • Major (x.0.0): backwards-incompatible changes.

Anything under a leading underscore (e.g. revolut._http) is internal and may change at any time. Deprecations are announced in the CHANGELOG, keep working for at least one minor release, and emit DeprecationWarning before removal.

Development

pip install -e ".[dev]"
ruff check . && ruff format --check .
mypy
pytest                       # hermetic unit tests (mocked HTTP)

Live sandbox integration tests are skipped unless a sandbox key is provided:

REVOLUT_SECRET_KEY=sk_... pytest tests/integration -v

License

MIT

MIT

Metadata

Release files for revolut-merchant-py 1.0.1

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

Source distribution (sdist)

Source distribution for revolut-merchant-py 1.0.1
File Size Uploaded
revolut_merchant_py-1.0.1.tar.gz 33.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for revolut-merchant-py 1.0.1
File Interpreter ABI Platform
revolut_merchant_py-1.0.1-py3-none-any.whl Python 3 none any Details

Total release size: 66.0 kB

Release files / revolut_merchant_py-1.0.1.tar.gz

Download URL revolut_merchant_py-1.0.1.tar.gz
Size 33.3 kB
Tags Source
SHA-256 checksum
How to use checksums
0590402c6158733d4726382aafa91babdcf93e9862835ff6a0211e635eb1893e
BLAKE2b-256 checksum
How to use checksums
c92813dd5799daaef3b0095f159099e926933fe70dde37530da4ea1e074e76fe
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

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 Jun 22, 2026.

Transparency log

Release files / revolut_merchant_py-1.0.1-py3-none-any.whl

Download URL revolut_merchant_py-1.0.1-py3-none-any.whl
Size 32.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
8aa9d9c13e4081b33de4ba8ab22a246cd77fbd4a75ff6a279cd6356c02ffadb6
BLAKE2b-256 checksum
How to use checksums
1dacd6fc8936019c93eb9ecff6ddaf4dd3448170e9db7aeeeab7dae4911f0d04
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

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 Jun 22, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

1.0.1 This release

2 release files

1.0.0

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