In-App Subscriptions

Take Native In-App Subscriptions - Cross Platform

This is an advanced feature suggested for developers. If you don’t want to worry about security or the complexities of paywalled workflows, we recommend using RevenueCat.

The Despia In-App Subscription SDK enables secure in-app subscriptions across iOS, Android, and web platforms. It provides a seamless way to implement subscription-based features in your mobile applications with enhanced security features and native validation through Apple StoreKit and Google Billing Center.

Important: Make sure to publish a new version via TestFlight to test and use In-App Subscriptions successfully using our SDK.

Key Features

  • Server-side webhooks for subscription status updates

  • Native validation through Apple/Google payment systems

  • Real-time transaction verification

  • Automatic receipt validation

  • Support for subscription management

  • Cross-platform compatibility (iOS, Android, Web)

Prerequisites

Before implementing in-app subscriptions:

  • Published Despia mobile application

  • Apple Developer account or Google Play Developer account

  • Configured bank account in respective app stores

  • Completed tax forms and business information

  • Subscription Product IDs set up in App Store Connect/Google Play Console

Basic Implementation

1. Initialize Subscription Purchase

Single Product ID (iOS and Android)

If your subscription product bundle ID is the same for both iOS and Android:

const product_id = "yoursubscriptionid"; // Your Subscription Product ID
const expired_url = "your_expired_url"; // URL to redirect when subscription expires
const baseUrl = 'inapppurchase://?package=' + product_id;
    
const finalDeeplink = navigator.userAgent === "despia-iphone" || 
                navigator.userAgent === "despia-ipad"
    ? `${baseUrl}&successful_url=""`
    : `${baseUrl}&successful_url=""&expired_url=${expired_url}`;

window.despia = finalDeeplink;

Different Product IDs (Platform-Specific)

If you need to use different subscription product IDs for iOS and Android:

// Example for different subscription product IDs on iOS and Android
let product_id = "yourdefaultsubscriptionid"; // Default subscription ID

// Check for iOS devices
if (navigator.userAgent.includes("despia-iphone") || navigator.userAgent.includes("despia-ipad")) {
    product_id = "your.ios.subscriptionid"; // Use iOS subscription ID
}
// Check for Android devices
else if (navigator.userAgent.includes("despia-android")) {
    product_id = "your.android.subscriptionid"; // Use Android subscription ID
}

const expired_url = "your_expired_url"; // URL to redirect when subscription expires
const baseUrl = 'inapppurchase://?package=' + product_id;
    
const finalDeeplink = navigator.userAgent === "despia-iphone" || 
                navigator.userAgent === "despia-ipad"
    ? `${baseUrl}&successful_url=""`
    : `${baseUrl}&successful_url=""&expired_url=${expired_url}`;

window.despia = finalDeeplink;

2. Variable Tracker Implementation

The VariableTracker is a crucial component that helps your application detect when Despia injects subscription-related variables into your application's window object.

Why use VariableTracker?
  • Despia injects variables into your application after a subscription is completed

  • These variables contain essential transaction data (planID, transactionID, receipts)

  • VariableTracker reliably monitors when these variables become available

  • More robust than callbacks or global functions as it continues checking until variables are found

  • Works even if variables are injected after your tracking code has already executed

  • Persists within the current session until the variables are detected

class VariableTracker {
    constructor(variables, onReady) {
        this.variables = variables;
        this.onReady = onReady;
        this.triggered = false;
        this.processing = false;
        
        // Create tracker element
        this.tracker = document.createElement('div');
        this.tracker.style.display = 'none';
        document.body.appendChild(this.tracker);
        
        // Setup observer with debounce
        let timeout;
        this.observer = new MutationObserver(() => {
            clearTimeout(timeout);
            timeout = setTimeout(() => this.check(), 100);
        });
        
        // Start observing and checking
        this.observer.observe(this.tracker, { attributes: true });
        this.check();
        this.interval = setInterval(() => this.check(), 1000);
    }

    check() {
        if (this.processing || this.triggered) return;
        this.processing = true;

        try {
            const values = {};
            const allSet = this.variables.every(name => {
                const val = window[name];
                // Check for undefined, "n/a" string, or null values
                if (val === undefined || val === "n/a" || val === null) return false;
                values[name] = val;
                return true;
            });

            if (allSet && !this.triggered) {
                this.triggered = true;
                this.cleanup();
                this.onReady(values);
            }
        } catch (err) {
            console.error("Error during check:", err);
        }
        
        this.processing = false;
    }

    cleanup() {
        this.observer.disconnect();
        clearInterval(this.interval);
        this.tracker.remove();
    }
}

3. Track Subscription Variables

Use the VariableTracker to monitor for specific subscription-related variables that Despia will inject into your app:

new VariableTracker(
    ['planID', 'transactionID', 'subreceipts'],
    values => {

        // If using JavaScript simply use the object below as needed.
        console.log("Subscription Success", {
            plan: values.planID,
            transaction: values.transactionID,
            receipt: values.subreceipts
        });
        
        // EXAMPLES FOR SECURE SUBSCRIPTION VALIDATION (for NoCode/LowCode Tools)

        // ---------- WEWEB ----------
        // WEWEB example using wwWorkflow to handle callback natively

        // 1. Create a Native WeWeb Global Worklfow to receive following paramaters:
        // A. plan
        // B. transaction
        // C. receipt

        // 2. Add Logic to that Workflow to send those parameters to your backend for secure validation.

        // 3. Call your Workflow from exactly this line of code here:
        wwLib.wwWorkflow.executeGlobal('YOUR WEWEB WORKFLOW ID', {
            plan: values.planID,
            transaction: values.transactionID,
            receipt: values.subreceipts
        });

        // ---------- WIZED ---------- 

        // WIZED example using Wized's JS API
        v.inappsubscription = {
            plan: values.planID,
            transaction: values.transactionID,
            receipt: values.subreceipts
        }
       
       // Create a request (API Call) that will send "v.inappsubscription" to your server for validation.
       const result = await Wized.requests.execute('REQUEST NAME TO VALIDATE SUBSCRIPTION');
       console.log(result); // Or set result as variable if needed
      
       // ---------- NORDCRAFT / TODDLE ---------- 
       // For Nordcraft / Toddle - simply install the "Despia" Package and listen for the event callback.
       // Clone the package from here: https://toddle.dev/projects/despia_package/branches/main
       // Or run following after registering an "onCallback" Event in your Custom Action:
       ctx.triggerActionEvent("onCallback", {
            plan: values.planID,
            transaction: values.transactionID,
            receipt: values.subreceipts
        });
    }
);

Note: To restore previous subscriptions when a user reinstalls your app or uses it on a new device, refer to our Restore In-App Subscription documentation.

Subscription Flow

1

Initial Subscription

  • User triggers subscription through your UI

  • SDK initiates native subscription flow

  • Returns transaction data including receipt and ID

2

Validation

  • SDK automatically validates subscription with platform (client-side)

  • Returns base64 encoded receipt data + transaction id + bundle

  • Server can verify this data with Apple/Google API

3

Success Handling

  • Receive transaction ID and receipt

  • Send to your server with user authentication

  • Update user access/permissions

Make sure to validate the subscription data via your server on the backend using Apple's / Google's Official APIs - server side handling of subscription data is critical!

Security Best Practices

Backend Verification (Important)

Always verify subscriptions on your backend server to prevent fraud:

When your frontend receives transaction data from the VariableTracker, send the complete object to your backend

Your backend should then verify the subscription with the platform's API:

For Apple App Store:

For Google Play Store:

Only grant access to subscribed content/features after successful backend verification!

Example Frontend Code:

If you're using a no-code/low-code tool, please refer to the examples above. Below is a sample JavaScript implementation showing both front-end and server-side validation.

new VariableTracker(
    ['planID', 'transactionID', 'subreceipts'],
    values => {
        // Send complete subscription data to your backend
        fetch('https://your-backend.com/verify-subscription', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'Authorization': 'Bearer YOUR_AUTH_TOKEN' // Include user authentication
            },
            body: JSON.stringify({
                planId: values.planID,
                transactionId: values.transactionID,
                receipt: values.subreceipts,
                platform: navigator.userAgent.includes("despia-iphone") || 
                          navigator.userAgent.includes("despia-ipad") ? "ios" : "android"
            })
        })
        .then(response => response.json())
        .then(data => {
            if (data.verified === true) {
                // Grant access to subscribed content
                console.log("Subscription verified on backend");
            } else {
                // Handle verification failure
                console.error("Subscription verification failed");
            }
        })
        .catch(error => {
            console.error("Error verifying subscription:", error);
        });
    }
);

Receipt Validation

  • Always validate receipts server-side

  • Use Apple/Google APIs for verification

  • Store transaction IDs for reference

User Authentication

  • Link subscriptions to authenticated users

  • Include device ID or auth token with validation

  • Prevent unauthorized access sharing

Webhook Handling

  • Implement secure webhook endpoints

  • Verify webhook authenticity

  • Process subscription updates in real-time

Testing Tips

Sandbox Testing

  • Use subscription products with shorter billing periods for testing

  • Create test accounts in App Store/Play Console

  • Verify webhook functionality in test environment

Common Issues

  • Ensure bank account is linked in developer console

  • Verify product IDs match exactly

  • Check user agent detection for platform-specific code

Need Help?

If you need assistance implementing the SDK or have questions, contact our support team at support@despia.com

Updated on