Python: tuppence
The server-side SDK for Python 3.10+, sync and async: pydantic models, an idempotency key on every write, safe retries and webhook verification.
1. Install and configure
Section titled “1. Install and configure”pip install tuppenceimport osfrom tuppence import Tuppence
tuppence = Tuppence(os.environ["TUPPENCE_SECRET_KEY"])
customer = tuppence.customers.create(name="Ada Lovelace", email="ada@example.com")payment = tuppence.payments.create(amount=2000, currency="gbp", customer=customer.id)print(payment.id, payment.status, payment.approval_url)
# Iterate every item in a list. Pages are fetched as you reach them.for c in tuppence.customers.list_all(limit=100): print(c.id, c.email) breakimport osfrom tuppence import Tuppence
tuppence = Tuppence(os.environ["TUPPENCE_SECRET_KEY"])
customer = tuppence.customers.create(name="Ada Lovelace", email="ada@example.com")payment = tuppence.payments.create(amount=2000, currency="usd", customer=customer.id)print(payment.id, payment.status, payment.approval_url)
# Iterate every item in a list. Pages are fetched as you reach them.for c in tuppence.customers.list_all(limit=100): print(c.id, c.email) breakimport osfrom tuppence import Tuppence
tuppence = Tuppence(os.environ["TUPPENCE_SECRET_KEY"])
customer = tuppence.customers.create(name="Ada Lovelace", email="ada@example.com")payment = tuppence.payments.create(amount=2000, currency="eur", customer=customer.id)print(payment.id, payment.status, payment.approval_url)
# Iterate every item in a list. Pages are fetched as you reach them.for c in tuppence.customers.list_all(limit=100): print(c.id, c.email) break2. Common tasks
Section titled “2. Common tasks”| Task | Call |
|---|---|
| Create a payment link | tuppence.payment_links.create(name=…, amount=…, currency=…) |
| Create a payment | tuppence.payments.create(amount=…, currency=…, customer=…) |
| Confirm one (test mode) | tuppence.payments.confirm(id, payment_method="pm_card_gb")tuppence.payments.confirm(id, payment_method="pm_card_us")tuppence.payments.confirm(id, payment_method="pm_card_de") |
| Capture, cancel | tuppence.payments.capture(id), tuppence.payments.cancel(id) |
| Refund | tuppence.refunds.create(payment=…, amount=…) |
| Customers | tuppence.customers.create / retrieve / update / list / list_all |
| Balance | tuppence.balance.retrieve() |
| Pay out | tuppence.payouts.create(), tuppence.payouts.settings() |
| Webhook endpoints | tuppence.webhook_endpoints.create / list / retrieve / update / delete |
| Verify a webhook | tuppence.webhooks.construct_event(raw_body, signature_header, secret) returns a dict |
| Act as a connected account | tuppence.as_account("acct_…") returns a client that sends Tuppence-Account |
| Anything else | tuppence.request("GET", "/v1/…", body=…, params=…) returns a dict |
Every list has list(**params) for one page (.data, .has_more) and list_all(**params), an
iterator over all pages.
from tuppence import AsyncTuppence
async with AsyncTuppence(os.environ["TUPPENCE_SECRET_KEY"]) as tuppence: payment = await tuppence.payments.create(amount=2000, currency="gbp") async for refund in tuppence.refunds.list_all(payment=payment.id): ...from tuppence import AsyncTuppence
async with AsyncTuppence(os.environ["TUPPENCE_SECRET_KEY"]) as tuppence: payment = await tuppence.payments.create(amount=2000, currency="usd") async for refund in tuppence.refunds.list_all(payment=payment.id): ...from tuppence import AsyncTuppence
async with AsyncTuppence(os.environ["TUPPENCE_SECRET_KEY"]) as tuppence: payment = await tuppence.payments.create(amount=2000, currency="eur") async for refund in tuppence.refunds.list_all(payment=payment.id): ...What it does for you
Section titled “What it does for you”| pydantic models | For every object, generated from the API’s contract. Money is always an int of minor units. Fields the API adds later are kept (model_extra) |
Idempotency-Key | On every POST, reused across that call’s retries; pass idempotency_key= to choose it |
| Retries | Twice by default, with jittered backoff, honouring Retry-After. Retries network errors, timeouts, 5xx, 429 and a request still in flight; never another 4xx |
| Typed errors | CardError, InvalidRequestError, AuthenticationError, PermissionError, RateLimitError, IdempotencyError, APIError, ConnectionError. All extend TuppenceError, with code, param, decline_code and request_id |
from tuppence import CardError
try: tuppence.payments.confirm(payment_id, payment_method="pm_card_chargeDeclined")except CardError as err: print(err.code, err.decline_code, err.request_id)Instant payouts
Section titled “Instant payouts”Get a quote first. It says what arrives and when, or why not and where to fix it. The bank is
the default destination. The card balance (destination="card_balance", UK only) is described before
it can be used. See Instant payouts.
quote = tuppence.payouts.instant_quote(amount=5872)if quote.available: # £58.13 arrives within 30 minutes. The £0.59 fee comes out of the £58.72. tuppence.payouts.create(method="instant", amount=quote.total_amount)else: print(quote.reason, quote.message, quote.fix_url, quote.available_from)
settings = tuppence.payouts.settings()print(settings.instant_payouts.destinations.card_balance.message)quote = tuppence.payouts.instant_quote(amount=5872)if quote.available: # $58.13 arrives within 30 minutes. The $0.59 fee comes out of the $58.72. tuppence.payouts.create(method="instant", amount=quote.total_amount)else: print(quote.reason, quote.message, quote.fix_url, quote.available_from)
settings = tuppence.payouts.settings()print(settings.instant_payouts.destinations.card_balance.message)quote = tuppence.payouts.instant_quote(amount=5872)if quote.available: # €58.13 arrives within 30 minutes. The €0.59 fee comes out of the €58.72. tuppence.payouts.create(method="instant", amount=quote.total_amount)else: print(quote.reason, quote.message, quote.fix_url, quote.available_from)
settings = tuppence.payouts.settings()print(settings.instant_payouts.destinations.card_balance.message)Next-day payouts and your prices
Section titled “Next-day payouts and your prices”payouts.set_next_day switches next-day payouts (UK) on and
off. Until next_day.available, the call is refused with the reason. account.retrieve_pricing()
returns every price the account pays now (list price or one agreed with Tuppence) with its dates.
settings = tuppence.payouts.settings()if settings.next_day.available: tuppence.payouts.set_next_day(enabled=True)
for p in tuppence.account.retrieve_pricing().prices: print(p.type, p.bps, p.fixed, p.source, p.ends_at)Options
Section titled “Options”Tuppence(key, api_version="2026-09-20", max_retries=4, timeout=10)tuppence.balance.retrieve(max_retries=0, timeout=2) # per requestbase_url= (or TUPPENCE_API_URL in the environment) points it at another API.