구글과 원스토어 동시 출시한 상태입니다.
- 소모성으로 설정되어 있습니다. (일부유저에게만 저런 현상이 벌어집니다.)
- 구글은 Unity IAP사용중, 원스토어는 OneIAP최신버전 사용중.
-
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.Purchasing; // 구글
using OneStore.Purchasing; // 원스토어
using BackEnd;
using System;
public class IAPManager : MonoBehaviour, IStoreListener, IPurchaseCallback
{
// 인앱 결제 제품의 ID
private string[] productIds =
{ "ads_remove",
"stage_pakage_1", "stage_pakage_2", "stage_pakage_3", "stage_pakage_4", "stage_pakage_5",
"stage_pakage_6", "stage_pakage_7", "stage_pakage_8", "stage_pakage_9", "stage_pakage_10",
"stage_pakage_11", "stage_pakage_12", "stage_pakage_13", "stage_pakage_14", "stage_pakage_15",
"stage_pakage_16", "stage_pakage_17", "stage_pakage_18", "stage_pakage_19", "stage_pakage_20",
"stage_pakage_21", "stage_pakage_22", "stage_pakage_23", "stage_pakage_24",
"daily_pakage_1", "daily_pakage_2",
"weekly_pakage_1", "weekly_pakage_2",
"monthly_pakage_1", "monthly_pakage_2",
"pass_level_1", "pass_stage_1", "pass_hunt_1", "pass_grow_1",
"pass_level_2", "pass_stage_2", "pass_hunt_2", "pass_grow_2",
"diamond_1", "diamond_2", "diamond_3", "diamond_4", "diamond_5"
};
//// 구글 용
private static IStoreController storeController;
private static IExtensionProvider extensionProvider;
//// 원스토어 용
// input your license key
private string _licenseKey = ConstantsAKR.PublicKey;
// 이 객체를 통해 OneStore의 API를 호출하여 인앱결제를 처리합니다.
private PurchaseClientImpl _purchaseClient;
//// 인앱 결제 초기화 (구글 & 원스토어 서비스에 연결하기 위한 선행 작업)
public void Init()
{
// 구글일 때
if (DataManager.instance.settingData.storeNum == 0)
{
if (IsInitialized())
{
return;
}
var builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance());
for (int i = 0; i < productIds.Length; i++)
{
builder.AddProduct(productIds[i], UnityEngine.Purchasing.ProductType.Consumable, new IDs()
{
{ productIds[i], GooglePlay.Name }
});
}
UnityPurchasing.Initialize(this, builder);
}
// 원스토어일 때
else
{
_purchaseClient = new PurchaseClientImpl(_licenseKey);
_purchaseClient.Initialize(this);
}
}
private bool IsInitialized()
{
return storeController != null && extensionProvider != null;
}
//// 특정 상품 구매 요청하기 (버튼 이벤트)
public void OnClickPurchase(string productId)
{
// 구글일 때
if (DataManager.instance.settingData.storeNum == 0)
{
if (IsInitialized())
{
// 상품 정의
Product product = storeController.products.WithID(productId);
// 상품이 존재하면서 구매 가능하면..
if (product != null && product.availableToPurchase)
{
// 구매가 가능하면 진행한다.
storeController.InitiatePurchase(product);
}
else
{
GameManager.instance.SystemTextViewer.PrintText(SystemType.Debug, 0, false,
"PurchaseProductID: FAIL. 상품을 찾을 수 없거나 구매할 수 없습니다.");
}
}
else
{
GameManager.instance.SystemTextViewer.PrintText(SystemType.Debug, 0, false,
"PurchaseProductID FAIL. IAP가 초기화되지 않았습니다.");
}
}
// 원스토어일 때
else
{
// 결제 로직
var purchaseFlowParams = new PurchaseFlowParams.Builder()
.SetProductId(productId) // mandatory
.SetProductType(OneStore.Purchasing.ProductType.INAPP) // mandatory
.SetDeveloperPayload(DataManager.instance.playerData.gamerId) // optional (따로 메모할만한 정보 여기에 넣기)
.Build();
_purchaseClient.Purchase(purchaseFlowParams);
}
}
//// [ 구글용 Impl ]
// 초기화 성공시 호출됨
public void OnInitialized(IStoreController controller, IExtensionProvider extensions)
{
storeController = controller;
extensionProvider = extensions;
}
// 초기화 실패시 호출됨
public void OnInitializeFailed(InitializationFailureReason error)
{
Debug.LogError("Unity IAP 초기화 실패: " + error);
}
public void OnInitializeFailed(InitializationFailureReason error, string message)
{
Debug.LogError("Unity IAP 초기화 실패: " + error);
}
// 구매 실패시 호출됨
public void OnPurchaseFailed(Product product, PurchaseFailureReason failureReason)
{
Debug.LogError("구매 실패: " + product.definition.id + ", 사유: " + failureReason);
}
// 구매 확정 처리 + 뒤끝 영수증 검증 처리
public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs args)
{
string receiptJson = args.purchasedProduct.receipt;
BackendReturnObject validation = Backend.Receipt.IsValidateGooglePurchase(receiptJson, "추가로 저장하고자 하는 내용", false);
// 영수증 검증에 성공한 경우
if (validation.IsSuccess())
{
// 상품 구매에 따른 상품 지급
}
// 영수증 검증에 실패한 경우
else
{
GameManager.instance.SystemTextViewer.PrintText(SystemType.Debug, 0, false,
string.Format("ProcessPurchase: FAIL. Unrecognized product: '{0}'", args.purchasedProduct.definition.id));
}
// 구매 확정 처리.
return PurchaseProcessingResult.Complete;
}
//// [ 원스토어용 Impl ]
// 구매 성공시 호출 됨
public void OnPurchaseSucceeded(List<PurchaseData> purchases)
{
// 구매 리스트가 비어 있을 경우 수동으로 구매 내역을 조회
if (purchases == null || purchases.Count == 0)
{
Debug.LogError("구매 성공 호출되었으나 purchases 리스트가 null이거나 비어 있습니다. 구매 내역을 수동으로 조회합니다.");
// 구매 내역 수동 조회
_purchaseClient.QueryPurchases(OneStore.Purchasing.ProductType.INAPP);
}
foreach (PurchaseData purchase in purchases)
{
if (purchase != null)
{
try
{
// 구매 소비 (Consume) 처리
_purchaseClient.ConsumePurchase(purchase);
Debug.Log("구매 소비 성공: " + purchase.ProductId);
}
catch (Exception e)
{
Debug.LogError("구매 소비 중 오류 발생: " + e.Message);
}
}
else
{
Debug.LogError("purchase 객체가 null입니다.");
}
}
}
// 구매 실패시 호출 됨
public void OnPurchaseFailed(IapResult iapResult)
{
GameManager.instance.SystemTextViewer.PrintText(SystemType.Debug, 0, false,
"구매에 실패했습니다.");
}
// 구매 후 소비 (3일 이내 자동환불 방지) + 뒤끝 영수증 검증 처리
public void OnConsumeSucceeded(PurchaseData purchase)
{
// 원스토어에 국내에서만 앱을 출시하였을 경우
bool isGlobal = false;
BackendReturnObject validation =
Backend.Receipt.IsValidateOneStorePurchase
(isGlobal, purchase.ProductId, purchase.PurchaseToken, "receiptDescription");
if (validation.IsSuccess())
{
// 상품 구매에 따른 상품 지급
}
else
{
// 영수증 검증 실패
GameManager.instance.SystemTextViewer.PrintText(SystemType.Debug, 0, false, "영수증 검증 실패.");
}
}
public void OnConsumeFailed(IapResult iapResult)
{
GameManager.instance.SystemTextViewer.PrintText(SystemType.Debug, 0, false, "소비에 실패했습니다.");
}
코드들입니다. IAP 코드들입니다.