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 developmentThe 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
integrityis 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-originandX-Content-Type-Options: nosniff.
GET /assets/playerpal-widget/0.1.0/manifest.json
{
"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
srcandintegrity.
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.
<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.
| Attribute | Required | Meaning |
|---|---|---|
game-id | Yes | The game this page runs. Session context for another game is ignored |
allowed-integration | Yes | Your integration ID. Context for any other integration is ignored |
portal-origin | Yes | The PlayerPal player-portal origin you were given. Link pages from any other origin are refused |
hidden | No | Hides the widget while present |
| Method on the element | Use |
|---|---|
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.
<playerpal-widget
game-id="your-game-id"
allowed-integration="pp_int_XXXX"
portal-origin="https://<playerpal-player-portal>"></playerpal-widget>
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 mode | What the widget does |
|---|---|
pre-linked | The provider alias already exists; the widget shows "Earning points" |
operator-led | The 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.
After you dispatch the context, the capsule leaves its loading state and shows "Earning points" or "Connect to earn".
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_…
})
{
"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"
}
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
| Directive | Allow |
|---|---|
script-src | The 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-src | Your own origin ('self'). The widget never calls PlayerPal directly |
| Popups | Do 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.
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.
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