IAP Game Unreal Developer Guide - ONE-store/inapp-sdk GitHub Wiki
< ์ฐธ๊ณ >
๋ณธ ๋ฌธ์๋ ์์คํ ์ด ์ธ์ฑ SDK v16(API v4)์ ๋ํ ๊ฐ์ด๋ ๋ฌธ์์ ๋๋ค. ์ต์ ์์คํ ์ด ์ธ์ฑ SDK v17(API v5)์ ๋ํ ๊ฐ์ด๋ ๋ฌธ์๋ ์์คํ ์ด ๊ฐ๋ฐ์์ผํฐ์์ ์ ๊ณต๋๊ณ ์์ต๋๋ค.
์ต์ ์ธ์ฑ SDK v17 ๊ฐ์ด๋ ๋ฌธ์ ๋งํฌ : https://dev.onestore.co.kr/devpoc/reference/view/IAP_v17
ํด๋น ๋ด์ฉ์ Unreal์ ์ฌ์ฉํ๋ ์๋๋ก์ด๋ ์ฑ์์ IAP SDK ์ ์ฉ์ ๋๊ธฐ ์ํ ์ค๋ช
์์ด๋ค. ์ด๋ฅผ ์ํด OnestoreIapHelper ํด๋์ค(์ดํ OIAPhelper)๋ฅผ ์ ๊ณตํ๋ค.
OIAPhelper๋ IAP SDK ๋ด ๊ฒฐ์ ๊ด๋ จ ๋ฉ์๋๋ฅผ ํธ์ถํ๊ณ , ์๋ต ๊ฒฐ๊ณผ๋ฅผ native ๋ ๋ฒจ๋ก ๋ณด๋ด๋ ์ญํ ์ ํ๋ ์ํ ๊ฐ์ด๋ ์ฝ๋์ด๋ค. OIAPhelper๋ด์ ์ ์๋ native ๋ฉ์๋๋ฅผ ํตํด ์๋ต ๊ฒฐ๊ณผ๊ฐ ์ ๋ฌ๋๊ธฐ ๋๋ฌธ์, OIAPhelper ๋ด์ ์ ์๋ native ๋ฉ์๋๋ค์ ์์ง(ํน์ ๊ทธ ์ธ native ๋ ๋ฒจ)๋จ์ ๋ฐ๋์ ๊ตฌํ ํด์ผ ํ๋ค.
Folder | File Name | Description |
---|---|---|
UE4/src | OnestoreIapHelper.java | IAP SDK ๊ฒฐ์ API์ ์ฐ๋ํ๊ธฐ ์ํ ์ํ ๊ฐ์ด๋ ์ฝ๋ ํธ์ถ ๊ฒฐ๊ณผ๋ native ๋ฉ์๋๋ฅผ ํตํด ์ ๋ฌ |
IAP SDK(.jar)์์ ์ ๊ณตํ๋ API๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด OIAPhelper๋ฅผ ์์ง๊ณผ ์ฐ๋์์ผ์ผ ํ๋ค.
1)์ต์ IAP SDK(.jar)๋ฅผ ์์ ํ๋ก์ ํธ libs ํด๋์ ๋ณต์ฌ
2)OnestoreIapHelper.java ํ์ผ์ ์๋ ์์ง ํด๋์ ๋ณต์ฌ (GameActivity.java๊ฐ ์กด์ฌํ๋ ํด๋)
\Engine\Build\Android\Java\src\com\epicgames\ue4\
3)GameActivity.java ์์
import ์ ์ธ
import com.epicgames.ue4.OnestoreIapHelper;
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;
}
}
4)AndroidJNI.h ๋ฐ Android.cpp ์์ Native ์์ญ์์ Java ์์ญ(GameActivity.java ํด๋์ค)์ ์ ์๋ ๋ฉ์๋์ ์ ๊ทผํ๊ธฐ ์ํด AndroidJNI.h ํ์ผ ๋ฐ AndroidJNI.cpp ํ์ผ์ ์์ ํด์ผ ํ๋ค. ํด๋น ํ์ผ์ ์๋ ๊ฒฝ๋ก์ ์์นํ๋ค. (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);
์๊ธฐ ์์
์ ํตํด Native ์์ญ์์๋ JavaWrapper ํด๋์ค์ ๋ฉค๋ฒํจ์์ธ FindClassesAndMethods ํจ์๋ฅผ ํตํด GameActivity์ ์์ฑํด๋์ ๋ฉ์๋์ ์ ๊ทผํ ์ ์์ผ๋ฏ๋ก ๊ฒฐ์ ๊ฐ ํ์ํ ์์ ์ ํธ์ถํ์ฌ ์ฌ์ฉํ๋ฉด ๋๋ค.
5)native ๋ฉ์๋ ๊ตฌํ
๊ฒฐ์ ์๋ต์ ๋ํ ์ฝ๋ฐฑ์ OnestoreIapHelper.java๋ด์ ์ ์๋ native ๋ฉ์๋๋ก ์ ๋ฌ๋๊ธฐ ๋๋ฌธ์ native ์์ญ์ ํด๋น ๋ฉ์๋๋ค์ ๊ตฌํํด์ผ ํ๋ค. ๋ณดํต ๊ฒฐ์ ์๋ต์ ๋ฐ๋ฅธ ์ ์์์์ฆ ์ ๋ณด์ ๊ฒ์ฆ(์๋ฒ to ์๋ฒ)๊ณผ ์ด์ ๋ฐ๋ฅธ ์์ดํ
์ง๊ธ๋ฑ์ ๋ก์ง์ ๊ตฌํํ๊ฒ ๋๋ค.
6)์๋๋ก์ด๋ ์ธํ
IAP SDK์ ์ ์์ ์ผ๋ก ์ฐ๋ํ๊ธฐ ์ํด์๋ ๋ฐ๋์ ์๋๋ก์ด๋ Manifest ์ค์ ์ ํด์ฃผ์ด์ผ ํ๋ค. ์ธ๋ฆฌ์ผ ์์ง์ ํ๋ก์ ํธ ์ค์ ๋ฉ๋ด์์ ์๋์ ๊ฐ์ด ์ค์ ํ ์ ์๋ค.
์ถ๊ฐ ์ ๋ณด๋ ์๋์ ๊ฐ์ด 3๊ฐ์ง์ด๋ค. Meta-data ์ค์ ์ 'Extra Settings for section..' ํญ๋ชฉ์ ์ถ๊ฐํ๊ณ , Permission ์ค์ ์ '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" />
์๋๋ก์ด๋ ์ธ์ฑ๊ฒฐ์ ์๋น์ค์ manifest ํ์ผ์ ์
<?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);
๊ฒฐ์ ๊ฒฐ๊ณผ ์์์ฆ์ ๋ํ ๊ฒ์ฆ ๊ธฐ๋ฅ์ ํ๋ค. ๋ณ๋์ ๊ฐ๋ฐ์๋ฒ๊ฐ ์์ ๊ฒฝ์ฐ์๋ง ์ฌ์ฉํ๊ธธ ๊ถ์ฅํ๋ค. ๊ฐ๊ธ์ ๊ฐ๋ฐ์๋ฒ์์ Server API๋ฅผ ํตํด ํธ์ถํ๋ ๊ฒ์ ๊ถ์ฅํ๋ค. (https://github.com/ONE-store/inapp-sdk/wiki/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);