Skip to content

Session Handling

Session handling is the most important part of a Fluid integration and the most common source of first integration problems.

Fluid doesn’t run its own login. It trusts your site to authenticate the user, then identifies that user through the operator-id, user-id, session-id, locale, country and currency attributes for the length of their session.

Fluid doesn’t own or interpret session-id: it’s your platform’s session ID, passed through as is. It travels with the user’s transactions and can come back to your platform later in the payment lifecycle, for example when a payment provider presents it to attribute or verify activity. So it must be the real, current session ID of the authenticated user — not a placeholder, not a stale value, and nothing your platform wouldn’t recognise when it comes back.

  1. Initialise once after authentication — not on page load before login or on every render
  2. Keep session-id stable — must reflect the current session, not change on every render. An empty, placeholder, stale or constantly changing session-id is the number one cause of failed integrations
  3. Rotate sessions by updating the attribute — push the new value to session-id rather than removing the widget
  4. Never change identity attributes on mounted elements — operator-id, user-id, locale, country, and currency are read once at mount; setting a new value on a live element does nothing, and the widget silently keeps the identity it started with
  5. Wait for initialised before opening — never set open="true" before this event fires; opening earlier shows an empty or broken wallet
  6. Remove the widget on logout — take it from the DOM when the session ends
  7. Keep exactly one widget in the DOM — multiple widgets cause unpredictable behaviour
  1. User authenticates on your site
  2. Load Fluid script and initialise widget with identity attributes
  3. Widget remains mounted throughout the session; update reactive attributes as needed
  4. On logout, remove widget from the DOM
Action Method Reason
Rotate session ID Update session-id attribute Observed live, even during open transactions
Change user/operator/locale/country/currency Remove element, mount new one These are read once at mount; changing them on a live element does nothing
Open wallet Set open="true" after initialised Wallet only ready after initialisation
End session Remove from DOM Clean shutdown, emits destroyed

This method pre-warms the widget for faster opening but does not open the wallet.

  • Call it once per session, right after loading the script
  • Throttled to roughly once every two seconds: a call made too soon after the previous one returns a promise that rejects with Error('Function call is throttled. Please wait.'), so handle the rejection if you can’t guarantee the spacing
  • Uses countryCode and currencyCode parameters (not country and currency like HTML attributes)
  • Per browser tab — each tab starts fresh
  • Refreshes automatically when credentials expire
  • No “session expired” event is emitted
  • Separate from your session-id — you must keep session-id current

Before the widget initialises, Fluid checks the attributes it was given, and checks the player’s session with your platform or payment provider. If they aren’t accepted, the widget doesn’t open and emits fluid-error with initialisation-failure, code: 'SESSION_REJECTED' and a reason. Check that:

  • operator-id is the one Fluid Payments gave you for this environment;
  • user-id and session-id are the player’s own and current, not an expired or placeholder session;
  • country and currency are the player’s own, as your platform holds them.

If Fluid couldn’t reach your platform or payment provider to check the session, the widget tries once more after a short wait. If that fails too, it emits initialisation-failure with code: 'SESSION_CHECK_UNAVAILABLE' instead. Nothing is wrong with your attributes, and the player can try again shortly.

With programmatic initialisation, the refusal happens before any element exists, so there is no event yet: the promise returned by fluid.init(...) rejects, and its error carries the same reason as its message. Catch that rejection and treat it as the refusal: an element mounted afterwards doesn’t open either, and with only the attributes programmatic initialisation asks for, it reports missing attributes rather than SESSION_REJECTED.

The session can also be refused later: while the cashier is open, or when a page reload restores the widget from the browser instead of initialising it again. There is no initialisation-failure then. The next deposit or withdrawal fails instead, and its deposit-failure or withdrawal-failure event carries errorCode: 'SESSION_REJECTED'. If Fluid couldn’t reach your platform or payment provider to check the session, the code is SESSION_CHECK_UNAVAILABLE instead, and the player can try again shortly.

For security, neither the event nor the rejection says which of these failed. If you can’t find the cause, contact Fluid Payments with the time of the failure and the player’s user-id.

Event Message Meaning
fluid-info initialised Widget is ready to open
fluid-info destroyed Widget has shut down
fluid-error initialisation-failure Configuration failed, for example because of a bad session-id or a network or backend problem. With code: 'SESSION_REJECTED', the player’s session was refused; with code: 'SESSION_CHECK_UNAVAILABLE', it couldn’t be checked for now. See A refused session
fluid-error invalid-init-attributes An init attribute failed validation
fluid-error invalid-user-data User data format incorrect
// Bad: changes on every render
widget.setAttribute('session-id', getLatestCsrfToken());
// Good: stable session identifier
widget.setAttribute('session-id', getStableSessionId());
<!-- Bad: widget exists before login -->
<fluid-widget session-id="" ...></fluid-widget>
// Good: create widget only after login
onLoginSuccess((session) => {
const widget = document.createElement('fluid-widget');
widget.setAttribute('session-id', session.id);
document.body.appendChild(widget);
});
function onLogout() {
document.querySelector('fluid-widget')?.remove();
}

Sessions are per-tab. Ensure initialisation runs in every browser tab — sessions don’t share across tabs.