WebView & Direct Link
Below is the integration guide for WebView and Direct Link
Although we provide support for WebView or direct-link integration, this is not the recommended approach. Whenever possible, you should integrate our official SDK instead.
Using the SDK ensures:
Better tracking accuracy — SDK handles device signals and user sessions more reliably
Stronger fraud protection — Anti-fraud checks and security layers are baked into SDK flows
Automatic updates — You benefit from improvements and bug fixes without touching your integration
Faster integration — No need to manually handle WebView setup, edge cases, or command bridges
WebView or direct links should only be used in special cases where SDK usage is not possible (e.g., HTML-based apps, low-code platforms). Even then, you must strictly follow the rules in this guide to ensure reliability.
Your offerwall can be integrated either with our SDK or by embedding the URL directly via WebView. If you’re using WebView or direct links, please follow the instructions below to avoid common issues.
1. Building the Offerwall URL
You must append correct parameters to ensure tracking and reward delivery.
Base URL:
https://sdk.mychips.io/contentRequired Parameters:
content_id– your assigned adunit IDuser_id– a unique user ID on your side (can be a hash or UUID or numeric)webview– bool
Optional But Highly Recommended
gaid– Google Advertising ID is a unique, randomly generated identifier assigned by Google to Android devices, which enables advertisers to track and measure users’ advertising interactions across apps and services.idfa– Identifier for Advertisers is a unique, randomly generated code that Apple assigns to each user’s device, allowing advertisers to monitor and analyze advertising-related activity.gender–m,f, oroage– user age (numeric 0-100)os_version– version of the os
Final URL Example for iOS users:
https://sdk.mychips.io/content?content_id={your_adunit_id]&user_id={your_user_id}&idfa={your_user_idfa}&age={your_user_age}&gender={your_user_gender}&webview=1Example final URL with values replaced for iOS users:
https://sdk.mychips.io/content?content_id=28617c7e-0178-4b89-b258-74bcf171e&user_id=5d41402abc4b2a76b9719d911017c592&idfa=AEBE52E7-03EE-455A-B3C4-E57283966239&age=27&gender=f&webview=1Final URL Example for ANDROID users:
https://sdk.mychips.io/content?content_id={your_adunit_id]&user_id={your_user_id}&gaid={your_user_gaid}&age={your_user_age}&gender={your_user_gender}&webview=1Example final URL with values replaced for ANDROID users:
https://sdk.mychips.io/content?content_id=28617c7e-0178-4b89-b258-74bcf171e&user_id=5d41402abc4b2a76b9719d911017c592&gaid=9b2f0c84-17a3-4f19-9c51-3e8b0a7c3c91&age=27&gender=f&webview=1Best Practice WebView
These must be followed when integrating the offerwall via WebView or a direct link. Failure to implement them may result in broken reward flows, missing tracking, or bad UX.
✅ Rule
❓ Why It Matters
🛠 What to Do
content_id & user_id &webview=1 are mandatory
Without them, users can't be identified and rewards can't be tracked
Append both in the URL: ?content_id=...&user_id=...
gaid &idfa are highly recommended.
We highly recommend including the GAID or IDFA parameter, depending on the platform, as this helps improve eCPM performance.
Append the parameter to the URL depending on the platform:
iOS:
?idfa=...Android:
?gaid=...
Enable JavaScript in WebView
Required for rendering and logic of the offerwall
webView.getSettings().setJavaScriptEnabled(true);
Enable DOM Storage
Enables session/localStorage — often used by the offerwall
webView.getSettings().setDomStorageEnabled(true);
Disable WebView cache
Prevents bugs from loading outdated or broken content
webView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
Open api.mychips.io/redirect URLs in external browser
These are used for conversion tracking and if not well implemented could affect trackability
Open External Browser
Handle mychips:// URL scheme
Bridge used by offerwall to trigger native logic (e.g. reward sync)
override and ignore this schema
Add a custom error page on failure
Prevents user from seeing a blank screen when offline or error occurs
Override onReceivedError() and show fallback HTML content
Implement proper back navigation logic
Prevents broken UX and enables going back within the WebView
close activity on back press when url contains "home"
Support file uploads in WebView
support require file/image upload/
Use WebChromeClient.onShowFileChooser() and route result back via onActivityResult()
Do not preload
preloading will affect impression tracking and impact eCPM negatively
Code Example
Below are sample codes for different languages.
Note: Replace the values of the Offerwall URL parameters with your actual data. The values used in the code example are for demonstration purposes only.
// Initialize the WebView and configure it
WebView webView = findViewById(R.id.webview);
webView.getSettings().setJavaScriptEnabled(true);
webView.getSettings().setDomStorageEnabled(true);
webView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
webView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
// External link handling
if(url.contains("api.mychips.io/redirect")) {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(intent);
return true;
}
// Custom scheme handling
if(url.startsWith("mychips://")) {
// Trigger native logic here (e.g., reward synchronization)
return true;
}
return false;
}
@Override
public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
// Load error page
view.loadData("<html><body><h3>Failed to load. Please try again later.</h3></body></html>", "text/html", "UTF-8");
}
});
String url = "https://sdk.mychips.io/content?content_id=28617c7e-0178-4b89-b258-74bcf171e&user_id=5d41402abc4b2a76b9719d911017c592&gaid=9b2f0c84-17a3-4f19-9c51-3e8b0a7c3c91&age=27&gender=f&webview=1";
webView.loadUrl(url);
// Initialize the WebView and configure it
val webView = findViewById<WebView>(R.id.webview)
webView.settings.apply {
javaScriptEnabled = true
domStorageEnabled = true
cacheMode = WebSettings.LOAD_NO_CACHE
}
webView.webViewClient = object : WebViewClient() {
override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
url?.let {
if(it.contains("api.mychips.io/redirect")){
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(it)))
return true
}
if(it.startsWith("mychips://")){
// Handle native logic here, e.g., reward synchronization
return true
}
}
return false
}
override fun onReceivedError(view: WebView?, request: WebResourceRequest?, error: WebResourceError?) {
view?.loadData("<html><body><h3>Failed to load. Please try again later.</h3></body></html>", "text/html", "UTF-8")
}
}
val url = "https://sdk.mychips.io/content?content_id=28617c7e-0178-4b89-b258-74bcf171e&user_id=5d41402abc4b2a76b9719d911017c592&gaid=9b2f0c84-17a3-4f19-9c51-3e8b0a7c3c91&age=27&gender=f&webview=1"
webView.loadUrl(url)
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'package:url_launcher/url_launcher.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Offerwall Demo',
theme: ThemeData(primarySwatch: Colors.blue),
home: const OfferwallPage(),
);
}
}
class OfferwallPage extends StatefulWidget {
const OfferwallPage({super.key});
@override
State<OfferwallPage> createState() => _OfferwallPageState();
}
class _OfferwallPageState extends State<OfferwallPage> {
late final WebViewController _controller;
// 用提供的数据拼出 URL
final String offerwallUrl = Uri.https(
'sdk.mychips.io',
'/content',
{
'content_id': '28617c7e-0178-4b89-b258-74bcf171e',
'user_id': '5d41402abc4b2a76b9719d911017c592',
'gaid': '9b2f0c84-17a3-4f19-9c51-3e8b0a7c3c91',
'age': '27',
'gender': 'f',
'webview': '1',
},
).toString();
bool _isLoading = true;
@override
void initState() {
super.initState();
_controller = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..clearCache()
..clearLocalStorage()
..setBackgroundColor(const Color(0x00000000))
..setNavigationDelegate(
NavigationDelegate(
onPageStarted: (String url) {
setState(() => _isLoading = true);
},
onPageFinished: (String url) {
setState(() => _isLoading = false);
},
onNavigationRequest: (NavigationRequest request) async {
final url = request.url;
// 自定义 scheme:奖励同步
if (url.startsWith('mychips://')) {
_syncRewardFromUri(url);
return NavigationDecision.prevent;
}
// 外部链接交给系统浏览器/应用商店
if (url.contains('/redirect') ||
url.contains('play.google.com/store') ||
url.startsWith('market://')) {
final uri = Uri.parse(url);
if (await canLaunchUrl(uri)) {
await launchUrl(uri, mode: LaunchMode.externalApplication);
}
return NavigationDecision.prevent;
}
// 其他在 WebView 内加载
return NavigationDecision.navigate;
},
onWebResourceError: (WebResourceError error) {
_controller.loadHtmlString(
'<html><body><h3>Failed to load. Please try again later.</h3></body></html>',
);
},
),
)
..loadRequest(Uri.parse(offerwallUrl));
}
/// 奖励同步逻辑(示例)
void _syncRewardFromUri(String uriString) {
final uri = Uri.parse(uriString);
final amount = uri.queryParameters['amount'];
final currency = uri.queryParameters['currency'];
if (amount != null && currency != null) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('You received $amount $currency')),
);
// TODO: 本地记账/余额更新逻辑
}
}
@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async {
final currentUrl = await _controller.currentUrl() ?? '';
if (currentUrl.contains('/home')) {
return true;
}
if (await _controller.canGoBack()) {
_controller.goBack();
return false;
}
return true;
},
child: Scaffold(
appBar: AppBar(
title: const Text('Offerwall'),
leading: IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: () async {
if (await _controller.canGoBack()) {
_controller.goBack();
} else {
Navigator.of(context).pop();
}
},
),
),
body: Stack(
children: [
WebViewWidget(controller: _controller),
if (_isLoading)
const Center(child: CircularProgressIndicator()),
],
),
),
);
}
}
import UIKit
import WebKit
class OfferwallViewController: UIViewController, WKNavigationDelegate {
var webView: WKWebView!
override func viewDidLoad() {
super.viewDidLoad()
let webConfiguration = WKWebViewConfiguration()
webConfiguration.preferences.javaScriptEnabled = true
// Use non-persistent data storage to disable caching
webConfiguration.websiteDataStore = .nonPersistent()
webView = WKWebView(frame: self.view.frame, configuration: webConfiguration)
webView.navigationDelegate = self
self.view.addSubview(webView)
if let url = URL(string: "https://sdk.mychips.io/content?content_id=28617c7e-0178-4b89-b258-74bcf171e&user_id=5d41402abc4b2a76b9719d911017c592&idfa=AEBE52E7-03EE-455A-B3C4-E57283966239&age=27&gender=f&webview=1") {
let request = URLRequest(url: url)
webView.load(request)
}
}
// Display an error page if loading fails
func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
let errorHTML = "<html><body><h3>Failed to load. Please try again later.</h3></body></html>"
webView.loadHTMLString(errorHTML, baseURL: nil)
}
// Handle external links and custom URL schemes
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction,
decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
if let urlString = navigationAction.request.url?.absoluteString {
if urlString.contains("api.mychips.io/redirect") {
if let redirectUrl = URL(string: urlString) {
UIApplication.shared.open(redirectUrl)
decisionHandler(.cancel)
return
}
}
if urlString.hasPrefix("mychips://") {
// Handle native logic here, e.g., reward synchronization
decisionHandler(.cancel)
return
}
}
decisionHandler(.allow)
}
}
Last updated