[Unity 플러그인] 원스토어 인앱결제 업그레이드 하기

Import Unity Package 비교

SDK V17

SDK V19

PURCHASE

Onestore_IapCallbackManager.cs

GaaCallbackManager.cs

Onestore_IapCallManager.cs

GaaCallManager.cs

Onestore_IapResultListener.cs

GaaIapResultListener.cs

Onestore_PurchaseResponse.cs

GaaPurchaseResponse.cs

UTIL

AndroidNative.cs

AndroidNative.cs

UI

Onestore_IapUi.cs

MainUIScript.cs

LogScript.cs

LoadingScript.cs

JSON

global-appstores.json

IapCallManager API 비교

v17 Onestore_IapCallManager.cs

v19 GaaIapCallManager.cs

결제모듈과의 연결

connect

StartConnection

결제모듈과의 연결해제

terminate

EndConnection

지원여부 확인

isBillingSupported

x

인앱 상품 구매

launchPurchaseFlowAsync

LaunchPurchaseFlow

인앱 상품 소비

consume

Consume

인앱 상품 확인

x

Acknowledge

소비되지 않은 상품의 구매내역 (월 자동결제 포함)

getPurchases

QueryPurchases

인앱 상품 상세정보

getProductDetails

QueryProductDetails

월 자동결제 상태 변경

manageRecurringAuto

ManageRecurringProduct

결제 모듈 업데이트 또는 설치

x

LaunchUpdateOrInstallFlow

원스토어 로그인 호출

login

LaunchLoginFlow

마켓 구분 코드 확인

x

GetStoreInfo

인앱결제 초기화 및 연결

v19 SDK 부터는 IapResult.code 의 값으로 성공, 실패 여부를 판단할 수 있습니다.

결제 모듈과 연결하는 방식은 똑같습니다.

Before

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

After

// MainUIScript.cs
public void StartConnection()
{
    if (GaaIapCallManager.IsServiceAvailable() == false)
    {
        GaaIapCallManager.StartConnection( /* your public key */ );
    }
}

연결 성공, 실패에 대한 응답은 많이 달라졌습니다.

SDK V17에서는 연결 성공 이후에 isBillingSupported()를 통해 지원 가능여부를 확인해야 했습니다.

하지만, SDK V19부터는 더 이상 지원 가능여부 체크를 하지 않아도 됩니다. SDK 내부에서 자동으로 지원 가능여부를 체크합니다.

IapResult.code 의 값을 확인하려면 PurchaseResponse.ResponseCode 를 참조하세요.

Before

// 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");
}

After

// GaaIapResultListener.cs
GaaIapCallbackManager.PurchaseClientStateEvent += PurchaseClientStateEvent;
void PurchaseClientStateEvent(IapResult iapResult)
{
    if (iapResult.IsSuccess())
    {
        ...
    }
    else if (iapResult.code == ResponseCode.RESULT_NEED_UPDATE)
    {
        ...
    }
    else if (iapResult.code == ResponseCode.RESULT_NEED_LOGIN)
    {
        ...
    }
}

상품정보 조회하기

Before

// 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);
}

인앱상품 아이디(productId) 배열값과 그에 해당하는 상품타입을 지정합니다. 인앱상품 유형으로는 관리형 상품(ProductType.INAPP), 월정액 상품(ProductType,AUTO)가 있으며, 두 유형을 모두 조회하려면 ProductType.ALL을 입력하면 됩니다.

After

// MainUIScript.cs
// 상품 상세 정보를 조회한다.
string[] all_products = { inapp_p5000, inapp_p10000, inapp_p50000, auto_a100000 };
GaaIapCallManager.QueryProductDetails(all_products, ProductType.ALL);
 
or
 
string[] inapp_products = new string[] {inapp_p5000, inapp_p10000, inapp_p50000};
string[] auto_products = new string[] {auto_a100000};
 
GaaIapCallManager.QueryProductDetails(inapp_products, ProductType.INAPP);
GaaIapCallManager.QueryProductDetails(auto_products, ProductType.AUTO);

상품정보 조회의 응답 결과값은 GaaIapResultListener의 ProductDetailsSuccessEvent(), ProductDetailsErrorEvent()를 통해 전달 받을 수 있습니다.

Andorid와 Unity와의 통신은 문자열 통신이고 한번에 전달할 수 있는 문자열의 길이가 제한되어 한 번에 한 상품씩 전달됩니다.

그래서, ProductDetailsSuccessEvent(..., ..., int count, int totalCount) 값이 중요합니다.

ex) 상품이 1개인 경우: count = 1, totalCount = 1 상품이 10개인 경우: count = 1, totalCount = 10 ... count = 10, totalCount = 10

Beforede


// 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 queryProductsSuccessEvent (ProductDetail result)
{
    AndroidNative.showMessage ("queryProducts success", result.ToString (), "ok");
}
  
void queryProductsErrorEvent (string result) {
    AndroidNative.showMessage ("queryProducts error", result, "ok");
}

After

// GaaIapResultListener.cs
List<ProductDetail> products = new List<ProductDetail>();
void ProductDetailsSuccessEvent(ProductDetail productDetail, int count, int totalCount)
{
    if (count == 1)
    {
        products.Clear();
    }
      
    products.Add(productDetail);
  
    if (count == totalCount)
    {
        // send ProductDetail List to UI
    }
}
  
void ProductDetailsErrorEvent(IapResult iapResult)
{
    if (iapResult.code == ResponseCode.RESULT_NEED_UPDATE)
    {
        ...
    }
    else if (iapResult.code == ResponseCode.RESULT_NEED_LOGIN)
    {
        ...
    }
}

구매 요청하기

인앱 상품 구매를 위해 QueryProductDetails로 얻어온 productId, productType 등을 PurchaseFlowParams 객체에 담아 전달 하도록 변경 되었습니다.

Before

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

After

// MainUIScript.cs
void BuyProduct(string productId, string type)
{
    PurchaseFlowParams param = new PurchaseFlowParams();
    param.productId = productId;
    param.productType = type;
    //param.productName = "";
    //param.devPayload = "your Developer Payload"; // 최대 사이즈 200byte까지만 허용됩니다.
    //param.gameUserId = "";
    //param.promotionApplicable = false;
 
    GaaIapCallManager.LaunchPurchaseFlow(param);
}

LaunchPurchaseFlow() 메서드를 호출하면 원스토어 결제화면이 나타납니다.

그림1. 원스토어 상품 결제 화면

결제에 대한 응답 결과를 받는 부분에 '상품정보 조회하기'와 같은 이유로 count, totalCount가 존재 합니다.

Before

// 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");
}

After

// GaaIapResultListener.cs
private List<PurchaseData> purchases = new List<PurchaseData>();
private List<string> signatures = new List<string>();
  
  
void PurchaseUpdatedSuccessEvent(PurchaseData purchaseData, string signature, int count, int totalCount)
{
    if (purchaseData != null) {
        if (count == 1)
        {
            purchases.Clear();
            signatures.Clear();
        }
  
        purchases.Add(purchaseData);
        signatures.Add(signature);
  
        if (count == totalCount)
        {
            OnPurchaseUpdatedResponse(purchases, signatures); // send to ui
        }
    }
    else
    {
        // no PurchaseData
    }
}
  
void PurchaseUpdatedErrorEvent(IapResult iapResult)
{
    ...
}

구매 확인하기

원스토어 인앱결제 라이브러리 V6(SDK V19)에 새롭게 추가된 API 입니다.

자세한 내용은 'Unity에서 원스토어 인앱결제 사용하기'에서 확인 할 수 있습니다.

관리형 상품 소비하기

Before

// 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");
    }
}

After

// MainUIScript.cs
PurchaseData purchaseData = ...
GaaIapCallManager.Consume(purchaseData, "your developer payload");

구매내역 조회하기

구매내역 조회하기의 요청은 큰 변동 사항이 없습니다.

Before

// 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");
}

After

// MainUIScript.cs
GaaIapCallManager.QueryPurchases(ProductType.INAPP);
GaaIapCallManager.QueryPurchases(ProductType.AUTO);

다음은 응답 부분이며, '구매 요청하기' 응답과 동일하게 전달 됩니다.

Before

// 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);
        }
    }
}

After

// GaaIapResultListener.cs
void QueryPurchasesSuccessEvent(PurchaseData purchaseData, string signature, int count, int totalCount)
 
void QueryPurchasesErrorEvent(IapResult iapResult)

월정액 상품 상태 변경하기

SDK V17에서는 개발사에서 직접 recurringState를 판단하여 command 값을 전달 해야 했지만, SDK V19에서는 PurchaseData를 넣으면 SDK에서 판단하여 command 값을 전달 합니다.

Before

// 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");
    }
}

Aftere

// MainUIScript.cs
PurchaseData purchaseData = ...
string recurringAction;
if (purchaseData.recurringState == RecurringState.RECURRING) {
    recurringAction = RecurringAction.CANCEL;
} else if (purchaseData.recurringState == RecurringState.CANCEL) {
    recurringAction = RecurringAction.REACTIVATE;
}
GaaIapCallManager.ManageRecurringProduct(purchaseData, recurringAction);

원스토어 로그인 요청하기

Before

// Onestore_IapUi.cs (C#)
IapCallManager.login();
 
 
// Onestore_IapResultListener.cs (C#)
void getLoginIntentEvent (string result)
{
    AndroidNative.showMessage ("getLoginIntent ", result.ToString (), "ok");
}

After

// MainUIScript.cs
GaaIapCallManager.LaunchLoginFlow();
  
// GaaIapResultListener.cs
void LoginFlowEvent(IapResult iapResult)
{
    if (iapResult.IsSuccess()) {
        // You need to specify the scenario after successful login.
    }
}

원스토어 서비스 설치하기

원스토어 인앱결제 라이브러리 V6(SDK V19)에 새롭게 추가된 API 입니다.

자세한 내용은 'Unity에서 원스토어 인앱결제 사용하기'에서 확인 할 수 있습니다.

스토어 구분 코드 얻기

원스토어 인앱결제 라이브러리 V6(SDK V19)에 새롭게 추가된 API 입니다.

자세한 내용은 'Unity에서 원스토어 인앱결제 사용하기'에서 확인 할 수 있습니다.

Last updated