PlaySuper LogoPlaySuper
WebView Integration

Platform Examples

Complete store screens for Android (Kotlin & Java), iOS (Swift), React Native, and Flutter

Each example below is a complete store screen: it builds the store URL with credentials, enables JavaScript and DOM storage, locks to portrait, adds a native close button, and handles back navigation as described in WebView Setup.

Replace YOUR_API_KEY and ACCESS_TOKEN with your API key and the token from Authentication.

Android (Kotlin)

A dedicated Activity with a toolbar close button and hardware-back handling. Lock it to portrait in the manifest:

<activity
    android:name=".StoreActivity"
    android:screenOrientation="portrait" />
import android.net.Uri
import android.os.Bundle
import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.activity.OnBackPressedCallback
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar

class StoreActivity : AppCompatActivity() {

    private lateinit var webView: WebView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_store)

        // Native close button — always available to the player
        findViewById<Toolbar>(R.id.toolbar).setNavigationOnClickListener { finish() }

        webView = findViewById(R.id.webView)
        webView.settings.javaScriptEnabled = true
        webView.settings.domStorageEnabled = true
        webView.webViewClient = WebViewClient() // keep navigation inside the WebView

        // Hardware back: WebView history first, then close the screen
        onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
            override fun handleOnBackPressed() {
                if (webView.canGoBack()) webView.goBack() else finish()
            }
        })

        val storeUrl = Uri.parse("https://store.playsuper.club/").buildUpon()
            .appendQueryParameter("apiKey", "YOUR_API_KEY")
            .appendQueryParameter("authToken", intent.getStringExtra("authToken"))
            .appendQueryParameter("utm_content", "main_menu_button")
            .build()
            .toString()

        webView.loadUrl(storeUrl)
    }
}

Android (Java)

import android.net.Uri;
import android.os.Bundle;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import androidx.activity.OnBackPressedCallback;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;

public class StoreActivity extends AppCompatActivity {

    private WebView webView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_store);

        Toolbar toolbar = findViewById(R.id.toolbar);
        toolbar.setNavigationOnClickListener(v -> finish());

        webView = findViewById(R.id.webView);
        webView.getSettings().setJavaScriptEnabled(true);
        webView.getSettings().setDomStorageEnabled(true);
        webView.setWebViewClient(new WebViewClient());

        getOnBackPressedDispatcher().addCallback(this, new OnBackPressedCallback(true) {
            @Override
            public void handleOnBackPressed() {
                if (webView.canGoBack()) {
                    webView.goBack();
                } else {
                    finish();
                }
            }
        });

        String storeUrl = Uri.parse("https://store.playsuper.club/").buildUpon()
                .appendQueryParameter("apiKey", "YOUR_API_KEY")
                .appendQueryParameter("authToken", getIntent().getStringExtra("authToken"))
                .appendQueryParameter("utm_content", "main_menu_button")
                .build()
                .toString();

        webView.loadUrl(storeUrl);
    }
}

iOS (Swift)

A view controller with a navigation-bar close button, presented inside a UINavigationController. Portrait is enforced via supportedInterfaceOrientations:

import UIKit
import WebKit

class StoreViewController: UIViewController {

    var webView: WKWebView!
    let apiKey = "YOUR_API_KEY"
    let authToken = "ACCESS_TOKEN"

    override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
        return .portrait // the store is portrait-only
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        title = "Rewards Store"
        navigationItem.leftBarButtonItem = UIBarButtonItem(
            barButtonSystemItem: .close,
            target: self,
            action: #selector(closeStore)
        )

        webView = WKWebView(frame: view.bounds)
        webView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        // WKWebView has JavaScript and localStorage enabled by default
        view.addSubview(webView)

        var components = URLComponents(string: "https://store.playsuper.club/")!
        components.queryItems = [
            URLQueryItem(name: "apiKey", value: apiKey),
            URLQueryItem(name: "authToken", value: authToken),
            URLQueryItem(name: "utm_content", value: "main_menu_button"),
        ]

        webView.load(URLRequest(url: components.url!))
    }

    @objc private func closeStore() {
        dismiss(animated: true)
    }
}

// Presenting the store from your game screen:
// let store = UINavigationController(rootViewController: StoreViewController())
// store.modalPresentationStyle = .fullScreen
// present(store, animated: true)

React Native

A full-screen store using react-native-webview, with a close button and Android hardware-back handling:

import React, { useRef, useState, useEffect } from 'react';
import { BackHandler, Button, SafeAreaView, StyleSheet, View } from 'react-native';
import { WebView } from 'react-native-webview';

export function StoreScreen({ authToken, onClose }) {
  const webViewRef = useRef(null);
  const [canGoBack, setCanGoBack] = useState(false);

  const params = new URLSearchParams({
    apiKey: 'YOUR_API_KEY',
    authToken,
    utm_content: 'main_menu_button',
  });

  // Android hardware back: WebView history first, then close the screen
  useEffect(() => {
    const sub = BackHandler.addEventListener('hardwareBackPress', () => {
      if (canGoBack) {
        webViewRef.current?.goBack();
        return true;
      }
      onClose();
      return true;
    });
    return () => sub.remove();
  }, [canGoBack, onClose]);

  return (
    <SafeAreaView style={styles.container}>
      <View style={styles.toolbar}>
        <Button title="Close" onPress={onClose} />
      </View>
      <WebView
        ref={webViewRef}
        source={{ uri: `https://store.playsuper.club/?${params.toString()}` }}
        javaScriptEnabled={true}
        domStorageEnabled={true}
        onNavigationStateChange={(navState) => setCanGoBack(navState.canGoBack)}
      />
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1 },
  toolbar: { flexDirection: 'row', justifyContent: 'flex-end', padding: 8 },
});

Lock the screen to portrait with your navigation library (e.g. orientation: 'portrait' in React Navigation screen options) or a package like react-native-orientation-locker.

Flutter

A full store page using webview_flutter, with an AppBar close button and back handling:

import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';

class StorePage extends StatefulWidget {
  final String authToken;
  const StorePage({super.key, required this.authToken});

  @override
  State<StorePage> createState() => _StorePageState();
}

class _StorePageState extends State<StorePage> {
  late final WebViewController _controller;

  @override
  void initState() {
    super.initState();

    final storeUri = Uri.https('store.playsuper.club', '/', {
      'apiKey': 'YOUR_API_KEY',
      'authToken': widget.authToken,
      'utm_content': 'main_menu_button',
    });

    _controller = WebViewController()
      ..setJavaScriptMode(JavaScriptMode.unrestricted)
      ..loadRequest(storeUri);
  }

  // Back: WebView history first, then close the page
  Future<void> _handleBack() async {
    if (await _controller.canGoBack()) {
      await _controller.goBack();
    } else if (mounted) {
      Navigator.of(context).pop();
    }
  }

  @override
  Widget build(BuildContext context) {
    return PopScope(
      canPop: false,
      onPopInvokedWithResult: (didPop, _) {
        if (!didPop) _handleBack();
      },
      child: Scaffold(
        appBar: AppBar(
          title: const Text('Rewards Store'),
          leading: IconButton(
            icon: const Icon(Icons.close),
            onPressed: () => Navigator.of(context).pop(),
          ),
        ),
        body: WebViewWidget(controller: _controller),
      ),
    );
  }
}

DOM storage is enabled by default in webview_flutter on Android. If you use a different WebView package, make sure localStorage is enabled.

Next, handle token expiry, logout, and account switching in Session Management.