원스토어 인앱결제 Unity Plugin 가이드

원스토어 Unity Plugin 소개

Unity 개발 환경에서 원스토어 인앱결제를 간편하게 적용할 수 있도록 원스토어 Unity Plugin이 제공됩니다. 원스토어 Unity Plugin은 결제 관련 함수와 해당 함수의 호출 결과를 처리하는 메서드를 포함합니다.

참고

원스토어 Unity Plugin은 Unity 5.6.0f3 버전으로 개발되었습니다.

원스토어 Unity Plugin

com.onestore.iap.sdk.unity

IapPlugin.java

SDK와 연결해주는 역할을 담당합니다. UnityPlayer내부의 unitySendMessage와 Activity를 reflection을 통해 가져와서 사용하기 때문에 Unity가 설치된 내부의 class.jar를 사용하지 않을수 있습니다.

IapUnityPluginActivity.java

상품구매나 로그인시에는 Activity를 띄우기 때문에 결과값을 받기 위해서 가지고 있는 Activity 입니다.

JsonUtil

안드로이드에서 유니티로 데이터를 전달하기 위해서 사용되는 유틸리티 입니다.

PopupManager

각 명령어 실행시에 결과 값 에러값들을 편하게 보여주기 위한 유틸리티 입니다.

sdk com.onestore.iap.api.*

IapPlugin과 연동하는 SDK (이전 원스토어 IAP V4(SDK V16 이하)에서는 SDK를 제외한 나머지 자바 파일 부분들은 Unity Adaptor라 지칭함)

원스토어 Unity Plugin 폴더 구성

원스토어 Unity Plugin은 아래와 같은 폴더 구성을 가지고 있습니다.

FolderDescription

Assets/Plugins/Android/Onestore

원스토어 Unity Plugin의 루트 폴더로 purchase, example 두 개의 하위 폴더를 갖고 있습니다. 해당 폴더에 원스토어 결제 모듈과 연결하기 위한 라이브러리 및 기본 AndroidManifest.xml 파일이 존재합니다.

Assets/Plubins/Android/Onestore/purchase

원스토어에서 제공하는 라이브러리와 연결을 담당하고 있는 Unity C# Script (CS) file 들이 위치합니다. 개발사는 해당 파일들을 이용하여 원스토어 결제 모듈과 통신 할 수 있습니다. purchase 내부의 파일들은 라이브러리와 통신을 담당하는 bridge 파일들로 example 내용을 통해 해당 CS file들의 사용법을 익히실 수 있습니다. 해당 내용을 수정하여 사용하셔도 되나, 가능한 수정 없이 사용 하길 권장 드립니다.

Assets/Plubins/Android/Onestore/example

원스토어에서 제공하는 라이브러리 및 CS file들을 이용한 sample 앱이 위치하고 있습니다. 해당 내용은 Unity 앱의 view(scene)에서 purchase의 CS file을 이용해 원스토어에서 제공하는 모든 InApp API를 사용하는 방법을 sample로 제공하고 있습니다.

원스토어 Unity Plugin Sample 파일 구성

File NameFolderDescription

iap_plugin_all_v17.00.00_20171225.aar (SDK와 Unity Adaptor , Manifest가 모두 합쳐진 형태)

Assets/Plugins/Android/Onestore

안드로이드 빌드에 필요한 Plugin 파일이 포함되어 있습니다. 간편하게 aar 파일 하나로 적용 가능합니다.

iap_plugin_v17.00.00_20171225.jar (SDK 부분) unity_adaptor_v17.00.00_20171225.jar (Unity & SDK 연결부분) AndroidManifest.xml

Assets/Plugins/Android/Onestore

안드로이드 빌드에 필요한 Plugin 파일이 포함되어 있습니다. 원스토어 인앱결제 API V4(SDK 16 이하)버전과 동일한 형태로 구성되었습니다.

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

Onestore_IapCallManager.cs

Assets/script/onestore/purchase

Unity에서 Android를 호출하는 역할을 담당합니다. IapUi에서 받은 값들을 이용해서 Android Unity Plugin을 직접 개별 명령을 호출합니다.

Onestore_IapCallbackManager.cs

Assets/script/onestore/purchase

Android에서 Unity로 결과값을 전달받는 1차 역할을 담당합니다. 개별 메소드는 Method 이름 + Listener 형태이며, 상품정보 형태의 값들은 Json 형태로, 에러나 기타 기본값들은 일반 String 형태로 넘어옵니다. 응답 메소드 체크, 파싱 및 Json을 PurchaseResponse, Signature, ProductDetail 형태로 변경해줍니다. 기본적으로 각 명령에 대한 함수 포인터 역할인 event Action을 가지고 있어서 1차 처리된 결과값을 최종 개발자가 사용하기 편하게 작업하도록 Onestore_IapResultListener로 넘겨줍니다.

Onestore_IapResultListener

Assets/script/onestore/purchase

Onestore_IapCallbackManager로 부터 가공된 각 타입에 맞게 개발자가 정보를 저장, 변형, 체크 등으로 활용하면 됩니다. 각 결과값에 대한 팝업 메시지 형태로 기본적으로 보여주도록 설정하였습니다.

Onestore_PurchaseResponse

Assets/script/onestore/purchase

응답 Json 형태를 각 타입으로 변경하기 위한 파일입니다.

Onestore_IapUi.cs

Assets/script/onestore/example/UI/

샘플 앱 각 Button UI를 눌렀을 때 대응되는 역할을 담당합니다. 테스트하기 위한 상품명, 상품 타입, publicKey, developPayload 등을 개발자가 각자 변경하여 사용할 수 있습니다. 다른 부분은 수정할 필요가 없고, 단지 개발자가 설정한 파라미터들을 Onestore_IapCallManager로 넘겨주는 역할만 합니다.

developPayload는 최대 100 bytes 까지 허용됩니다. developPayload는 null을 허용하지 않습니다.

AndroidNative.cs

Assets/script/onestore/example/UI/

각각 하위 폴더에 결과값을 보여주는 팝업 관련 유틸리티 입니다. 결과값을 adb 연결 없이 바로 화면에 보여주기 위한 용도입니다.

Example.unity

Assets/script/onestore/example/scene

원스토어 인앱결제 API V5(SDK V17)의 샘플 테스트 화면입니다.

one_brandmark_breadth, UI Textures/Textures and Sprites/SF Background

Assets/script/onestore/example/Image

배경화면과 로고 관련 이미지 입니다.

원스토어 Unity Plugin 적용

Unity Plugin 적용

  • iap_plugin_all_v17.00.00_20171225.aar 을 (또는 iap_plugin_v17.00.00_20171225.jar ,unity_adaptor_v17.00.00_20171225.jar , AndroidManifest.xml ) Asset/Plugins/Android/Onestore에 넣습니다.

    • 또는 Assets/Import Package/Custom Package를 선택해서 첨부된 IapV17_UnityPluginSample.unityPackage를 import해서 사용할수 있습니다.

  • 빌드 시 com.onestore.iap.apisample로 packageName을 입력하고 signkey01.keystore (Use Existing Keystore에 체크) 를 선택하고 Keystore password: signkey01 , Key Alias에 signkey01을 선택하고 password:signkey01 입력합니다.

  • Canvas는 10개의 버튼을 가지고 있으며 각 버튼의 OnClick은 IapUi.cs 내부의 메소드로 각각 1대 1로 연결되어 있습니다.

  • Onestore_IapUi GameObject는 Onestore_IapUi.cs 한 개의 스크립트 컴포넌트를 연결하고 있습니다.

  • Onestore_IapCallbackManager GameObject는 Onestore_IapCallbackManager.cs, Onestore_IapResultListener.cs 두 개의 컴포넌트를 연결하고 있습니다.

  • Background GameObject 는 단순히 배경 화면을 연결하는 역할을 합니다.

원스토어 Unity Plugin 레퍼런스

기본 동작 및 주의 사항

Android와 Unity 상호 간에 PurchaseData, ProudctDetail, 구매 상품원본, 시그너처 등의 데이터는 모두 Json 형태로 서로 주고 받습니다. 상품명, payload, public key를 변경하기 위해서는 Onestore_IapUi_IapUi.cs를 수정하시면 됩니다.

샘플 앱은 상품 및 구매정보를 저장하기 위해 PlayerPrefs를 사용하여 productId를 키값으로 Json 형식으로 저장하였습니다. 개발사에서는 자신의 개발환경 및 상품의 특성을 잘 파악하여, 원하는 자료구조를 이용하여 상품 및 구매정보를 저장하시는 것을 권장합니다.

Onestore_IapUi.cs, Onestore_IapCallbackManager.cs는 전송과 수신을 담당합니다. 원스토어는 보다 편리하게 사용하기 위해 별도의 Onestore_IapCallMnager, Onestore_IapResultListener를 만들었습니다. 이를 활용하면 테스트 값을 간단하게 변경할 수 있고, 결과 값을 받을 때 String, Json 등을 PurchaseData, ProductDetail 같이 변형된 데이터로 편리하게 받을 수 있습니다.

Onestore_IapCallManger 의 역할은 IapUi와 달리 API version을 향후 업데이트 시 변경할 수 있도록 하기 위한 클래스 변수를 가지고 있으며 초기화가 되지 않았을 때 버튼 동작에 대한 에러 팝업을 보여주는 기능을 가지고 있습니다. 또한 생성자 부분에 com.onestore.iap.sdk.unity.IapPlugin 인스턴스를 가지고 와서 안드로이드에서 유니티로 호출할때 어느 게임 오브젝트로 호출해야 하는지를 지정할수 있도록 생성자 파라미터로 넘기는 아래와 같은 코드를 사용합니다. 해당 코드는 개발사의 필요에 따라 변경할수 있습니다.


jc = new AndroidJavaClass("com.onestore.iap.sdk.unity.IapPlugin");
iapRequestAdapter = jc.CallStatic<AndroidJavaObject>("initAndGet", "IapCallbackManager");

IapUi.cs 예제에서는 화면 공간문제로 cancel/reactivate auto는 cancel/reactivate 동작이 on/off방식으로 번갈아 동작하도록 구현되었습니다.본 명령을 수행시킨 후 팝업으로 보여지는 결과값에서는 recurringState가 변하지가 않고 getPurchase를 통해서 업데이트된 recurringState를 확인할 수 있습니다.

Unity에서 Android 로 기본 Flow

Android 에서 Unity로 기본 Flow

원스토어 Unity Plugin 메서드 및 동작 소개

Service Connect 버튼 클릭: 결제 초기화 (생성, 바인딩, 연결) 동작

Service Connect : public key를 넘겨주어서 pulgin 내의 PurchaseClient를 생성하고 원스토어 인앱 서비스를 바인딩해서 그 결과를 리스너를 통해서 전달 받습니다. 호출방향은 위에서 아래이며 Unity 에서 Android를 호출하게 됩니다.

// Onestore_IapUi.cs (C#)
public void connectService ()
{
    var base64EncodedPublicKey = "MIGfMA0GCSqGSIb3D....";
    IapCallManager.connectService (base64EncodedPublicKey);
}


// Onestore_IapCallMnager.cs (C#)
public static void connectService (string publicKey)
{
    iapRequestAdapter.Call ("connect", publicKey);
}

// ONE store Unity Plugin (Java)
public void connect(String base64EncodedPublicKey) {
    Log.d(TAG, "connect - base64EncodedPublicKey " + base64EncodedPublicKey);
    mPurchaseClient = new PurchaseClient(getActivity(), base64EncodedPublicKey);
    publicKeyBase64 = base64EncodedPublicKey;
    mPurchaseClient.connect(mServiceConnectionListener);
}

serviceAvailableEvent()는 파라미터로 "onConnected"를 받았을때 성공적 연결이므로 연결 성공이 되지 않았을때 에러 팝업을 보여주기 위한 용도입니다. 연결 리스너를 통해서 Android에서 Unity를 호출하는 순서입니다.

gameObject는 Android에서 Unity로 결과값을 전달받기 위한 목적지로 IapCallManager.cs의 생성자 부분에서 이미 "IapCallbackManager"라는 이름의 GameObject를 등록하였고 Unity Plugin에서 이 값을 가지고 해당 GameObject로 결과 값을 전달하게 됩니다. "IapCallbackManager" 라는 이름의 GameObject에 Onestore_IapCallbackManager.cs 를 등록하였기 때문에 해당 파일의 ServiceConnectionListener 메소드에 String 파라미터로 "onConnected", "onDisconnected", "onErrorNeedUpdateException" 중 하나로 callback 값으로 넘겨주게 됩니다. 각각 서비스 Binding 성공, 서비스 unBinding, 원스토어 서비스가 최신 버전이 아니거나 미설치 라는 의미를 가지고 있습니다.

Onestore_IapResultListener.cs 도 "IapCallbackManager" 라는 이름의 GameObject에 같이 등록되어 있습니다. 초기화 될때 Onestore_IapCallbackManager.serviceConnectionEvent += serviceConnectionResult; 를 통해서 serviceConnectionEvent(callback) 이 Onestore_IapResultListener.cs의 serviceConnectionResult 를 호출하게 되고 결국 연결관련 메시지를 팝업 형태로 보여주게 됩니다.

// ONE store Unity Plugin (Java)
PurchaseClient.ServiceConnectionListener mServiceConnectionListener = new PurchaseClient.ServiceConnectionListener() {
    @Override
    public void onConnected() {
        unitySendMessage(gameObject, "ServiceConnectionListener", "onConnected");
    }
      
    @Override
    public void onDisconnected() {
        unitySendMessage(gameObject, "ServiceConnectionListener", "onDisconnected");
    }
      
    @Override
    public void onErrorNeedUpdateException() {
        unitySendMessage(gameObject, "ServiceConnectionListener", "onErrorNeedUpdateException");
    }
};

// Onestore_IapCallbackManager.cs (C#)
public static event Action<string> serviceConnectionEvent;
  
public void ServiceConnectionListener (string callback)
{
    if (callback.Contains (preDefinedStrings [CBType.Connected])) {
        serviceAvailableEvent ();
    }
    serviceConnectionEvent (callback);
}

// Onestore_IapResultListener.cs (C#)
Onestore_IapCallbackManager.serviceConnectionEvent += serviceConnectionResult;
  
void serviceConnectionResult (string result)
{
    AndroidNative.showMessage ("serivce connect", result, "ok");
}

isBillingSupported 버튼 클릭 : 인앱결제 API V5(SDK V17) 지원여부 확인에 대한 응답

호출방향은 위에서 아래이며 Unity 에서 Android를 호출하게 됩니다.

// Onestore_IapUi.cs (C#)
public void isBillingSupported ()
{
    Onestore_IapCallManager.isBillingSupported ();
}

// Onestore_IapCallMnager.cs (C#)
public static void isBillingSupported ()
{
    checkServiceAvailable();
    iapRequestAdapter.Call("isBillingSupported", IAP_API_VERSION);
}

// ONE store Unity Plugin (Java)
public void isBillingSupported(int apiVersion) {
    if (mPurchaseClient == null) {
        Log.i(TAG, "PurchaseClient is not initialized");
        return;
    }
    mPurchaseClient.isBillingSupportedAsync(apiVersion, mBillingSupportedListener);
}

연결 리스너를 통해서 Android에서 Unity를 호출하는 순서입니다. callback으로 들어오는 결과값 스트링 종류는 다음과 같습니다.

  • onSuccess: 상태정보 조회가 성공일 경우

  • onError : 실패일 경우 애플리케이션으로 에러코드 전달. 숫자로 된 code와 string으로 된 description을 ToString형태로 넘겨줍니다.

  • onErrorRemoteException: 서비스 Un-bind 시점에 Remote call 을 요청할 경우 발생

  • onErrorSecurityException: 애플리케이션이 변조되거나 정상적이지 않는 APK 형태 일때 발생

  • onErrorNeedUpdateException: 원스토어 서비스가 최신버전이 아닐 경우 또는 미설치 시 발생

// ONE store Unity Plugin (Java)
PurchaseClient.BillingSupportedListener mBillingSupportedListener = new PurchaseClient.BillingSupportedListener() {
  
    @Override
    public void onSuccess() {
        unitySendMessage(gameObject, "BillingSupportedListener", "onSuccess");
    }
  
    @Override
    public void onError(IapResult result) {
        unitySendMessage(gameObject, "BillingSupportedListener", "onError" + result.toString());
    }
  
    @Override
    public void onErrorRemoteException() {
        unitySendMessage(gameObject, "BillingSupportedListener", "onErrorRemoteException");
    }
  
    @Override
    public void onErrorSecurityException() {
        unitySendMessage(gameObject, "BillingSupportedListener", "onErrorSecurityException");
    }
  
    @Override
    public void onErrorNeedUpdateException() {
        unitySendMessage(gameObject, "BillingSupportedListener", "onErrorNeedUpdateException");
    }
};

// Onestore_IapCallbackManager.cs (C#)
public static event Action<string> isBillingSupportedEvent;
  
public void BillingSupportedListener (string callback)
{
    isBillingSupportedEvent (callback);
}

{// Onestore_IapResultListener.cs (C#)
IapCallbackManager.isBillingSupportedEvent += isBillingSupportedResult;
  
void isBillingSupportedResult (string result)
{
    AndroidNative.showMessage ("isBillingSupported", result, "ok");
}

getPurchase 버튼 클릭 : 인앱결제 API V5(SDK V17) 구매 내역 조회를 위한 메서드

호출방향은 위에서 아래이며 Unity 에서 Android를 호출하게 됩니다.

// Onestore_IapUi.cs (C#)
public void getPurchases ()
{
    Onestore_IapCallManager.getPurchases ();
}

// Onestore_IapCallMnager.cs (C#)
//inapp과 auto 두 가지 상품 타입이 존재하는데 각각 상품에 대해서 각각 따로 호출해야 합니다.  개발사에서 변경 가능한 부분
public static void getPurchases ()
{
    checkServiceAvailable ();
    iapRequestAdapter.Call ("getPurchase", IAP_API_VERSION, "inapp");
    iapRequestAdapter.Call ("getPurchase", IAP_API_VERSION, "auto");
}


// ONE store Unity Plugin (Java)
public void getPurchase(int apiVersion, final String productType) {
    if (mPurchaseClient == null) {
        Log.d(TAG, "PurchaseClient is not initialized");
        return;
    }
  
    mPurchaseClient.queryPurchasesAsync(apiVersion, productType, mQueryPurchaseListener);
}

연결 리스너를 통해서 Android에서 Unity를 호출하는 순서입니다. callback으로 들어오는 결과값 스트링 종류는 다음과 같습니다.

  • onSuccess: 구매정보 조회 호출에 대한 성공 응답 : 만약 구매한 데이터가 있다면 “onSuccess” + json (productType: inapp or auto인지 상품타입, purchaseData: 구매한 원본 json 구매 데이터, signature: 구매한 시그너처), 구매한 데이터가 없다면 “onSuccess”+ “[]” + producttype(“inapp” or “auto”)와 같은 형식으로 넘어옵니다.

  • onErrorRemoteException

  • onErrorSecurityException

  • onErrorNeedUpdateException

  • onError: 실패일 경우 애플리케이션으로 에러코드 전달. 숫자로 된 code와 string으로 된 description을 ToString형태로 넘겨줍니다.

현재 signature는 IapCallbackManager에서 받아오고 있으나 여기로 넘겨주지는 않고 있습니다. 일단 SDK 내부에서 구매 데이터와 시그너처를 통한 검증을 디폴트로 수행하고 있기 때문입니다. 만약 시그너처가 필요하면 IapCallbackManager에서 가져다 쓸수 있습니다. PlayerPref값을 셋팅하는것은 간단히 테스트 앱을 위한 것이지 실제로 아이템에 대한 관리나 저장은 개발사에서 해야 합니다.

// ONE store Unity Plugin (Java)
PurchaseClient.QueryPurchaseListener mQueryPurchaseListener = new PurchaseClient.QueryPurchaseListener() {
    @Override
    public void onSuccess(List<PurchaseData> purchaseDataList, String productType) {
        if (purchaseDataList.size() > 0) {
            for (PurchaseData purchaseData : purchaseDataList) {
                unitySendMessage(gameObject, "QueryPurchaseListener", "onSuccess" + JsonUtil.makePurchaseDataToJson(purchaseData, productType));
            }
        } else {
            unitySendMessage(gameObject, "QueryPurchaseListener", "onSuccess" + purchaseDataList + productType);
        }
    }
  
    @Override
    public void onErrorRemoteException() {
        unitySendMessage(gameObject, "QueryPurchaseListener", "onErrorRemoteException");
    }
  
    @Override
    public void onErrorSecurityException() {
        unitySendMessage(gameObject, "QueryPurchaseListener", "onErrorSecurityException");
    }
  
    @Override
    public void onErrorNeedUpdateException() {
        unitySendMessage(gameObject, "QueryPurchaseListener", "onErrorNeedUpdateException");
    }
  
    @Override
    public void onError(IapResult result) {
        unitySendMessage(gameObject, "QueryPurchaseListener", "onError" + result.toString());
    }
};

// Onestore_IapCallbackManager.cs (C#)
public static event Action<PurchaseData> getPurchaseSuccessEvent;
public static event Action<string> getPurchaseErrorEvent;
  
public void QueryPurchaseListener (string callback)
{
    string data = findStringAfterCBType (callback, CBType.Success);
    if (data.Length > 0) {
        try {
            PurchaseResponse purchaseRes = JsonUtility.FromJson<PurchaseResponse> (data);
            PurchaseData purchaseData = JsonUtility.FromJson<PurchaseData> (purchaseRes.purchaseData);
            getPurchaseSuccessEvent (purchaseData);
        } catch (System.Exception ex) {
            Debug.Log ("QueryPurchaseListener Exception " + ex.Message);
            getPurchaseErrorEvent (data); //success but no data
        }
    } else {
        //onError만 뒤에 추가데이터 IapResult 가 있으므로 추가 데이터만 전달해주고 나머지 에러들은 추가 데이터가 없으므로 callback 그대로만 전달해줍니다.
        string errorData = findStringAfterCBType (callback, CBType.Error);
        if (errorData.Length > 0) {
            getPurchaseErrorEvent (errorData);
        } else {
            getPurchaseErrorEvent (callback);
        }
    }
}

// IapResultListener.cs (C#)
void getPurchaseSuccessResult (PurchaseData result)
{
    AndroidNative.showMessage ("getPurchase success", result.ToString (), "ok");
    PlayerPrefs.SetString (result.productId, JsonUtility.ToJson (result));
}
  
void getPurchaseErrorResult (string result)
{
    if (result.Contains ("inapp")) {
        PlayerPrefs.SetString ("p5000", "");
    } else {
        PlayerPrefs.SetString ("a100000", "");
    }
    AndroidNative.showMessage ("getPurchase error", result, "ok");
}

getProductDetail 버튼 클릭 : 인앱결제 API V5(SDK V17) 상품 정보 조회를 위한 메서드

호출방향은 위에서 아래이며 Unity 에서 Android를 호출하게 됩니다.

// Onestore_IapUi.cs (C#)
public void getProductDetails ()
{
    var inapp_products = new string[] {inapp_pId1, inapp_pId2, inapp_pId3};
    var auto_products = new string[] {auto_pId1};
  
    IapCallManager.getProductDetails (inapp_products, inapp_type);
    IapCallManager.getProductDetails (auto_products, auto_type);
}

// Onestore_IapCallMnager.cs (C#)
public static void getProductDetails (string[] products, string productType)
{
    checkServiceAvailable ();
    iapRequestAdapter.Call ("getProductDetails", new object[]{IAP_API_VERSION, products, productType});
}

// ONE store Unity Plugin (Java)
public void getProductDetails(int apiVersion, Object[] productItems, String productType) {
    if (mPurchaseClient == null) {
        Log.d(TAG, "PurchaseClient is not initialized");
        return;
    }
    ArrayList<Object> obj_products = new ArrayList<>(Arrays.asList(productItems));
    ArrayList<String> products = new ArrayList<>();
    for (Object object : obj_products) {
        products.add(object != null ? object.toString() : null);
    }
      
    mPurchaseClient.queryProductsAsync(apiVersion, products, productType, mQueryProductsListener);
}

연결 리스너를 통해서 Android에서 Unity를 호출하는 순서입니다. callback으로 들어오는 결과값 스트링 종류는 다음과 같습니다.

  • onSuccess: 상품정보 조회 호출에 대한 성공 응답 , json 형태 , 최종 IapResultListener에서 데이터를 받아서 구입 가능한 상품 목록과 상품 타입등 정보를 개발사에서 처리해야 한다. 샘플에서는 결과를 팝업으로 단순히 보여주기만 합니다.

  • onErrorRemoteException

  • onErrorSecurityException

  • onErrorNeedUpdateException

  • onError: 실패일 경우 애플리케이션으로 에러코드 전달. 숫자로 된 code와 string으로 된 description을 ToString형태로 넘겨줍니다.

// ONE store Unity Plugin (Java)
PurchaseClient.QueryProductsListener mQueryProductsListener = new PurchaseClient.QueryProductsListener() {
    @Override
    public void onSuccess(List<ProductDetail> productDetails) {
        for (ProductDetail detaildata : productDetails) {
            unitySendMessage(gameObject, "QueryProductsListener", "onSuccess" + JsonUtil.makeProductDetailToJson(detaildata));
        }
    }
  
    @Override
    public void onErrorRemoteException() {
        unitySendMessage(gameObject, "QueryProductsListener", "onErrorRemoteException");
    }
  
    @Override
    public void onErrorSecurityException() {
        unitySendMessage(gameObject, "QueryProductsListener", "onErrorSecurityException");
    }
  
    @Override
    public void onErrorNeedUpdateException() {
        unitySendMessage(gameObject, "QueryProductsListener", "onErrorNeedUpdateException");
    }
  
    @Override
    public void onError(IapResult result) {
        unitySendMessage(gameObject, "QueryProductsListener", "onError" + result.toString());
    }
};

// Onestore_IapCallbackManager.cs (C#)
public static event Action<ProductDetail> queryProductsSuccessEvent;
public static event Action<string> queryProductsErrorEvent;
  
public void QueryProductsListener (string callback)
{
    string data = findStringAfterCBType (callback, CBType.Success);
    if (data.Length > 0) {
        ProductDetail productDetail = JsonUtility.FromJson<ProductDetail> (data);
        queryProductsSuccessEvent (productDetail);
    } else {
        string errorData = findStringAfterCBType (callback, CBType.Error);
        queryProductsErrorEvent (errorData);
    }
}

// Onestore_IapResultListener.cs (C#)
void queryProductsSuccessResult (ProductDetail result)
{
    AndroidNative.showMessage ("queryProducts success", result.ToString (), "ok");
}
  
void queryProductsErrorResult (string result) {
    AndroidNative.showMessage ("queryProducts error", result, "ok");
}

buyItem(Inapp) 버튼 클릭 : 인앱결제 API V5(SDK V17) 인앱 상품 구매

호출방향은 위에서 아래이며 Unity 에서 Android를 호출하게 됩니다. Inapp, auto 상품의 경우 IapUi.cs에서 상품id, 상품 타입만 다르고 나머지 흐름은 동일합니다. gameUserId, promotionApplicable은 일반적으로 잘 사용하지 않기 때문에 default ("" , false) 값으로 넘겨주며 사용하고자 할 때 IapCallManager에서 개발자가 값을 변경해서 넣으면 됩니다.

  • gameUserId - 애플리케이션을 사용 중인 사용자의 고유 인식 번호를 입력합니다. 해당 값은 프로모션 참가 가능 여부 및 프로모션 사용 여부를 판가름하는 key value로 사용됩니다.

  • promotionApplicable - 프로모션 참가 가능 여부를 전달합니다.

    • true : gameUserId로 전달된 사용자는 단일 프로모션에 1회 참가가 가능합니다.

    • false : gameUserId로 전달된 사용자는 프로모션에 참가가 불가능합니다.

구매의 경우 서비스의 Activity를 필요로 하고 그 결과값을 받아야 하기 때문에 Plugin내부의 Activity 를 호출합니다. 그리고 인텐트에 파라미터값을 모두 저장해서 넘겨줍니다.

// Onestore_IapUi.cs (C#)
public void buyProductInapp ()
{
    //developPayload는 최대 사이즈 100byte까지만 허용됩니다.
    //developPayload는 null을 허용하지 않습니다.
    IapCallManager.buyProduct (inapp_pId1,  inapp_type, devPayload);
}

// Onestore_IapCallMnager.cs (C#)
public static void buyProduct (string productId, string productType,  string payload)
{
    checkServiceAvailable ();
    string gameUserId = "";
    bool promotionApplicable = false;
    iapRequestAdapter.Call ("launchPurchaseFlow", IAP_API_VERSION, productId, productType, payload, gameUserId, promotionApplicable);
}

// ONE store Unity Plugin (Java)
public void launchPurchaseFlow(int apiVersion, final String productId, final String productType,
        final String payload, final String gameUserId, boolean promotionApplicable) {
      
    Intent i = new Intent(getActivity(), IapUnityPluginActivity.class);
    i.putExtra("productId", productId);
    i.putExtra("productType", productType);
    i.putExtra("payload", payload);
    i.putExtra("apiVersion", apiVersion);
    i.putExtra("gameUserId", gameUserId);
    i.putExtra("promotionApplicable", promotionApplicable);
      
    getActivity().startActivity(i);
}

mPurchaseClient.launchPurchaseFlow 호출 후 onActivityResult로 구매 완료 결과값을 결과값을 받습니다. mPurchaseClient.handlePurchaseData 내에서 구매 완료시 전달 받은 Intent 데이터에 대해서 데이터가 지정된 서명으로 서명되었는지 검증하고 그 결과를 구매시 등록한 리스너를 통해서 전달합니다.

// ONE store Unity Plugin (Java) : IapUnityPluginActivity
public class IapUnityPluginActivity extends Activity {
  
    private static final int LOGIN_REQUEST_CODE = 1001;
    private static final int PURCHASE_REQUEST_CODE = 2001;
  
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
          
        Intent i = getIntent();
        final String productId = i.getStringExtra("productId");
        final String productType = i.getStringExtra("productType");
        final String payload = i.getStringExtra("payload");
        final int apiVersion = i.getIntExtra("apiVersion", 5);
  
        final String gameUserId = i.getStringExtra("gameUserId");
        final boolean promotionApplicable = i.getBooleanExtra("promotionApplicable", false);
  
        if (TextUtils.isEmpty(productId) == false && TextUtils.isEmpty(productType) == false) {
            IapPlugin.INSTANCE.mPurchaseClient.launchPurchaseFlow(
                    apiVersion,
                    IapUnityPluginActivity.this,
                    PURCHASE_REQUEST_CODE,
                    productId,
                    "",//productName
                    productType,
                    payload,
                    gameUserId, //String gameUserId
                    promotionApplicable, //boolean promotionApplicable
                    IapPlugin.INSTANCE.mPurchaseFlowListener);
        }
    }
}

// ONE store Unity Plugin (Java) : IapUnityPluginActivity
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    Log.d("IapUnityPluginActivity", "onActivityResult requestCode " + requestCode);
  
    switch (requestCode) {
        case PURCHASE_REQUEST_CODE:
             /*
             * launchPurchaseFlow API 호출 시 전달받은 intent 데이터를 handlePurchaseData를 통하여 응답값을 파싱한다.
             * 파싱 이후 응답 결과를 launchPurchaseFlow 호출 시 넘겨준 PurchaseFlowListener 를 통하여 전달한다.
             */
            if (resultCode == RESULT_OK) {
                if (IapPlugin.INSTANCE.mPurchaseClient.handlePurchaseData(data) == false) {
                    // listener is null
                }
            } else {
                // user canceled , do nothing..
            }
            break;
        default:
    }
    finish();
}

연결 리스너를 통해서 Android에서 Unity를 호출하는 순서입니다. callback으로 들어오는 결과값 스트링 종류는 다음과 같습니다.

  • onSuccess - 구매요청에 대한 성공 응답

  • onErrorRemoteException

  • onErrorSecurityException

  • onErrorNeedUpdateException

  • onError: 실패일 경우 애플리케이션으로 에러코드 전달. 숫자로 된 code와 string으로 된 description을 ToString형태로 넘겨줍니다.

// ONE store Unity Plugin (Java)
PurchaseClient.PurchaseFlowListener mPurchaseFlowListener = new PurchaseClient.PurchaseFlowListener() {
    @Override
    public void onSuccess(PurchaseData purchaseData) {
        unitySendMessage(gameObject, "PurchaseFlowListener", "onSuccess" + JsonUtil.makePurchaseDataToJson(purchaseData, ""));
    }
  
    @Override
    public void onErrorRemoteException() {
        unitySendMessage(gameObject, "PurchaseFlowListener", "onErrorRemoteException");
    }
  
    @Override
    public void onErrorSecurityException() {
        unitySendMessage(gameObject, "PurchaseFlowListener", "onErrorSecurityException");
    }
  
    @Override
    public void onErrorNeedUpdateException() {
        unitySendMessage(gameObject, "PurchaseFlowListener", "onErrorNeedUpdateException");
    }
  
    @Override
    public void onError(IapResult result) {
        unitySendMessage(gameObject, "PurchaseFlowListener", "onError" + result.toString());
    }
};

// Onestore_IapCallbackManager.cs (C#)
public static event Action<PurchaseData> getPurchaseIntentSuccessEvent;
public static event Action<string> getPurchaseIntentErrorEvent;
  
public void PurchaseFlowListener (string callback)
{
    string data = findStringAfterCBType (callback, CBType.Success);
  
    if (data.Length > 0) {
        try {
            PurchaseResponse purchaseRes = JsonUtility.FromJson<PurchaseResponse> (data);
            PurchaseData purchaseData = JsonUtility.FromJson<PurchaseData>  (purchaseRes.purchaseData);
            getPurchaseIntentSuccessEvent (purchaseData);
        } catch (System.Exception ex) {
            Debug.Log ("PurchaseFlowListener Exception " + ex.Message);
        }
    } else {
        //onError만 뒤에 추가데이터 IapResult 가 있으므로 추가 데이터만 전달해주고 나머지 에러들은 추가 데이터가 없으므로 callback 그대로만 전달해준다.
        string errorData = findStringAfterCBType (callback, CBType.Error);
        if (errorData.Length > 0) {
            getPurchaseIntentErrorEvent (errorData);
        } else {
            getPurchaseIntentErrorEvent (callback);
        }
    }
}

// Onestore_IapResultListener.cs (C#)
void getPurchaseIntentSuccessResult (PurchaseData result)
{
    AndroidNative.showMessage ("getPurchaseIntent sucess",  result.ToString (), "ok");
    PlayerPrefs.SetString (result.productId, JsonUtility.ToJson  (result));
}
  
void getPurchaseIntentErrorResult (string result)
{
    AndroidNative.showMessage ("getPurchaseIntent error", result, "ok");
}

consume 버튼 클릭 : 인앱결제 API V5(SDK V17) 상품소비 호출 메서드

호출방향은 위에서 아래이며 Unity 에서 Android를 호출하게 됩니다. consume을 하기 위해서 getPurchase 나 buyItem을 통해서 저장된 구매 상품에 대한 JSON데이터가 필요하며 이것을 가져 오기 위해서 PlayerPrefs를 사용해서 상품 Id를 키값으로 가지고 와서 안드로이드에 전달하고 최종적으로 안드로이드에서는 Json 을 PurchaseData형식으로 변형해서 사용합니다.

// Onestore_IapUi.cs (C#)
public void consume ()
{
    string inapp_json = PlayerPrefs.GetString  (inapp_pId1);
  
    if (inapp_json.Length > 0) {
        IapCallManager.consume (inapp_json);
    } else {
        AndroidNative.showMessage ("error", "no  data to consume", "ok");
    }
}

// Onestore_IapUi.cs (C#)
public void consume ()
{
    string inapp_json = PlayerPrefs.GetString  (inapp_pId1);
  
    if (inapp_json.Length > 0) {
        IapCallManager.consume (inapp_json);
    } else {
        AndroidNative.showMessage ("error", "no  data to consume", "ok");
    }
}

// ONE store Unity Plugin (Java)
public void consumeItem(int apiVersion, final String data) {
    if (mPurchaseClient == null) {
        Log.d(TAG, "PurchaseClient is not initialized");
        return;
    }
    mPurchaseClient.consumeAsync(apiVersion, JsonUtil.makeJsonToPurchaseData(data), mConsumeListener);
}

연결 리스너를 통해서 Android에서 Unity를 호출하는 순서입니다. callback으로 들어오는 결과값 스트링 종류는 다음과 같습니다.

  • onSuccess: 소비요청에 대한 성공 응답

  • onErrorRemoteException

  • onErrorSecurityException

  • onErrorNeedUpdateException

  • onError: 실패일 경우 애플리케이션으로 에러코드 전달. 숫자로 된 code와 string으로 된 description을 ToString형태로 넘겨줍니다.

// ONE store Unity Plugin (Java)
PurchaseClient.ConsumeListener mConsumeListener = new PurchaseClient.ConsumeListener() {
    @Override
    public void onSuccess(PurchaseData purchaseData) {
        unitySendMessage(gameObject, "ConsumeListener", "onSuccess" + JsonUtil.makePurchaseDataToJson(purchaseData));
    }
  
    @Override
    public void onErrorRemoteException() {
        unitySendMessage(gameObject, "ConsumeListener", "onErrorRemoteException");
    }
  
    @Override
    public void onErrorSecurityException() {
        unitySendMessage(gameObject, "ConsumeListener", "onErrorSecurityException");
    }
  
    @Override
    public void onErrorNeedUpdateException() {
        unitySendMessage(gameObject, "ConsumeListener", "onErrorNeedUpdateException");
    }
  
    @Override
    public void onError(IapResult result) {
        unitySendMessage(gameObject, "ConsumeListener", "onError" + result.toString());
    }
};

// Onestore_IapCallbackManager.cs (C#)
public static event Action<PurchaseData> consumeSuccessEvent;
public static event Action<string> consumeErrorEvent;
  
public void ConsumeListener (string callback)
{
    string data = findStringAfterCBType (callback, CBType.Success);
    if (data.Length > 0) {
        PurchaseData purchaseData = JsonUtility.FromJson<PurchaseData> (data);
        consumeSuccessEvent (purchaseData);
    } else {
        string errorData = findStringAfterCBType (callback, CBType.Error);
        if (errorData.Length > 0) {
            consumeErrorEvent (errorData);
        } else {
            consumeErrorEvent (callback);
        }
    }
}

// Onestore_IapResultListener.cs (C#)
void consumeSuccessResult (PurchaseData  result)
{
    AndroidNative.showMessage ("consume sucess", result.ToString (), "ok");
    PlayerPrefs.SetString (result.productId, "");
}
  
void consumeErrorResult (string result)
{
    AndroidNative.showMessage ("consume error", result.ToString (), "ok");
}

cancel/reactivate auto 버튼 클릭 : 월정액 상품(auto)의 상태변경(해지예약 / 해지예약 취소)을 호출하는 메서드

호출방향은 위에서 아래이며 Unity 에서 Android를 호출하게 됩니다. 월정액 상품(auto)인 경우 recurringState가 0이면 정상가입 상태임을 의미하며, 월정액 상품을 해지하기 위해서 "cancel" 파라미터를 보내서 해지 명령을 보내게 됩니다. recurringState가 1이면, 해지예약된 상태를 의미하며, 해지예약을 취소하려면 "reactivate" 파라미터를 보내서 해지예약을 취소할 수 있습니다. (recurringState에 대한 상세 내용은 인앱결제 레퍼런스 페이지의 <표 15>를 참고하시기 바랍니다)

IapUi.cs에서는 cancel/reactivate 동작이 on/off방식으로 번갈아 동작하도록 샘플로 구현되었습니다.

// Onestore_IapUi.cs (C#)
public void cancelAutoItem ()
{
    string auto_json = PlayerPrefs.GetString (auto_pId1);
    PurchaseData response = JsonUtility.FromJson<PurchaseData> (auto_json);
  
    if (auto_json.Length > 0) {
        string command = "";
        if (response.recurringState == 0) {
            command = "cancel";
        } else if (response.recurringState == 1) {
            command = "reactivate";
        }
        IapCallManager.manageRecurringAuto (auto_json, command);
    } else {
        AndroidNative.showMessage ("Warning!!", "no data for manageRecurringAuto", "ok");
    }
}

// Onestore_IapCallMnager.cs (C#)
public static void manageRecurringAuto (string auto_json, string command)
{
    checkServiceAvailable ();
    iapRequestAdapter.Call("manageRecurringAuto", IAP_API_VERSION, auto_json, command);
}

// ONE store Unity Plugin (Java)
// 월정액 상품(auto)의 상태변경(해지예약 / 해지예약 취소)을 진행합니다.
public void manageRecurringAuto(int apiVersion, final String data, final String action) {
    if (mPurchaseClient == null) {
        Log.d(TAG, "PurchaseClient is not initialized");
        return;
    }
      
    mPurchaseClient.manageRecurringProductAsync(apiVersion, JsonUtil.makeJsonToPurchaseData(data), action, mManageRecurringProductListener);
}

연결 리스너를 통해서 Android에서 Unity를 호출하는 순서입니다. callback으로 들어오는 결과값 스트링 종류는 다음과 같습니다.

  • onSuccess: 월정액 상태변경 호출에 대한 성공 응답

  • onErrorRemoteException

  • onErrorSecurityException

  • onErrorNeedUpdateException

  • onError: 실패일 경우 애플리케이션으로 에러코드 전달. 숫자로 된 code와 string으로 된 description을 ToString형태로 넘겨줍니다.

// ONE store Unity Plugin (Java)
PurchaseClient.ManageRecurringProductListener mManageRecurringProductListener = new PurchaseClient.ManageRecurringProductListener() {
    @Override
    public void onSuccess(PurchaseData purchaseData, String manageAction) {
        unitySendMessage(gameObject, "ManageRecurringProductListener", "onSuccess" + JsonUtil.makePurchaseDataToJson(purchaseData));
    }
  
    @Override
    public void onErrorRemoteException() {
        unitySendMessage(gameObject, "ManageRecurringProductListener", "onErrorRemoteException");
    }
  
    @Override
    public void onErrorSecurityException() {
        unitySendMessage(gameObject, "ManageRecurringProductListener", "onErrorSecurityException");
    }
  
    @Override
    public void onErrorNeedUpdateException() {
        unitySendMessage(gameObject, "ManageRecurringProductListener", "onErrorNeedUpdateException");
    }
  
    @Override
    public void onError(IapResult result) {
        unitySendMessage(gameObject, "ManageRecurringProductListener", "onError" + result.toString());
    }
};

// Onestore_IapCallbackManager.cs (C#)
public static event Action<PurchaseData>manageRecurringSuccessEvent;
public static event Action<string> manageRecurringErrorEvent;
  
public void ManageRecurringProductListener (string callback)
{
    string data = findStringAfterCBType (callback, CBType.Success);
    if (data.Length > 0) {
        PurchaseData response =  JsonUtility.FromJson<PurchaseData> (data);
        manageRecurringSuccessEvent (response);
    } else {
        string errorData = findStringAfterCBType (callback,  CBType.Error);
        if (errorData.Length > 0) {
            manageRecurringErrorEvent (errorData);
        } else {
            manageRecurringErrorEvent (callback);
        }
    }
}
// IapResultListener.cs (C#)
void manageRecurringSuccessResult (PurchaseData result)
{
    AndroidNative.showMessage ("ManageRecurring sucess", result.ToString (), "ok");
    PlayerPrefs.SetString (result.productId, JsonUtility.ToJson (result));
}
  
void manageRecurringErrorResult (string result)
{
    AndroidNative.showMessage ("ManageRecurring error", result.ToString (), "ok");
}

login 버튼 클릭 : 인앱결제 API V5(SDK V17) 로그인 요청을 위한 메서드

호출방향은 위에서 아래이며 Unity 에서 Android를 호출하게 됩니다. 결제를 하기전에 기본적으로 로그인 체크를 수행하게 되는데 각 명령을 수행시에 로그인 관련 에러가 발생시 사용하면 됩니다.

// Onestore_IapUi.cs (C#)
public void login ()
{
    IapCallManager.login ();
}
// Onestore_IapCallMnager.cs (C#)
public static void login ()
{
    checkServiceAvailable ();
    iapRequestAdapter.Call ("launchLoginFlow", IAP_API_VERSION);
}
// ONE store Unity Plugin (Java)
public void launchLoginFlow(int apiVersion) {
    if (mPurchaseClient == null) {
        Log.d(TAG, "PurchaseClient is not initialized");
        return;
    }
  
    Intent i = new Intent(getActivity(), IapUnityPluginActivity.class);
    i.putExtra("apiVersion", apiVersion);
    getActivity().startActivity(i);
}

mPurchaseClient.launchLoginFlow 호출 후 onActivityResult로 구매 완료 결과값을 결과값을 받습니다.

mPurchaseClient.handleLoginData 내에서 로그인 완료시 전달 받은 Intent 데이터에 대해서 파싱해서 최종 리스너에게 성공또는 에러 유무를 전달합니다.

// ONE store Unity Plugin (Java) : IapUnityPluginActivity
public class IapUnityPluginActivity extends Activity {
  
    private static final int LOGIN_REQUEST_CODE = 1001;
    private static final int PURCHASE_REQUEST_CODE = 2001;
  
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Intent i = getIntent();
        final String productId = i.getStringExtra("productId");
        final String productType = i.getStringExtra("productType");
        final String payload = i.getStringExtra("payload");
        final int apiVersion = i.getIntExtra("apiVersion", 5);
  
        final String gameUserId = i.getStringExtra("gameUserId");
        final boolean promotionApplicable = i.getBooleanExtra("promotionApplicable", false);
  
        IapPlugin.INSTANCE.mPurchaseClient.launchLoginFlow(
                apiVersion,
                IapUnityPluginActivity.this,
                LOGIN_REQUEST_CODE,
                IapPlugin.INSTANCE.mLoginFlowListener);
    }
}

// ONE store Unity Plugin (Java) : IapUnityPluginActivity
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    Log.d("IapUnityPluginActivity", "onActivityResult requestCode " + requestCode);
  
    switch (requestCode) {
        case LOGIN_REQUEST_CODE:
            /*
             * launchLoginFlow API 호출 시 전달받은 intent 데이터를 handleLoginData를 통하여 응답값을 파싱한다.
             * 파싱 이후 응답 결과를 launchLoginFlow 호출 시 넘겨준 LoginFlowListener 를 통하여 전달한다.
             */
            if (resultCode == RESULT_OK) {
                if (IapPlugin.INSTANCE.mPurchaseClient.handleLoginData(data) == false) {
                    // listener is null
                }
            } else {
                // user canceled , do nothing..
            }
            break;
    }
}

연결 리스너를 통해서 Android에서 Unity를 호출하는 순서입니다. 성공 여부와 에러 스트링으로만 구성되어있어서 Action event는 1개로만 구성됩니다. callback으로 들어오는 결과값 스트링 종류는 다음과 같습니다.

  • onSuccess: 로그인 성공 대한 성공 응답

  • onErrorRemoteException

  • onErrorSecurityException

  • onErrorNeedUpdateException

  • onError: 실패일 경우 애플리케이션으로 에러코드 전달. 숫자로 된 code와 string으로 된 description을 ToString형태로 넘겨줍니다.

// ONE store Unity Plugin (Java)
PurchaseClient.LoginFlowListener mLoginFlowListener = new PurchaseClient.LoginFlowListener() {
    @Override
    public void onSuccess() {
        unitySendMessage(gameObject, "LoginFlowListener", "onSuccess");
    }
  
    @Override
    public void onErrorRemoteException() {
        unitySendMessage(gameObject, "LoginFlowListener", "onErrorRemoteException");
    }
  
    @Override
    public void onErrorSecurityException() {
        unitySendMessage(gameObject, "LoginFlowListener", "onErrorSecurityException");
    }
  
    @Override
    public void onErrorNeedUpdateException() {
        unitySendMessage(gameObject, "LoginFlowListener", "onErrorNeedUpdateException");
    }
  
    @Override
    public void onError(IapResult result) {
        unitySendMessage(gameObject, "LoginFlowListener", "onErrorNeedUpdateException" + result.toString());
    }
};
// Onestore_IapCallbackManager.cs (C#)
public static event Action<string> getLoginIntentEvent;
  
public void LoginFlowListener (string callback)
{
    getLoginIntentEvent (callback);//just pass
}
// Onestore_IapResultListener.cs (C#)
void getLoginIntentEvent (string result)
{
    AndroidNative.showMessage ("getLoginIntent ", result.ToString (), "ok");
}

Destroy Service 버튼 클릭 : 인앱결제 API V5(SDK V17) 서비스 unbind

호출방향은 위에서 아래이며 Unity 에서 Android를 호출하게 됩니다. AIDL 서비스 바인딩을 해제 합니다.

// Onestore_IapUi.cs (C#)
public void destroy ()
{
    IapCallManager.destroy ();
}
// Onestore_IapCallMnager.cs (C#)
public static void destroy ()
{
    iapRequestAdapter.Call ("dispose");
    isServiceCreated = false;
}
// ONE store Unity Plugin (Java)
public void dispose() {
    if (this.mPurchaseClient != null) {
        mPurchaseClient.terminate();
    }
    this.mPurchaseClient = null;
}
  
public void terminate() {
    if (mContext != null && mServiceConnection != null) {
        try {
            mContext.unbindService(mServiceConnection);
        } catch (Exception e) {
            Log.d("PurchaseClient", e.toString());
        }
  
        mContext = null;
        mServiceConnection = null;
        mInAppPurchaseService = null;
    }
}

ONE store Unity Plugin 패키지 / Plugin 다운로드

다운로드 페이지로 이동하기(Github)

  • UnityPackage (IapV17_UnityPluginSample.unityPackage)

  • plugin AAR

  • Unity 에서 IapV17_UnityPluginSample.unityPackage 를 Import 하게되면 관련 Asset들이 모두 자동으로 가져오게 됩니다.

  • Unity Sample V17 Project & Sample APK

Last updated