Unity进阶学习之实现苹果IOS内购功能(沙盒账号测试)

目录

一、前言

二、准备流程

2.1 苹果后台部署

2.1.1 苹果开发者账号注册

2.1.2 证书注册

2.1.3 协议检查

2.1.4 标识符创建

2.1.5 App创建 

2.1.6 创建内购商品

2.1.7 添加内购商品 

2.1.8 配置沙盒账号测试

2.1.9 添加测试机 

2.1.9.1 Register a Device

2.1.9.2 Register Multiple Devices

2.2 Unity部署

2.2.1  Unity IAP 安装

2.2.2 Unity 设置

三、脚本实现

四、构建

五、测试


一、前言

在进行游戏开发后,想要游戏产生收益,很好的一些方式就是调用广告接口或者是实现内购产品。分享一些在实现内购的基本流程、整理的链接以及踩过的坑。

二、准备流程

  • 苹果开发者账号(个人 公司账号)
  • 需上架的游戏
  • Mac机 (或者虚拟机)

2.1 苹果后台部署

2.1.1 苹果开发者账号注册

苹果开发者账号注册网上教程很多,列举两个如下:

苹果公司开发者账号注册

苹果个人开发者账号注册

2.1.2 证书注册

要实现苹果内购功能,需要上架苹果商店(测试期间不需要),需要使用到苹果自研的Xcode进行构建。电脑需要有开发资格才能够使用Xcode进行开发,证书则是开发资格。在注册好开发者账号完成缴费后,可以进行证书注册,教程如下:

(以下教程需要在Mac机上配置同时需要一个打包为Ios的unity项目,因为第一次配置,搞个测试的unity项目用Xcode打开进行配置就行了)

苹果开发者证书注册教程

在完成证书注册后,以公司苹果开发者账号,苹果开发者账号后台如图:

2.1.3 协议检查

如图点击进行查看

实现内购功能,付费协议的状态必须为有效,不然会导致无法读取到你设置的内购商品。

这个是踩坑点之一,即使免费协议有效,这仅仅针对免费的App,付费协议的状态只能是有效,任何其他状态或者为空都会导致无法读取到商品

苹果付费协议签署

2.1.4 标识符创建

Identifier 对应App中的套装 ID对应Unity中的Bundle Identifier

在设置证书的页面,如图

Identifier ​​​​​​

进入后选择App IDs -> App 会出现如图:

  • Description :描述,任意填写满足格式即可
  • Bundle ID:iOS 应用的唯一标识符 命名最好正规 例如 com.companyName.ProjectName
  • Capabilities:你的应用支持的功能 满足内购,需勾选如下:
以前名为 In App Purchase

配置好后,点击Continue即可。

2.1.5 App创建 

点击App的 + 选择 新建 App

  •  选择你要发布的 平台
  • 填写 名称
  • 选取 主要语言
  • 选取 套装 ID (上文创建的标识符)
  • 填写 SKU

2.1.6 创建内购商品

如图所示。点击 + 进行添加

 

  • 类型:消耗性项目(可购买多次,例如点券、钻石、金币等游戏道具)和非消耗性项目(仅可购买一次,例如一次购买终身解锁的关卡、游戏中的永久卡等) 
  • 参考名称:任意填写
  • 产品 ID: 慎重命名,在你的开发者账号中不可以使用重复的。(即使你用此开发者账号创建新的App,添加新的内购商品则无法使用相同的ID)在Unity中调用内购商品会使用到此产品ID

设置好数据后,点击创建,需要内购商品内所有信息进行填写,设置好金额,区域等即可,会出现如下图: 

注意内购产品的状态,在填写好所有的信息后,出现准备提交,此时你的内购商品是成功创建的,详情见

App内购买项目状态

(状态为元数据丢失,则此商品是无效的)

按照以上步骤,可以创建多个内购商品 

2.1.7 添加内购商品 

在创建好内购商品后,此时部署并不算完成,需要回到你的App,进行App内购商品添加。如图(以iOS App为例):

点击  编辑 添加你创建的商品即可

2.1.8 配置沙盒账号测试

 在进行自身应用测试内购产品是否会调出支付界面、支付是否有效、支付后商品是否到账等时,苹果提供沙盒环境进行调试你的应用,可以保证购买内购功能的正常实现。此时需要去配置沙盒账号。如下图所示:

点击 + 即可进去配置,因为沙盒账号并不是真实的信息,所以邮箱是只要格式正确,是否真实并不影响。

Unity应用内内购流程:点击商品 -> 出现支付界面 -> 输入沙盒账号 -> 成功支付 -> 自定义商品到账

2.1.9 添加测试机 

由于需要使用Mac机的Xcode进行构建,需要在你的开发者账号中添加你测试的设备。如下图:

 

  •  Device Name: 设置设备的名字
  • Device ID (UDID):输入设备的UDID

Register a DeviceRegister Multiple Devices分别是注册单台设备和多台设备,添加流程如下:

2.1.9.1 Register a Device

UDID的获取需要手机连接Mac机

方法1:电脑连接手机(手机弹窗选择信任),打开XCode,菜单栏Window-Devices and Simulates-就可以看到对应的udid

方法2.:打开访达Fidler,位置栏目点击手机,手机名字下面的信息,即可看到udid,右键即可复制

2.1.9.2 Register Multiple Devices

 需要编写配置文件进行导入多台设备,如属性列表文件(.deviceids)和纯文本文件(.txt),纯文本文件格式如下:

Device ID    Device Name    Device Platform
A123456789012345678901234567890123456789    NAME1    ios
B123456789012345678901234567890123456789    NAME2    ios

2.2 Unity部署

2.2.1  Unity IAP 安装

Unity提供自身的应用内购买,详见:

Unity实现应用内购买

按照下图所示,点击 Install(已安装,显示Remove) 

选择,如下图:

按照提示,进行设置(这里不详细说明)

成功后如下图所示,状态为 ON ,则意味着可以使用

2.2.2 Unity 设置

如下图,对Unity项目进行设置

  • Bundle Identifier:  iOS 应用的唯一标识符 这个是标识符,是苹果识别到你的应用的标识,一定要设置为你苹果后台部署的值是一样的

三、脚本实现

创建脚本IAPManager,需要挂载在场景中,如果存在多场景均可实现购买,需保证IAPManage在使用到的场景不会被销毁

using System;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Purchasing;
using UnityEngine.Purchasing.Extension;
using Unity.Services.Core;
using Unity.Services.Core.Environments;

//IDetailedStoreListener为 IAP的接口
public class IAPManager : MonoBehaviour, IDetailedStoreListener
{
    //这里设置商品的产品ID,即前面苹果后台部署提到的
    public string[] Products = {com.Coin_100,com.Coin_500,com.Coin_800};

    //这里是个人设置的游戏货币的值,实际项目自己写
    public int[] Coin = {100,500,800};

    private IStoreController m_StoreController;

    private async void Awake()
    {
        await InitializeUnityServices();
        InitProducts();
    }
    //初始化Unity的IAP服务
    private async Task InitializeUnityServices()
    {
        try
        {
            var options = new InitializationOptions().SetEnvironmentName("production");
            await UnityServices.InitializeAsync(options);
            Debug.Log("Unity 服务初始化成功。");
        }
        catch (Exception e)
        {
            Debug.LogError($"初始化 Unity 服务失败: {e.Message}");
        }
    }

    private void InitProducts()
    {
        var builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance());
        foreach (var product in Products)
        {
            builder.AddProduct(product, ProductType.Consumable);
            Debug.Log($"添加{product}商品");
        }
        UnityPurchasing.Initialize(this, builder);
    }

    public void OnInitialized(IStoreController controller, IExtensionProvider extensions)
    {
        Debug.Log("IAP 初始化成功");
        m_StoreController = controller;

        if (m_StoreController == null)
        {
            Debug.LogError("IStoreController 为空,初始化失败。");
        }
    }

    public void OnInitializeFailed(InitializationFailureReason error, string message)
    {
        Debug.LogError($"购买初始化失败。原因: {error}. 详细信息: {message}");
    }
    public void OnInitializeFailed(InitializationFailureReason error)
    {
        Debug.LogError($"购买初始化失败。原因: {error}.");
    }
    //购买成功后实现该函数
    public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs args)
    {
        var product = args.purchasedProduct;
        //这里将编写购买产品后实现的逻辑
        //个人逻辑如下:
        //根据购买的产品 ID 更新玩家的金币值
        int productIndex = Array.IndexOf(Products, product.definition.id);
        //将Products的产品Id与设置的Coin进行一一对应
        //例如:购买了Products[0],即产品ID为com.Coin_100的产品,则增加Coin[0],即100金币
        if (productIndex >= 0 && productIndex < Coin.Length)
        {
            //增加Coin[productIndex]的金币
            PlayerManager.Instance.UpdateScore(Coin[productIndex]);
            Debug.Log("花费价格 price:" + args.purchasedProduct.metadata.localizedPriceString);
        }
        else
        {
            Debug.LogWarning($"未找到对应的金币数量,产品 ID: {product.definition.id}");
        }
        Debug.Log($"购买完成 - 产品: {product.definition.id},金币增加: {Coin[productIndex]}");

        return PurchaseProcessingResult.Complete; // 处理完成,通知 IAP 交易可以关闭
    }

    public void OnPurchaseFailed(Product product, PurchaseFailureReason failureReason)
    {
        Debug.Log($"购买失败 - 产品: '{product.definition.id}', 失败原因: {failureReason}");
    }

    public void OnPurchaseFailed(Product product, PurchaseFailureDescription failureDescription)
    {
        Debug.Log($"购买失败 - 产品: '{product.definition.id}', 失败原因: {failureDescription.reason}, 详细信息: {failureDescription.message}");
    }

    /// <summary>
    /// 购买商品
    /// </summary>
    /// <param name="id">对应数组的商品字段</param>
    private void PurchaseProduct(int id)
    {
        m_StoreController.InitiatePurchase(m_StoreController.products.WithID(Products[id]));
    }
    //购买商品如下:
    //按钮直接进行绑定,或者调用即可
    public void BuyCoin01()
    {
        //购买产品ID为com.Coin_100的商品,获得100金币
        PurchaseProduct(0);
    }
    public void BuyCoin02()
    {
        //购买产品ID为com.Coin_500的商品,获得500金币
        PurchaseProduct(1);
    }
    public void BuyCoin03()
    {
        //购买产品ID为com.Coin_800的商品,获得800金币
        PurchaseProduct(2);
    }
}

将场景中的商品进行设置好后,直接进行打包IOS即可。

四、构建

现在是在Mac机中进行操作,打开Unity打包的文件中,找到.xcodeproj,直接打开(前提是已经安装好Xcode,直接去App Store下载即可),首次打开需要用邮箱去登录账户,不然无法构建成功的

按照下图进行登录:

去你的Unity项目中配置Bundle ID 标识符,前面提到的,连接上测试机,如下图

点击 三角形 会自动进行构建,在构建成功后,会自动安装到你的手机上,即可进行测试。 

五、测试

打开在测试机上构建成功的应用,点击商品会弹出弹窗,显示商品信息和价格,点击购买会让你登录沙盒账号(我在测试时,没有按照其他教程登出自己的账号后登录沙盒账号,这是不需要的,而是应用内会再让你登录一次,因为前面在创建标识符的时候,选择了需要登录的功能)

登录好后即可购买成功。在沙盒环境下商品(你购买成功后实现的逻辑)到账会比较慢,耐心等待几秒即可。

这个图是别人的,现在新的内购是会弹出商品信息的,包括图标,描述等