Skip to content
Documentation menu

Testing

Test mode is a complete parallel environment: the same API, the same objects and the same webhooks, backed by a hosted sandbox checkout instead of a payment provider. No card is charged and no money moves.

Test keys

Keys starting with vx_test_ operate in test mode. Everything they create (cs_test_…, py_test_…, evt_test_…) carries livemode: false and is invisible to live keys, and vice versa. Webhook endpoints are mode-scoped too: register your staging URL with the test key and your production URL with the live key.

Test modeLive mode
Key prefixvx_test_vx_live_
Checkout urlHosted sandbox page on api.vitix.netHosted payment page (Polar)
Minimum amountYour account minimum (default $10)$10.00
success_urlhttps, or http://localhosthttps on an allowed domain
Webhook endpoint urlhttps or httphttps only
RefundsSettle instantlySettle asynchronously (pending → succeeded)
InvoicesNot available (invoice_unavailable)PDF via /v1/payments/:id/invoice
fee_usdAlways 0Actual processing fee

The sandbox checkout page

In test mode, checkout.url points to a Vitix-hosted page that looks like a real checkout but has two buttons: Pay, which completes the checkout, and Simulate a declined card, which shows a decline and leaves the checkout open so you can try again. Completing it emits the same checkout.completed and payment.succeeded events as production and then redirects to your success_url.

bash
# 1. Create a test checkout (note the vx_test_ key and the localhost success_url)curl https://api.vitix.net/v1/checkouts \  -H "Authorization: Bearer vx_test_••••••••" \  -H "Content-Type: application/json" \  -d '{ "amount": 2000, "customer": { "id": "user_42" }, "success_url": "http://localhost:3000/billing/success?cs={CHECKOUT_ID}" }'# → "url": "https://vitix.net/pay?cs=cs_test_…"   (Vitix-hosted sandbox checkout)# 2. Open that url in a browser and click "Pay" (or "Simulate a declined card")# 3. Your webhook receives checkout.completed + payment.succeeded; the browser stays on Vitix's confirmation page
  • The email you type on the sandbox page is stored on the payment, exactly as in production.
  • Opening the page again after paying shows a "Payment complete" state; paying twice is impossible.
  • An expired checkout (expires_at passed, or POST /expire) renders an expired state and emits checkout.expired.

Testing webhooks

  1. Register a test endpoint. For local development put your dev server behind a tunnel first:
    bash
    # Expose a local dev server so Vitix can reach your webhooknpx cloudflared tunnel --url http://localhost:3000# or: ngrok http 3000# Register the public URL as a TEST endpoint (http is allowed only in test mode and only for localhost;# tunnels give you https anyway)curl https://api.vitix.net/v1/webhook_endpoints \  -H "Authorization: Bearer vx_test_••••••••" \  -H "Content-Type: application/json" \  -d '{ "url": "https://quiet-otter.trycloudflare.com/api/vitix/webhook", "description": "local dev" }'
  2. Ping it. The ping event is signed like any other, so a passing ping proves your secret and verification code are correct:
    bash
    curl -X POST https://api.vitix.net/v1/webhook_endpoints/we_…/ping \  -H "Authorization: Bearer vx_test_••••••••"# Your endpoint receives:# webhook-id: evt_test_…# webhook-timestamp: 1791959702# webhook-signature: v1,…# {"id":"evt_test_…","object":"event","livemode":false,"type":"ping","data":{"object":{"object":"ping","endpoint":"we_…","message":"Hello from Vitix Pay"}}}
  3. Run the sandbox flow above and watch payment.succeeded arrive. Inspect every attempt with GET /v1/events/:id/deliveries; it shows the status code and body your server returned.
  4. Make your handler return 500 once and observe the retry schedule (1 min, 5 min, …). Then fix it and resend instantly with POST /v1/events/:id/redeliver.

Testing refunds

bash
# Test refunds await Vitix staff approval, then emit refund.succeeded and payment.refundedcurl https://api.vitix.net/v1/refunds \  -H "Authorization: Bearer vx_test_••••••••" \  -H "Content-Type: application/json" \  -d '{ "payment": "py_test_…", "amount_usd": 500 }'

Testing error handling

Errors behave identically in both modes. Useful cases to cover:

RequestExpected
amount below your minimum400 amount_too_small, param amount
success_url on a domain you did not register400 url_not_allowed, param success_url
a second POST with the same Idempotency-Key and a different body400 idempotency_key_reused
GET /v1/payments/py_test_nope404 resource_missing
any request with a revoked key401 api_key_invalid
101 requests in 10 seconds429 rate_limit_exceeded

Checklist before going live

  • Swap the key for vx_live_ in production configuration only.
  • Create a live webhook endpoint (https) and store its separate secret.
  • Make sure your production success_url and cancel_url domains are on your account's allowed list (GET /v1/account).
  • Validate amounts against account.limits in your UI to avoid avoidable 400s.
  • Branch on livemode in your webhook handler so a stray test event can never credit a production user.

Questions or a missing feature? Email [email protected]