ayeT-Studios Publisher Unity SDK Integration Guide (v1.7.1)



Updates

2020-06-11: v1.7.1 - Updated documentation to introduce new parametars to differentiate chargebacks from conversions
2020-07-29: v1.7 - Updated to latest Android & iOS SDKs, based on Unity 2019 LTS
2019-07-19: v1.6 - Updated wrapper & SDKs with adslot changes, recompiled iOS SDK with Swift 5
2019-02-01: v1.5 - Fixed conversion tracking issues under certain conditions (application lifecycle monitoring improved)
2018-12-04: v1.4 - Updated to Android SDK 3.0 - support for API 26+ build targets, bugfixes & improved handling under poor network conditions
2018-09-29: v1.3 - Updated iOS SDK to Xcode 10 and Swift 4.2, stripped debug symbols
2018-07-09: v1.2 - Removed Simulator Code from iOS SDK, fixed iTunes validation issues with Xcode 9+
2018-07-02: v1.1 - Updated Unity SDK (iOS) to Unity 2018 / Swift 4.1 / XCode 9.4, added post-install script for automatic XCode configuration
2018-01-20: v1.0 - Initial Release of our Publisher SDK (Unity)


Quick 1.3 To 1.7 Migration Guide

- Update your AyetSdk.showOfferwall() calls to pass the target offerwall adslot name as parameter



Introduction

The ayeT-Studios Publisher SDK allows you to easily monetize your app and reward your users with in-app currency. Your users get access to our offerwalls, allowing them to earn currency for completing tasks such as trying new apps, answering surveys, signing up for free trials or watching video ads. The integration is simple, allows both managed (our servers store user information and balances) and unmanaged (you receive callbacks and handle user wallets yourself) currencies and also works well alongside traditional in-app purchases.




Topics

  1. Prerequisites
  2. Download The SDK
  3. Add The Package To Your Unity Project
  4. Android Specific: Update Or Create AndroidManifest.xml File
  5. Initialize The SDK & Managed User Balances
  6. Check User Balances (Managed Currency Handling)
  7. Show The Offerwall
  8. Unity Example Script
  9. Appendix I: Unmanaged Currency Handling / Conversion Callbacks
  10. Appendix II: Assets/Plugins/Android/AndroidManifest.xml Template





1. Prerequisites


Before integrating the SDK in your app, you should sign up for a publisher account.
Afterwards login as publisher and create a new Android App Placement and/or iOS App Placement using the correct android package name or iOS bundle identifier you intend to use for your final apps.
This is important since we'll generate APP_KEYs / Identifiers for you which have to be added to your Unity app.
So for each platform you intend to build on, you should create a separate placement and adslots (e.g. 2 placements for your Android / iOS cross-platform app).

The minimum required OS version is iOS 11.0 (and Swift 5.x ABI) and Android 5.1+.

2. Download The SDK


You can download the lastest version of our publisher package here:
 AyetUnityPlugin_v1.7.unitypackage

3. Add The Package To Your Unity Project


Open your Unity project
  1. Assets -> Import Package -> Custom Package
  2. Select the AyetUnityPlugin_vVersion.unitypackage file
  3. Press the "Import" button to complete the process

4. Android Specific: Update Or Create AndroidManifest.xml File


If you're planning to build against Android, please make sure you have an AndroidManifest.xml file in Assets/Plugins/Android/. If no AndroidManifest exists, you can check out the following template: Appendix II: AndroidManifest.xml Template.

Add these app permissions to the manifest:

<uses-permission android:name="android.permission.INTERNET" /> <!-- mandatory permission -->
<uses-permission android:name="android.permission.READ_PHONE_STATE" /> <!-- optional -->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <!-- optional -->	                

Add our offerwall activity to your <application> scope:

<activity
    android:name="com.ayetstudios.publishersdk.OfferwallActivity"
    android:configChanges="orientation|screenSize">
    <intent-filter android:label="offer">
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:scheme="offer" android:host="com.example.myapplication" /> <!-- Replace with your lower case package name -->
    </intent-filter>
</activity>
<activity android:name="com.ayetstudios.publishersdk.VideoActivity" android:configChanges="orientation|screenSize" />	                

5. Initialize The SDK & Managed User Balances


In this step, we are going to initialize the SDK. We also use callbacks to track the user's account balance - this is optional and not required if you're planning to manage user balances on your own servers. Attention: The username or external identifier passed in the init call (e.g. username, hashed email address, etc.) will be accessible in the conversion callbacks through the {external_identifier} parameter.

First, the SDK needs to be initialized like this (APP_KEY is the placement identifier you can find in your dashboard in the android or ios placement details):

#if UNITY_IOS
AyetSdk.init ("<YOUR_IOS_PLACEMENT_APP_KEY>", "<username (external identifier)>", this);
#endif
#if UNITY_ANDROID
AyetSdk.init ("<YOUR_ANDROID_PLACEMENT_APP_KEY>", "<username (external identifier)>", this);
#endif

Implement the SdkUserBalance interface in order to get notified when user balance changes occur:

public void userBalanceInitialized (int available_currency, int pending_currency, int spent_currency){
        Debug.Log("Your::Script::userBalanceInitialized => available_currency: " + available_currency.ToString() + "  pending_currency: "+pending_currency.ToString() + "  spent_currency: " + spent_currency.ToString() );
}
userBalanceInitialized is triggered after calling AyetSdk.init().

public void userBalanceChanged (int available_currency, int pending_currency, int spent_currency){
        Debug.Log("YourScript::userBalanceChanged => available_currency: " + available_currency.ToString() + "  pending_currency: "+pending_currency.ToString() + "  spent_currency: " + spent_currency.ToString() );
}
userBalanceChanged is triggered if the user balance changes while running the app.

If you want to spend user currency, for example if the user clicks a "purchaseInAppItem" button, you can utilize the deductUserBalance function:

AyetSdk.deductUserBalance (<DEDUCT_AMOUNT>, this);
Implement the SdkDeductCallback interface in order to get notified if a deduction succeeded:

public void deductSuccess (){
    Debug.Log ("YourScript::deductSuccess");
}
public void deductFailed (){
    Debug.Log ("YourScript::deductFailed");
}

6. Check User Balances (Managed Currency Handling)


After initializing the SDK, you can check the current user balances anywhere in your code:

AyetSdk.getAvailableCurrency();
AyetSdk.getPendingCurrency ();
AyetSdk.getSpentCurrency ();

7. Show The Offerwall


Showing the offerwall is straight-forward, you can simply call AyetSdk.showOfferwall:

AyetSdk.showOfferwall("your offerwall adslot name");
showOfferwall starts our offerwall activity and displays available tasks for the user to complete.

8. Unity Example Script


using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ExampleScript : MonoBehaviour , SdkUserBalance  , SdkDeductCallback  {

    public void userBalanceInitialized (int available_currency, int pending_currency, int spent_currency){

        Debug.Log("ExampleScript::userBalanceInitialized => available_currency: " + available_currency.ToString() + "  pending_currency: "+pending_currency.ToString() + "  spent_currency: " + spent_currency.ToString() );
    }

    public void userBalanceChanged (int available_currency, int pending_currency, int spent_currency){

        Debug.Log("ExampleScript::userBalanceChanged => available_currency: " + available_currency.ToString() + "  pending_currency: "+pending_currency.ToString() + "  spent_currency: " + spent_currency.ToString() );
    }

    public void deductSuccess (){

        Debug.Log ("ExampleScript::deductSuccess");
    }

    public void deductFailed (){

        Debug.Log ("ExampleScript::deductFailed");
    }

    void Start () {
    }

    void Update () {

        if (Input.touchCount == 1) {

            Touch touch = Input.touches [0];

            if (touch.phase == TouchPhase.Ended) {

                if (Input.GetTouch (0).position.y > (Screen.height/2)) {
                    //sdk initialization
                    AyetSdk.init ("abcde123abcde123abcde123", "user_id_123", this);

                } else {

                    if (Input.GetTouch (0).position.x < (Screen.width/3)) {
                        //show offerwall
                        AyetSdk.showOfferwall("offerwall adslot name");
                    }
                    else if(Input.GetTouch (0).position.x > ((Screen.width/3)*2)){
                        //show user current balance
                        Debug.Log ("ExampleScript  Available currency: " + AyetSdk.getAvailableCurrency());
                        Debug.Log ("ExampleScript  Pending currency: " + AyetSdk.getPendingCurrency ());
                        Debug.Log ("ExampleScript  Spent currency: " + AyetSdk.getSpentCurrency ());
                    }
                    else {
                        int amount = 1;
                        //deduct balance
                        AyetSdk.deductUserBalance (amount, this);
                    }
                }
            }
        }

    }
}

9. Appendix I: Unmanaged Currency Handling / Conversion Callbacks


If you want to manually manage your users currencies on your own servers, you can configure a conversion callback url in our publisher dashboard.
To do so, navigate to Placements / Apps, edit your app placement and set the Callback Url to your server's postback url:


If this is the callback url your set for your app placement:
https://your-server.com/callback?network=ayetstudios&amount={currency_amount}&uid={uid}&device={advertising_id}&payout_usd={payout_usd}

A typical conversion callback sent by our server will look like this:
https://your-server.com/callback?network=ayetstudios&amount=360&uid=username&device=[GAID/IDFA]&payout_usd=0.36
* Note: This assumes you set the user identifier to username in your AyetSdk.init(..) call, the currency conversion rate in your placement was 1000 per $1 and the user completed an offer with a $0.36 payout.

Important: Your server must always reply with an HTTP 200 status code to our postbacks. Otherwise we will resend the postback 12 times over a span of one hour before giving up.

Available Macros for Postback URLs:
{transaction_id}stringUnique transaction id - use for duplicate checks. If chargeback it's prepend with r-
{payout_usd}floatThe actual conversion payout in USD. If chargeback value is negative.
{currency_amount}floatThe amount of currency the user earned (taken from your offerwall currency configuration). If chargeback value is negative.
{external_identifier} | {uid}stringThe user identifier set in your app for this user when initializing the sdk
{user_id}integerOur internal id for this offerwall user
{placement_identifier}stringThe placement_identifier for which the conversion occured
{ip}stringConverting device's IP address if known, 0.0.0.0 otherwise
{offer_id}intOffer ID of the converting offer
{offer_name}stringName / title of the converting offer
{device_uuid}stringayeT-Studios internal device identificator
{device_make}stringDevice manufacturer
{device_model}stringDevice model
{advertising_id}stringDevice advertising id (GAID/IDFA) if known, otherwise empty
{sha1_android_id}stringDevice sha1 hashed android id if known, otherwise empty
{sha1_imei}stringDevice sha1 hashed imei if known, otherwise empty
{is_chargeback}intValues 0 or 1. Indicator if the callback is conversion or chargeback. If set to 0 then it's conversion else it's chargeback.
{chargeback_reason}stringReason why chargeback created. Only available if is_chargeback set to 1.
{chargeback_date}stringDate of chargeback creation. Only available if is_chargeback set to 1.
{task_name}stringOnly available for cpe campaigns, shows individual task name for that conversion


Postback Verification with HMAC Security Hash (optional):
Our server will always add a custom header, X-Ayetstudios-Security-Hash, containing a SHA256 HMAC hash of the request parameters and your publisher api key.
Your API key can be found in your dashboard at ayetstudios.com under settings.

To verify the hash, perform the following steps:
(1) Get all request parameters
(2) Order the request parameters alphabetically
(3) Build and compare the HMAC hash using the ordered request parameter string and your API key

PHP Example:
ksort($_REQUEST, SORT_STRING);
$sortedQueryString = http_build_query($_REQUEST, '', '&'); // "adslot_id=123&currency_amount=100&payout_usd=1.5...."
$securityHash = hash_hmac('sha256', $sortedQueryString, 'YOUR PUBLISHER API KEY');
if($_SERVER['HTTP_X_AYETSTUDIOS_SECURITY_HASH']===$securityHash) { // actually sent as X-Ayetstudios-Security-Hash but converted by apache2 in this example
    // success
}
else {
    // invalid signature
}                    


If your want to restrict postbacks to our callback server IPs, please whitelist the following IPs and check back regularly for possible changes:
35.165.166.40
35.166.159.131
52.40.3.140	    
Last IP List Update: 2017-04-07


10. Appendix II: Assets/Plugins/Android/AndroidManifest.xml Template


This is a sample AndroidManifest template in case you're not using a custom AndroidManifest file yet. Please make sure to update package, versionCode and versionName.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.myapplication" <!-- Replace with your lower case package name -->
	android:versionCode="1" android:versionName="1.0" xmlns:tools="http://schemas.android.com/tools" android:installLocation="preferExternal">

    <supports-screens android:smallScreens="true" android:normalScreens="true" android:largeScreens="true" android:xlargeScreens="true" android:anyDensity="true" />

    <application android:theme="@style/UnityThemeSelector" android:icon="@drawable/app_icon" android:label="@string/app_name">
        <activity android:name="com.unity3d.player.UnityPlayerActivity" android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            <meta-data android:name="unityplayer.UnityActivity" android:value="true" />
        </activity>

        <activity android:name="com.ayetstudios.publishersdk.OfferwallActivity"
	    android:configChanges="orientation|screenSize">
	    <intent-filter android:label="offer">
	        <action android:name="android.intent.action.VIEW" />
	        <category android:name="android.intent.category.DEFAULT" />
	        <category android:name="android.intent.category.BROWSABLE" />
	        <data android:scheme="offer" android:host="com.example.myapplication" /> <!-- Replace with your lower case package name -->
	    </intent-filter>
	</activity>

    </application>
    <uses-permission android:name="android.permission.INTERNET" /> <!-- mandatory permission -->
    <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <!-- optional -->
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <!-- optional -->
</manifest>