In App Purchases

Take Native In-App Purchases - 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 Purchase SDK v2 enables secure in-app purchases across iOS, Android, and web platforms. This new version introduces enhanced security features, server-side webhooks, 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 Purchases successfully using our V2 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 both consumable and non-consumable purchases

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

Prerequisites

Before implementing in-app purchases:

  • 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

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

Basic Implementation

1. Initialize Purchase

Single Product ID (iOS and Android)

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

const product_id = "yourproductid"; // Your Product ID
const consumable = false; // Set true for consumable purchases
const baseUrl = 'inapppurchase://?package=' + product_id;
    
const finalDeeplink = navigator.userAgent === "despia-iphone" || 
                navigator.userAgent === "despia-ipad"
    ? `${baseUrl}&successful_url=""`
    : `${baseUrl}&successful_url=""&consumable=${consumable}`;

window.despia = finalDeeplink;

Different Product IDs (Platform-Specific)

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

// Simple example for different product IDs on iOS and Android
let product_id = "yourdefaultproductid"; // Default product ID

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

const consumable = false; // Set true for consumable purchases
const baseUrl = 'inapppurchase://?package=' + product_id;
    
const finalDeeplink = navigator.userAgent === "despia-iphone" || 
                navigator.userAgent === "despia-ipad"
    ? `${baseUrl}&successful_url=""`
    : `${baseUrl}&successful_url=""&consumable=${consumable}`;

window.despia = finalDeeplink;

2. Variable Tracker Implementation

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

Why use VariableTracker?
  • Despia injects variables into your application after a purchase 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);
        });
        
        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];
                if (val === undefined || val === "n/a") 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 Purchase Variables

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

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

        // If usng JavaScript simply use the object below as needed.
        console.log("Purchase Success", {
            plan: values.planID,
            transaction: values.transactionID,
            receipt: values.subreceipts
        });
        
        // EXAMPLES FOR SECURE PURCHASE 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.inapppurchase = {
            plan: values.planID,
            transaction: values.transactionID,
            receipt: values.subreceipts
        }
       
       // Create a request (API Call) that will send "v.inapppurchase" to your server for validation.
       const result = await Wized.requests.execute('REQUEST NAME TO VALIADTE PURCHASE');
       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 purchases when a user reinstalls your app or uses it on a new device, refer to our Restore In-App Purchase documentation.

Purchase Flow

1

Initial Purchase

  1. User triggers purchase through your UI

  2. SDK initiates native purchase flow

  3. Returns transaction data including receipt and ID

2

Validation

  1. SDK automatically validates purchase with platform (client-side)

  2. Returns base64 encoded receipt data + transaction id + bundle

  3. Server can verify this data with Apple/Google APIs

3

Success Handling

  1. Receive transaction ID and receipt

  2. Send to your server with user authentication

  3. Update user access/permissions

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

Security Best Practices

Backend Verification (Important)

Always verify purchases on your backend server to prevent fraud:

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

  2. Your backend should then verify the purchase with the platform's API:

    For Apple App Store:

    For Google Play Store:

  3. Only grant access to purchased 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 purchase data to your backend
        fetch('https://your-backend.com/verify-purchase', {
            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 purchased content
                console.log("Purchase verified on backend");
            } else {
                // Handle verification failure
                console.error("Purchase verification failed");
            }
        })
        .catch(error => {
            console.error("Error verifying purchase:", error);
        });
    }
);

Receipt Validation

  • Always validate receipts server-side

  • Use Apple/Google APIs for verification

  • Store transaction IDs for reference

User Authentication

  • Link purchases 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 consumable products for repeated 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