Skip to content

Reference ​

Environments ​

EnvironmentWidget originAPI origin
Pilot / staginghttps://staging.pay.kvarnx.comhttps://api.staging.pay.kvarnx.com
ProductionTo be announced.To be announced.

Staging runs against sandbox card processing and sandbox identity checks: no real money and no real documents. Your staging merchant id and secret are not your production ones, and neither is a webhook endpoint registered for staging nor an API key created there.

Token checklist ​

alg
HS256, always. Nothing else is accepted.
iss
Your merchant id (a UUID), exactly as issued.
aud
The literal string kvarn-pay.
sub
Your reference for the customer. Stable, one per customer.
iat
Present, and not in the future. 30 seconds of clock tolerance.
exp
Present, and at most iat + 10 minutes.
merchantFeeBps
Optional, and only when Kvarn has enabled your own fee for your account. A whole number of basis points, 0–2000. Omit it to charge no fee of your own.
everything else
Ignored. Do not send a name or an email — we will not use them.

Troubleshooting ​

SymptomLikely cause
The widget opened fine, then a later attempt is rejectedThe token is older than 10 minutes. Sign a fresh one each time the customer opens the flow.
Every token is rejected, and the code looks rightYour server's clock is off by more than 30 seconds. Check that it is synchronised.
A new integration is rejected from the first callaud is not exactly kvarn-pay, or iss is not your merchant id — a display name or a staging id used against production will both fail.
Rejected only on the pages where you charge a feemerchantFeeBps is not a whole number between 0 and 2000 — 1.5 or the string "150" are both refused — or your own fee is not enabled for your account (the portal's overview says; ask Kvarn). The 401 looks like any other bad token.
Rejected after rotating credentialsThe secret and the merchant id no longer match each other. They are a pair; a token is verified with the secret belonging to the id in iss.
The widget says it was opened without a sessionNo token reached it — usually a token put in the query string instead of after the #, or an iframe whose src was set before the token arrived. A session that has failed or expired cannot be restarted from inside the iframe either: fetch a fresh token and set src again.
The identity step cannot open the cameraThe iframe's allow attribute is missing or incomplete. It must carry camera; payment.
No event signature ever verifiesThe body was parsed and re-serialized before hashing. Hash the raw request bytes — or the secret belongs to the other environment.
The same event is delivered more than onceExpected, not a fault. Delivery is at-least-once; dedupe on the event id.

Powered by Kvarn X

© 2026 Kvarn Capital Ltd