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.0a2.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.0a2-py3-none-any.whl (25.0 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: revolut_merchant_py-0.1.0a2.tar.gz
  • Upload date:
  • Size: 24.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for revolut_merchant_py-0.1.0a2.tar.gz
Algorithm Hash digest
SHA256 5f7d959fa87ccead050019cc377eae4dd60ff3cfdc49ca2d392cb88149d1e282
MD5 f896b299e913dbcefbcd1a7637585ce0
BLAKE2b-256 87e6098b2bfaba10c1d5a4a6bffa14c7a71e29905158eae321874ae962ce6f54

See more details on using hashes here.

Provenance

The following attestation bundles were made for revolut_merchant_py-0.1.0a2.tar.gz:

Publisher: publish.yml on robertruben98/revolut-merchant-py

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

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

File metadata

File hashes

Hashes for revolut_merchant_py-0.1.0a2-py3-none-any.whl
Algorithm Hash digest
SHA256 e543deb14c69c4874d85ec0586bb4bfed4e7f59304cd71f2a6092e82bfea4c4d
MD5 3a8a6732b0487e702504e1be06fc6bd1
BLAKE2b-256 0fdb1f845ccf1c752ec2c3ec3916759c05fd08aea25cb0f7452191e9c014729c

See more details on using hashes here.

Provenance

The following attestation bundles were made for revolut_merchant_py-0.1.0a2-py3-none-any.whl:

Publisher: publish.yml on robertruben98/revolut-merchant-py

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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