Developers · Hashry Pay
Hashry Pay developer guide
Take card payments inside your own checkout page. Hashry's card form runs in a frame on your page. Card numbers never touch your page or your server. Your keys never touch the browser.
This guide is for the developer who builds the integration. The merchant sends you this link from the Hashry admin.
How to use this guide
- Read How it works on this page.
- Do the admin setup with the merchant.
- Follow your stack's page from start to end. It has every file, and ends with a paid test order.
- Then test and go live.
The packages are an early release (0.x). Their API can change before 1.0.
1. How it works
Your server holds the secret key and talks to Hashry. Your page only shows Hashry's card form. A payment goes like this:
- The shopper is ready to pay. Your page asks your server to start the payment.
- Your server creates the payment with its secret key (
POST /v1/channel/payment-intents). It sets the amount from its own order. The page never sends an amount. - Your server passes the four checkout values to the page, and nothing else:
embed_url,transaction_uid,status_urlandstatus_token. - The page shows the card form. The shopper enters the card. If the bank wants a 3-D Secure check, it runs inside the card form.
- If the bank declines the card, the card form asks for another one, up to 3 cards. Your server does nothing.
- The card form says completed. That is a cue, not proof. The page asks your server.
- Your server confirms with Hashry (
POST /v1/channel/payment-intents/{uid}/refresh), or gets a webhook. Only then does it mark the order paid.
Words used in this guide
| Word | What it means |
|---|---|
| Payment | One payment per order, with its amount, currency and status. The API calls it a payment intent. Its ID is uid in the API, payment_intent_uid in webhooks. |
| Card form | Hashry's form, in a frame on your page. The shopper types the card there, so card numbers never reach your page. |
| Full payment page | The same form on a Hashry page of its own. The card form offers it if the bank check has trouble in a frame. The shopper comes back through your return_url. |
| Checkout values | The four values your server passes to the page: embed_url, transaction_uid, status_url and status_token. |
| Status token | Lets the page read one payment's status, for display. It can't confirm or change anything. |
| Attempt | One go at paying, inside a payment. When every card in a card form is declined, your server starts a new attempt on the same payment. |
| Confirm | Your server asks Hashry for the latest outcome, with Hashry's refresh call. Only then does it mark the order paid. |
| Webhook | A signed POST from Hashry to your server when a payment changes. |
| Idempotency key | A name you choose for each create call, such as order-1042-pay-1. Sending the same key again is safe: you get the same payment back. |
| Embed origin | The https address of the page that shows the card form. |
| Return origin | The https address of your return_url and cancel_url. |
| Sandbox and live | Sandbox is for testing, with test cards and no real money. The key you use picks the mode. |
What may go in the browser
| Value | Looks like | Where it may go |
|---|---|---|
| Client secret | hsk_live_…, hsk_test_… | Server only. |
| Webhook signing secret | hwhsec_live_…, hwhsec_test_… | Server only. |
| Access token | From POST /auth/token | Server only. |
| Client ID | Shown with the key | Server. Keep it with the secret. |
embed_url | https://pay.hashrytech.com/… | The page, once. It works like a password for this one payment: never log or store it. |
transaction_uid | An ID | The page. |
status_url and status_token | hst_… | The page. The token reads this payment's status, amount and currency, and nothing else. |
Never call the Hashry API from the browser. If you send your client secret from a browser:
- Hashry refuses the call with 403
secret_key_used_in_browser. - It shows the call in the admin's Integration health panel, and emails the account owners.
- If the merchant turned on Revoke keys used from a web browser, the key stops working at once.
Hashry spots browser calls by the Sec-Fetch-Site header. Server HTTP clients
don't send it. If your server forwards a shopper's browser headers to Hashry, drop the Sec-Fetch-* headers.
2. Set up in the Hashry admin
The merchant does this once, or gives you access. In the Hashry admin, open the store, then its Payment Integration sales channel. Live and Test Sandbox are separate tabs, each with its own keys and settings. Start on Test Sandbox.
- Issue a test key. In Test API keys, choose Issue test key. Copy the client ID and the client secret (
hsk_test_…). The secret is shown once. Share it through a password manager, not a chat message. - Allow your page's address. In Test return addresses, add your
page's origin to Allowed return origins. Then add the same origin to Allowed embed origins, under Embedded card form. For development, that is
https://localhost:3000(Next.js) orhttps://localhost:5173(the others). Use the scheme, the host, and the port if it is not 443. No path, no wildcard. - Turn on key safety. On the Details tab, in Key safety, turn on Revoke keys used from a web browser.
Use https, even on your own machine. Allowed return origins accept http://localhost, but allowed embed origins do not, and the card form shows
only in https pages. Each stack page sets up a local certificate for you.
If the card says the embedded card form is off, ask Hashry to turn it on for the account. You set up the webhook URL later, when you test: see Test and go live.
Sandbox and live
Sandbox and live use the same API host. The key picks the mode. A test key sees only test payments. Your code does not change when you go live: only the settings do.
| Sandbox | Live | |
|---|---|---|
| Client secret | hsk_test_… | hsk_live_… |
| Webhook secret | hwhsec_test_… | hwhsec_live_… |
HASHRY_ENVIRONMENT | sandbox | live |
| Card form host | devpay.hashrytech.com | pay.hashrytech.com |
| Money | Test cards only. Not in payouts or reports. | Real cards |
Your server settings
Every stack page reads these settings. Each one shows a settings file ready to fill in.
| Setting | What it is | Secret? |
|---|---|---|
HASHRY_CLIENT_ID | The client ID of your key. | Keep it with the secret. |
HASHRY_CLIENT_SECRET | The client secret: hsk_test_… or hsk_live_…. | Yes. |
HASHRY_ENVIRONMENT | sandbox or live. It must match the key. | No. |
HASHRY_WEBHOOK_SECRET | The webhook signing secret. You get it when you set up webhooks. | Yes. |
PUBLIC_BASE_URL | The https address shoppers open. Not needed in SvelteKit. | No. |
Never put a secret in browser code, in a mobile app, or in a setting with a public prefix
such as NEXT_PUBLIC_, VITE_ or PUBLIC_. Anyone who
loads the page can read those. If a key leaks, the merchant revokes it in the admin and
issues a new one.
3. Pick your stack
Each page takes you from a new app to a paid test order, with every file in order. Follow one page from start to end.
| Your stack | What you build |
|---|---|
| Next.js | One app: route handlers and a React page. |
| Express + React | An Express server and a React page built with Vite. |
| SvelteKit | One app: +server.ts routes and a Svelte page. |
| Python | A FastAPI server and a React page. Notes for Django and Flask. |
Another stack? Use the parts on their own:
- For the page: the React component or the Svelte component.
- For the server: the Node SDK, the Python SDK, or the API reference for any other language.
Help and support
- Something not working? Start with Troubleshooting.
- Questions: email support@hashrytech.com.
- Security problems: email support@hashrytech.com with "Security" in the subject. Say which package and version, what you found, and how to repeat it. Don't post the details anywhere public.
- Licence: Apache-2.0. Each package ships
LICENSEandNOTICE. The licence gives no right to use the Hashry name or logo. - The API schema is public:
GET https://api.hashrytech.com/openapi/partner(OpenAPI 3.1).
Supported versions
| Package | From | Runs on |
|---|---|---|
@hashrytech/pay | npm | Current Chrome, Edge, Firefox and Safari, including Safari on iPhone |
@hashrytech/pay-react | npm | React 18 (18.2 or newer) and React 19 |
@hashrytech/pay-svelte | npm | Svelte 5 |
@hashrytech/pay-build-guard | npm | Node 22 or newer; Vite 5 or newer, or webpack 5 |
@hashrytech/pay-node | npm | Node 22.12 or newer. Server only. |
hashry-pay | PyPI | Python 3.11 or newer |
"Current" browsers means about the last two years of releases.