Step 1. Get API key from project settings

Step 2. Add your deeplink in project settings
- Go to Deeplink tab
- Click on help icon on the right to the field to launch Helper
- Add base deeplink
- Add user properties you want to include
- Usually just user_id
- Final link will be inserted

After the purchase of a subscription on web, user will get this deeplink to install the app.
3. Read deeplink value and use "user_id" param to request web2wave API to get Subscription status
Here is an example for AppsFlyer:
-
Read user_id from deeplink value using AppsFlyer
-
Store user_id locally on the device
-
Request web2wave API
-
If "subscriptions" array contains subscription with (subscription.status = active || subscription.status = trialing)
- then don't show paywalls and give the user paid content
-
Update subscription status on every app launch, if no result returned - use previous value
-
In Subscription management section in the app show the user button with "manage_link" URL
-
Use SDK to simplify requests
iOS SDK – https://github.com/web2wave/web2wave_swift
Flutter SDK – https://github.com/web2wave/web2wave_flutter
Kotlin SDK – https://github.com/web2wave/web2wave_kotlin
Java SDK – https://github.com/web2wave/web2wave_java
Sample code:
import UIKit
import AppsFlyerLib
import Adapty
import Web2Wave
class ViewController: UIViewController, DeepLinkDelegate {
var userId: String?
override func viewDidLoad() {
super.viewDidLoad()
Adapty.activate("your_adapty_public_sdk_key")
AppsFlyerLib.shared().deepLinkDelegate = self
AppsFlyerLib.shared().start()
if let userId = UserDefaults.standard.string(forKey: "userId") {
fetchSubscriptionStatus(userId: userId)
}
}
func didResolveDeepLink(_ result: DeepLinkResult) {
guard case .found = result.status, let deepLink = result.deepLink else { return }
handleDeepLink(deepLink: deepLink)
}
private func handleDeepLink(deepLink: DeepLink) {
guard let deepLinkValue = deepLink["deep_link_value"] as? String,
let data = deepLinkValue.data(using: .utf8),
let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
let extractedUserId = json["user_id"] as? String else { return }
self.userId = extractedUserId
UserDefaults.standard.set(extractedUserId, forKey: "userId")
fetchSubscriptionStatus(userId: extractedUserId)
}
private func fetchSubscriptionStatus(userId: String) async {
// Fetch user subscriptions
let isActive = await Web2Wave.shared.hasActiveSubscription(userID: userId)
DispatchQueue.main.async {
isActive ? self.providePaidContent() : self.showPaywall()
}
}
private func providePaidContent() {
print("User has an active subscription. Providing access to paid content.")
}
private func showPaywall() {
print("No active subscription. Displaying paywall.")
}
// Example usage of Web2Wave library methods
private func web2WaveExamples() async {
guard let userId = userId else { return }
// Fetch all user properties
let properties = await Web2Wave.shared.fetchUserProperties(userID: userId)
print("User properties: \(String(describing: properties))")
// Update a user property
let updateResult = await Web2Wave.shared.updateUserProperty(
userID: userId,
property: "theme",
value: "dark"
)
if case .failure(let error) = updateResult {
print("Failed to update user property: \(error)")
}
// Set Adapty Profile ID
let adaptyResult = await Web2Wave.shared.setAdaptyProfileID(
appUserID: userId,
adaptyProfileID: "example_adapty_id"
)
if case .failure(let error) = adaptyResult {
print("Failed to save Adapty profile ID: \(error)")
}
}
}
import android.content.Context
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.appsflyer.AppsFlyerLib
import com.appsflyer.deeplink.DeepLink
import com.appsflyer.deeplink.DeepLinkListener
import kotlinx.coroutines.*
import web2wave.Web2Wave
import org.json.JSONObject
class MainActivity : AppCompatActivity() {
private val scope = CoroutineScope(Dispatchers.IO)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Web2Wave.initWith("YOUR_API_KEY_TO_WEB2WAVE")
AppsFlyerLib.getInstance().apply {
init("yourAppsFlyerDevKey", null, this@MainActivity)
start(this@MainActivity)
registerConversionListener(this@MainActivity, object : DeepLinkListener {
override fun onDeepLinking(deepLink: DeepLink?) {
deepLink?.deepLinkValue?.let { handleDeepLink(it) }
}
override fun onAttributionFailure(error: String?) {
println("Failed deep link: $error")
}
})
}
getSharedPreferences("prefs", Context.MODE_PRIVATE)
.getString("userId", null)?.let { fetchSubscriptionStatus(it) }
}
private fun handleDeepLink(deepLinkValue: String) {
runCatching {
JSONObject(deepLinkValue).getString("user_id").also {
getSharedPreferences("prefs", Context.MODE_PRIVATE).edit().putString("userId", it).apply()
fetchSubscriptionStatus(it)
}
}.onFailure { println("Failed to parse deep link: ${it.message}") }
}
private fun fetchSubscriptionStatus(userId: String) {
scope.launch {
val isActive = runCatching { Web2Wave.hasActiveSubscription(userId) }.getOrDefault(false)
runOnUiThread { if (isActive) providePaidContent() else showPaywall() }
}
}
private fun providePaidContent() = println("Access granted to paid content.")
private fun showPaywall() = println("Displaying paywall.")
}
import 'package:flutter/material.dart';
import 'package:web2wave/web2wave.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
Web2Wave.shared.initialize(apiKey: 'your-api-key');
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: HomeScreen(),
);
}
}
class HomeScreen extends StatefulWidget {
@override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
String? userId;
@override
void initState() {
super.initState();
_initializeUser();
}
Future<void> _initializeUser() async {
final prefs = await SharedPreferences.getInstance();
final storedUserId = prefs.getString('userId');
if (storedUserId != null) {
setState(() {
userId = storedUserId;
});
_fetchSubscriptionStatus(storedUserId);
}
}
Future<void> _handleDeepLink(String deepLinkValue) async {
try {
final Map<String, dynamic> json = deepLinkValue as Map<String, dynamic>;
final extractedUserId = json['user_id'] as String?;
if (extractedUserId != null) {
final prefs = await SharedPreferences.getInstance();
await prefs.setString('userId', extractedUserId);
setState(() {
userId = extractedUserId;
});
_fetchSubscriptionStatus(extractedUserId);
}
} catch (e) {
print('Failed to parse deep link: $e');
}
}
Future<void> _fetchSubscriptionStatus(String userId) async {
final isActive = await Web2Wave.shared.hasActiveSubscription(web2waveUserId: userId);
if (isActive) {
_providePaidContent();
} else {
_showPaywall();
}
}
void _providePaidContent() {
print("User has an active subscription. Providing access to paid content.");
}
void _showPaywall() {
print("No active subscription. Displaying paywall.");
}
Future<void> _web2WaveExamples() async {
if (userId == null) return;
// Fetch user properties
final properties = await Web2Wave.shared.fetchUserProperties(web2waveUserId: userId!);
print("User properties: ${properties.toString()}");
// Update a user property
final updateResult = await Web2Wave.shared.updateUserProperty(
web2waveUserId: userId!,
property: "theme",
value: "dark",
);
if (!updateResult.isSuccess) {
print("Failed to update user property: ${updateResult.errorMessage}");
}
// Set Adapty Profile ID
final adaptyResult = await Web2Wave.shared.setAdaptyProfileID(
web2waveUserId: userId!,
adaptyProfileId: "example_adapty_id",
);
if (!adaptyResult.isSuccess) {
print("Failed to save Adapty profile ID: ${adaptyResult.errorMessage}");
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Web2Wave Example")),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(userId != null ? "User ID: $userId" : "No User ID"),
ElevatedButton(
onPressed: _web2WaveExamples,
child: Text("Test Web2Wave"),
),
],
),
),
);
}
}