Unity内IAP支付二次验证/服务器验证————最白话,手把手教你做系列。
之前的一篇写了Unity支付的IAP支付接入。
后来就出现了一些问题,数据统计的时候出现大量购买订单。但是实际上账户的钱却没有增加。@¥……&¥……*@¥&@初步判定可能存在部分用户通过其他渠道刷单的现象,然后才有以下操作,验证用户的订单是否的真实有效。
原理很简单,就是用户购买的时候我们把用户的购买信息发送给Goole/APPLE做个验证。然后通过返回的信息判断是否是有效订单,最后在游戏内执行相应逻辑。
这里我们是通过服务器验证,也就是需要
1,我们把信息发到我们的指定服务器(不是直接发送给Goole/APPLE,网上写的这种方法更安全)。
2,指定服务器端进行验证逻辑并给出返回值。(这点可以交给服务端的小伙伴做,服务器什么的我也不太懂)。
3,我们把返回值做解析然后关联客户端逻辑。
理论上1,3是要在Unity内进行的。
下面是代码部分,需要的可以对照一下IAP支付的代码看一下[链接地址][http://blog.csdn.net/qq_39860954/article/details/78880767]
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Purchasing;// Placing the Purchaser class in the CompleteProject namespace allows it to interact with ScoreManager,
// one of the existing Survival Shooter scripts.
// Deriving the Purchaser class from IStoreListener enables it to receive messages from Unity Purchasing.
//脚本需在调用购买方法之前初始化public class Purchaser : MonoBehaviour, IStoreListener{ //定义商品private const string product_1 = "商品ID1";private const string product_2 = "商品ID2";private const string product_3 = "商品ID3";private static IStoreController m_StoreController; // The Unity Purchasing system.private static IExtensionProvider m_StoreExtensionProvider; // The store-specific Purchasing subsystems.public static string kProductIDConsumable = "consumable";public static string kProductIDNonConsumable = "nonconsumable";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 we haven't set up the Unity Purchasing referenceif (m_StoreController == null){// Begin to configure our connection to PurchasingInitializePurchasing();}}public void InitializePurchasing(){// If we have already connected to Purchasing ...if (IsInitialized()){// ... we are done here.return;}// Create a builder, first passing in a suite of Unity provided stores.var builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance());//添加商品ID和类型 对应定义的商品IDbuilder.AddProduct(product_1, ProductType.Consumable, new IDs{{"商品ID1", GooglePlay.Name }});builder.AddProduct(product_2, ProductType.Consumable, new IDs{{"商品ID1", GooglePlay.Name }});builder.AddProduct(product_3, ProductType.Consumable, new IDs{{"商品ID1", GooglePlay.Name }});// Kick off the remainder of the set-up with an asynchrounous call, passing the configuration // and this class' instance. Expect a response either in OnInitialized or OnInitializeFailed.UnityPurchasing.Initialize(this, builder);}private bool IsInitialized(){// Only say we are initialized if both the Purchasing references are set.return m_StoreController != null && m_StoreExtensionProvider != null;}public void BuyConsumable(){// Buy the consumable product using its general identifier. Expect a response either // through ProcessPurchase or OnPurchaseFailed asynchronously.BuyProductID(kProductIDConsumable);}public void BuyNonConsumable(){// Buy the non-consumable product using its general identifier. Expect a response either // through ProcessPurchase or OnPurchaseFailed asynchronously.BuyProductID(kProductIDNonConsumable);}public void BuySubscription(){// Buy the subscription product using its the general identifier. Expect a response either // through ProcessPurchase or OnPurchaseFailed asynchronously.// Notice how we use the general product identifier in spite of this ID being mapped to// custom store-specific identifiers above.BuyProductID(kProductIDSubscription);}//购买商品调用的方法public void BuyProductID(string productId){// If Purchasing has been initialized ...if (IsInitialized()){// ... look up the Product reference with the general product identifier and the Purchasing // system's products collection.Product product = m_StoreController.products.WithID(productId);// If the look up found a product for this device's store and that product is ready to be sold ... if (product != null && product.availableToPurchase){Debug.Log(string.Format("Purchasing product asychronously: '{0}'", product.definition.id));// ... buy the product. Expect a response either through ProcessPurchase or OnPurchaseFailed // asynchronously.m_StoreController.InitiatePurchase(product);}// Otherwise ...else{// ... report the product look-up failure situation Debug.Log("BuyProductID: FAIL. Not purchasing product, either is not found or is not available for purchase");}}// Otherwise ...else{// ... report the fact Purchasing has not succeeded initializing yet. Consider waiting longer or // retrying initiailization.Debug.Log("BuyProductID FAIL. Not initialized.");}}// Restore purchases previously made by this customer. Some platforms automatically restore purchases, like Google. // Apple currently requires explicit purchase restoration for IAP, conditionally displaying a password prompt.public void RestorePurchases(){// If Purchasing has not yet been set up ...if (!IsInitialized()){// ... report the situation and stop restoring. Consider either waiting longer, or retrying initialization.Debug.Log("RestorePurchases FAIL. Not initialized.");return;}// If we are running on an Apple device ... if (Application.platform == RuntimePlatform.IPhonePlayer ||Application.platform == RuntimePlatform.OSXPlayer){// ... begin restoring purchasesDebug.Log("RestorePurchases started ...");// Fetch the Apple store-specific subsystem.var apple = m_StoreExtensionProvider.GetExtension<IAppleExtensions>();// Begin the asynchronous process of restoring purchases. Expect a confirmation response in // the Action<bool> below, and ProcessPurchase if there are previously purchased products to restore.apple.RestoreTransactions((result) => {// The first phase of restoration. If no more responses are received on ProcessPurchase then // no purchases are available to be restored.Debug.Log("RestorePurchases continuing: " + result + ". If no further messages, no purchases available to restore.");});}// Otherwise ...else{// We are not running on an Apple device. No work is necessary to restore purchases.Debug.Log("RestorePurchases FAIL. Not supported on this platform. Current = " + Application.platform);}}// // --- IStoreListener//public void OnInitialized(IStoreController controller, IExtensionProvider extensions){// Purchasing has succeeded initializing. Collect our Purchasing references.Debug.Log("OnInitialized: PASS");// Overall Purchasing system, configured with products for this application.m_StoreController = controller;// Store specific subsystem, for accessing device-specific store features.m_StoreExtensionProvider = extensions;}public void OnInitializeFailed(InitializationFailureReason error){// Purchasing set-up has not succeeded. Check error for reason. Consider sharing this reason with the user.Debug.Log("OnInitializeFailed InitializationFailureReason:" + error);}//这部分是改动部分,二次验证在这个方法内进行处理。//购买不同商品结束后的处理方法 对应定义的商品public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs args){//获取并解析你需要上传的数据。解析成string类型var wrapper = (Dictionary<string, object>) MiniJson.JsonDecode (args.purchasedProduct.receipt);if (null == wrapper) {return PurchaseProcessingResult.Complete;}// Corresponds to http://docs.unity3d.com/Manual/UnityIAPPurchaseReceipts.htmlvar store = (string)wrapper ["Store"];//下面的payload 即为IOS的验证商品信息的数据。即我们需要上传的部分。var payload = (string)wrapper ["Payload"]; // For Apple this will be the base64 encoded ASN.1 receipt#if UNITY_IPHONEStartCoroutine(PostRepict("http://www.xxxxxxxxxxxxxx/purchase/Verifytrade",args.purchasedProduct.definition.id,payload));#endif//For GooglePlay payload contains more JSONif (Application.platform == RuntimePlatform.Android){var gpDetails = (Dictionary<string, object>)MiniJson.JsonDecode(payload);var gpJson = (string)gpDetails["json"];var gpSig = (string)gpDetails["signature"];//Google验证商品信息的数据包含在gpJson里面还需要在服务端进行解析一下,对应的键是"purchaseToken"。StartCoroutine(PostRepict("http://www.xxxxxxxxxxxxx/purchase/Andverifytrade", args.purchasedProduct.definition.id, gpJson));}// A consumable product has been purchased by this user.// Return a flag indicating whether this product has completely been received, or if the application needs // to be reminded of this purchase at next app launch. Use PurchaseProcessingResult.Pending when still // saving purchased products to the cloud, and when that save is delayed. return PurchaseProcessingResult.Complete;}//Unity内传输信息到服务器的方法,www:服务器网址,payID:商品ID,验证商品的数据。 Google需要传商品ID(必要)IOS(不必要)。IEnumerator PostRepict(string www,string payID,string gpJson){WWWForm form = new WWWForm(); form.AddField("productId",payID); form.AddField("purchaseToken",gpJson); WWW getData = new WWW(www,form); yield return getData; if(getData.error!= null) { Debug.Log(getData.text); } //srcString即为服务器返回信息。string srcString = getData.text; try {//解析服务器返回的数据,根据服务器端设定的逻辑进行判断,比如我这里是如果返回数据中"code"的值为200则为真实订单,所以在判定成功时继续客户端逻辑。JSONNode jsonNode = JSONNode.Parse(srcString);if(jsonNode["code"].AsInt==200){//判定为真实订单,这里我把游戏内购买逻辑放在这个位置if (String.Equals(payID., product_1, StringComparison.Ordinal)){//商品1购买成功逻辑} else if (String.Equals(payID, product_2, StringComparison.Ordinal)) {//商品2购买成功逻辑 }else if (String.Equals(payID, product_3, StringComparison.Ordinal)){//商品3购买成功逻辑}}else{Debug.Log(string.Format("ProcessPurchase: FAIL. Unrecognized product: '{0}'", args.purchasedProduct.definition.id));//判定为假订单}} catch(System.Exception e) {Debug.LogError ("Server Error:" + e.Message);//其他网络问题}}public void OnPurchaseFailed(Product product, PurchaseFailureReason failureReason){// A product purchase attempt did not succeed. Check failureReason for more detail. Consider sharing // this reason with the user to guide their troubleshooting actions.Debug.Log(string.Format("OnPurchaseFailed: FAIL. Product: '{0}', PurchaseFailureReason: {1}", product.definition.storeSpecificId, failureReason));}}
以上代码已验证可行,但仍有部分可优化的地方。
1,因为支付后的逻辑是用协程(StartCoroutine)做的,所以支付可能会出现支付成功但没有即时数据更新。
2,支付失败也没有另外做处理,比如显示支付失败的页面。
3,二次验证只是作为购买部分的一套处理逻辑,对于不通过虚假购买而是直接破解改动游戏数值的情况并没有什么卵用。
Unity内IAP支付二次验证/服务器验证————最白话,手把手教你做系列。相关推荐
- 关于IAP支付,谷歌和苹果订阅商品——最白话,手把手教你做系列。
简述:最近项目要接入订阅商品,这里总结一下公司大佬们的经验和我整理后脚本. 一.关于订阅 1,跟消耗性和非消耗性的购买类似,开发者账后后台建订阅型商品. 订阅型商品分两种,自动续订和非自动续订的,一般 ...
- 实例:用C#.NET手把手教你做微信公众号开发(20)--使用微信支付线上收款:jsapi方式
在做线上.线下销售时,可以使用微信便捷支付,通过微信公众号收款有很多种收款方式,如下图: 今天我们来讲一下jsapi支付,场景就是在微信内打开某个页面,完成在线支付,同样一个网页,使用微信打开就是js ...
- 实例:用C#.NET手把手教你做微信公众号开发(21)--使用微信支付线上收款:H5方式
在做线上.线下销售时,可以使用微信便捷支付,通过微信公众号收款有很多种收款方式,如下图: 今天我们来讲一下H5场景支付,使用手机浏览器打开就是H5方式,最常见的推广是短信内置链接,这种场景需要调用微信 ...
- 我的世界服务器显示器,我的世界红石显示器制作教程 手把手教你做显示器
我的世界红石显示器制作教程 手把手教你做显示器.那下面给大家分享的是我的世界里面的一个红石显示器的制作教程,那对下面的这个显示器感兴趣的玩家不妨进来卡看哦!希望大家喜欢. 游戏园我的世界官方群:325 ...
- 手把手教你做树莓派魔镜-MagicMirror(二)-烧写系统卡
本系列文章: 手把手教你做树莓派魔镜-MagicMirror(一)-准备工作 手把手教你做树莓派魔镜-MagicMirror(二)-烧写系统卡 手把手教你做树莓派魔镜-MagicMirror(三)-系 ...
- Adobe host验证服务器,验证您的 Adobe Connect 安装
运行安装程序并在部署之前,请先验证您的 Adobe Connect 安装,以确保所有组件正常工作. 安装 Adobe Connect 后,建议先检查安装是否成功,确保所有组件均正常工作,然后再进行部署 ...
- 搜狐邮件服务器地址加密方式,手把手教你如何用SMIME加密任意邮件
原标题:手把手教你如何用SMIME加密任意邮件 在此之前,我曾写过一篇关于邮件服务提供商安全问题的文章[传送门],你可以根据我的建议来选择一个比较合适的邮件提供商.但是,如果你已经有自己的邮箱并且使用 ...
- ❤️❤️❤️Unity废柴看过来,手把手教你做植物大战僵尸(二)—— 序列帧动画
开始制作游戏,首先,我们要把游戏素材导入到项目中,我这里整理出来了一些项目中用到的图片音乐等素材,大家可以下载下来使用,或者自己从网上找其他好看的素材也可以. 植物大战僵尸素材 链接:https:// ...
- ❤️❤️❤️Unity废柴看过来,手把手教你做植物大战僵尸(十二)—— 向日葵生产太阳
生产太阳 1.给向日葵模板添加子物体,子物体是太阳 2.太阳初始大小是0,给太阳添加Tween Scale组件 3.在SunFlowerAI脚本中每过三秒钟播放太阳的Tween动画,即让太阳从小变大 ...
最新文章
- Matlab与线性代数--矩阵的正交分解
- 成功解决当Win10系统进行深度学习的时候发现系统C盘满了,教你如何正确卸载一些非必要的内容
- linux 在不同终端之下的切换方式
- 数据资产纳入国资保值增值考核
- JS中遍历数组的两种方式
- RAID5阵列掉盘显示未初始化---解决过程
- 安装虚拟机报错 This kernel requires an X86-64 CPU,but only detected an i686 CPU
- 工业物联网网关 数据采集网关
- 2004年 联想员工亲历联想大裁员:公司不是我的家 (网易裁员事件相关文章)
- 数学知识-三角函数公式大全(值得收藏)
- 0x80131500打不开微软商店的解决办法
- Android烂笔头
- 经典游戏江湖医馆文字版
- Soya语言1.0.1-alpha发布!
- 出现 leaked ServiceConnection 解决办法
- mac清空废纸篓怎么恢复?
- Golang 浮点数运算 避免精度损失 Decimal包
- vue路由传参—params—query
- Wordpress及插件更新问题排解
- APP软件项目开发费用