In App Purchases

Take Native In-App Purchases - Cross Platform

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