Skip to main content

Sandbox

HelaMesh has a full sandbox for end-to-end integration testing without real money. It works the same way in production as it does locally โ€” same API host, same code paths, same webhook delivery pipeline. The only difference: payments arrive via a fast-forward endpoint instead of the blockchain poller.

Stripe-style test mode, not a separate deployment

There is no sandbox-api.helamesh.com. HelaMesh uses the same-host-two-modes pattern that Stripe, Plaid, and Coinbase Commerce use. Test and live data are fully isolated by the environment field on each client and by separate API key prefixes (hm_test_* vs hm_live_*). One URL, two modes.

The mental modelโ€‹

Every client you create has an environment: test or live. The environment is baked into the client at creation time and cannot be changed.

  • Test clients (hm_test_*, pk_test_*) can be fast-forwarded via the simulate endpoint. Their invoices are isolated from live data and clearly flagged in the dashboard.
  • Live clients (hm_live_*, pk_live_*) can never be simulated. The simulate endpoint returns 403 Forbidden regardless of auth. Live payments only come from real on-chain transactions.

This invariant is enforced server-side in the InvoiceSimulationService. Even if a bug slipped past every other check, the service itself refuses to touch live clients.

The sandbox loopโ€‹

  1. Create a test client in the dashboard. Save the API key + publishable key + webhook secret (shown once).
  2. Point the webhook URL at your dev backend. For local dev, use webhook.site or an ngrok tunnel to your localhost.
  3. Create an invoice via the API โ€” just like you would in production.
  4. Simulate the payment via either the dashboard button or the API endpoint below.
  5. Your backend receives a signed webhook within 1-2 seconds, including a simulated: true flag in the payload body.
  6. Verify your handler did the right thing. Repeat.

Option 1 โ€” Simulate via the dashboardโ€‹

On any test-environment PENDING invoice, click the โšก Simulate payment button on the invoice detail page. Within 1-2 seconds the invoice flips to COMPLETED, a signed webhook lands at your endpoint, and the dashboard shows a yellow SIMULATED badge on the invoice title.

Great for manual testing during local development.

Option 2 โ€” Simulate via the API (for CI tests)โ€‹

curl -X POST https://api.helamesh.com/v1/invoices/INVOICE_ID/simulate \
-H "x-api-key: hm_test_YOUR_KEY"
200 OK
{
"ok": true,
"message": "Payment simulated. Webhook and email dispatched.",
"network": "BSC",
"txHash": "sim_abc123def456...",
"simulated": true
}

Use this in your CI pipeline or integration test suite. Full example:

integration.test.js
// 1. Create a test invoice
const createRes = await fetch('https://api.helamesh.com/v1/invoices', {
method: 'POST',
headers: {
'x-api-key': process.env.HELAMESH_TEST_API_KEY,
'content-type': 'application/json',
},
body: JSON.stringify({ amount: 25, metadata: { orderId: 'test-1234' } }),
});
const invoice = await createRes.json();

// 2. Simulate the payment
await fetch(`https://api.helamesh.com/v1/invoices/${invoice.id}/simulate`, {
method: 'POST',
headers: { 'x-api-key': process.env.HELAMESH_TEST_API_KEY },
});

// 3. Your webhook handler runs asynchronously โ€” wait a beat
await new Promise((r) => setTimeout(r, 3000));

// 4. Assert: your backend should have marked the order paid
const order = await db.orders.findOne({ helamesh_invoice_id: invoice.id });
expect(order.payment_status).toBe('paid');
expect(order.tx_hash).toMatch(/^sim_/); // sandbox tx hashes prefix with 'sim_'

The simulated flagโ€‹

Every webhook payload from a simulated invoice includes simulated: true at the top level:

{
"event": "payment.confirmed",
"invoiceId": "65f8a1b2c3d4e5f6a7b8c9d0",
"timestamp": "2026-04-11T18:32:14.000Z",
"amount": "25.000000",
"token": "USDT",
"network": "BSC",
"txHash": "sim_abc123def456789012345678",
"confirmations": 20,
"simulated": true
}
Respect the simulated flag in production handlers

If your webhook handler updates real accounting systems, moves real balances, or triggers irreversible fulfillment, use this flag to route simulated events to a staging database or a no-op branch. Never mirror simulated events into production tables.

app.post('/webhooks/helamesh', async (req, res) => {
// ... verify signature ...
const event = JSON.parse(req.body.toString());

if (event.simulated) {
// Test webhook โ€” update staging tables, don't touch prod
await stagingDb.orders.update(...);
} else {
// Real payment โ€” full fulfillment flow
await realDb.orders.update(...);
await sendConfirmationEmail(...);
await triggerFulfillment(...);
}

res.status(200).send('ok');
});

Tx hash formatโ€‹

Simulated transactions use a synthetic hash prefix so you can recognise them instantly in logs, databases, and block explorers:

  • Real Tron tx: abc123def456... (64 hex chars)
  • Real BSC tx: 0xabc123def456... (0x + 64 hex chars)
  • Simulated tx: sim_abc123def456... (sim prefix + 32 hex chars)

If you see sim_ in a transaction ID anywhere in your system, you know it was fast-forwarded by the sandbox, not confirmed by a real chain.

What the sandbox does NOT doโ€‹

  • Does not generate real tokens. No BNB or TRX is created. You cannot withdraw from a simulated wallet.
  • Does not bypass security. Every simulate call is authenticated (merchant session, test API key, or publishable key from the hosted checkout) and scoped to clients owned by the caller. You cannot simulate a payment for someone else's client.
  • Does not work on live clients. Ever. Under any circumstances. The InvoiceSimulationService rejects live environment with 403 Forbidden at the service layer, before any state mutation happens.