Unity3D-5.5版本的IAP-IOS使用方式(In-App-Purchase)
2017-04-17 11:29
525 查看
参考:http://blog.csdn.net/pz789as/article/details/70208867
关于IAP的设置问题,网上其实已经写了很多了,我也不多赘述,那么我在这里只写一些细节,特别是对于新手来说的那种。
第一步肯定是去iTunes Connect里面添加项目,并且设置商品:
现在苹果其实已经不需要你上传ipa包了,只要设置好了相关信息既可以测试iap功能。
需要注意的是:“协议、税务和银行业务” 填写,这是最最重要的,如果没有填写,你永远不会收到结果,但是也不报错。
税务信息如果填写完毕,那么应该是这样的:
![](https://oscdn.geek-share.com/Uploads/Images/Content/201704/a6c0722d816ff9181fb1e82cd1a2c57b)
也就是后面可以download的
在 功能 里面设置商品,你需要上传截图,不然是不会显示的,设置好之后,它们的状态应该是“准备提交”状态,说明这一步已经好了。然后还需要在APP Store信息中的初始版本里面选择你刚刚设置的商品,结果如下:
![](https://oscdn.geek-share.com/Uploads/Images/Content/201704/70ede0bce793693ccec793cb43453811)
另一个是沙盒测试账号,这个必须要使用你在iTunes Connect人员配置中设置的测试账号,否则是无法测试的。而且,在测试之前,一定要把设置里面的“iTunes Store与 App Store”退出。然后在游戏中点击购买的时候,会弹出一个登录账号的框,选择已经存在的账号,填写账号密码即可开始测试了。
这是需要注意的三个重要点
接下来就是unity这边了
在unity5.5版本以后,你需要在创建项目的时候打开unity服务,不然后面去弄,比较麻烦,半天连不上。
这个unity服务中,你找到In-App Purchasing,将他开启,然后import Unity的内购插件。
这里需要说明的一点是关于Restore的使用方式,其实Unity已经做好了,不需要关心,我要说的是他的返回的方式。
因为之前不知道,在看到Restore之后,返回只有一个bool类型的变量,所以不知道用户到底买了哪个非消费类型的商品。
其实,Unity在返回bool类型之前,其实还是会回调购买成功的函数,然后参数带了商品的ID,如果有多个非消费类型商品,它会多次回调,这样你就可以根据ID去做你的处理了。
对于其他的操作,unity的文档和案例已经写的很清楚了。
另外,使用代码和使用IAPButton是一样的效果,我贴出使用代码的方法:
两个方法使用其中一个即可。。
下午补充:
在后面的测试中发现,如果商品有非消费类型时,使用UnityIAP插件中自带的IAPButton组件时,会出现很奇怪的情况:
在购买完非消费类型的商品后,然后删除APP,重新安装APP的时候,它会莫名其妙的自动调用Restore,这样等于我还没登录,它就使用原来的账号去获取检测是否购买。我查了半天也不知道哪里出的问题。但是当我不使用IAPButton,全部用自己代码去调用就不会出现这种情况。。
不过没有非消费类型的商品还是可以用那个Button的!
个人建议最好还是使用代码调用,代码已经在上面贴出来可~
2017年11月26号补充:
其实Unity给我们提供了CataLog,可以很方便的添加各个平台不同的id,而只使用同样的加载方式即可。
下面给出获取Catalog数据方式的代码:(方便以后其他平台移植,到时候直接在catalog里面设置就好啦)
这样就可以不用管这边加载代码,其他地方都是通用的。哈哈哈
2017.12.19日补充,发现好多新手小伙伴还是一脸懵逼,我今天把最新的方式发在这里,供大家参考:
本次使用的还是和catalog结合使用,另外关于商品id还不清楚的我在这里再说一下。我这里有两个商品ID,第一个是Unity这边使用的ID,用于Unity端调用,也是用来映射不同平台商品id的自定义ID,他填的位置如下:
![](https://oscdn.geek-share.com/Uploads/Images/Content/201704/471ba4001065d7502bcd36d8fba47484)
这个id可以自定义,也可以用实际商店的id,只要是一个字符串就可以了。
然后第二个ID,就是个个平台设置的商品ID了,他们可能会有各种格式,所以unity这边做了处理,第一个自定义的id就是多了一层映射,方便管理和使用,了解了这一层关系,就知道我为什么要推荐用catalog了。因为在移植到其他平台时,我只要维护catalog的的每个平台的id就可以了,代码那边都需要关心。
![](https://oscdn.geek-share.com/Uploads/Images/Content/201704/95820352abe651fae2f132d301a69bd4)
明白这两个ID的作用,那么就可以直接看我的下面的代码了,我把ShopList.cs都贴出来,相信大家能明白其中的原由了。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Purchasing;
/// <summary>
/// 这是通用方式,通过读取catalog里面的信息,获取所有商品信息
/// </summary>
public class ShopList : MonoBehaviour, IStoreListener{
private static IStoreController m_StoreController;
private static IExtensionProvider m_StoreExtensionProvider;
bool blnRestore = false;//用来表示
bool blnPressRestore = false;//用来区分是否按了 restore 按钮
int dataLen = 3;
//每个商品的内容(金币),价格(除以100),折扣
int[] shopData = new int[]{
150,99,0, //0.99 美元购买 150 个金币
450,299,0, //2.99 美元购买 450 个金币
850,499,12, //4.99 美元购买 850 个金币
1850,999,22, //9.99 美元购买 1850 个金币
3950,1999,30, //19.99 美元购买 3950 个金币
0,199,0 //1.99 美元购买去广告功能,可以Restore的项目
};
//catalog 里面设置的id,和这边一一对应,这个id是unity端的一个映射,在catalog里面可以对应不同平台的真实的 商品id
private string[] kProducts = new string[] {
"buycoins0",
"buycoins1",
"buycoins2",
"buycoins3",
"buycoins4",
"removeads"
};
void Start () {
InitializePurchasing();
}
private bool IsInitialized()
{
return m_StoreController != null && m_StoreExtensionProvider != null;
}
//初始化内购项目,主要是从catalog中获取商品信息,设置给 UnityPurchasing
void InitializePurchasing()
{
if (IsInitialized())
{
Debug.Log("初始化失败");
return;
}
StandardPurchasingModule module = StandardPurchasingModule.Instance();
module.useFakeStoreUIMode = FakeStoreUIMode.StandardUser;
ConfigurationBuilder builder = ConfigurationBuilder.Instance(module);
//通过编辑器中的Catalog添加,方便操作
ProductCatalog catalog = ProductCatalog.LoadDefaultCatalog();
// Debug.Log(catalog.allProducts.Count);
foreach (var product in catalog.allProducts) {
if (product.allStoreIDs.Count > 0) {
// Debug.Log("product:" + product.id);
var ids = new IDs();
foreach (var storeID in product.allStoreIDs) {
ids.Add(storeID.id, storeID.store);
// Debug.Log("stordId:" + storeID.id + ", " + storeID.store);
}
builder.AddProduct(product.id, product.type, ids);
} else {
builder.AddProduct(product.id, product.type);
}
}
UnityPurchasing.Initialize(this, builder);
}
//供外部调用,当按 Restore 按钮时触发
public void OnRestore(){
if (Application.platform == RuntimePlatform.OSXEditor || Application.platform == RuntimePlatform.WindowsEditor){
Debug.Log("Restore success!");
}else{
blnRestore = true;
RestorePurchases();
blnPressRestore = true;
}
}
//供外部调用,按下哪个按钮,就可以购买哪一档的金币,我这里是通过按钮的名称得到购买的 idx 的,可以根据自己需要更改,比如:OnBuyCoins(int idx)
//idx 是上面 shopData 对应的每行数据
public void OnBuyCoins(Button btn){
int idx = System.Convert.ToInt32(btn.name);
BuyCoinsWithIdx(idx);
}
//实际购买调用的函数,根据idx拿到unity端的商品id
void BuyCoinsWithIdx(int idx){
if (idx == 5){//购买去广告
if (Application.platform == RuntimePlatform.OSXEditor || Application.platform == RuntimePlatform.WindowsEditor){
Debug.Log("购买去广告!");
}else{
blnPressRestore = false;
BuyProductID(kProducts[idx]);
}
}else{
if (Application.platform == RuntimePlatform.OSXEditor || Application.platform == RuntimePlatform.WindowsEditor){
Debug.Log("editor buy coins");
}else{
BuyProductID(kProducts[idx]);
}
}
}
//这里是通过商品id购买物品
void BuyProductID(string productId)
{
if (IsInitialized())
{
Debug.Log("Buy ProductID: " + productId);
Product product = m_StoreController.products.WithID(productId);
if (product != null && product.availableToPurchase) {
Debug.Log(string.Format("Purchasing product asychronously: '{0}'", product.definition.id));
m_StoreController.InitiatePurchase(product);
} else {
Debug.Log("BuyProductID: FAIL. Not purchasing product, either is not found or is not available for purchase");
}
}else {
Debug.Log("没出初始化");
}
}
//真是的发起Restore请求
public void RestorePurchases()
{
if (!IsInitialized()) {
Debug.Log("没出初始化");
return;
}
if (Application.platform == RuntimePlatform.IPhonePlayer || Application.platform == RuntimePlatform.OSXPlayer) {
// Debug.Log("RestorePurchases started ...");
var apple = m_StoreExtensionProvider.GetExtension<IAppleExtensions>();
apple.RestoreTransactions(HandleRestored);
}else {
Debug.Log("RestorePurchases FAIL. Not supported on this platform. Current = " + Application.platform);
}
}
//如果restore之后,会返回一个状态,如果状态为true,那边以前购买的非消耗物品都会回调一次 ProcessPurchase 然后在这里个回调里面进行处理
void HandleRestored(bool result){
//返回一个bool值,如果成功,则会多次调用支付回调,然后根据支付回调中的参数得到商品id,最后做处理(ProcessPurchase)
// Debug.Log("RestorePurchases continuing: " + result + ". If no further messages, no purchases available to restore.");
blnRestore = false;
if (result){
Debug.Log("Restore success!");
}else{
Debug.Log("Restore Failed!");
}
}
//初始化回调
public void OnInitialized(IStoreController controller, IExtensionProvider extensions){
//初始化成功
Debug.Log("OnInitialized: PASS");
m_StoreController = controller;
m_StoreExtensionProvider = extensions;
}
public void OnInitializeFailed(InitializationFailureReason error){
//初始化失败
Debug.Log("OnInitializeFailed InitializationFailureReason:" + error);
if (error == InitializationFailureReason.AppNotKnown){
//
}else if (error == InitializationFailureReason.NoProductsAvailable){
//
}else if (error == InitializationFailureReason.PurchasingUnavailable){
//
}
}
//购买成功后的回调,包括restore的商品
public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs args)
{
// Debug.Log(string.Format("ProcessPurchase: PASS. Product: '{0}'", args.purchasedProduct.definition.id));
//根据不同的id,做对应的处理。。
int key = -1;
for(int i=0;i<kProducts.Length;i++){
if (string.Equals(args.purchasedProduct.definition.id, kProducts[i], System.StringComparison.Ordinal)){
key = i;
break;
}
}
if (key == -1){
Debug.Log(string.Format("ProcessPurchase: FAIL. Unrecognized product: '{0}'", args.purchasedProduct.definition.id));
}else{
if (key == 5){
if (!blnPressRestore){
Debug.Log("Ads have been removed!");
}
}else{
Debug.Log("购买了"+shopData[key*dataLen].ToString()+"个金币");
}
}
return PurchaseProcessingResult.Complete;
}
//购买失败回调,根据具体情况给出具体的提示
public void OnPurchaseFailed(Product product, PurchaseFailureReason failureReason)
{
//支付失败
Debug.Log(string.Format("OnPurchaseFailed: FAIL. Product: '{0}', PurchaseFailureReason: {1}", product.definition.storeSpecificId, failureReason));
if (failureReason == PurchaseFailureReason.UserCancelled){
//用户取消交易
}else if (failureReason == PurchaseFailureReason.ExistingPurchasePending){
//上一笔交易还未完成
}else if (failureReason == PurchaseFailureReason.PaymentDeclined){
//拒绝付款
}else if (failureReason == PurchaseFailureReason.ProductUnavailable){
//商品不可用
}else if (failureReason == PurchaseFailureReason.PurchasingUnavailable){
//支付不可用
}else{
//位置错误
}
}
}
具体使用,就是把上面这个脚本挂载到一个场景物体上,然后在Button里面设置按钮事件,我现在的写法是根据按钮的名字来得到需要购买的商品序号,通过序号拿到商品的Unity端ID,最后就可以发起购买啦。
![](https://oscdn.geek-share.com/Uploads/Images/Content/201704/efbe8d49a875e635ffb2c889884f57e1)
当然,这个方式也可以根据自己的需求去改,比如传递一个固定序号等等,不需要用按钮名称。
关于IAP的设置问题,网上其实已经写了很多了,我也不多赘述,那么我在这里只写一些细节,特别是对于新手来说的那种。
第一步肯定是去iTunes Connect里面添加项目,并且设置商品:
现在苹果其实已经不需要你上传ipa包了,只要设置好了相关信息既可以测试iap功能。
需要注意的是:“协议、税务和银行业务” 填写,这是最最重要的,如果没有填写,你永远不会收到结果,但是也不报错。
税务信息如果填写完毕,那么应该是这样的:
也就是后面可以download的
在 功能 里面设置商品,你需要上传截图,不然是不会显示的,设置好之后,它们的状态应该是“准备提交”状态,说明这一步已经好了。然后还需要在APP Store信息中的初始版本里面选择你刚刚设置的商品,结果如下:
另一个是沙盒测试账号,这个必须要使用你在iTunes Connect人员配置中设置的测试账号,否则是无法测试的。而且,在测试之前,一定要把设置里面的“iTunes Store与 App Store”退出。然后在游戏中点击购买的时候,会弹出一个登录账号的框,选择已经存在的账号,填写账号密码即可开始测试了。
这是需要注意的三个重要点
接下来就是unity这边了
在unity5.5版本以后,你需要在创建项目的时候打开unity服务,不然后面去弄,比较麻烦,半天连不上。
这个unity服务中,你找到In-App Purchasing,将他开启,然后import Unity的内购插件。
这里需要说明的一点是关于Restore的使用方式,其实Unity已经做好了,不需要关心,我要说的是他的返回的方式。
因为之前不知道,在看到Restore之后,返回只有一个bool类型的变量,所以不知道用户到底买了哪个非消费类型的商品。
其实,Unity在返回bool类型之前,其实还是会回调购买成功的函数,然后参数带了商品的ID,如果有多个非消费类型商品,它会多次回调,这样你就可以根据ID去做你的处理了。
对于其他的操作,unity的文档和案例已经写的很清楚了。
另外,使用代码和使用IAPButton是一样的效果,我贴出使用代码的方法:
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Purchasing; using System; namespace IAPCustom { //其实直接使用插件提供的IAPButton即可,不需要关心其他的 public class UnityPurchaser : MonoBehaviour, IStoreListener { private static IStoreController m_StoreController; private static IExtensionProvider m_StoreExtensionProvider; public static string kProductIDConsumable = "buycoins0"; public static string kProductIDNonConsumable = "removeads"; public static string kProductIDWeapon = "weapon"; public static string kProductNameAppleConsumable = "com.gjc.wf.buycoins0"; public static string kProductNameAppleNonConsumable = "com.gjc.wf.buyremoveads"; public static string kProductNameAppleWeapon = "com.gjc.wf.weapon"; public static string kProductIDSubscription = "subscription"; // Apple App Store-specific product identifier for the subscription product. // private static string kProductNameAppleSubscription = "com.unity3d.subscription.new"; // Google Play Store-specific product identifier subscription product. // private static string kProductNameGooglePlaySubscription = "com.unity3d.subscription.original"; void Start () { if (m_StoreController == null){ InitializePurchasing(); } } public void InitializePurchasing() { if (IsInitialized()) { return; } ConfigurationBuilder builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance()); // builder.AddProduct(kProductIDConsumable, ProductType.Consumable, new IDs(){ // {kProductNameAppleConsumable, AppleAppStore.Name} // }); // builder.AddProduct(kProductIDNonConsumable, ProductType.NonConsumable, new IDs(){ // {kProductNameAppleNonConsumable, AppleAppStore.Name} // }); // builder.AddProduct(kProductIDSubscription, ProductType.Subscription, new IDs(){ // { kProductNameAppleSubscription, AppleAppStore.Name }, // { kProductNameGooglePlaySubscription, GooglePlay.Name }, // }); //添加商品 IDs kProductBuyCoins0 = new IDs(); IDs kProductRemoveAds = new IDs(); kProductBuyCoins0.Add(kProductNameAppleConsumable, new string[]{AppleAppStore.Name}); kProductRemoveAds.Add(kProductNameAppleNonConsumable, new string[]{AppleAppStore.Name}); builder.AddProduct(kProductIDConsumable, ProductType.Consumable, kProductBuyCoins0); builder.AddProduct(kProductIDNonConsumable, ProductType.NonConsumable, kProductRemoveAds); builder.AddProduct(kProductIDWeapon, ProductType.NonConsumable, new IDs(){ {kProductNameAppleWeapon, AppleAppStore.Name} }); // ProductCatalog pc = ProductCatalog.LoadDefaultCatalog(); UnityPurchasing.Initialize(this, builder); } private bool IsInitialized() { return m_StoreController != null && m_StoreExtensionProvider != null; } public void BuyConsumable() { BuyProductID(kProductIDConsumable); } public void BuyNonConsumable() { BuyProductID(kProductIDNonConsumable); } public void BuyWeapon(){ BuyProductID(kProductIDWeapon); } public void BuySubscription() { BuyProductID(kProductIDSubscription); } void BuyProductID(string productId) { if (IsInitialized()) { Product product = m_StoreController.products.WithID(productId); if (product != null && product.availableToPurchase) { Debug.Log(string.Format("Purchasing product asychronously: '{0}'", product.definition.id)); m_StoreController.InitiatePurchase(product); } else { Debug.Log("BuyProductID: FAIL. Not purchasing product, either is not found or is not available for purchase"); } } else { Debug.Log("BuyProductID FAIL. Not initialized."); } } public void RestorePurchases() { if (!IsInitialized()) { Debug.Log("RestorePurchases FAIL. Not initialized."); return; } if (Application.platform == RuntimePlatform.IPhonePlayer || Application.platform == RuntimePlatform.OSXPlayer) { Debug.Log("RestorePurchases started ..."); var apple = m_StoreExtensionProvider.GetExtension<IAppleExtensions>(); apple.RestoreTransactions((result) => { //返回一个bool值,如果成功,则会多次调用支付回调,然后根据支付回调中的参数得到商品id,最后做处理(ProcessPurchase) Debug.Log("RestorePurchases continuing: " + result + ". If no further messages, no purchases available to restore."); }); } else { Debug.Log("RestorePurchases FAIL. Not supported on this platform. Current = " + Application.platform); } } // // --- IStoreListener // public void OnInitialized(IStoreController controller, IExtensionProvider extensions) { //初始化成功 Debug.Log("OnInitialized: PASS"); m_StoreController = controller; m_StoreExtensionProvider = extensions; } public void OnInitializeFailed(InitializationFailureReason error) { //初始化失败 Debug.Log("OnInitializeFailed InitializationFailureReason:" + error); } public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs args) { //根据不同的id,做对应的处理。。 if (String.Equals(args.purchasedProduct.definition.id, kProductIDConsumable, StringComparison.Ordinal)) { Debug.Log(string.Format("ProcessPurchase: PASS. Product: '{0}'", args.purchasedProduct.definition.id)); } else if (String.Equals(args.purchasedProduct.definition.id, kProductIDNonConsumable, StringComparison.Ordinal)) { Debug.Log(string.Format("ProcessPurchase: PASS. Product: '{0}'", args.purchasedProduct.definition.id)); } else if (String.Equals(args.purchasedProduct.definition.id, kProductIDWeapon, StringComparison.Ordinal)){ Debug.Log(string.Format("ProcessPurchase: PASS. Product: '{0}'", args.purchasedProduct.definition.id)); } else if (String.Equals(args.purchasedProduct.definition.id, kProductIDSubscription, StringComparison.Ordinal)) { Debug.Log(string.Format("ProcessPurchase: PASS. Product: '{0}'", args.purchasedProduct.definition.id)); } else { Debug.Log(string.Format("ProcessPurchase: FAIL. Unrecognized product: '{0}'", args.purchasedProduct.definition.id)); } return PurchaseProcessingResult.Complete; } public void OnPurchaseFailed(Product product, PurchaseFailureReason failureReason) { //支付失败 Debug.Log(string.Format("OnPurchaseFailed: FAIL. Product: '{0}', PurchaseFailureReason: {1}", product.definition.storeSpecificId, failureReason)); } } }
两个方法使用其中一个即可。。
下午补充:
在后面的测试中发现,如果商品有非消费类型时,使用UnityIAP插件中自带的IAPButton组件时,会出现很奇怪的情况:
在购买完非消费类型的商品后,然后删除APP,重新安装APP的时候,它会莫名其妙的自动调用Restore,这样等于我还没登录,它就使用原来的账号去获取检测是否购买。我查了半天也不知道哪里出的问题。但是当我不使用IAPButton,全部用自己代码去调用就不会出现这种情况。。
不过没有非消费类型的商品还是可以用那个Button的!
个人建议最好还是使用代码调用,代码已经在上面贴出来可~
2017年11月26号补充:
其实Unity给我们提供了CataLog,可以很方便的添加各个平台不同的id,而只使用同样的加载方式即可。
下面给出获取Catalog数据方式的代码:(方便以后其他平台移植,到时候直接在catalog里面设置就好啦)
public void InitializePurchasing() { if (IsInitialized()) { return; } StandardPurchasingModule module = StandardPurchasingModule.Instance(); module.useFakeStoreUIMode = FakeStoreUIMode.StandardUser; ConfigurationBuilder builder = ConfigurationBuilder.Instance(module); //通过编辑器中的Catalog添加,方便操作 ProductCatalog catalog = ProductCatalog.LoadDefaultCatalog(); foreach (var product in catalog.allProducts) { if (product.allStoreIDs.Count > 0) { var ids = new IDs(); foreach (var storeID in product.allStoreIDs) { ids.Add(storeID.id, storeID.store); } builder.AddProduct(product.id, product.type, ids); } else { builder.AddProduct(product.id, product.type); } } UnityPurchasing.Initialize(this, builder); }
这样就可以不用管这边加载代码,其他地方都是通用的。哈哈哈
2017.12.19日补充,发现好多新手小伙伴还是一脸懵逼,我今天把最新的方式发在这里,供大家参考:
本次使用的还是和catalog结合使用,另外关于商品id还不清楚的我在这里再说一下。我这里有两个商品ID,第一个是Unity这边使用的ID,用于Unity端调用,也是用来映射不同平台商品id的自定义ID,他填的位置如下:
这个id可以自定义,也可以用实际商店的id,只要是一个字符串就可以了。
然后第二个ID,就是个个平台设置的商品ID了,他们可能会有各种格式,所以unity这边做了处理,第一个自定义的id就是多了一层映射,方便管理和使用,了解了这一层关系,就知道我为什么要推荐用catalog了。因为在移植到其他平台时,我只要维护catalog的的每个平台的id就可以了,代码那边都需要关心。
明白这两个ID的作用,那么就可以直接看我的下面的代码了,我把ShopList.cs都贴出来,相信大家能明白其中的原由了。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Purchasing;
/// <summary>
/// 这是通用方式,通过读取catalog里面的信息,获取所有商品信息
/// </summary>
public class ShopList : MonoBehaviour, IStoreListener{
private static IStoreController m_StoreController;
private static IExtensionProvider m_StoreExtensionProvider;
bool blnRestore = false;//用来表示
bool blnPressRestore = false;//用来区分是否按了 restore 按钮
int dataLen = 3;
//每个商品的内容(金币),价格(除以100),折扣
int[] shopData = new int[]{
150,99,0, //0.99 美元购买 150 个金币
450,299,0, //2.99 美元购买 450 个金币
850,499,12, //4.99 美元购买 850 个金币
1850,999,22, //9.99 美元购买 1850 个金币
3950,1999,30, //19.99 美元购买 3950 个金币
0,199,0 //1.99 美元购买去广告功能,可以Restore的项目
};
//catalog 里面设置的id,和这边一一对应,这个id是unity端的一个映射,在catalog里面可以对应不同平台的真实的 商品id
private string[] kProducts = new string[] {
"buycoins0",
"buycoins1",
"buycoins2",
"buycoins3",
"buycoins4",
"removeads"
};
void Start () {
InitializePurchasing();
}
private bool IsInitialized()
{
return m_StoreController != null && m_StoreExtensionProvider != null;
}
//初始化内购项目,主要是从catalog中获取商品信息,设置给 UnityPurchasing
void InitializePurchasing()
{
if (IsInitialized())
{
Debug.Log("初始化失败");
return;
}
StandardPurchasingModule module = StandardPurchasingModule.Instance();
module.useFakeStoreUIMode = FakeStoreUIMode.StandardUser;
ConfigurationBuilder builder = ConfigurationBuilder.Instance(module);
//通过编辑器中的Catalog添加,方便操作
ProductCatalog catalog = ProductCatalog.LoadDefaultCatalog();
// Debug.Log(catalog.allProducts.Count);
foreach (var product in catalog.allProducts) {
if (product.allStoreIDs.Count > 0) {
// Debug.Log("product:" + product.id);
var ids = new IDs();
foreach (var storeID in product.allStoreIDs) {
ids.Add(storeID.id, storeID.store);
// Debug.Log("stordId:" + storeID.id + ", " + storeID.store);
}
builder.AddProduct(product.id, product.type, ids);
} else {
builder.AddProduct(product.id, product.type);
}
}
UnityPurchasing.Initialize(this, builder);
}
//供外部调用,当按 Restore 按钮时触发
public void OnRestore(){
if (Application.platform == RuntimePlatform.OSXEditor || Application.platform == RuntimePlatform.WindowsEditor){
Debug.Log("Restore success!");
}else{
blnRestore = true;
RestorePurchases();
blnPressRestore = true;
}
}
//供外部调用,按下哪个按钮,就可以购买哪一档的金币,我这里是通过按钮的名称得到购买的 idx 的,可以根据自己需要更改,比如:OnBuyCoins(int idx)
//idx 是上面 shopData 对应的每行数据
public void OnBuyCoins(Button btn){
int idx = System.Convert.ToInt32(btn.name);
BuyCoinsWithIdx(idx);
}
//实际购买调用的函数,根据idx拿到unity端的商品id
void BuyCoinsWithIdx(int idx){
if (idx == 5){//购买去广告
if (Application.platform == RuntimePlatform.OSXEditor || Application.platform == RuntimePlatform.WindowsEditor){
Debug.Log("购买去广告!");
}else{
blnPressRestore = false;
BuyProductID(kProducts[idx]);
}
}else{
if (Application.platform == RuntimePlatform.OSXEditor || Application.platform == RuntimePlatform.WindowsEditor){
Debug.Log("editor buy coins");
}else{
BuyProductID(kProducts[idx]);
}
}
}
//这里是通过商品id购买物品
void BuyProductID(string productId)
{
if (IsInitialized())
{
Debug.Log("Buy ProductID: " + productId);
Product product = m_StoreController.products.WithID(productId);
if (product != null && product.availableToPurchase) {
Debug.Log(string.Format("Purchasing product asychronously: '{0}'", product.definition.id));
m_StoreController.InitiatePurchase(product);
} else {
Debug.Log("BuyProductID: FAIL. Not purchasing product, either is not found or is not available for purchase");
}
}else {
Debug.Log("没出初始化");
}
}
//真是的发起Restore请求
public void RestorePurchases()
{
if (!IsInitialized()) {
Debug.Log("没出初始化");
return;
}
if (Application.platform == RuntimePlatform.IPhonePlayer || Application.platform == RuntimePlatform.OSXPlayer) {
// Debug.Log("RestorePurchases started ...");
var apple = m_StoreExtensionProvider.GetExtension<IAppleExtensions>();
apple.RestoreTransactions(HandleRestored);
}else {
Debug.Log("RestorePurchases FAIL. Not supported on this platform. Current = " + Application.platform);
}
}
//如果restore之后,会返回一个状态,如果状态为true,那边以前购买的非消耗物品都会回调一次 ProcessPurchase 然后在这里个回调里面进行处理
void HandleRestored(bool result){
//返回一个bool值,如果成功,则会多次调用支付回调,然后根据支付回调中的参数得到商品id,最后做处理(ProcessPurchase)
// Debug.Log("RestorePurchases continuing: " + result + ". If no further messages, no purchases available to restore.");
blnRestore = false;
if (result){
Debug.Log("Restore success!");
}else{
Debug.Log("Restore Failed!");
}
}
//初始化回调
public void OnInitialized(IStoreController controller, IExtensionProvider extensions){
//初始化成功
Debug.Log("OnInitialized: PASS");
m_StoreController = controller;
m_StoreExtensionProvider = extensions;
}
public void OnInitializeFailed(InitializationFailureReason error){
//初始化失败
Debug.Log("OnInitializeFailed InitializationFailureReason:" + error);
if (error == InitializationFailureReason.AppNotKnown){
//
}else if (error == InitializationFailureReason.NoProductsAvailable){
//
}else if (error == InitializationFailureReason.PurchasingUnavailable){
//
}
}
//购买成功后的回调,包括restore的商品
public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs args)
{
// Debug.Log(string.Format("ProcessPurchase: PASS. Product: '{0}'", args.purchasedProduct.definition.id));
//根据不同的id,做对应的处理。。
int key = -1;
for(int i=0;i<kProducts.Length;i++){
if (string.Equals(args.purchasedProduct.definition.id, kProducts[i], System.StringComparison.Ordinal)){
key = i;
break;
}
}
if (key == -1){
Debug.Log(string.Format("ProcessPurchase: FAIL. Unrecognized product: '{0}'", args.purchasedProduct.definition.id));
}else{
if (key == 5){
if (!blnPressRestore){
Debug.Log("Ads have been removed!");
}
}else{
Debug.Log("购买了"+shopData[key*dataLen].ToString()+"个金币");
}
}
return PurchaseProcessingResult.Complete;
}
//购买失败回调,根据具体情况给出具体的提示
public void OnPurchaseFailed(Product product, PurchaseFailureReason failureReason)
{
//支付失败
Debug.Log(string.Format("OnPurchaseFailed: FAIL. Product: '{0}', PurchaseFailureReason: {1}", product.definition.storeSpecificId, failureReason));
if (failureReason == PurchaseFailureReason.UserCancelled){
//用户取消交易
}else if (failureReason == PurchaseFailureReason.ExistingPurchasePending){
//上一笔交易还未完成
}else if (failureReason == PurchaseFailureReason.PaymentDeclined){
//拒绝付款
}else if (failureReason == PurchaseFailureReason.ProductUnavailable){
//商品不可用
}else if (failureReason == PurchaseFailureReason.PurchasingUnavailable){
//支付不可用
}else{
//位置错误
}
}
}
具体使用,就是把上面这个脚本挂载到一个场景物体上,然后在Button里面设置按钮事件,我现在的写法是根据按钮的名字来得到需要购买的商品序号,通过序号拿到商品的Unity端ID,最后就可以发起购买啦。
当然,这个方式也可以根据自己的需求去改,比如传递一个固定序号等等,不需要用按钮名称。
相关文章推荐
- iOS In-App Purchase 内购之使用产品元数据
- iOS内购(IAP,In App Purchases-在APP内部支付),设置及使用
- iOS In-App Purchase(IAP)内购服务端二次验证注意事项
- unity3d 嵌入iOS的 In App Purchase 应用程序内购买
- unity3d 嵌入iOS的 In App Purchase 应用程序内购买
- IOS开发中,IAP(In-App Purchase, 程序内置收费)相关总结及备忘
- unity3d 嵌入iOS的 In App Purchase 应用程序内购买
- 【引用】unity3d 嵌入iOS的 In App Purchase 应用程序内购买
- AIR Native Extension实现iOS应用内付费(In-App Purchase)全教程(四)——ANE面向IAP的测试和开发
- unity3d 嵌入iOS的 In App Purchase 应用程序内购买
- ios内购(iap)关于问题“您已经购买了此程序内购买(In App Purchase)项目,但尚未下载"的解决方案
- IOS开发中,IAP(In-App Purchase, 程序内置收费)相关总结, 及备忘
- AIR Native Extension实现iOS应用内付费(In-App Purchase)全教程(四)——ANE面向IAP的测试和开发
- C# [IPA]IOS In App Purchase(内购)验证(asp.net 版本)
- iOS内购(IAP,In App Purchases-在APP内部支付),设置及使用
- ios 应用内支付(In-App Purchase,沙盒测试,后台验证)iap
- iOS应用内置付费 In-App Purchase 详细介绍(IAP详解)
- Guideline 3.1.1 - In-App Purchase 被拒绝!IOS App上传App Store 由于使用第三方支付而被拒绝的解决方案
- iOS应用内支付(IAP,In-App Purchase)的那些坑