Skip to content

Testing

Everything works in test mode, with a test key (sk_test_…), and no money moves. Test and live data never mix: a test key cannot see a live payment, and the dashboard shows which mode you are in on every page.

The hosted page offers Stripe’s test cards (4242 4242 4242 4242 — any future expiry, any CVC). Server-side, in test mode, confirm a payment with a test payment method instead: pm_card_gb (succeeds), pm_card_chargeDeclined (declined), pm_card_createDispute (succeeds, then is disputed) and the others.

What would take days, or a bank, or an angry cardholder — on demand. They exist only in test mode:

POST Does
/v1/test_helpers/balance/make_available Settles pending funds into your available balance
/v1/test_helpers/payouts/{id}/settle The bank accepts (outcome: "paid") or bounces ("failed") a payout
/v1/test_helpers/payments/{id}/dispute The cardholder disputes a payment (reason optional)
/v1/test_helpers/disputes/{id}/close The bank decides: outcome: "won" or "lost"
/v1/test_helpers/webhook_inboxes A URL that receives your webhooks
/v1/test_helpers/issuing/fund Money arrives in what your cards can spend (amount), as if you had sent the bank transfer
/v1/test_helpers/issuing/authorizations A purchase on one of your agents’ cards (card, amount, merchant), decided by its rules exactly as a real one would be — and, as Stripe does, declined insufficient_funds first if what the cards can spend won’t cover it. merchant_currency (and merchant_amount) make it a purchase abroad, which costs FX
/v1/test_helpers/issuing/authorizations/{id}/capture The merchant takes the money for a purchase — all of it, or amount of it; the rest of the hold is released
/v1/test_helpers/issuing/authorizations/{id}/reverse The merchant lets a purchase’s hold go
/v1/test_helpers/issuing/transactions/{id}/refund The merchant refunds a capture — all of it, or amount of it
/v1/test_helpers/card_disputes/{id}/close The card network decides a dispute of a card purchase: outcome: "won" (the money comes back to what the cards can spend) or "lost"
/v1/test_helpers/cards/{id}/shipping/{step} The post moves a physical card along: ship, deliver, fail or return

From a payment to money in the bank, in a few lines:

import os
import time
from tuppence import Tuppence
tuppence = Tuppence(os.environ["TUPPENCE_SECRET_KEY"])
payment = tuppence.payments.create(amount=10_000, currency="gbp")
tuppence.payments.confirm(payment.id, payment_method="pm_card_gb")
while tuppence.payments.retrieve(payment.id).net is None:
time.sleep(0.5)
tuppence.request("POST", "/v1/test_helpers/balance/make_available") # a few days pass
payout = tuppence.payouts.create() # everything available
tuppence.request("POST", f"/v1/test_helpers/payouts/{payout.id}/settle", body={"outcome": "paid"})
while tuppence.payouts.retrieve(payout.id).status != "paid":
time.sleep(0.5)
print("paid out", payout.amount) # 9921: £100 less our 0.79%

Your test mode can run ahead of real time. POST /v1/test_helpers/clock/advance moves it forward by days, hours, minutes and seconds (up to five years ahead in all); from then on, every test-mode request of your account sees the new time — what you create is stamped then, and “the last 30 days” ends then. GET /v1/test_helpers/clock says where it is, and POST /v1/test_helpers/clock/reset puts it back. Only your account’s test mode moves: live mode, and everyone else, stay on real time.

import { Tuppence } from "@tuppence/node";
const tuppence = new Tuppence(process.env.TUPPENCE_SECRET_KEY);
const clock = await tuppence.request("POST", "/v1/test_helpers/clock/advance", {
body: { days: 30 },
});
const customer = await tuppence.customers.create({ name: "Made a month from now" });
console.log(clock.offset_seconds, customer.created >= clock.now); // 2592000 true
await tuppence.request("POST", "/v1/test_helpers/clock/reset");

Work that runs on a schedule — payout runs, statements, hourly usage rollups — still runs on real time: the clock moves what your requests see, not our schedulers.

Two exceptions. Subscriptions renew when you advance the clock: move a month forward and the renewals that month contained are charged there and then, rather than on the next sweep — which is what makes a year of billing something you can test in a second.

The other: your agents’ spending profiles — what’s normal for each agent, which a purchase far out of character is held against — move with the clock. Buy a week of routine a day at a time, and GET /v1/agents/{id}/profile reads it as a week, straight away.

Point your tests at the test API with a test key, and use the helpers above for the slow parts. The SDKs read TUPPENCE_API_URL, which is how these docs run their own examples against a test API.