API Reference

Step 1. Add required info from RevenueCat to web2wave project settings

  • RevenueCat Project ID
  • API keys
  • Entitlement name

Step 2. Add your deeplink

  1. Go to Deeplink tab
  2. Click on help icon on the right to the field to launch Helper
  3. Add base deeplink
    1. https://YYYYY.onelink.me/XXXXXX?af_xp=custom&pid=web2wave&deep_link_value=
  4. Add user properties you want to include
    1. Usually just user_id
  5. Final link will be inserted
    1. https://YYYYY.onelink.me/XXXXXX?af_xp=custom&pid=web2wave&deep_link_value=%7B%22user_id%22%3A%22{user_id}%22%7D&user_id={user_id}

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


3. Read deeplink value and send RevenueCat App User ID to web2wave

Here is an example for AppsFlyer:

  1. Read deeplink_value, extract user_id from JSON
  2. Get RevenueCat App User ID
  3. Send it to web2wave API with web2wave's user_id from deeplink
    1. API endpoint: https://web2-tfsv.readme.io/reference/post_user-properties
    2. iOS SDK – https://github.com/web2wave/web2wave_swift
    3. Flutter SDK – https://github.com/web2wave/web2wave_flutter
    4. Kotlin SDK – https://github.com/web2wave/web2wave_kotlin
    5. Java SDK – https://github.com/web2wave/web2wave_java
import UIKit
import AppsFlyerLib
import Purchases
import Web2Wave

class ViewController: UIViewController, DeepLinkDelegate {
    var userId: String?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        Purchases.configure(withAPIKey: "your_revenuecat_public_api_key") // Initialize RevenueCat
        AppsFlyerLib.shared().deepLinkDelegate = self
        AppsFlyerLib.shared().start() // Set up AppsFlyer SDK
        Web2Wave.shared.apiKey = "your-api-key" // Configure Web2Wave SDK
    }
    
    func didResolveDeepLink(_ result: DeepLinkResult) {
        if case .found = result.status, let deepLink = result.deepLink {
            handleDeepLink(deepLink)
        } else {
            print("Deep link not found or failed: \(String(describing: result.error))")
        }
    }
    
    private func handleDeepLink(_ deepLink: DeepLink) {
        guard let deepLinkValue = deepLink["deep_link_value"] as? String,
              let userData = deepLinkValue.data(using: .utf8),
              let userDict = try? JSONSerialization.jsonObject(with: userData) as? [String: Any],
              let extractedUserId = userDict["user_id"] as? String else {
            print("Failed to parse deep_link_value")
            return
        }
        
        userId = extractedUserId
        print("User ID from deep link: \(extractedUserId)")
        fetchRevenueCatAppUserID()
    }
    
    private func fetchRevenueCatAppUserID() {
        Purchases.shared.getCustomerInfo { customerInfo, error in
            guard let appUserID = customerInfo?.appUserID else {
                print("Failed to fetch RevenueCat user info: \(error?.localizedDescription ?? "Unknown error")")
                return
            }
            print("RevenueCat App User ID: \(appUserID)")
            if let userId = self.userId { Task { await self.sendAppUserIDToWeb2Wave(userId, appUserID) } }
        }
    }
    
    private func sendAppUserIDToWeb2Wave(_ userId: String, _ appUserID: String) async {
        switch await Web2Wave.shared.setRevenuecatProfileID(web2waveUserId: userId, revenueCatProfileID: appUserID) {
        case .success: print("Successfully sent RevenueCat ID to Web2Wave API")
        case .failure(let error): print("Error sending data to Web2Wave API: \(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 com.revenuecat.purchases.Purchases
import com.revenuecat.purchases.PurchasesConfiguration
import com.revenuecat.purchases.CustomerInfo
import com.revenuecat.purchases.interfaces.ReceiveCustomerInfoCallback
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")
        
        // Initialize RevenueCat
        Purchases.configure(PurchasesConfiguration.Builder(this, "your_revenuecat_public_api_key").build())
        
        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)
                fetchRevenueCatProfileID(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 fetchRevenueCatProfileID(userId: String) {
        Purchases.sharedInstance.getCustomerInfo(object : ReceiveCustomerInfoCallback {
            override fun onReceived(customerInfo: CustomerInfo) {
                val revenueCatProfileID = customerInfo.appUserID
                scope.launch {
                    Web2Wave.setRevenuecatProfileID(userId, revenueCatProfileID)
                }
            }
            override fun onError(error: com.revenuecat.purchases.PurchasesError) {
                println("Failed to fetch RevenueCat user info: ${error.message}")
            }
        })
    }

    private fun providePaidContent() = println("Access granted to paid content.")
    private fun showPaywall() = println("Displaying paywall.")
}
import 'package:flutter/material.dart';
import 'package:appsflyer_sdk/appsflyer_sdk.dart';
import 'package:purchases_flutter/purchases_flutter.dart';
import 'package:web2wave/web2wave.dart';

class DeepLinkHandler extends StatefulWidget {
  @override
  _DeepLinkHandlerState createState() => _DeepLinkHandlerState();
}

class _DeepLinkHandlerState extends State<DeepLinkHandler> {
  String? userId;
  late AppsflyerSdk _appsflyerSdk;

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

  void _initializeSDKs() async {
    await Purchases.setup("your_revenuecat_public_api_key"); // Initialize RevenueCat
    Web2Wave.shared.initialize(apiKey: 'your-api-key'); // Configure Web2Wave SDK

    _appsflyerSdk = AppsflyerSdk(
      AppsFlyerOptions(afDevKey: 'your_dev_key', appId: 'your_app_id')
    );
    _appsflyerSdk.onDeepLink((deepLink) => _handleDeepLink(deepLink));
    _appsflyerSdk.startSDK();
  }

  void _handleDeepLink(Map<dynamic, dynamic> deepLink) {
    final deepLinkValue = deepLink['deep_link_value'];
    if (deepLinkValue != null) {
      try {
        final userDict = Map<String, dynamic>.from(deepLinkValue);
        userId = userDict['user_id'];
        if (userId != null) {
          print("User ID from deep link: $userId");
          _fetchRevenueCatAppUserID();
        }
      } catch (e) {
        print("Failed to parse deep_link_value: $e");
      }
    }
  }

  void _fetchRevenueCatAppUserID() async {
    try {
      CustomerInfo customerInfo = await Purchases.getCustomerInfo();
      String appUserID = customerInfo.originalAppUserId;
      print("RevenueCat App User ID: $appUserID");
      if (userId != null) _sendAppUserIDToWeb2Wave(userId!, appUserID);
    } catch (e) {
      print("Failed to fetch RevenueCat user info: $e");
    }
  }

  Future<void> _sendAppUserIDToWeb2Wave(String userId, String appUserID) async {
    final result = await Web2Wave.shared.setRevenuecatProfileID(
      web2waveUserId: userId, revenuecatProfileId: appUserID
    );

    if (result.isSuccess) {
      print("Successfully sent RevenueCat ID to Web2Wave API");
    } else {
      print("Error sending data to Web2Wave API: ${result.errorMessage}");
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Deep Link Handler')),
      body: Center(child: Text('Waiting for deep links...')),
    );
  }
}

Restore purchases in 1-2 seconds: https://www.revenuecat.com/docs/getting-started/restoring-purchases

Purchases.shared.restorePurchases { customerInfo, error in
    // ... check customerInfo to see if entitlement is now active
}

Purchases.sharedInstance.restorePurchasesWith() { customerInfo ->
	//... check customerInfo to see if entitlement is now active
}

4. The entitlement will be granted to the user on RevenueCat

All subscription changes – pauses, cancellations, will be reflected on the user later.