PlaySuper LogoPlaySuper
WebView Integration

Complete Example & Troubleshooting

The full backend + client flow end to end, plus troubleshooting and links to related guides

This page brings the previous guides together into one end-to-end flow: a backend that owns the API key, and a client that opens the store.

The full flow

┌────────────┐   1. request token           ┌────────────┐   create-with-uuid    ┌───────────┐
│  Your app  │ ───────────────────────────▶ │  Your      │ ────────────────────▶ │ PlaySuper │
│  (client)  │ ◀─────────────────────────── │  backend   │   federatedByStudio   │    API    │
└────────────┘   2. access_token            └────────────┘                       └───────────┘
      │                                           ▲
      │ 3. open store URL in WebView              │ 5. webhook on purchase/refund
      ▼                                           │
┌────────────┐   4. player shops & redeems  ┌───────────┐
│  WebView   │ ────────────────────────────▶│ PlaySuper │
│  (store)   │                              │   store   │
└────────────┘                              └───────────┘
  1. The app asks your backend for a store token.
  2. The backend find-or-creates the player and runs federated login (Authentication).
  3. The app builds the store URL (Store URL) and opens it in a configured WebView (WebView Setup, Platform Examples).
  4. The player browses and redeems rewards inside the store.
  5. Your backend hears about purchases and refunds via webhooks (Coins & Transactions).

Backend (Node.js / Express)

One service that issues store tokens, distributes coins, and receives webhooks:

const express = require('express');
const app = express();
app.use(express.json());

const API_BASE = process.env.NODE_ENV === 'production'
  ? 'https://api.playsuper.club'
  : 'https://dev.playsuper.club';

const headers = {
  'Content-Type': 'application/json',
  'x-api-key': process.env.PLAYSUPER_API_KEY,
};

// 1. Issue a store token for the authenticated app user
app.post('/playsuper/token', async (req, res) => {
  const uuid = req.user.id; // your app's user ID

  // Find-or-create — safe to call every time
  await fetch(`${API_BASE}/player/create-with-uuid`, {
    method: 'POST',
    headers,
    body: JSON.stringify({ uuid }),
  });

  const login = await fetch(`${API_BASE}/player/login/federatedByStudio`, {
    method: 'POST',
    headers,
    body: JSON.stringify({ uuid }),
  });
  const { access_token } = await login.json();

  res.json({ accessToken: access_token });
});

// 2. Award coins when the user completes a rewarded action
app.post('/playsuper/reward', async (req, res) => {
  const response = await fetch(
    `${API_BASE}/coins/${process.env.PLAYSUPER_COIN_ID}/distribute`,
    {
      method: 'POST',
      headers: { ...headers, 'x-game-uuid': req.user.id },
      body: JSON.stringify({ amount: req.body.amount }),
    }
  );
  res.json(await response.json());
});

// 3. React to store purchases and refunds
app.post('/webhooks/playsuper', (req, res) => {
  const { reason, user_uuid, delta, new_balance } = req.body;

  // Skip events from your own distribute calls
  if (reason === 'GAME_CREDIT' || reason === 'GAME_DEBIT') {
    return res.status(200).json({ received: true, skipped: true });
  }

  if (reason === 'PURCHASE_DEBIT') {
    // e.g. notify the user, log the purchase, trigger an achievement
    console.log(`${user_uuid} spent ${Math.abs(delta)} coins, balance ${new_balance}`);
  } else if (reason === 'REFUND_CREDIT') {
    console.log(`${user_uuid} was refunded ${delta} coins`);
  }

  res.status(200).json({ received: true });
});

Verify the X-PlaySuper-Signature header on every webhook before trusting it — see Webhook Security.

Client

The client side is thin — fetch the token, then open the store screen for your platform:

// Pseudocode — the platform-specific screen is in Platform Examples
async function openRewardsStore() {
  let token = getCachedToken();
  if (!token) {
    const res = await fetch('https://your-server.com/playsuper/token', { method: 'POST' });
    token = (await res.json()).accessToken;
    cacheToken(token);
  }
  showStoreScreen(token); // StoreActivity / StoreViewController / StoreScreen / StorePage
}

Full screens for each platform are in Platform Examples.

Troubleshooting

SymptomLikely causeFix
Blank or broken store pageDOM storage disabledEnable localStorage/DOM storage in the WebView settings
Player shown as logged out in the storeMissing or expired authTokenRe-run the federated login and reopen the store with a fresh token
Login works in dev but not productionEnvironment mismatchA token from dev.playsuper.club only works with dev-store.playsuper.club — match API and store environments
Previous user's data appears after account switchStale WebView localStorageOpen the store once with clearSession=true
404 from federated loginPlayer was never createdCall /player/create-with-uuid first — it is find-or-create and safe to call every launch
400 Invalid coin for this game on distribute, or empty rewardsCoin not linked to the gameLink the coin to your game in the console — org-wide coins link automatically
Store layout looks wrongScreen not locked to portraitThe store is portrait-only — lock the hosting screen's orientation
No webhook events arrivingEndpoint unreachable or misconfiguredCheck the delivery history and confirm your endpoint returns a 2xx within 30 seconds
GuideDescription
Console SetupSet up your account, game, coins, and API key
API ReferenceDistribute coins, fetch balances, and manage players via REST
WebhooksServer-side notifications for coin transactions
Touchpoints APIShow reward widgets in your own app UI
Gift Card APIPurchase and distribute gift card vouchers
Unity SDK GuideIf your game is built with Unity, use the SDK instead

Need help?