Security & server verification
Protect credentials, bind payments to orders, and verify payment status on your server.
Browser credentials
Use a publishable key in the browser: hw_pk_test_… for sandbox or hw_pk_live_… for
production. Keep secret API keys, HTTP Basic credentials, and other server credentials on your
backend. A publishable key identifies the checkout configuration. It does not authorize access to
an order or prove that a payment succeeded.
The publishable key and apiBase must belong to the same environment and account. The key does
not redirect a mismatched URL. fieldsOrigin defaults to apiBase; set it only to the separate
origin issued for your account. Use HTTPS for apiBase, fieldsOrigin, and the checkout page,
except for localhost development.
Checkout origins
Provide HighWire with each checkout origin during onboarding. Scheme, hostname, and port matter:
https://shop.example.com, https://staging.shop.example.com, and http://localhost:5173 are
separate origins. Preview deployments may need separate registration.
A refused key or unregistered checkout origin can report HW_UNAUTHORIZED. Some gateway refusals
are not readable by browser code and can appear as HW_NETWORK. For either error, check the full
publishable key, gateway URL, account status, and registered origin before treating it as a network
problem.
SDK-managed payment sessions
The mounted checkout runs its own device session and attaches the session key to its own payments.
For a Direct API charge, take the key from the collector's riskSessionKey(), created with the same
publishableKey and apiBase as HighWire.init(). Without the key it always resolves null;
without apiBase it uses the production gateway. It resolves to a string or null. Send the string to your backend
and forward it unchanged as the top-level riskSessionKey on the authorization or
authorization-and-capture request. Omit the field when the value is null. See
Device screening for Direct API charges.
Treat the value as optional context, not authentication or proof of payment. Do not log it or put it in URLs.
Bind and verify orders on your server
Use this sequence for every order:
- Before
mount(), call your backend's eligibility route (the examples returncanStartPayment). It must authenticate the owning account or validate a guest session scoped to the order, load the server-authoritative total and currency, and reconcile any existing payment. Start a new checkout only when the backend permits it. - As soon as
mount()resolves, sendcheckout.paymentIdto the same backend and keep the checkout non-interactive until binding succeeds. A wallet or drop-in form can start payment without your Pay button. - At binding time, repeat the authorization and eligibility checks. Retrieve the payment with server credentials, confirm that it belongs to your merchant, and compare it with the order's server-side amount and currency.
- Bind the payment and order in one transaction. Enforce unique constraints so one payment belongs to one order and one order has at most one active payment. Treat the same payment-order pair as idempotent. Reject a payment bound elsewhere, and reject replacement when the current payment is paid or unresolved. After reconciliation, create a replacement through an explicit transition that retains the earlier payment history.
- Fulfill only after your server verifies the bound payment's captured status, amount, and currency
through
GET /v1/payments/{paymentId}or a verified gateway webhook. Keep reconciling pending orders after the shopper leaves because a payment can complete later.
Browser callbacks, browser amounts, and a browser-supplied payment ID do not authorize fulfillment.
Protect same-origin binding routes
For each state-changing JSON route used by the checkout:
- Require an
Originheader that exactly matches the merchant checkout origin. Reject a missing or different origin. - Require
Content-Type: application/jsonbefore parsing the body. - Apply the application's normal cookie and session policy, including its
SameSitesetting. If the application uses CSRF tokens, validate the token on this route too.
The same-origin JSON fetch examples in this guide omit a CSRF token because the route contract
requires the exact origin and JSON content type. Add the application's token whenever its session
policy requires one.
Use the Gateway authentication guide for server credentials.
Safe diagnostics
Share SDK_VERSION, browser version, timestamp, checkout origin, and HighWireError.code with
support. Do not send secret credentials, card values, session or risk keys, cookies, authorization
headers, or full payment request and response bodies.