Direct integration in mobile app with web2wave API

Step 1. Get API key from project settings

Step 2. Add your deeplink in project settings

📘

Using an MMP? Configure the deeplink below. Not using AppsFlyer, Adjust, or Branch? Skip to Step 3 and use web2wave deferred deeplinks instead — no deeplink setup required.

  1. Go to Deeplinks & Billing tab
  2. Click on help icon on the right to the field to launch Helper
  3. Add base deeplink
  4. Add user properties you want to include
    • Usually just user_id
  5. Final link will be inserted

After the purchase of a subscription on web, user will get this deeplink to install the app.

Step 3. Resolve user_id and check subscription status

With an MMP — read user_id from the deeplink (AppsFlyer example below).

Without an MMP — call identify() on first launch. See web2wave deferred deeplinks.

AppsFlyer example

  1. Read user_id from deeplink value using AppsFlyer

  2. Store user_id locally on the device

  3. Request web2wave API

  4. If "subscriptions" array contains subscription with (subscription.status = active || subscription.status = trialing)

    • then don't show paywalls and give the user paid content
  5. Update subscription status on every app launch, if no result returned - use previous value

  6. In Subscription management section in the app show the user button with "manage_link" URL

  7. 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"),
            ),
          ],
        ),
      ),
    );
  }
}