The Unity Offerwall SDK: Complete C# Integration Guide (2026)

·

Offerwall Placement Strategy






The Unity Offerwall SDK: Complete C# Integration Guide (2026)



The Unity Offerwall SDK: Complete C# Integration Guide (2026)

Published August 22, 2026 — by the Perkox Developer Team

Unity is the engine behind a huge share of mobile games, and monetization is a core concern for every game developer. An offerwall lets your players earn in-game currency by completing real-world tasks — surveys, app downloads, free trials — which you then convert into gems, coins, or premium items. The Perkox Unity Offerwall SDK brings this capability to Unity with a clean C# API, native Android and iOS plugins, and seamless Unity Package Manager (UPM) installation.

This guide covers the complete integration: prerequisites, UPM installation, C# initialization, event handlers for rewards, Android and iOS build settings, server-side postback setup, testing, debugging, and an FAQ. Whether you are building a casual mobile game or a complex RPG, by the end of this guide your Unity project will display offers, track completions, and credit players securely.

If you are unfamiliar with how offerwalls work, start with our introduction to offerwalls. For native platform details, refer to the Android SDK guide and the iOS SDK guide. The postback guide covers server-side reward verification, and the security best practices guide is essential reading for protecting your integration.

Table of Contents

  1. Prerequisites
  2. Installation via Unity Package Manager
  3. C# Initialization
  4. Showing the Offerwall
  5. Event Handlers for Rewards
  6. Android and iOS Build Settings
  7. Server-Side Postback Setup
  8. Testing and Debugging
  9. FAQ

1. Prerequisites

Before installing the Perkox Unity SDK, verify your environment meets these requirements:

Requirement Minimum
Unity 2021.3 LTS or later (including Unity 6)
Scripting Backend Mono or IL2CPP
Android minSdkVersion 23 (Android 6.0)
iOS Deployment Target 14.0
Xcode (for iOS builds) 15.0+
Perkox Publisher Account Required — sign up free
App ID & API Key Available in the Perkox dashboard

You will also need your Postback Secret for server-side verification, available in the Perkox dashboard under App Settings → Postback. If you have not registered your game yet, create a publisher account here.

2. Installation via Unity Package Manager

The Perkox Unity SDK is distributed as a UPM package hosted on Perkox’s registry. You can install it through the Unity Editor or by editing your manifest.json directly.

2.1 Installing via the Unity Editor

  1. Open your Unity project.
  2. Go to Window → Package Manager.
  3. Click the + button in the top-left corner.
  4. Select Add package from git URL….
  5. Enter the Perkox SDK git URL:
    https://github.com/perkox/unity-offerwall-sdk.git
  6. Click Add. Unity will download and import the package.

2.2 Installing via manifest.json

Alternatively, add the package directly to your project’s Packages/manifest.json:

{
  "dependencies": {
    "com.perkox.offerwall": "https://github.com/perkox/unity-offerwall-sdk.git#1.2.0",
    "com.unity.modules.android": "1.0.0",
    "com.unity.modules.ios": "1.0.0"
  }
}

Replace 1.2.0 with the latest version number. Check the Perkox documentation for the current release.

2.3 Importing Native Plugins

After installation, the package automatically places native plugins in the correct folders:

  • Plugins/Android/perkox-offerwall.aar — Android native library
  • Plugins/iOS/PerkoxOfferwall.framework — iOS native framework
  • Plugins/Perkox/PerkoxOfferwall.cs — C# binding script

Unity’s plugin importer settings are pre-configured, so you typically do not need to adjust them. Verify that the Android plugin is set to Android platform only and the iOS framework to iOS only in the Inspector.

3. C# Initialization

Initialize the SDK early in your game’s lifecycle — typically in a bootstrap scene or an initial-loading MonoBehaviour. The initialization call configures the SDK with your credentials and the current player’s ID.

using UnityEngine;
using Perkox;

public class PerkoxManager : MonoBehaviour
{
    private const string APP_ID = "your-app-id";
    private const string API_KEY = "your-publisher-api-key";

    private async void Start()
    {
        var options = new PerkoxInitOptions
        {
            AppId = APP_ID,
            ApiKey = API_KEY,
            UserId = GetPlayerId(),       // your internal player ID
            IsTestMode = Debug.isDebugBuild,
            LogLevel = PerkoxLogLevel.Info
        };

        bool success = await PerkoxOfferwall.InitializeAsync(options);

        if (success)
        {
            Debug.Log("[Perkox] SDK initialized successfully");
        }
        else
        {
            Debug.LogError("[Perkox] SDK initialization failed");
        }
    }

    private string GetPlayerId()
    {
        // Return your game's stable player ID
        // e.g., from PlayerPrefs, a backend auth call, or PlayFab
        return PlayerPrefs.GetString("player_id", "default-player");
    }
}

PerkoxInitOptions Structure

namespace Perkox
{
    public class PerkoxInitOptions
    {
        public string AppId;
        public string ApiKey;
        public string UserId;
        public bool IsTestMode;
        public PerkoxLogLevel LogLevel;
        public Dictionary<string, string> CustomParams;
    }

    public enum PerkoxLogLevel
    {
        None,
        Error,
        Info,
        Debug
    }
}
Best practice: Use Debug.isDebugBuild for IsTestMode so development builds use test offers (instant conversion) while release builds use real offers. This avoids accidentally shipping with test mode enabled.

Making the Manager Persistent

Attach the PerkoxManager script to a GameObject in your first scene and mark it with DontDestroyOnLoad so the SDK persists across scene changes:

void Awake()
{
    DontDestroyOnLoad(gameObject);
}

4. Showing the Offerwall

Once initialized, showing the offerwall is a single async call. The SDK opens a native view controller (iOS) or activity (Android) over your Unity game.

using UnityEngine;
using Perkox;
using UnityEngine.UI;

public class OfferwallButton : MonoBehaviour
{
    public Button earnButton;

    private void Start()
    {
        earnButton.onClick.AddListener(OnEarnButtonClicked);
    }

    private async void OnEarnButtonClicked()
    {
        bool hasOffers = await PerkoxOfferwall.HasOffersAvailableAsync();

        if (!hasOffers)
        {
            Debug.Log("[Perkox] No offers available for this user");
            return;
        }

        await PerkoxOfferwall.ShowOfferwallAsync(new PerkoxShowOptions
        {
            Placement = "store_button",
            OnClose = () =>
            {
                Debug.Log("[Perkox] Offerwall closed by player");
                RefreshPlayerBalance();
            }
        });
    }

    private void RefreshPlayerBalance()
    {
        // Refresh the player's currency display
    }
}

Placement Tags

Placements let you track which entry point generates the most revenue. Common placements for games:

  • store_button — next to the in-app purchase button in your shop
  • game_over — shown at the end of a level or match
  • rewarded_prompt — when a player tries to buy something they cannot afford
  • main_menu — a button on the main menu screen

5. Event Handlers for Rewards

The SDK emits C# events for reward completions, offerwall lifecycle, and errors. Subscribe to these events to update your game UI in real time. Remember: client-side events are for UX only — always verify rewards server-side via postback.

using UnityEngine;
using Perkox;

public class PerkoxEventListener : MonoBehaviour
{
    private void OnEnable()
    {
        PerkoxEvents.OnReward += HandleReward;
        PerkoxEvents.OnOfferwallOpen += HandleOfferwallOpen;
        PerkoxEvents.OnOfferwallClose += HandleOfferwallClose;
        PerkoxEvents.OnError += HandleError;
    }

    private void OnDisable()
    {
        PerkoxEvents.OnReward -= HandleReward;
        PerkoxEvents.OnOfferwallOpen -= HandleOfferwallOpen;
        PerkoxEvents.OnOfferwallClose -= HandleOfferwallClose;
        PerkoxEvents.OnError -= HandleError;
    }

    private void HandleReward(PerkoxReward reward)
    {
        Debug.Log($"[Perkox] Reward earned: {reward.Payout} coins " +
                  $"from offer '{reward.OfferName}' " +
                  $"(txn: {reward.TransactionId})");

        // Show reward animation / toast in-game
        ShowRewardAnimation(reward.Payout, reward.OfferName);

        // NOTE: Do NOT credit the player's actual balance here.
        // The server-side postback is the source of truth.
    }

    private void HandleOfferwallOpen()
    {
        Debug.Log("[Perkox] Offerwall opened");
        // Pause game audio / music
        AudioListener.pause = true;
    }

    private void HandleOfferwallClose()
    {
        Debug.Log("[Perkox] Offerwall closed");
        // Resume game audio
        AudioListener.pause = false;
    }

    private void HandleError(PerkoxError error)
    {
        Debug.LogError($"[Perkox] Error {error.Code}: {error.Message}");
    }

    private void ShowRewardAnimation(int payout, string offerName)
    {
        // Trigger your game's reward UI (e.g., a particle effect + text popup)
    }
}

PerkoxReward Structure

namespace Perkox
{
    public struct PerkoxReward
    {
        public string TransactionId;   // unique per completion
        public string OfferId;
        public string OfferName;
        public int Payout;             // in your virtual currency units
        public int PayoutUsdCents;     // in USD cents
        public string UserId;
        public long Timestamp;         // Unix epoch ms
    }

    public struct PerkoxError
    {
        public string Code;
        public string Message;
    }
}
Security: Client-side reward events can be intercepted or spoofed. Always credit the player’s actual balance based on the server-side postback. Use the C# event only for immediate UI feedback like a reward animation. For comprehensive protection strategies, read our security best practices guide.

6. Android and iOS Build Settings

6.1 Android Build Settings

The SDK’s UPM package includes a Unity Plugin manifest (perkox-offerwall.androidlib) that automatically merges required permissions and activities into your AndroidManifest.xml. Verify the following in your project settings:

Player Settings → Other Settings → Identification:

Setting Required Value
Minimum API Level Android 6.0 (API level 23)
Scripting Backend IL2CPP (recommended) or Mono
Target Architectures ARM64 (required for Google Play); ARMv7 optional

Gradle configuration: The SDK’s AAR includes its own dependencies, which Unity’s Gradle resolver handles automatically. If you use a custom mainTemplate.gradle, ensure it includes:

dependencies {
    implementation 'androidx.appcompat:appcompat:1.6.1'
    implementation 'com.google.android.material:material:1.11.0'
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3'
}

ProGuard / R8: If you enable minification, add these rules to your proguard-user.txt:

-keep class com.perkox.offerwall.** { *; }
-keepclassmembers class com.perkox.offerwall.** { *; }
-dontwarn com.perkox.offerwall.**

For the complete native Android configuration, see the Android SDK guide.

6.2 iOS Build Settings

The SDK includes a PostProcessBuild script that automatically configures the Xcode project after Unity generates it. This script adds the Perkox framework to the project, sets the deployment target, and injects required Info.plist entries. Verify the following:

Player Settings → Other Settings → Configuration:

Setting Required Value
Target iOS Version 14.0 or later
Scripting Backend IL2CPP (required for iOS)
Architecture ARM64
Camera Usage Description Required if any offer uses the camera

Info.plist entries (auto-injected):

// The PostProcessBuild script adds these automatically:
// - LSApplicationQueriesSchemes: https
// - NSAppTransportSecurity: allows loading offerwall content over HTTPS

If your game already uses a PostProcessBuild script, ensure there are no conflicts. The Perkox script runs at priority 100 and handles only Perkox-specific modifications. For the complete native iOS configuration, see the iOS SDK guide.

6.3 Universal Windows Platform (UWP) and Other Platforms

The Perkox Unity SDK currently supports Android and iOS. If you are building for other platforms (UWP, WebGL, Standalone), the SDK’s C# layer will compile but InitializeAsync will return false and ShowOfferwallAsync will be a no-op. This lets you keep a single codebase without conditional compilation for unsupported platforms.

// Safe to call on any platform — no-op on unsupported ones
#if UNITY_ANDROID || UNITY_IOS
    await PerkoxOfferwall.ShowOfferwallAsync(options);
#else
    Debug.Log("[Perkox] Offerwall not supported on this platform");
#endif

7. Server-Side Postback Setup

The postback is the secure channel through which Perkox notifies your backend when a player completes an offer. This is where you actually credit the player’s balance. Configure the postback URL in the Perkox dashboard under App Settings → Postback.

Postback URL Format

https://api.yourgame.com/perkox/postback?user_id={user_id}&txn_id={txn_id}&payout={payout}&offer_id={offer_id}&signature={signature}

Perkox replaces the placeholders with actual values and appends an HMAC-SHA256 signature. Your server must verify the signature before crediting the player.

Server-Side Verification (C# / ASP.NET Core)

using Microsoft.AspNetCore.Mvc;
using System.Security.Cryptography;
using System.Text;

[ApiController]
[Route("perkox/postback")]
public class PerkoxPostbackController : ControllerBase
{
    private const string PERKOX_SECRET = "your-postback-secret";
    private readonly IGameService _gameService;

    public PerkoxPostbackController(IGameService gameService)
    {
        _gameService = gameService;
    }

    [HttpGet]
    public async Task<IActionResult> HandlePostback(
        [FromQuery] string user_id,
        [FromQuery] string txn_id,
        [FromQuery] string payout,
        [FromQuery] string offer_id,
        [FromQuery] string signature)
    {
        // Reconstruct the signature
        var data = $"{user_id}{txn_id}{payout}{offer_id}";
        using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(PERKOX_SECRET));
        var expectedSigBytes = hmac.ComputeHash(Encoding.UTF8.GetBytes(data));
        var expectedSig = BitConverter.ToString(expectedSigBytes)
            .Replace("-", "").ToLower();

        if (signature != expectedSig)
        {
            return Unauthorized("Invalid signature");
        }

        // Idempotency check — prevent double-crediting
        if (await _gameService.IsTransactionProcessedAsync(txn_id))
        {
            return Ok("OK"); // Already processed, acknowledge with 200
        }

        // Credit the player
        var payoutAmount = int.Parse(payout);
        await _gameService.CreditPlayerAsync(user_id, payoutAmount, txn_id);

        return Ok("OK");
    }
}
Always return 200: Perkox retries postbacks that do not receive a 200 status. Return 200 even for duplicate transactions to stop the retry loop. For the full postback specification, see our complete postback guide.

Node.js Alternative

const express = require('express');
const crypto = require('crypto');

const app = express();
const PERKOX_SECRET = 'your-postback-secret';

app.get('/perkox/postback', async (req, res) => {
  const { user_id, txn_id, payout, offer_id, signature } = req.query;
  const data = `${user_id}${txn_id}${payout}${offer_id}`;
  const expectedSig = crypto
    .createHmac('sha256', PERKOX_SECRET)
    .update(data)
    .digest('hex');

  if (signature !== expectedSig) {
    return res.status(403).send('Invalid signature');
  }

  if (await isDuplicateTxn(txn_id)) {
    return res.status(200).send('OK');
  }

  await creditPlayer(user_id, parseInt(payout, 10), txn_id);
  res.status(200).send('OK');
});

Player ID Consistency

The UserId you pass to InitializeAsync in Unity must match the user_id in the postback. Use a stable, server-assigned player ID — never a device-specific identifier, because players switch devices. If you use PlayFab, Firebase Auth, or a custom backend, use that service’s user ID.

8. Testing and Debugging

8.1 Test Mode

When IsTestMode is true, the SDK displays test offers that convert instantly. This lets you verify your entire flow — show offerwall, complete offer, receive C# event, receive postback — without waiting for real offer completions.

var options = new PerkoxInitOptions
{
    AppId = APP_ID,
    ApiKey = API_KEY,
    UserId = "test-player-1",
    IsTestMode = true,
    LogLevel = PerkoxLogLevel.Debug
};

await PerkoxOfferwall.InitializeAsync(options);

8.2 Log Levels

Level Output
None No logs
Error Errors only (production default)
Info Errors + lifecycle events
Debug Full verbose logs including native bridge calls

With PerkoxLogLevel.Debug, the SDK logs all native bridge calls to the Unity Console, visible in both the Editor and device logs (logcat on Android, Console.app on iOS).

8.3 Testing on Device

Test mode offers work on real devices and in the Unity Editor’s device simulation. For full end-to-end testing, build to a physical device:

// Build and run on Android
File → Build Settings → Android → Build and Run

// Build and run on iOS
File → Build Settings → iOS → Build
// Then open the generated Xcode project and run on device

8.4 Debugging Postbacks

Use the Postback Tester in the Perkox dashboard to send a test postback to your server. This verifies your endpoint, signature validation, and idempotency logic without completing a real offer. Check your server logs to confirm receipt and processing.

8.5 Common Issues

Issue Cause Fix
SDK not found after install UPM package not imported Check Package Manager; verify manifest.json URL
iOS build fails in Xcode Framework not linked or deployment target too low Set iOS target to 14.0+; ensure PostProcessBuild ran
Android build fails (Gradle) Missing dependencies or ProGuard stripping Add Gradle deps and ProGuard keep rules
Offerwall blank in release build IsTestMode left true Set IsTestMode = Debug.isDebugBuild
Postback not received Endpoint unreachable or not returning 200 Test with curl; check server firewall; ensure 200 response
No reward event in Unity Event handler not subscribed or GameObject destroyed Subscribe in OnEnable; use DontDestroyOnLoad

9. FAQ

Does the Perkox Unity SDK work with both Android and iOS builds?

Yes. The SDK includes native plugins for both Android (Kotlin/Java via AAR) and iOS (Swift via framework). A single C# API handles both platforms through platform-specific compilation. You do not need to write separate code for each platform.

Can I use the SDK with Unity’s IL2CPP backend?

Yes. The SDK is fully compatible with IL2CPP, which is required for iOS builds and recommended for Android 64-bit builds. The native plugins are invoked through P/Invoke and Unity’s platform-specific compilation directives.

Do I need to modify the AndroidManifest.xml or Info.plist?

The SDK’s UPM package includes a Unity Plugin manifest that automatically merges required permissions and activities into your AndroidManifest.xml. For iOS, required Info.plist entries are injected via the PostProcessBuild script. You typically do not need to edit these files manually.

How do I handle rewards safely in a Unity game?

Use client-side C# events for immediate UI feedback (showing a reward animation), but credit the player’s actual balance via the server-side postback. The postback sends an HTTP request to your backend with an HMAC signature. Verify the signature on your server before crediting.

What Unity versions are supported?

The SDK supports Unity 2021.3 LTS and later, including Unity 6 (2024+). Both the Mono and IL2CPP scripting backends are supported. The minimum API level for Android is 23 (Android 6.0), and the minimum iOS deployment target is 14.0.

Conclusion

The Perkox Unity Offerwall SDK gives game developers a clean C# API to integrate offerwall monetization with native performance on both Android and iOS. With UPM installation, automatic native plugin configuration, event-driven reward handling, and a secure server-side postback system, you can go from package import to first reward in a single development session.

Unity games are particularly well-suited to offerwall monetization because players already expect virtual currencies and in-game economies. By placing the offerwall button next to your in-app purchase button, at the game-over screen, or when a player cannot afford an item, you give free-to-play users a way to earn currency without spending real money — while you still earn revenue from each completed offer.

Always pair the SDK with a properly configured server-side postback and follow the security best practices to protect your game’s economy from abuse.

Ready to monetize your Unity game?

Create your free Perkox publisher account and get your API keys in minutes.

Sign up as a publisher →

Read the full API documentation →

Related guides: What is an Offerwall? · Android SDK Guide · iOS SDK Guide · Postback Guide · Security Best Practices


Start monetizing your app with Perkox.

One SDK. Android, iOS, React Native, Flutter, Unity. A premium reward layer for your non-paying users — live in about 10 minutes.

Related articles