Browser Info & 3-D Secure

Created by Kalin Ivanov, Modified on Tue, 23 Jun at 8:41 AM by Kalin Ivanov

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:

  1. browser_info_token -- a compact opaque token produced by our hosted collector script. Recommended.
  2. 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/payments reference, 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

Let us know how can we improve this article!

Select at least one of the reasons
CAPTCHA verification is required.

Feedback sent

We appreciate your effort and will try to fix the article