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.
Test cards
Section titled “Test cards”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.
Test helpers
Section titled “Test helpers”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 osimport timefrom 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 passpayout = tuppence.payouts.create() # everything availabletuppence.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%Moving time
Section titled “Moving time”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.