Skip to main content
In development · demoVerified against 76c1f85Markdown

Embed the PlayerPal widget

The PlayerPal widget is a small capsule that sits on a game provider's page. It tells the player whether PlayerPal is connected ("Earning points") or not ("Connect to earn"), and, where the provider runs the Link, lets the player connect without leaving the game. It never shows Points balances, ledger details or other partners' data; those stay on PlayerPal's own pages. If the widget fails to load, the game plays normally.

In development
You will
  • Load a pinned widget release with Subresource Integrity
  • Configure the widget for your game and integration
  • Hand it a verified launch session and set your CSP
You need
  • Your integration ID (pp_int_…) and the player-portal origin from PlayerPal
  • A backend that consumes the operator launch bootstrap
  • For provider-led linking, the link intent and exchange from Link players
For
Game providers
In development

The widget runs today in PlayerPal's reference provider integration. The release format and the loading rules below are built; the launch-context contract may still change. Contact PlayerPal before building against it.

How it works​

The operator stays in charge of the wallet, the provider owns the game and its rounds, and PlayerPal receives only activity after settlement. Launch tokens, signed assertions and aliases never appear in the game URL.

1Read the release manifest

Each widget release is published at a fixed version path and never changes. Its manifest names the exact file and its integrity digest.

  • The file name carries a fingerprint of its contents, and integrity is a SHA-384 digest.
  • PlayerPal verifies the digest again before serving a release.
  • Release files are served with a one-year immutable cache, Access-Control-Allow-Origin: *, Cross-Origin-Resource-Policy: cross-origin and X-Content-Type-Options: nosniff.
Request
GET /assets/playerpal-widget/0.1.0/manifest.json
manifest.json200
{
"name": "@playerpal/widget",
"version": "0.1.0",
"file": "playerpal-widget.<fingerprint>.iife.js",
"url": "https://<playerpal-asset-host>/assets/playerpal-widget/0.1.0/playerpal-widget.<fingerprint>.iife.js",
"integrity": "sha384-<base64-digest>"
}

2Pin the script with SRI

Copy url into src and integrity into integrity. The browser refuses the script if a single byte differs.

  • crossorigin="anonymous" is required for the integrity check on a cross-origin script.
  • Load it async: the game never waits for the widget.
  • Load the release from PlayerPal. Do not copy, rebuild or bundle the widget into your own build.
  • To upgrade, read the new version's manifest and update both src and integrity.
Checkpoint

The script loads with no integrity error in the console. Change one character of integrity and the browser blocks it while the game still plays.

Your game page
<script
src="https://<playerpal-asset-host>/assets/playerpal-widget/0.1.0/playerpal-widget.<fingerprint>.iife.js"
integrity="sha384-<base64-digest>"
crossorigin="anonymous"
async></script>

3Place and configure the element

Add one <playerpal-widget> element to the page.

AttributeRequiredMeaning
game-idYesThe game this page runs. Session context for another game is ignored
allowed-integrationYesYour integration ID. Context for any other integration is ignored
portal-originYesThe PlayerPal player-portal origin you were given. Link pages from any other origin are refused
hiddenNoHides the widget while present
Method on the elementUse
handleGameplayStarted()Call when a round starts. The widget collapses, then tucks itself to the edge of the screen
revealManual()Call from your own menu or info button to bring the hidden widget back

Only one widget is active per page. It renders in a shadow root, respects reduced-motion settings, and supports keyboard focus and Escape to close its panels.

Element
<playerpal-widget
game-id="your-game-id"
allowed-integration="pp_int_XXXX"
portal-origin="https://<playerpal-player-portal>"></playerpal-widget>
Gameplay hooks
const widget = document.querySelector('playerpal-widget')

// When a round starts
widget?.handleGameplayStarted?.()

// From your own menu or info button
widget?.revealManual?.()

4Hand over the launch session

Once your backend has consumed the launch bootstrap, your page dispatches the verified context as a same-document event. If the widget loads later, it asks for the context again with playerpal:request-session-context, so answer that event too.

The context names the launch, the game, your integration, the linking mode, your own origin as the Link broker, the player-portal origin and your play-session token. The widget accepts it only if every value matches the attributes above and the page's own origin. The play-session token is sent only to your backend, never to PlayerPal. Parent-frame postMessage is not used for launch context.

When the player is connected, the widget dispatches playerpal:alias-ready with the provider alias. Your page can dispatch the same event to hand the widget an alias it already holds.

Linking modeWhat the widget does
pre-linkedThe provider alias already exists; the widget shows "Earning points"
operator-ledThe operator linked the player before launch and passed only the provider alias. Without one, the game continues without PlayerPal
provider-led"Connect to earn" opens the PlayerPal link page in a popup. Your backend creates and exchanges the provider-led Link with your integration credentials

In provider-led mode the widget calls only your own origin. Your backend then calls POST /v1/link-intents and POST /v1/link-results/exchange as described in Link players. The popup times out after five minutes.

Checkpoint

After you dispatch the context, the capsule leaves its loading state and shows "Earning points" or "Connect to earn".

Publish context and receive the alias
const publish = () =>
window.dispatchEvent(
new CustomEvent('playerpal:session-context', { detail: context }),
)

publish()
window.addEventListener('playerpal:request-session-context', publish)

window.addEventListener('playerpal:alias-ready', (event) => {
const { launch_id, integration_id, player_id } = event.detail
// player_id is the provider alias, pp_plr_prv_…
})
Session context (detail)
{
"type": "operator-game:launch-context",
"version": 1,
"launch_id": "<launch-id>",
"demo_session_id": "<session-id>",
"game_id": "your-game-id",
"integration_id": "pp_int_XXXX",
"linking_mode": "provider-led",
"api_base": "<your page origin>",
"player_portal_origin": "https://<playerpal-player-portal>",
"session_token": "ps_XXXX"
}
note

demo_session_id carries your game session's ID. The launch contract is in development, so field names may change.

5Set your Content Security Policy

DirectiveAllow
script-srcThe PlayerPal asset origin. Integrity checking also needs crossorigin="anonymous" on the tag
style-src'unsafe-inline' today, because the widget injects its styles into its shadow root
connect-srcYour own origin ('self'). The widget never calls PlayerPal directly
PopupsDo not sandbox away popups. If you send Cross-Origin-Opener-Policy, use same-origin-allow-popups so the link page can message back

The widget keeps a short-lived flag and the provider alias in sessionStorage for the current tab; storage being unavailable does not break it.

Checkpoint

The browser console shows no CSP violations while the widget loads, checks link status and, in provider-led mode, opens and closes the link popup.

Response headers for your game page
Content-Security-Policy: script-src 'self' https://<playerpal-asset-host>; style-src 'self' 'unsafe-inline'; connect-src 'self'
Cross-Origin-Opener-Policy: same-origin-allow-popups