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.
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 |
You are required to connect OIAPhelper to the engine to use the API provided in the IAP SDK (.jar)
-
Copy the latest IAP SDK(.jar) to the work project libs folder
-
Copy OnestoreIapHelper.java file to the engine folder below (the folder where GameActivity.java exists).
\Engine\Build\Android\Java\src\com\epicgames\ue4\
-
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;
}
}
- 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.
-
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.
-
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.
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>
// 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);
}
// Callback
public native void nativeOnPaymentComplete(String resultString);
public native void nativeOnPaymentError(String reqId, String errCode, String errMsg);
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);
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);