基于Unity3D的iOS内购开发

 

iOS内购的开发步骤

  1. 苹果官网配置
  2. 工程项目配置
  3. 代码开发集成

 

苹果官网配置

 

工程项目配置

1. 使用xcode打开项目工程,并将In-App Purchase选上,

2. 添加StoreKit.framework

 

也可以利用PBXProject进行代码自动化添加,

PBXProject proj = new PBXProject();
string targetName = PBXProject.GetUnityTargetName();
string target = proj.TargetGuidByName(targetName);

......

proj.AddCapability(target, PBXCapabilityType.InAppPurchase);
proj.AddFrameworkToProject(target, "StoreKit.framework", false);

 

代码开发集成

1. 内购支付流程

  1. 客户端向服务器请求商品信息(注意此时的商品信息要跟苹果官网配置的商品信息一致)。
  2. 玩家点击购买按钮,客户端向苹果服务器发送消息请求购买产品,苹果服务器验证产品成功后,从用户的Apple账户余额中扣费。
  3. 苹果服务器向客户端返回一个JSON格式的消息,里面有产品名称、Receipt(string类型)等信息。
  4. 客户端取得Receipt,发送给服务器验证。
  5. 服务器对Receipt进行一次base64编码并发往苹果服务器进行二次验证。
  6. 苹果服务器返回JSON格式的验证结果给服务器。
  7. 服务器向客户端发放相应的道具与推送数据更新通知。

2. 关于服务器向苹果服务器二次验证

说明:客户端跟苹果服务器的交互集成在插件里,服务器跟苹果服务器的交互需要自己开发

目的:防止玩家外挂恶意刷单

官网说明:Validating Receipts With the App Store

正式验证接口:https://buy.itunes.apple.com/verifyReceipt

沙盒测试接口:https://sandbox.itunes.apple.com/verifyReceipt

请求方式:POST

参数:receipt-data,如

{
    'receipt-data':'Bdkd7D97DBVLhjldj6LLFD.....'
}

苹果服务器返回验证结果:JSON格式,如

{
  "status": 0,
  "environment": "Sandbox",
  "receipt": {
    "receipt_type": "ProductionSandbox",
    "adam_id": 0,
    "app_item_id": 0,
    
    ......
 }
}

苹果服务器返回验证状态:status

O:验证成功。
21000: App Store无法读取你提供的JSON数据。
21002: receipt-data域的数据有问题。
21003: receipt无法通过验证。
21004: 你提供的共享密钥和账户的共享密钥不一致。
21005: receipt服务器当前不可用。
21006: receipt合法,但是订阅已过期。服务器接收到这个状态码时,receipt数据仍然会解码并一起发送。
21007: receipt是沙盒环境的收据,但却发送至生产环境验证。
21008: receipt是生产环境的收据,但却发送至沙盒环境验证。

 

3. 代码开发集成

引擎:Unity3D

插件:OpenIAB(https://github.com/onepf/OpenIAB-Unity-Plugin

1. 首先在github上拉取插件工程,并使用Unity打开OpenIAB-Unity-Plugin\unity_plugin\unity_src,如下

2. 由于我们只做iOS开发,所以下面标颜色的文件可以删除

3. 将Assets/Plugins/Prefabs/OpenIABEventManager.prefab拖到Scene下

4. 新建C#文件,命名为OpenIAPMananger.cs

添加代码如下,

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using OnePF;

public enum PurchaseResult
{
    Product_Info_Error = 1,
    Product_Null_Error,
    Purchase_Fail,
    Purchase_Success
}

public class OpenIAPMananger
{
    private static List<SkuDetails> skuList = null;

    void OnEnable()
    {
#if UNITY_IOS
        //添加回调响应函数
        OpenIABEventManager.billingSupportedEvent += OnBillingSupported;
        OpenIABEventManager.billingNotSupportedEvent += OnBillingNotSupported;
        OpenIABEventManager.queryInventorySucceededEvent += OnQueryInventorySucceeded;
        OpenIABEventManager.queryInventoryFailedEvent += OnQueryInventoryFailed;
        OpenIABEventManager.purchaseSucceededEvent += OnPurchaseSucceded;
        OpenIABEventManager.purchaseFailedEvent += OnPurchaseFailed;
        OpenIABEventManager.consumePurchaseSucceededEvent += OnConsumePurchaseSucceeded;
        OpenIABEventManager.consumePurchaseFailedEvent += OnConsumePurchaseFailed;
        OpenIABEventManager.transactionRestoredEvent += OnTransactionRestored;
        OpenIABEventManager.restoreSucceededEvent += OnRestoreSucceeded;
        OpenIABEventManager.restoreFailedEvent += OnRestoreFailed;
#endif
    }

    void OnDisable()
    {
#if UNITY_IOS
        //移除回调响应函数
        OpenIABEventManager.billingSupportedEvent -= OnBillingSupported;
        OpenIABEventManager.billingNotSupportedEvent -= OnBillingNotSupported;
        OpenIABEventManager.queryInventorySucceededEvent -= OnQueryInventorySucceeded;
        OpenIABEventManager.queryInventoryFailedEvent -= OnQueryInventoryFailed;
        OpenIABEventManager.purchaseSucceededEvent -= OnPurchaseSucceded;
        OpenIABEventManager.purchaseFailedEvent -= OnPurchaseFailed;
        OpenIABEventManager.consumePurchaseSucceededEvent -= OnConsumePurchaseSucceeded;
        OpenIABEventManager.consumePurchaseFailedEvent -= OnConsumePurchaseFailed;
        OpenIABEventManager.transactionRestoredEvent -= OnTransactionRestored;
        OpenIABEventManager.restoreSucceededEvent -= OnRestoreSucceeded;
        OpenIABEventManager.restoreFailedEvent -= OnRestoreFailed;
#endif
    }

    void Start()
    {
#if UNITY_IOS
        -- 映射sku, 将自定义sku与苹果官网配置的商品的sku映射
        for(int i=0, shopListFormServer.Count, i++)
        {
            string strSku = GetServerSku(i); //获取苹果官网配置的商品的sku
            OpenIAB.mapSku("sku" + i,toString(), OpenIAB_iOS.STORE, strSku);
        }

        //初始化options,这一步可忽略
        Options options = new Options();
        options.storeSearchStrategy = SearchStrategy.INSTALLER_THEN_BEST_FIT;
        options.prefferedStoreNames = new string[] { OpenIAB_Android.STORE_GOOGLE, OpenIAB_Android.STORE_YANDEX };
        options.storeKeys = new Dictionary<string, string> { { OpenIAB_Android.STORE_GOOGLE, "publicKey" } };
        options.verifyMode = OptionsVerifyMode.VERIFY_SKIP;
        OpenIAB.init(options);
#endif
    }

    private void OnBillingSupported()
    {
        //Debug.Log("Billing supported");
        OpenIAB.queryInventory();
    }

    private void OnBillingNotSupported(string error)
    {
        //Debug.Log("Billing not supported: " + error);
    }

    private void OnQueryInventorySucceeded(Inventory inventory)
    {
        //苹果服务器返回商品信息
        //Debug.Log("Query inventory succeeded: " + inventory.ToString());
        skuList = inventory.GetAllAvailableSkus();
    }

    private void OnQueryInventoryFailed(string error)
    {
        //Debug.Log("Query inventory failed: " + error);
    }

    private void OnPurchaseSucceded(Purchase purchase)
    {
        //苹果服务器返回购买成功
        OnPurchaseResult(PurchaseResult.Purchase_Success, purchase.Receipt, purchase.Sku, purchase.OrderId);
    }

    private void OnPurchaseFailed(int errorCode, string error)
    {
        //苹果服务器返回购买失败
        OnPurchaseResult(PurchaseResult.Purchase_Fail);
    }

    private void OnConsumePurchaseSucceeded(Purchase purchase)
    {
        //Debug.Log("Consume purchase succeded: " + purchase.ToString());
    }

    private void OnConsumePurchaseFailed(string error)
    {
        //Debug.Log("Consume purchase failed: " + error);
    }

    private void OnTransactionRestored(Purchase purchase)
    {
        //Debug.Log("Transaction restored: " + purchase.Sku);
    }

    private void OnRestoreSucceeded()
    {
        //Debug.Log("Transactions restored successfully");
    }

    private void OnRestoreFailed(string error)
    {
        //Debug.Log("Transaction restore failed: " + error);
    }

   
    //购买结果回调
    private void OnPurchaseResult(PurchaseResult result, string receipt = "", string sku = "", string orderId = "")
    {
        if (result == PurchaseResult.Purchase_Success)
        {
            Debug.Log("购买成功,receipt:" + receipt);
        }
        else if (result == PurchaseResult.Purchase_Fail)
        {
            Debug.Log("购买失败,请重新购买");
        }
        else if (result == PurchaseResult.Product_Null_Error)
        {
            Debug.Log("购买失败,无此商品");
        }
        else if (result == PurchaseResult.Product_Info_Error)
        {
            Debug.Log("购买失败,商品信息有误");
        }
    }

    //购买接口,传入的是自定义sku
    public void Buy(string sku)
    {
        int index = 0;

        //没有商品信息
        if (skuList.Count == 0)
        {
            OnPurchaseResult(PurchaseResult.Product_Info_Error);
            return;
        }

        //验证要购买的商品是否存在
        for (index = 0; index < skuList.Count; index++)
        {
            if (sku == skuList[index].Sku)
            {
                break;
            }
        }
        if (index >= skuList.Count)
        {
            OnPurchaseResult(PurchaseResult.Product_Null_Error);
            return;
        }

        //向苹果服务器发送购买信息
        OpenIAB.purchaseProduct(sku, "");
    }
}

5. 创建空物体,将OpenIAPMananger.cs挂上去,就可以调用购买接口进行商品购买

 

常见问题

1. 漏单

说明:由于网络原因,服务器与苹果服务器的交互可能存在延迟,当玩家在客户端购买某件商品后,服务器一直没返回验证结构给客户端,因此导致玩家扣费却收不到商品。

解决:客户端将苹果服务器返回的Receipt进行存储,并定期或者适当的时机向服务器多次请求,等收到服务器返回结果后在将Receipt删除。

2. 苹果服务器向游戏服务器返回status:21002

说明:POST的参数格式有问题。

解决:要注意"receipt-data"中间的横杠必需而且要包装成JSON进行请求。

3. 苹果服务器向游戏服务器返回status:21007

说明:receipt是沙盒环境的收据,但却发送至生产环境验证,其实主要是沙盒验证接口与正式验证接口搞混了。

解决:先生产后沙盒,即先调用正式验证接口进行验证,等苹果服务器返回21007之后再调用沙盒验证接口验证。

 

参考链接

https://www.jianshu.com/p/d0ac8cec794b
https://blog.csdn.net/wjsshhx/article/details/73088094
https://blog.csdn.net/weixin_33796177/article/details/91376840
https://www.jianshu.com/p/307a3b7cfd6c
https://www.jianshu.com/p/17e0d11149f3

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值