Unity Backend OSP

1. Create the C# Script

To implement OSP in Unity, create a script that contains the logic and can be added to the IAP buttons. Firstly create a dynamic and static value (product id and server URL), this way every button will have the server URL that contains the endpoint to get the OSP URL, but each button can have a different product associated.

public string productId;
 
//The url to get the one-step payment url
private static string SERVER_URL = "mygamestudio.com/url";

Then create a function that starts the OSP purchase flow and set it as the onClick listener for the associated button. To avoid repetition you can also store the Unity activity as a field and instantiate it on Start

//This two fields will be necessary later
private AndroidJavaObject activity;
private static int MATCH_DEFAULT_ONLY_ANDROID_PM =  65536;
 
void Start () {
    var unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
    activity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
    GetComponent<Button>().onClick.AddListener(TaskOnClick);
}

void TaskOnClick(){
    StartCoroutine(GetURL());
}

2. Get URL from the server

To accomplish this step you first need a server with an endpoint that generates the OSP URL, the process of doing this is explained in the backend step-by-step.

Then, to get the URL from the server you can use the UnityEngine.Networking classes like so:

📘

In the GetUrlRequest you should add a field regarding the user id so that the server can build the URL with a reference to the user and when it's time to validate the purchase know to who should the product be assigned.

IEnumerator GetURL()
{
    var postData = new GetUrlRequest();
    postData.product = productId;
    var json = JsonUtility.ToJson(postData);
    byte[] bodyRaw = Encoding.UTF8.GetBytes(json);
 
    UnityWebRequest request = new UnityWebRequest(SERVER_URL, "POST", (DownloadHandler) new DownloadHandlerBuffer(), (UploadHandler) new UploadHandlerRaw(bodyRaw));
    request.SetRequestHeader("Content-Type", "application/json");
      
    yield return request.SendWebRequest();
 
    if (request.result != UnityWebRequest.Result.Success)
        yield break;
 
    var response = JsonUtility.FromJson<GetUrlResponse>(request.downloadHandler.text);
    Purchase(response.url);
}
 
[Serializable]
private class GetUrlRequest {
    public string product;
}
 
[Serializable]
private class GetUrlResponse {
    public string url;
}

3. Create and start Intent

Inside the app/game, an intent with the URL should be opened either using the web browser (whenever the AppCoins Wallet is not installed), or via the AppCoins Wallet.

In some cases, the user chooses to open the URL with the browser when the AppCoins Wallet is already installed or even chooses the option to always open the URL with the browser. To avoid this add the following sample to trigger the One Step billing flow.

// This is the method called at the end of GetURL
void Purchase(string url)
{
       var intent = BuildTargetIntent(url);
       activity.Call("startActivity", intent);
}

// This method generates the intent with the provided One Step URL to target the
// AppCoins Wallet.
AndroidJavaObject BuildTargetIntent(string url) 
{
       var uriClass = new AndroidJavaClass("android.net.Uri");
       var uri = uriClass.CallStatic<AndroidJavaObject>("parse", url);
 
       AndroidJavaClass intentClass = new AndroidJavaClass("android.content.Intent");
       AndroidJavaObject actionView = intentClass.GetStatic<AndroidJavaObject>("ACTION_VIEW");
       AndroidJavaObject intent = new AndroidJavaObject("android.content.Intent", actionView);
       intent.Call<AndroidJavaObject>("setData", uri);
 
       var appsList = GetAppsList(intent);
       var appsCount = appsList.Call<int>("size");
       for (int i=0; i<appsCount; i++) {
           var app = appsList.Call<AndroidJavaObject>("get", i);
           var packageName = app.Get<AndroidJavaObject>("activityInfo").Get<string>("packageName");
           if (packageName == "cm.aptoide.pt") {
               // If there's aptoide installed always choose Aptoide as default to open url
               intent.Call<AndroidJavaObject>("setPackage", packageName);
               break;
           } else if (packageName == "com.appcoins.wallet") {
               // If Aptoide is not installed and wallet is installed then choose Wallet
               // as default to open url
               intent.Call<AndroidJavaObject>("setPackage", packageName);
           }
       }
 
       return intent;
}
 
//Gets the apps that can run the intent
private AndroidJavaObject GetAppsList(AndroidJavaObject intent) 
{
       var packageManager = activity.Call<AndroidJavaObject>("getApplicationContext").Call<AndroidJavaObject>("getPackageManager");
       var appsList = packageManager.Call<AndroidJavaObject>("queryIntentActivities", intent, MATCH_DEFAULT_ONLY_ANDROID_PM);
       return appsList;
}

4. Prepare your server

The next steps are to prepare your server to handle the request to the callback URL and to assign the purchased item to the corresponding user, the steps on how to do this can be found in the backend step-by-step.

With these steps, there will be no client-side validation since the validation should be server-side, but if you want to still have some validation client-side check how the no-backend implementation accomplishes it.