ํ˜„์žฌ ๋ฌธ์ œ :

์œ ๋‹ˆํ‹ฐ ํ™˜๊ฒฝ์—์„œ๋Š” ์ธ์•ฑ ๊ฒฐ์ œ ํ…Œ์ŠคํŠธ ์ž˜ ๋จ

๋ชจ๋ฐ”์ผ ํ™˜๊ฒฝ์—์„œ๋Š” ๋ฒ„ํŠผ์„ ๋ˆŒ๋Ÿฌ๋„ ์•„๋ฌด ํŒ์—…์ด ์•ˆ๋œธ


์œ ๋‹ˆํ‹ฐ ํ™˜๊ฒฝ์—์„œ๋Š” ๊ฒŒ์ž„์„ ์‹œ์ž‘ํ•˜๋ฉด

IAP ์ดˆ๊ธฐํ™”๊ฐ€ ์„ฑ๊ณตํ–ˆ๋‹ค๋Š” ๋กœ๊ทธ๊ฐ€ ์ถœ๋ ฅ๋˜์ง€๋งŒ

๋ชจ๋ฐ”์ผ ํ™˜๊ฒฝ์€ ์ดˆ๊ธฐํ™” ์‹คํŒจ ๋กœ๊ทธ๊ฐ€ ์ถœ๋ ฅ๋จ


์ผ๋‹จ ๊ตฌ๊ธ€ ๋ผ์ด์„ ์Šค ์ฝ”๋“œ ์„ธํŒ…์€ ์ด๋ ‡๊ฒŒ ํ–ˆ์–ด์š”


๊ตฌ๊ธ€ ๋ผ์ด์„ ์Šค ํ‚ค ๋ณต์‚ฌ ํ•ด์„œ ๊ณต๋ฐฑ ์ œ๊ฑฐ ํ•˜๊ณ  ํ•˜์–€์ƒ‰ ์นธ์— ๋„ฃ์—ˆ์–ด์š”



์œ ๋‹ˆํ‹ฐ ๋Œ€์‰ฌ๋ณด๋“œ์— ํ˜„์žฌ ์ œ๊ฐ€ ์‚ฌ์šฉ ์ค‘์ธ ํ”„๋กœ์ ํŠธ๊ฐ€ ์—†๊ธธ๋ž˜

์ง์ ‘ ๋Œ€์‰ฌ๋ณด๋“œ์— ์ž‘์—…์ค‘์ธ ํ”„๋กœ์ ํŠธ ์ด๋ฆ„์„ ์ž…๋ ฅํ•ด์„œ ์ถ”๊ฐ€ ํ•˜๊ณ 

๋ผ์ด์„ ์Šค ํ‚ค๋ฅผ ๋„ฃ์—ˆ์Šต๋‹ˆ๋‹ค



์นดํƒˆ๋กœ๊ทธ๋Š” ์ด๋ ‡๊ฒŒ ์งฐ์Šต๋‹ˆ๋‹ค



IAPManager ์˜ค๋ธŒ์ ํŠธ์— IAPManager ์Šคํฌ๋ฆฝํŠธ ๋„ฃ์—ˆ์Šต๋‹ˆ๋‹ค


์ด๊ฑฐ๋Š” IAPManager ์Šคํฌ๋ฆฝํŠธ ์ฝ”๋“œ์—์š”

using UnityEngine;

using UnityEngine.Purchasing;


public class IAPManager : MonoBehaviour, IStoreListener

{

[Header("Product ID")]

public readonly string gold500_id = "com.games.srpg.gold500";

public readonly string gold1000_id = "com.games.srpg.gold1000";


[Header("Cache")]

private IStoreController storeController; // ๊ตฌ๋งค ๊ณผ์ •์„ ์ œ์–ดํ•˜๋Š” ํ•จ์ˆ˜ ์ œ๊ณต์ž

private IExtensionProvider storeExtensionProvider; // ์—ฌ๋Ÿฌ ํ”Œ๋žซํผ์„ ์œ„ํ•œ ํ™•์žฅ ์ฒ˜๋ฆฌ ์ œ๊ณต์ž


private void Start()

{

InitUnityIAP(); // Start ๋ฌธ์—์„œ ์ดˆ๊ธฐํ™” ํ•„์ˆ˜

}


/* Unity IAP๋ฅผ ์ดˆ๊ธฐํ™”ํ•˜๋Š” ํ•จ์ˆ˜ */

private void InitUnityIAP()

{

ConfigurationBuilder builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance());


/* ๊ตฌ๊ธ€ ํ”Œ๋ ˆ์ด ์ƒํ’ˆ๋“ค ์ถ”๊ฐ€ */

builder.AddProduct(gold500_id, ProductType.Consumable);

builder.AddProduct(gold1000_id, ProductType.Consumable);


UnityPurchasing.Initialize(this, builder);

}


/* ๊ตฌ๋งคํ•˜๋Š” ํ•จ์ˆ˜ */

public void Purchase(string productId)

{

Product product = storeController.products.WithID(productId); // ์ƒํ’ˆ ์ •์˜


if (product != null && product.availableToPurchase) // ์ƒํ’ˆ์ด ์กด์žฌํ•˜๋ฉด์„œ ๊ตฌ๋งค ๊ฐ€๋Šฅํ•˜๋ฉด

{

storeController.InitiatePurchase(product); // ๊ตฌ๋งค๊ฐ€ ๊ฐ€๋Šฅํ•˜๋ฉด ์ง„ํ–‰

}

else // ์ƒํ’ˆ์ด ์กด์žฌํ•˜์ง€ ์•Š๊ฑฐ๋‚˜ ๊ตฌ๋งค ๋ถˆ๊ฐ€๋Šฅํ•˜๋ฉด

{

Debug.Log("์ƒํ’ˆ์ด ์—†๊ฑฐ๋‚˜ ํ˜„์žฌ ๊ตฌ๋งค๊ฐ€ ๋ถˆ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค");

}

}


#region Interface

/* ์ดˆ๊ธฐํ™” ์„ฑ๊ณต ์‹œ ์‹คํ–‰๋˜๋Š” ํ•จ์ˆ˜ */

public void OnInitialized(IStoreController controller, IExtensionProvider extension)

{

Debug.Log("์ดˆ๊ธฐํ™”์— ์„ฑ๊ณตํ–ˆ์Šต๋‹ˆ๋‹ค");


storeController = controller;

storeExtensionProvider = extension;

}


/* ์ดˆ๊ธฐํ™” ์‹คํŒจ ์‹œ ์‹คํ–‰๋˜๋Š” ํ•จ์ˆ˜ */

public void OnInitializeFailed(InitializationFailureReason error)

{

Debug.Log("์ดˆ๊ธฐํ™”์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค");

}


public void OnInitializeFailed(InitializationFailureReason error, string message)

{

Debug.Log($"์ดˆ๊ธฐํ™”์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด์œ : {error.ToString()}. ๋ฉ”์‹œ์ง€: {message}");

}


/* ๊ตฌ๋งค์— ์‹คํŒจํ–ˆ์„ ๋•Œ ์‹คํ–‰๋˜๋Š” ํ•จ์ˆ˜ */

public void OnPurchaseFailed(Product product, PurchaseFailureReason reason)

{

Debug.Log("๊ตฌ๋งค์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค");

}


/* ๊ตฌ๋งค๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ํ•จ์ˆ˜ */

public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs args)

{

Debug.Log("๊ตฌ๋งค์— ์„ฑ๊ณตํ–ˆ์Šต๋‹ˆ๋‹ค");


if (args.purchasedProduct.definition.id == gold500_id)

{

/* test_id ๊ตฌ๋งค ์ฒ˜๋ฆฌ */

point.gold += 500;

}

else if (args.purchasedProduct.definition.id == gold1000_id)

{

point.gold += 1000;

}


return PurchaseProcessingResult.Complete;

}

#endregion

}




์ด๊ฑด IStoreController ์ฝ”๋“œ

using System;

using System.Collections.Generic;


namespace UnityEngine.Purchasing

{

ย  ย  /// <summary>

ย  ย  /// Used by Applications to control Unity Purchasing.

ย  ย  /// </summary>

ย  ย  public interface IStoreController

ย  ย  {

ย  ย  ย  ย  /// <summary>

ย  ย  ย  ย  /// Gets the collection of products in the store.

ย  ย  ย  ย  /// </summary>

ย  ย  ย  ย  /// <value> The product collection. </value>

ย  ย  ย  ย  ProductCollection products { get; }


ย  ย  ย  ย  /// <summary>

ย  ย  ย  ย  /// Initiate a purchase from the controlled store.

ย  ย  ย  ย  /// </summary>

ย  ย  ย  ย  /// <param name="product"> The product to be purchased. </param>

ย  ย  ย  ย  /// <param name="payload"> The developer payload provided for certain stores that define such a concept (ex: Google Play). </param>

ย  ย  ย  ย  void InitiatePurchase(Product product, string payload);


ย  ย  ย  ย  /// <summary>

ย  ย  ย  ย  /// Initiate a purchase from the controlled store.

ย  ย  ย  ย  /// </summary>

ย  ย  ย  ย  /// <param name="productId"> The id of the product to be purchased. </param>

ย  ย  ย  ย  /// <param name="payload"> The developer payload provided for certain stores that define such a concept (ex: Google Play). </param>

ย  ย  ย  ย  void InitiatePurchase(string productId, string payload);


ย  ย  ย  ย  /// <summary>

ย  ย  ย  ย  /// Initiate a purchase from the controlled store.

ย  ย  ย  ย  /// </summary>

ย  ย  ย  ย  /// <param name="product"> The product to be purchased. </param>

ย  ย  ย  ย  void InitiatePurchase(Product product);


ย  ย  ย  ย  /// <summary>

ย  ย  ย  ย  /// Initiate a purchase from the controlled store

ย  ย  ย  ย  /// </summary>

ย  ย  ย  ย  /// <param name="productId"> The id of the product to be purchased. </param>

ย  ย  ย  ย  void InitiatePurchase(string productId);



ย  ย  ย  ย  /// <summary>

ย  ย  ย  ย  /// Fetch additional products from the controlled store.

ย  ย  ย  ย  /// </summary>

ย  ย  ย  ย  /// <param name="additionalProducts"> The set of product definitions to be fetched. </param>

ย  ย  ย  ย  /// <param name="successCallback"> The event triggered on a successful fetch. </param>

ย  ย  ย  ย  /// <param name="failCallback"> The event triggered on a failed fetch. </param>

ย  ย  ย  ย  [Obsolete]

ย  ย  ย  ย  void FetchAdditionalProducts(HashSet<ProductDefinition> additionalProducts, Action successCallback,

ย  ย  ย  ย  ย  ย  Action<InitializationFailureReason> failCallback);


ย  ย  ย  ย  /// <summary>

ย  ย  ย  ย  /// Fetch additional products from the controlled store.

ย  ย  ย  ย  /// </summary>

ย  ย  ย  ย  /// <param name="additionalProducts"> The set of product definitions to be fetched. </param>

ย  ย  ย  ย  /// <param name="successCallback"> The event triggered on a successful fetch. </param>

ย  ย  ย  ย  /// <param name="failCallback"> The event triggered on a failed fetch. Contains the FailureReason and an optional error message. </param>

ย  ย  ย  ย  void FetchAdditionalProducts(HashSet<ProductDefinition> additionalProducts, Action successCallback,

ย  ย  ย  ย  ย  ย  Action<InitializationFailureReason, string> failCallback);


ย  ย  ย  ย  /// <summary>

ย  ย  ย  ย  /// Where an Application returned ProcessingResult.Pending

ย  ย  ย  ย  /// from IStoreListener.ProcessPurchase(), Applications should call

ย  ย  ย  ย  /// this method when processing completes.

ย  ย  ย  ย  /// </summary>

ย  ย  ย  ย  /// <param name="product"> The product for which its pending purchase it to be confirmed. </param>

ย  ย  ย  ย  void ConfirmPendingPurchase(Product product);

ย  ย  }

}




์ด๊ฑด IStoreListener ์Šคํฌ๋ฆฝํŠธ


using System;


namespace UnityEngine.Purchasing

{

ย  ย  /// <summary>

ย  ย  /// Implemented by Application developers using Unity Purchasing.

ย  ย  /// </summary>

ย  ย  public interface IStoreListener

ย  ย  {

ย  ย  ย  ย  /// <summary>

ย  ย  ย  ย  /// Purchasing failed to initialise for a non recoverable reason.

ย  ย  ย  ย  /// </summary>

ย  ย  ย  ย  /// <param name="error"> The failure reason. </param>

ย  ย  ย  ย  [Obsolete]

ย  ย  ย  ย  void OnInitializeFailed(InitializationFailureReason error);


ย  ย  ย  ย  /// <summary>

ย  ย  ย  ย  /// Purchasing failed to initialise for a non recoverable reason.

ย  ย  ย  ย  /// </summary>

ย  ย  ย  ย  /// <param name="error"> The failure reason. </param>

ย  ย  ย  ย  /// <param name="message"> More detail on the error : for example the GoogleBillingResponseCode. </param>

ย  ย  ย  ย  void OnInitializeFailed(InitializationFailureReason error, string message);


ย  ย  ย  ย  /// <summary>

ย  ย  ย  ย  /// A purchase succeeded.

ย  ย  ย  ย  /// </summary>

ย  ย  ย  ย  /// <param name="purchaseEvent"> The <c>PurchaseEventArgs</c> for the purchase event. </param>

ย  ย  ย  ย  /// <returns> The result of the successful purchase </returns>

ย  ย  ย  ย  PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs purchaseEvent);


ย  ย  ย  ย  /// <summary>

ย  ย  ย  ย  /// A purchase failed with specified reason.

ย  ย  ย  ย  /// </summary>

ย  ย  ย  ย  /// <param name="product"> The product that was attempted to be purchased. </param>

ย  ย  ย  ย  /// <param name="failureReason"> The failure reason. </param>

ย  ย  ย  ย  [Obsolete("Use IDetailedStoreListener.OnPurchaseFailed for more detailed callback.", false)]

ย  ย  ย  ย  void OnPurchaseFailed(Product product, PurchaseFailureReason failureReason);


ย  ย  ย  ย  /// <summary>

ย  ย  ย  ย  /// Purchasing initialized successfully.

ย  ย  ย  ย  ///

ย  ย  ย  ย  /// The <c>IStoreController</c> and <c>IExtensionProvider</c> are

ย  ย  ย  ย  /// available for accessing purchasing functionality.

ย  ย  ย  ย  /// </summary>

ย  ย  ย  ย  /// <param name="controller"> The <c>IStoreController</c> created during initialization. </param>

ย  ย  ย  ย  /// <param name="extensions"> The <c>IExtensionProvider</c> created during initialization. </param>

ย  ย  ย  ย  void OnInitialized(IStoreController controller, IExtensionProvider extensions);

ย  ย  }

}


์ด๊ฑด GooglePlayTangle ์Šคํฌ๋ฆฝํŠธ ์ฝ”๋“œ

// WARNING: Do not modify! Generated file.


namespace UnityEngine.Purchasing.Security {

public class GooglePlayTangle

{

private static byte[] data = System.Convert.FromBase64String("๊ธธ๊ฒŒ ๋‚˜์—ด๋œ ์˜์–ด์™€ ์ˆซ์ž, ํŠน์ˆ˜ ๊ธฐํ˜ธ๋“ค");

private static int[] order = new int[] { 12,3,13,8,9,9,8,10,11,10,11,11,12,13,14 };

private static int key = 160;


public static readonly bool IsPopulated = true;


public static byte[] Data() {

if (IsPopulated == false)

return null;

return Obfuscator.DeObfuscate(data, order, key);

}

}

}


๊ฒฐ์ œ ๋ฒ„ํŠผ ์ธ์ŠคํŽ™ํ„ฐ๋Š” ์ด๋ ‡๊ฒŒ ํ–ˆ์Šต๋‹ˆ๋‹ค


๊ตฌ๊ธ€์— ์ƒํ’ˆ ๋“ฑ๋ก๋„ ์ „๋ถ€ ์ •ํ™•ํ•˜๊ฒŒ ๋งˆ์ณค์Šต๋‹ˆ๋‹ค

์ฒ ์ž ์•ˆ ํ‹€๋ ธ๊ณ 

๋ชจ๋‘ ํ™œ์„ฑํ™” ์‹œํ‚จ ์ง€ ์ผ์ฃผ์ผ์€ ์ง€๋‚ฌ์Šต๋‹ˆ๋‹ค


๋งŒ๋“ ์ง€ 2~3์ฃผ๋Š” ์ง€๋‚ฌ๋Š”๋ฐ ๋ณ„ ์ง“์„ ๋‹ค ํ•ด๋„ ์•ˆ๋˜๊ธธ๋ž˜

ํ˜น์‹œ๋‚˜ ๋†“์นœ ์ ์ด ์žˆ๋Š”์ง€ ๋ณด๊ณ ์ž


๋ชจ๋“  ์ •๋ณด๋ฅผ ์—ฌ๊ธฐ์— ์˜ฌ๋ฆฝ๋‹ˆ๋‹ค


์ถ”๊ฐ€๋กœ ํ•„์š”ํ•˜์‹  ์ •๋ณด๊ฐ€ ์žˆ๋‹ค๋ฉด ๋Œ“๊ธ€ ๋‚จ๊ฒจ์ฃผ์„ธ์š”

๋ฐ”๋กœ ์˜ฌ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค