TidePay

Test mode

One test mode, two ways to pay — test wallets, or a real wallet on a testnet.

Every merchant has two API keys: a live key (tp_live_...) and a test key (tp_test_...). Both are shown once, in the dashboard (Settings → API keys), at creation or regeneration.

Requests authenticated with a tp_test_... key — and everything you create in the dashboard with the Test mode toggle on — run in test mode (called sandbox in the API: isSandbox on objects and webhook payloads). No real funds ever move in test mode.

The base URL is the same for both keys. Switching between test and live is only a matter of which key you send.

Two ways to pay

Test mode is a single mode. What differs is how the payer pays, and every test checkout offers the first way:

Test walletTestnet wallet
What it isA fixed address that stands in for a wallet, like a test cardA real browser wallet (MetaMask, Phantom…) on Base Sepolia or Solana Devnet
BlockchainSimulated — synthetic 0xdead... hashesReal testnet transactions, visible on the block explorer
You needNothingFaucet ETH/SOL for gas and faucet USDC
Declines on demandYes — pick a declining walletOnly by draining the wallet
Available onEvery test-mode checkout, any tokenAccounts enabled by Tidepay, on testnet-priced offers
Best forBuilding the integration, webhooks, failure paths, automated testsA final rehearsal of the real approve → pull → split flow

Testnet wallets are enabled per account by the Tidepay team — every testnet charge spends real testnet gas on Tidepay's side. Without it, test wallets cover everything, testnet-priced offers included (they are simulated too).

For an enabled account, a test-mode checkout priced in a testnet token shows both: the test wallets first, then or pay on-chain with a testnet wallet. On a test-mode checkout priced in a mainnet token (e.g. usdc-base under a test key), only the test wallets are offered, since a real wallet there would move real funds.

In the dashboard, Settings → Payments lists the test wallets while the Test mode toggle is on. For an enabled account it also shows the testnet option: adding a receiving wallet on Base Sepolia or Solana Devnet switches on the testnet option for that token. Without one, test offers are created against a placeholder payout address and can only be paid with test wallets — the placeholder is refused on mainnet tokens.

See Testnet chains for the faucets and the testnet walkthrough.

Test wallets

Test wallets play the role of test card numbers. Give a customer one of these addresses — through POST /customers, PATCH /customers/{customerId}, or by picking it on the checkout page — and every charge against that customer produces the listed result:

ResultEVM addressSolana address
Payment succeeds0x4242424242424242424242424242424242424242TidepayTestSuccess1111111111111111111111111
Declined: insufficient_balance0x4000000000000000000000000000000000009995TidepayTestNoFunds1111111111111111111111111
Declined: insufficient_allowance0x4000000000000000000000000000000000000002TidepayTestNoAuth11111111111111111111111111
  • A declined charge records a failed invoice with that failureReason, moves the subscription to past_due, and fires payment.failed — exactly what a real wallet without funds or approval would cause.
  • On a mainnet token, any other address also succeeds in test mode, so existing test data keeps working. On a testnet token, any other address is treated as a real testnet wallet and charged on-chain.
  • Several test customers may share the same test wallet; the one-wallet-per-customer rule does not apply to them.
  • Test wallets are rejected with 400 on a live key, the same way live mode refuses test cards.
curl https://api.tidepay.cc/v1/customers \
  -H "X-API-Key: tp_test_..." \
  -H "Content-Type: application/json" \
  -d '{
    "email": "declined@example.com",
    "wallets": [
      { "chainFamily": "evm", "address": "0x4000000000000000000000000000000000009995" }
    ]
  }'

Paying from the checkout page

Every test-mode checkout — subscriptions (subscribeUrl), one-time payments (checkoutUrl) and payment links (/pay/...) — lists the test wallets above. Picking one pays with it, through the same backend path a real payment takes, so the result shows up in the dashboard and in your webhooks exactly as it would live. On a testnet-priced checkout, the regular wallet-connect flow follows below them.

Subscriptions

  • Payment succeeds — records a settled invoice, moves the subscription to active, advances currentPeriodEnd by one interval, and fires payment.succeeded and subscription.active.
  • Declined — records a failed invoice with the matching failureReason, moves the subscription to past_due, and fires payment.failed.

The chosen wallet stays on the customer, so later renewals keep producing the same result — like a customer who saved a test card.

One-time payments and payment links

  • Payment succeeds — the payment becomes settled and payment.succeeded fires.
  • Declined — the payment becomes failed with the matching failureReason and payment.failed fires. A one-time payment is final, so open a new link to try another wallet.

To test a payment link end to end: create the offer with your tp_test_... key (or in the dashboard with Test mode on), open its link, and pick a test wallet — or, for a testnet-priced offer, connect a funded testnet wallet.

Advancing time

Subscriptions renew on their billing interval. Rather than waiting a month, fast-forward a test subscription with POST /sandbox/advance — the equivalent of a test clock:

curl https://api.tidepay.cc/v1/sandbox/advance \
  -H "X-API-Key: tp_test_..." \
  -H "Content-Type: application/json" \
  -d '{ "subscriptionId": "sub_...", "cycles": 3 }'

Each cycle runs the same renewal charge the billing cron would, with the same invoices and webhooks. cycles is 1–12 (default 1). It stops at the first cycle that does not settle, so a subscription paying from a declining test wallet ends up past_due after one cycle. The response lists each cycle's outcome and the subscription's resulting status and currentPeriodEnd.

POST /sandbox/charge runs exactly one cycle and is kept for existing integrations; POST /sandbox/advance with cycles: 1 does the same.

Both endpoints accept only test keys — a live key gets 403.

What else differs in test mode

  • For a mainnet token, POST /subscriptions returns a placeholder operatorWallet (0x000000000000000000000000000000005a4d42 for EVM, a fixed placeholder base58 address for Solana). Never send real token approvals to it — it holds no authority and is not monitored. For a testnet token it returns the real operator, since a testnet wallet approves it for real.
  • Simulated charges (any test wallet, or any mainnet token) carry synthetic pullTxHash / merchantTransferTxHash / platformTransferTxHash values — prefixed 0xdead... for EVM, a recognizable fake signature for Solana. They never resolve on a block explorer. Testnet-wallet charges carry real testnet hashes.
  • Test and live data are fully isolated: offers, customers, subscriptions and payments created with the test key are invisible to the live key, and vice versa.
  • Test subscriptions and their payments are deleted automatically after 30 days.

Webhooks

Test events can go to their own endpoint: the dashboard (Settings → Business) takes an optional Sandbox webhook URL alongside the live one. Leave it empty and test events go to your live URL. Live events never go to the sandbox URL.

Test events are signed exactly like live ones (x-tidepay-signature, HMAC-SHA256 over ${timestamp}.${rawBody}), and every payload carries a top-level isSandbox: boolean so a shared endpoint can tell them apart. See Webhooks.

Going live

Test and live share no data, so switching over is deliberate:

  1. Recreate your offers and prices with your tp_live_... key — test-key offers cannot sell for real.
  2. Send the live key instead of the test key. The base URL does not change; see Authentication.
  3. Confirm your webhook handler accepts events with isSandbox: false.
  4. Make sure the destination wallet on each live price is one you control on the mainnet chain you priced in.

On this page