Unity端
这里需要使用Unity内置的IAP插件,下载IAP插件的方式:Window–>Services–>In-App Purchasing(这个就是unity内置的内购插件),点击把OFF->ON
点击之后会有个询问是否是对13岁以下儿童设计的游戏,按照情况勾选,之后点击continue进入安装界面,点击input安装
下面的Options是谷歌商店支付相关的,可以空着不用管它。
下载好插件之后会包含IAP内购的Demo,可以点击查看效果,此Demo在Unity上运行点击购买会出现一个fakeStore界面,在真机测试中会弹出正常IP的支付界面。
此时当你下载好IAP插件点击Window后会发现多出来一个UnityIAP选项,这时插件就已经安装好了。
当安装好插件后,有两种方式实现IAP内购:
一、使用IAPButton按钮
这种方法可以在层级界面点击右键可以看到UnityIAP->IAPButton,在要生成支付按钮的位置生成它,它就是一个挂载IAPButton脚本的Button按钮。
Product ID是设置在Unity端你要购买物品的ID,它是与你在APPStore上设置的购买项目在Unity端的映射,这个ID可以自己设定,点击下面IAP Catalog可以设定这个映射关系。
ID就是上文中所说的Product ID,Type是在APPStore生成内购物品时的哪几种类型,在下面是物品描述,在下面的Store ID Overrides就是在各个平台的商店里物品的正式ID了,第一个就是苹果APPStore的。再然后最下面Apple Configuration选择的是购买物品的费用,和你APPStore选一样的就行了。
IAPButton图中ButtonType有两种,当前的是交易类型按钮,还有一种是Restore按钮,用来查询和重置非消耗项目物品时使用。
在下面有两个On Purchase Complete和On Purchase Failed,这两个分别可以添加回调成功和失败后要执行的事件,使用方法就跟Button按钮最下面的On Click一模一样。
然后运行点击这个按钮就可以触发内购了。(这种方式的OK了,就是这么智能。。。。但是这种方法要设定好这个IAPButton,我们的项目先进行的安卓端的测试,UI界面已经都设置好了,要使用的话我需要把之前的的Button都改一下,好像挺麻烦的,所以用下面代码控制更灵活一点)
二、使用代码控制
下面就来用代码实现IAP内购
下面我先贴一下代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Purchasing;
/// <summary>
/// 这是通用方式,通过读取catalog里面的信息,获取所有商品信息
/// </summary>
public class ApplePayModule: MonoBehaviour, IStoreListener
{
private static ApplePayModule m_instance;
public static ApplePayModule Instance { get { return m_instance; } }
private static IStoreController m_StoreController;
private static IExtensionProvider m_StoreExtensionProvider;
bool blnRestore = false;//用来表示
bool blnPressRestore = false;//用来区分是否按了 restore 按钮
//catalog 里面设置的id,和这边一一对应,这个id是unity端的一个映射,在catalog里面可以对应不同平台的真实的 商品id
private string[] kProducts = new string[] {
"buycoins0",
"buycoins1",
};
void Start()
{
if (m_instance == null)
{
m_instance = this;
}
#if UNITY_EDITOR
#elif UNITY_ANDROID
#elif UNITY_IPHONE
InitializePurchasing();
#endif
}
private bool IsInitialized()
{
return m_StoreController != null && m_StoreExtensionProvider != null;
}
/// <summary>
/// 初始化内购项目,主要是从catalog中获取商品信息,设置给 UnityPurchasing
/// </summary>
void InitializePurchasing()
{
if (IsInitialized())
{
Debug.Log("购买项目=======================>>>>>>>>>>>>>>>>>>>>>>>初始化失败");
return;
}
Debug.Log("开始初始化物品");
StandardPurchasingModule module = StandardPurchasingModule.Instance();
module.useFakeStoreUIMode = FakeStoreUIMode.StandardUser;
ConfigurationBuilder builder = ConfigurationBuilder.Instance(module);
//通过编辑器中的Catalog添加,方便操作
ProductCatalog catalog = ProductCatalog.LoadDefaultCatalog();
//下面是IAP最新封装好的读取IAP Catalog里映射表的方法和下面foreach中代码是等效的
//IAPConfigurationHelper.PopulateConfigurationBuilder(ref builder, catalog);
// 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;
}
}
/// <summary>
/// 供外部调起苹果支付
/// </summary>
/// <param name="str"></param>
public void ApplePay(string str)
{
BuyProductID(str);
}
//这里是通过商品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)
{
Debug.Log("NoProducts========================================Available");
}
else if (error == InitializationFailureReason.PurchasingUnavailable)
{
//
}
}
//购买成功后的回调,包括restore的商品
public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs args)
{
string str = args.purchasedProduct.receipt;
//下面是我们公司客户端购买成功后通知服务器的方法,这里填你自己的逻辑
GameManager.GetManager().GetGameModuleManager().GetModule<PayModule>().NetIOSPayTradeQuery(str);
Debug.Log(str);
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)
{
TipManager.Instance.ShowSimpleTextTip("用户取消交易");//用户取消交易
}
else if (failureReason == PurchaseFailureReason.ExistingPurchasePending)
{
TipManager.Instance.ShowSimpleTextTip("上一笔交易还未完成");//上一笔交易还未完成
}
else if (failureReason == PurchaseFailureReason.PaymentDeclined)
{
TipManager.Instance.ShowSimpleTextTip("拒绝付款");//拒绝付款
}
else if (failureReason == PurchaseFailureReason.ProductUnavailable)
{
TipManager.Instance.ShowSimpleTextTip("商品不可用");//商品不可用
}
else if (failureReason == PurchaseFailureReason.PurchasingUnavailable)
{
TipManager.Instance.ShowSimpleTextTip("支付不可用");//支付不可用
}
else
{
TipManager.Instance.ShowSimpleTextTip("未知错误");//未知错误
}
}
}
在上面的代码里也需要设定IAP Catalog中设定Unity和具体平台的映射关系:Window->UnityIAP->IAPCatalog 点开和那个按钮设定是一样的。(当然也可以手动添加,这里就不具体给出代码了)
在外部调用ApplePay(string str)方法时传入你设定在IAPCatalog里的Product ID就可以调起支付购买所对应APPStore上设置的物品了。
方法:ProcessPurchase(PurchaseEventArgs args)---->>>>>是购买成功后APPStore给的回调,args是它给返回的用以二次验证的信息,网络游戏的话在这里把它里面的receipt传到服务器端,然后服务器再到苹果给的验证平台上进行二次验证。
iOS平台进行打包
这里我们的项目使用了ShareSDK和UpSDK,所以打包之前我将这两个都删除掉然后重新导入package包,ShareSDK这里有两个控制使用平台的脚本ShareSDK.cs和ShareSDKDevInfo.cs暂时取出,两个package包导入成功后,把这两个替换回我们原来的。
然后就悬着iOS平台打包。这里打包完成后会少一个ShareSDK的依赖库,打完包需要在Xcode上进行是把它这个iOS的依赖库文件夹拖到Xcode上。
一、在AppStoreConnect上添加APP项目
先申请添加一个APP项目,这个项目的Bundle在下面生成证书的时候会用到。
在这个APP项目里添加需要购买的项目,信息填完之后最下面的屏幕截图一定要有,测试用的时候可以找一张大小适当的图片代替,(正式发布可能需要正式的截图以及描述)。这里添加的ID就是UnityIAPCatalog里添加的那个,一定要一致。
然后最下面有个添加测试员账号,用以添加沙箱测试用的账号,账号的邮箱可以随便写一个你能记得住的不存在的邮箱就可以。
二、配置Xcode环境
这里有个相关的操作博客:https://www.jianshu.com/p/b1b77d804254
三、打包测试
打开Xcode选择Unity iOS平台打包好的文件夹,会自动加载项目,然后需要把项目的bundleID写和APPStore,以及上一步生成的证书绑定的一致,不然程序无法链接到APPStore上的那个APP项目,从而无法初始化要购买的项目,还有苹果系统7.0以前使用的内购方式不一致,需要选择一下苹果运行的系统,现在最低也是8.0,如何你不改的会他会自动识别unity端打包的时候设定的最小支持的版本导则无法支付成功。
将在第二步中添加的测试设备连接到电脑上。
然后就是添加一些依赖库(这些依赖库并不是苹果内购IAP所需要的依赖库,而是使用其他插件时所需要的依赖库,如果其他插件不需要的话,直接打包运行)。
Xcode也可以看到过程日志,如果有错误就分局错误进行分析解决把。
来源:CSDN
作者:文HAO骏
链接:https://blog.csdn.net/FrootLoops/article/details/90771565