IAP Game Unreal Developer Guide - ONE-store/inapp-sdk-eng GitHub Wiki

< NOTE >

This documents are for ONE store IAP SDK v16(API v4). If you are looking for the latest version of ONE store IAP SDK v17(API v5), please refer to the url below:

URL for the latest documents and downloads of ONE store IAP SDK v17(API v5) : https://dev.onestore.co.kr/devpoc/reference/view/IAP_v17


This instruction guide is to help you apply the IAP SDK to an Android App, which uses Unreal. To this end, OnestoreIapHelper (hereafter referred to as “OIAPhelper”) is provided.

1. About OIAPhelper


OIAPhelper is a sample guide code that calls methods related to payment within the IAP SDK and sends response results to the native level. Since the response results are passed through the native methods defined in the OIAPhelper, such methods MUST be implemented in the engine stage (or otherwise in the native level).

Folder File Name Description
UE4/src OnestoreIapHelper.java This is a sample guide code to connect to IAP SDK’s payment API

This code passes the results of calling via native methods

2. How to Apply OIAPhelper


You are required to connect OIAPhelper to the engine to use the API provided in the IAP SDK (.jar)

  1. Copy the latest IAP SDK(.jar) to the work project libs folder

  2. Copy OnestoreIapHelper.java file to the engine folder below (the folder where GameActivity.java exists).

    \Engine\Build\Android\Java\src\com\epicgames\ue4\

  3. Edit GameActivity.java

Declare import

import com.epicgames.ue4.OnestoreIapHelper;

Add methods related to OIAPhelper
  // Onestore manager objects
 private OnestoreIapHelper mOnestoreIapHelper = null;
 
 // Onestore In-App-Purchase functionality
 public void AndroidThunkJava_OnestoreIapSetupService(boolean isDebugMode) {
     Log.debug("[JAVA] - AndroidThunkJava_OnestoreIapSetupService. DebugMode : " + isDebugMode);

     if(mOnestoreIapHelper == null){
         mOnestoreIapHelper = new OnestoreIapHelper(this, isDebugMode);
     }
 }
 
 public void AndroidThunkJava_OnestoreIapRequestPurchase(final String appId, final String pId, final String pName, final String tId, final String bpInfo)   {
     if (mOnestoreIapHelper == null) {
        Log.debug("[JAVA] - OneStoreIapHelper is invalid");
             return;
     }
     mOnestoreIapHelper.requestPurchase(appId, pId, pName, tId, bpInfo);
 }
 
 public void AndroidThunkJava_OnestoreIapReceiptVerify(final String appId, final String txId, final String receiptSignData)
 {
     if (mOnestoreIapHelper == null) {
         Log.debug("[JAVA] - OneStoreIapHelper is invalid");
         return;
     }       
     mOnestoreIapHelper.doReceiptVerification(appId, txId, receiptSignData);
 }

public void AndroidThunkJava_OnestoreIapCmdPurchaseHistory(final String appId, final String pId)
{
     if (mOnestoreIapHelper == null) {
        Log.debug("[JAVA] - OneStoreIapHelper is invalid");
             return;
      }
      mOnestoreIapHelper.doCmdPurchaseHistory(appId, pId);
 }
 
 public void AndroidThunkJava_OnestoreIapCmdProductInfo(final String appId)
 {
     if (mOnestoreIapHelper == null) {
         Log.debug("[JAVA] - OneStoreIapHelper is invalid");
         return;
     }
     mOnestoreIapHelper.doCmdProductInfo(appId);
 }
 
 public void AndroidThunkJava_OnestoreIapCmdCheckPurchasability(final String appId, final String pId)
 {
     if (mOnestoreIapHelper == null) {
         Log.debug("[JAVA] - OneStoreIapHelper is invalid");
         return;
     }
     mOnestoreIapHelper.doCmdCheckPurchasability(appId, pId);
 }
 
 public void AndroidThunkJava_OnestoreIapCmdChangeProduct_withDrawSubscription(final String appId, final String pId)
 {
     if (mOnestoreIapHelper == null) {
         Log.debug("[JAVA] - OneStoreIapHelper is invalid");
         return;
     }       
     mOnestoreIapHelper.doCmdChangeProduct_withDrawSubscription(appId, pId);
 }
 
 public void AndroidThunkJava_OnestoreIapCmdChangeProduct_descentPoints(final String appId, final String pId)
 {
     if (mOnestoreIapHelper == null) {
         Log.debug("[JAVA] - OneStoreIapHelper is invalid");
         return;
     }
     mOnestoreIapHelper.doCmdChangeProduct_descentPoints(appId, pId);
 }
 
 public void AndroidThunkJava_OnestoreIapCmdPurchaseHistory_bg(final String appId, final String pId)
 {
     if (mOnestoreIapHelper == null) {
         Log.debug("[JAVA] - OneStoreIapHelper is invalid");
         return;
     }
     mOnestoreIapHelper.doCmdPurchaseHistory_bg(appId, pId);
 }
 
public void AndroidThunkJava_OnestoreIapCmdProductInfo_bg(final String appId)
 {
     if (mOnestoreIapHelper == null) {
                Log.debug("[JAVA] - OneStoreIapHelper is invalid");
         return;
     }
      mOnestoreIapHelper.doCmdProductInfo_bg(appId);
}

 public void AndroidThunkJava_OnestoreIapCmdCheckPurchasability_bg(final String appId, final String pId)
 {
     if (mOnestoreIapHelper == null) {
         Log.debug("[JAVA] - OneStoreIapHelper is invalid");
         return;
     }
     mOnestoreIapHelper.doCmdCheckPurchasability_bg(appId, pId);
 }
 
 public void AndroidThunkJava_OnestoreIapCmdChangeProduct_withDrawSubscription_bg(final String appId, final String pId)
 {
     if (mOnestoreIapHelper == null) {
         Log.debug("[JAVA] - OneStoreIapHelper is invalid");
         return;
     }
     mOnestoreIapHelper.doCmdChangeProduct_withDrawSubscription_bg(appId, pId);
 }
 
 public void AndroidThunkJava_OnestoreIapCmdChangeProduct_descentPoints_bg(final String appId, final String pId)
 {
     if (mOnestoreIapHelper == null) {
         Log.debug("[JAVA] - OneStoreIapHelper is invalid");
         return;
     }
}       
  1. Modify AndroidJNI.h and Android.cpp files You are required to edit AndroidJNI.h and AndroidJNI.cpp files in the Native area to access the methods defined in the Java area. These files are located in the paths below (based on UE4 4.12):

\Engine\Source\Runtime\Launch\Private\Android\AndroidJNI.cpp \Engine\Source\Runtime\Launch\Public\Android\AndroidJNI.h

< AndroidJNI.h >

  class FJavaWrapper{
   public:
   ... 
   // In app purchase functionality for Onestore
   static jmethodID AndroidThunkJava_OnestoreIapSetupService;
   static jmethodID AndroidThunkJava_OnestoreIapRequestPurchase;
   static jmethodID AndroidThunkJava_OnestoreIapReceiptVerify;
   
   static jmethodID AndroidThunkJava_OnestoreIapCmdPurchaseHistory;
   static jmethodID AndroidThunkJava_OnestoreIapCmdProductInfo;
   static jmethodID AndroidThunkJava_OnestoreIapCmdCheckPurchasability;
   static jmethodID AndroidThunkJava_OnestoreIapCmdChangeProduct_withDrawSubscription;
   static jmethodID AndroidThunkJava_OnestoreIapCmdChangeProduct_descentPoints;

   static jmethodID AndroidThunkJava_OnestoreIapCmdPurchaseHistory_bg;
   static jmethodID AndroidThunkJava_OnestoreIapCmdProductInfo_bg;
   static jmethodID AndroidThunkJava_OnestoreIapCmdCheckPurchasability_bg;
   static jmethodID AndroidThunkJava_OnestoreIapCmdChangeProduct_withDrawSubscription_bg;
   static jmethodID AndroidThunkJava_OnestoreIapCmdChangeProduct_descentPoints_bg;
   ...

< Android.cpp >
//Declare all the static members of the class defs 

...

// In app purchase functionality for Onestore
jmethodID FJavaWrapper::AndroidThunkJava_OnestoreIapSetupService;
jmethodID FJavaWrapper::AndroidThunkJava_OnestoreIapRequestPurchase;
jmethodID FJavaWrapper::AndroidThunkJava_OnestoreIapReceiptVerify;

jmethodID FJavaWrapper::AndroidThunkJava_OnestoreIapCmdPurchaseHistory;
jmethodID FJavaWrapper::AndroidThunkJava_OnestoreIapCmdProductInfo;
jmethodID FJavaWrapper::AndroidThunkJava_OnestoreIapCmdCheckPurchasability;
jmethodID FJavaWrapper::AndroidThunkJava_OnestoreIapCmdChangeProduct_withDrawSubscription;
jmethodID FJavaWrapper::AndroidThunkJava_OnestoreIapCmdChangeProduct_descentPoints;

jmethodID FJavaWrapper::AndroidThunkJava_OnestoreIapCmdPurchaseHistory_bg;
jmethodID FJavaWrapper::AndroidThunkJava_OnestoreIapCmdProductInfo_bg;
jmethodID FJavaWrapper::AndroidThunkJava_OnestoreIapCmdCheckPurchasability_bg;
jmethodID FJavaWrapper::AndroidThunkJava_OnestoreIapCmdChangeProduct_withDrawSubscription_bg;
jmethodID FJavaWrapper::AndroidThunkJava_OnestoreIapCmdChangeProduct_descentPoints_bg;

void FJavaWrapper::FindClassesAndMethods(JNIEnv* Env)
{

...

// Onestore IAP method id
    AndroidThunkJava_OnestoreIapSetupService = FindMethod(Env, GameActivityClassID, "AndroidThunkJava_OnestoreIapSetupService", "(Z)V", bIsOptional);
    AndroidThunkJava_OnestoreIapRequestPurchase = FindMethod(Env, GameActivityClassID, "AndroidThunkJava_OnestoreIapRequestPurchase", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", bIsOptional);
    AndroidThunkJava_OnestoreIapReceiptVerify = FindMethod(Env, GameActivityClassID, "AndroidThunkJava_OnestoreIapReceiptVerify", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V",bIsOptional);

    AndroidThunkJava_OnestoreIapCmdPurchaseHistory = FindMethod(Env, GameActivityClassID, "AndroidThunkJava_OnestoreIapCmdPurchaseHistory", "(Ljava/lang/String;Ljava/lang/String;)V",bIsOptional);
    AndroidThunkJava_OnestoreIapCmdProductInfo = FindMethod(Env, GameActivityClassID, "AndroidThunkJava_OnestoreIapCmdProductInfo", "(Ljava/lang/String;)V",bIsOptional);
    AndroidThunkJava_OnestoreIapCmdCheckPurchasability = FindMethod(Env, GameActivityClassID, "AndroidThunkJava_OnestoreIapCmdCheckPurchasability", "(Ljava/lang/String;Ljava/lang/String;)V",bIsOptional);
    AndroidThunkJava_OnestoreIapCmdChangeProduct_withDrawSubscription = FindMethod(Env, GameActivityClassID, "AndroidThunkJava_OnestoreIapCmdChangeProduct_withDrawSubscription", "(Ljava/lang/String;Ljava/lang/String;)V",bIsOptional);
    AndroidThunkJava_OnestoreIapCmdChangeProduct_descentPoints = FindMethod(Env, GameActivityClassID, "AndroidThunkJava_OnestoreIapCmdChangeProduct_descentPoints", "(Ljava/lang/String;Ljava/lang/String;)V",bIsOptional);

    AndroidThunkJava_OnestoreIapCmdPurchaseHistory_bg = FindMethod(Env, GameActivityClassID, "AndroidThunkJava_OnestoreIapCmdPurchaseHistory_bg", "(Ljava/lang/String;Ljava/lang/String;)V",bIsOptional);
    AndroidThunkJava_OnestoreIapCmdProductInfo_bg = FindMethod(Env, GameActivityClassID, "AndroidThunkJava_OnestoreIapCmdProductInfo_bg", "(Ljava/lang/String;)V",bIsOptional);
    AndroidThunkJava_OnestoreIapCmdCheckPurchasability_bg = FindMethod(Env, GameActivityClassID, "AndroidThunkJava_OnestoreIapCmdCheckPurchasability_bg", "(Ljava/lang/String;Ljava/lang/String;)V",bIsOptional);
    AndroidThunkJava_OnestoreIapCmdChangeProduct_withDrawSubscription_bg = FindMethod(Env, GameActivityClassID, "AndroidThunkJava_OnestoreIapCmdChangeProduct_withDrawSubscription_bg", "(Ljava/lang/String;Ljava/lang/String;)V",bIsOptional);
    AndroidThunkJava_OnestoreIapCmdChangeProduct_descentPoints_bg = FindMethod(Env, GameActivityClassID, "AndroidThunkJava_OnestoreIapCmdChangeProduct_descentPoints_bg", "(Ljava/lang/String;Ljava/lang/String;)V",bIsOptional);

Through the actions above, you can access the methods written in GameActivity in the Native area via FindClassesAndMethods, which is a member function of JavaWrapper class, and you can call and use when payment is needed.

  1. Callback for payment request is passed via the native methods defined in OnestoreIapHelper.java. Therefore, the corresponding methods must be implemted in the Native area. In general, the logic is implemented, including verifying e-receipt information (servet to server) based on the payment request and providing items accordingly.

  2. The Android Manifest must be set to allow normal connections to the IAP SDK in Android settings. The setup can be made as seen below in the project settings menu of the Unreal engine.

enter image description here

There are three types of additional information as follows: Meta-data settings are added to 'Extra Settings for section..' and permission settings to 'Extra Permissions..'

Meta-data

<meta-data 
    android:name="iap:api_version"
    android:value="4" />

Permission

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />

Example manifest file for Android In-App Billing Service
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.skplanet.iapexample"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="14"
        android:targetSdkVersion="23" />

<uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.RECEIVE_SMS" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    
    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        
        <meta-data
            android:name="iap:api_version"
            android:value="4" />
        
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name" 
            android:theme="@android:style/Theme.Holo.Light">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

3. Implement In-App Purchase

Initialization


// Onestore manager objects
private OnestoreIapHelper mOnestoreIapHelper = null;
    
// Onestore In-App-Purchase functionality
public void AndroidThunkJava_OnestoreIapSetupService(boolean isDebugMode) {
        Log.debug("[JAVA] - AndroidThunkJava_OnestoreIapSetupService. DebugMode : " + isDebugMode);

        if(mOnestoreIapHelper == null){
            mOnestoreIapHelper = new OnestoreIapHelper(this, isDebugMode);
        }
}

Payment Processing


public void AndroidThunkJava_OnestoreIapRequestPurchase(final String appId, final String pId, final String pName, final String tId, final String bpInfo)	{
		if (mOnestoreIapHelper == null) {
			Log.debug("[JAVA] - OneStoreIapHelper is invalid");
            return;
		}

        mOnestoreIapHelper.requestPurchase(appId, pId, pName, tId, bpInfo);
}

// Callback
public native void nativeOnPaymentComplete(String resultString);
public native void nativeOnPaymentError(String reqId, String errCode, String errMsg);

Query Processing


public void AndroidThunkJava_OnestoreIapCmdPurchaseHistory(final String appId, final String pId)
{
		if (mOnestoreIapHelper == null) {
			Log.debug("[JAVA] - OneStoreIapHelper is invalid");
            return;
		}
        
        mOnestoreIapHelper.doCmdPurchaseHistory(appId, pId);
        //mOnestoreIapHelper.doCmdPurchaseHistory_bg(appId, pId);
}
    
public void AndroidThunkJava_OnestoreIapCmdProductInfo(final String appId)
{
        if (mOnestoreIapHelper == null) {
            Log.debug("[JAVA] - OneStoreIapHelper is invalid");
            return;
        }
        
        mOnestoreIapHelper.doCmdProductInfo(appId);
        //mOnestoreIapHelper.doCmdProductInfo_bg(appId);
}
    
public void AndroidThunkJava_OnestoreIapCmdCheckPurchasability(final String appId, final String pId)
{ 
        if (mOnestoreIapHelper == null) {
            Log.debug("[JAVA] - OneStoreIapHelper is invalid");
            return;
        }
       
        mOnestoreIapHelper.doCmdCheckPurchasability(appId, pId);
        //mOnestoreIapHelper.doCmdCheckPurchasability_bg(appId, pId);
}
    
public void AndroidThunkJava_OnestoreIapCmdChangeProduct_withDrawSubscription(final String appId, final String pId)
    {
        if (mOnestoreIapHelper == null) {
            Log.debug("[JAVA] - OneStoreIapHelper is invalid");
            return;
        }
        
        mOnestoreIapHelper.doCmdChangeProduct_withDrawSubscription(appId, pId);
        //mOnestoreIapHelper.doCmdChangeProduct_withDrawSubscription_bg(appId, pId);
}
    
public void AndroidThunkJava_OnestoreIapCmdChangeProduct_descentPoints(final String appId, final String pId)
{      
        if (mOnestoreIapHelper == null) {
            Log.debug("[JAVA] - OneStoreIapHelper is invalid");
            return;
        }
        
        mOnestoreIapHelper.doCmdChangeProduct_descentPoints(appId, pId);
        //mOnestoreIapHelper.doCmdChangeProduct_descentPoints_bg(appId, pId);
}

// Callback
public native void nativeOnCommandComplete(String resultString);
public native void nativeOnCommandError(String reqId, String errCode, String errMsg);

e-Receipt Verification Processing


This feature verifies the receipt of the payment result. The use of this feature is recommended only when there is no separate development server. If possible, it is recommended to make a call via server API in the development server
Reference : IAP Reference Server API

public void AndroidThunkJava_OnestoreIapReceiptVerify(final String appId, final String txId, final String receiptSignData)
{
        if (mOnestoreIapHelper == null) {
            Log.debug("[JAVA] - OneStoreIapHelper is invalid");
            return;
        }
        
        mOnestoreIapHelper.doReceiptVerification(appId, txId, receiptSignData);
}

// Callback
public native void nativeOnVerifyReceiptComplete(String resultString);
public native void nativeOnVerifyReceiptError(String errCode);
⚠️ **GitHub.com Fallback** ⚠️