Test & go live

Sandbox & testing

Test the full payment flow safely before going live.

How the sandbox works

  • Use an ok_test_… key. Orders are environment: "SANDBOX" and are invisible to production keys.
  • Real HTP wallets and real USDT are used, but only between you and your test users — nobody else can pay or be paid (SANDBOX_USER_ONLY).
  • Fees are calculated and then rebated to your wallet.
  • Limits are the lower of your approved limits and 100 USDT per payment, 500 per day, 2000 per month, counted separately from production.
  • Works before your app is published.

Set up test users

In the console, Test users → Add (up to 20) by HTP account. Each test user:

  1. Signs in to your app with Sign in with HTP — this creates the sub you charge.
  2. Needs a wallet with some USDT and a payment PIN in the HTP App.

You (the developer) can sign in to your sandbox app, but cannot pay your own app (SELF_PAYMENT_FORBIDDEN) — use a test user for payments.

Test plan

# Scenario Expected
1 Create payment → test user pays in the HTP App payment.paid received and verified; order PAID
2 Same create request retried with the same Idempotency-Key Same id returned, one order
3 Same out_order_no, new Idempotency-Key 409 ORDER_ALREADY_EXISTS
4 expire_minutes: 1, wait Order reported CLOSED, no checkout_url, cashier shows expired
5 Close a CREATED payment, then open its link Cashier shows closed; order CLOSED
6 Partial refund, then refund the rest Two REFUND orders, payment.refunded twice, refunded_amount = amount
7 Refund more than remaining 422 REFUND_AMOUNT_EXCEEDED
8 Amount above 100 USDT 422 LIMIT_EXCEEDED
9 payer_sub of a user who never signed in 422 USER_NOT_AUTHORIZED
10 Wrong secret / old timestamp / reused nonce 401 SIGNATURE_INVALID
11 Callback with a tampered body (use the Callback verifier) Your endpoint returns 401
12 Same callback delivered twice (console Resend) Processed once
13 Stop your server, pay, start it again after a few minutes Delivery retried and succeeds; or Resend from the console
14 Download the statement CSV Matches your database
15 Payout to a test user (if approved) SUCCEEDED, payout.completed received

Debugging

  • Every response has a trace_id — log it.
  • The console Callbacks page shows last_status_code and last_error for each delivery.
  • SIGNATURE_INVALID: compare your string_to_sign with the Signature generator using the same inputs.
  • The callback URL must be reachable from the internet over HTTPS with a valid certificate and resolve to a public IP. For local testing use a tunnel (ngrok, Cloudflare Tunnel) and set the tunnel URL as the callback.