Developers · Hashry Pay
Test and go live
You have paid a test order on your stack page. Now get webhooks working, try the cases that go wrong, and switch to live.
How the sandbox works
- A test key (
hsk_test_…) withHASHRY_ENVIRONMENT=sandboxmakes sandbox payments. Your code is the same for live: only the settings change. - Sandbox payments use test cards. They never reach payouts or reports.
- Sandbox webhooks have the same signature and body as live ones, so your checks are tested for real.
Get webhooks on your machine
Hashry sends webhooks only to a public https address. A tunnel gives your machine one. These steps use ngrok. Any tunnel works.
- Start a tunnel to the server that has the webhook route:
Terminal # Express or Python: the server itself, on port 8000 ngrok http 8000 # Next.js: the https dev server, on port 3000 ngrok http https://localhost:3000 # SvelteKit: the https dev server, on port 5173 ngrok http https://localhost:5173 - ngrok prints a public address, such as
https://abc123.ngrok-free.app. Your webhook URL is that address plus/api/webhooks/hashry. - In the Hashry admin, open the Test Sandbox tab. In Test notifications, add your webhook URL. (The admin calls it the notification address.)
- Choose Rotate test signing secret. Copy the secret (
hwhsec_test_…): it is shown once. - Put it in
HASHRY_WEBHOOK_SECRET, and restart your server. - Pay a new test order. The webhook arrives, and your route answers 200. ngrok's page at
http://localhost:4040shows each delivery.
A free ngrok address changes each time you start ngrok. When it changes, update the webhook URL in the admin.
Test cards
Use any expiry date in the future, and 123 as the security code (CVC).
| Card number | Result |
|---|---|
4012000000020071 | Succeeds. Visa. 3-D Secure passes with no challenge. |
5100270000000023 | Succeeds. Mastercard. 3-D Secure passes with no challenge. |
4012000000020006 | A 3-D Secure card. The sandbox bank approves it without a challenge for now. If the bank's page does appear in the card form, the password is 3ds2. |
4012000000020121 | Declined: 3-D Secure did not verify the card. The card form asks for another card. |
4012000000020089 | Declined: 3-D Secure was only attempted ("Card not verified"). The card form asks for another card. |
What to try
Use a new order for each case. With the stand-in database, change the test order's ID and restart your server.
- Pay with
4012000000020071. The order turns paid, and a webhook arrives. - Pay with
4012000000020121, then4012000000020071in the same card form. The form says the card was declined and takes the second one. The order turns paid. Nopayment.failedwebhook arrives. - Decline three cards in a row (
…0121,…0089,…0121). The page says every card was declined, andpayment.failedarrives. "Try again" brings a new card form. - Leave the card form open for 10 minutes. A fresh one appears for the same payment.
- Close the tab right after paying. The webhook still marks the order paid.
- Choose "Open the payment page in full", if the card form offers it. You come back through your return route to the pay page.
- Replay a webhook from ngrok's page within 5 minutes. Your route answers 200, and the order changes once.
- Replay a webhook with one byte of the body changed (ngrok: Replay with modifications). Your route answers 400.
Too many declines in a row pauses the card form for a few minutes. See Declines and retries for the limits.
Going live
The merchant sets up the Live tab in the admin. Then check each item:
- A live key is issued. Your server has
HASHRY_CLIENT_SECRET=hsk_live_…andHASHRY_ENVIRONMENT=live. - Your production https address is on the live Allowed return origins and Allowed embed origins.
PUBLIC_BASE_URLis that address. - The live webhook URL is set, and the live signing secret (
hwhsec_live_…) is inHASHRY_WEBHOOK_SECRET. - Revoke keys used from a web browser is on.
- The build guard runs in your production build. Secrets live only in server settings.
- Your real database replaces the stand-in, and the test order is gone.
- Handled webhook event IDs are saved in your database, in the same transaction as the order change.
- Your logs never hold
embed_url, tokens or secrets. - Make one small live payment. Check the order, the webhook, and the payment in the admin.