A 3-D Secure (3DS) browser challenge needs more from the shopper's browser than the request headers alone can supply. The card networks ask for the screen size, color depth, language, time zone, and a handful of similar signals -- values that only JavaScript running in the shopper's browser can read.
You have two ways to feed these signals into POST /api/v1/payments:
browser_info_token-- a compact opaque token produced by our hosted collector script. Recommended.browser_info-- pass the individual fields yourself. Useful if you already collect them, or if you do not run JavaScript on the checkout page.
If you send both, inline browser_info wins per field; missing fields fall back to the token, then to the request headers.
The hosted collector
We host a small framework-free script at:
https://app.swisspay.ai/v1/browser-info.js
Embed it on the page where the shopper enters card details, then call the synchronous global it exposes:
<script src="https://app.swisspay.ai/v1/browser-info.js"></script>
<script>
// Just before you POST to your server:
const token = Swisspay.collectBrowserInfo();
// -> "AwG5..." (about 60 chars, base64url)
fetch("/checkout/charge", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ /* your fields */, browser_info_token: token }),
});
</script>
The path is versioned and the response is cached as immutable in production, so you can hard-pin the <script src> to it and never worry about cache busting.
What the token contains
The token is an opaque base64url string. Treat it as a black box -- the format may evolve. We will keep the field name (browser_info_token) and the API stable.
It carries the seven 3DS browser signals JavaScript can read (color depth, Java enabled, JavaScript enabled, language, screen height, screen width, time zone offset). It does not carry the shopper's User-Agent -- we pick that up from the request headers, or you can supply it explicitly in browser_info.user_agent.
If a particular signal is unreadable in the shopper's browser, the collector drops just that signal rather than throwing -- the token is always safe to send, even on exotic browsers.
Recommended integration
The token is a convenience encoding, not a security control: it is not signed, it has no TTL, and an attacker who controls the shopper's browser could replace it. That is why we recommend the standard flow:
shopper's browser ---(token)---> your server ---(payment + token)---> SwissPay
Your secret API key (sk_test_... / sk_live_...) stays on your server. The token is just one of the JSON fields you forward.
Passing browser info manually
If you cannot run our script (no JavaScript on the checkout page, native app, etc.), pass the fields directly under browser_info:
{
"amount": 8999,
"currency": "EUR",
"authentication": "automatic",
"success_url": "https://shop.example.com/return",
"failure_url": "https://shop.example.com/return",
"shopper_ip": "203.0.113.42",
"browser_info": {
"user_agent": "Mozilla/5.0 ...",
"accept_header": "text/html,application/xhtml+xml",
"language": "en-US",
"color_depth": 24,
"screen_height": 1080,
"screen_width": 1920,
"time_zone_offset": -60,
"java_enabled": false,
"java_script_enabled": true
},
"payment_method": { "type": "card", "...": "..." }
}
| Field | Type | Notes |
|---|---|---|
user_agent |
string | Falls back to the request User-Agent header. |
accept_header |
string | Falls back to the request Accept header. |
language |
string | e.g. "en-US". |
color_depth |
integer | Bits per pixel, e.g. 24. |
screen_height |
integer | CSS pixels. |
screen_width |
integer | CSS pixels. |
time_zone_offset |
integer | Minutes behind UTC. -60 is UTC+1. Send 0 explicitly for UTC -- omitting it is not the same. |
java_enabled |
boolean | Almost always false on modern browsers. Send false explicitly rather than omitting. |
java_script_enabled |
boolean | Optional. true on any web checkout. |
Malformed values are dropped silently rather than failing the payment, so a partial browser_info block will not break a non-3DS charge -- but a 3DS browser challenge needs the full set, so verify what you send before relying on it.
Non-3DS payments
You do not need browser info on payments that will not go through a 3DS challenge. We still use what you send (or what we read from the request headers) for fraud scoring, but no field is required.
See also
- Payments -- the
POST /api/v1/paymentsreference, including the 3DS redirect flow. - Test cards -- cards that force a 3DS challenge in test mode.
Was this article helpful?
That’s Great!
Thank you for your feedback
Sorry! We couldn't be helpful
Thank you for your feedback
Feedback sent
We appreciate your effort and will try to fix the article