关于FlashBuilder4.7关于IOS打包上架AppStore
Posted 黑塞矩阵
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关于FlashBuilder4.7关于IOS打包上架AppStore相关的知识,希望对你有一定的参考价值。
网上有不少的关于ios打包的文章,不过都比较散和凌乱,有的有不少就是简单的记载。以自己的经验总结FlashBuilder4.7打包IOS并上交到AppStore。本文总共分为4大部分:(1)证书(2)FlashBuilder4.7项目针对IOS的修改部分(3)AppStore中的上架信息部分(4)FlashBuilder中的特殊内购。
第一部分:证书
1.首先要一台苹果电脑和IPhone5和IPhone4手机和一个个人开发者账https://developer.apple.com/account/ios/certificate/development。
2.首先创建一个证书请求文件
2.1首先打开应用程序--->钥匙串访问,证书助理中,选择“从证书颁发机构求证书”。如下图:
2.2按要求操作之后,就出现下面界面:如图:填写你申请的用户电子邮件地址,常用名称,CA电子邮件地址,请求是:选择存储到磁盘,点击“继续”:
2.3.出现下面的保存位置,如图:(名字默认就好):
2.4.这样一个CSR(证书请求文件Cerificate Signing Request)就创建好了。如图:
3.开发者账号方面的证书
登录到个人开发者账号到AppleDeveloper上,操作到如下的界面
3.1授权设备(Devices)
授权设备其实就会苹果产品,从上面可以看出苹果设备有比较多,最常用的iPad和iPhone。每个苹果设备都有一个唯一的UDUD。本文以iPhone5(非越狱)进行示例。
3.1.1手机连接到苹果电脑中,打开iTunes,则如下图:拷贝UDID。
3.1.2 按下图注册设备(Devices)
这样你测试时的真机设备就注册号了。
3.2注册App IDs
3.2.1注册一个AppID,是唯一性的,这个就像相当于给App注册了一个身份证。
3.2.2关于AppID Suffix中Explicit App ID中的BundleID中一般是唯一性的(唯一性会为我们省好多的麻烦)。其一般组成为com.公司名字.项目名字.有意义的字母。
3.2.3.如果BundleID中带有*,那么它会对很多App有效,当然前提你授权证书和证书时会用到它。
3.2.4.如果你的app用到了push等功能,那你的appID就需要唯一了,就是不用*。这样的话,你的app只有一个Bundle Identifier(下图注释就是基于这样的原理)。
3.2.5.按下图注册一个AppID
3.3证书有不少的分类,作为一个以把打包App送到AppStore上加为目的话,我个人以使用的目的分为两大类:
(1)调试和开发证书:这类证书以开发和调试为目的的证书,说白了就是用真机进行测试时用的证书。它们是:
(I)Certificates->Development(开发和调试证书文件.cer):安装在电脑上提供权限:开发人员通过设备进行真机测试。可以生成副本供多台电脑安装。
(II)Provisioning Profiles->Development(开发授权文件.mobileprovision):在装有开发证书或副本的电脑上使用,开发人员选择授权文件通过将程序安装到授权记录的设备中,即可进行真机测试。
注意:确保电脑有权限真机调试,即安装了开发证书或副本;在开发工具中程序的Bundle identifier和选中使用的授权文件的App Id要一致;连接调试的设备的UDID在选中的授权文件中有记录。
(2)发布证书:在类证书就是以打包IOS成App发布到AppStore时用的证书,它们是:
(I)Certificates->Production(分发应用证书,发布应用证书.cer):安装在电脑上提供发布IOS程序的权限:开发人员可以制做测试版和发布版程序(打包IOS时用的证书)。不可生成副本,仅有配置该证书的电脑才可以使用。
(II)Provisioning Profiles->Distribution(发布授权文件.mobileprovision): 在装有发布证书的电脑上(即配置证书的电脑,只有一台)制作测试版和发布版的程序。
注:
(I)发布版就是发布到AppStore上的程序文件,开发者账号创建爱你授权文件时store选项,选择App Id,无需选择UDID。
(II)测试版就是在发布之前交给测试人员可同步到设备上的程序文件,开发者账号创建授权文件时选择AdHoc,选择AppID和UDID;只有选中的UDID对应的设备才可能安装上通过该授权文件制作的程序(App)。
3.4按下图注册开发者和发布证书,它们十分相似:
这样开发者证书和发布证书就注册好了
3.5:按下图进行注册授权证书
注:
(1)授权证书基本上的步骤基本上差不多,唯一不同的就是上AppStore的授权证书不用选择测试设备(测试真机)。
4.制作P12信息交换证书
4.1你需要什么证书只需下载相应的证书就可以了。当然,授权证书可以直接用,但开发者证书和分发者证书(.cer)项目还不能直接用,还需要通过钥匙串转成P12的信息交换文件,项目才可以用。
4.2按照下面示例制作P12证书:
第一步:需要制作哪个证书(.cer)双击它,导入到要钥匙串访问。一定要查看密码中的专用秘钥与你需要的.cer一致.
这样,我们所需要的.cer证书就转换成P12个人信息交换证书就制作好了。
到此,我们项目中的所需要的证书就制作好了。
第二步:FlashBuilder4.7项目针对IOS上AppStore的修改部分
1配置相关的AppIcon图标问题和xiangmu.xml一些配置
1.1由于上AppStore需要配置相关的AppIcon图标,下面的图标是示例,如果你的App通过ApplicationLoader上传到AppStore时有下面类似的错误,你只需要进行修改就可以了。
ERROR ITMS-90022: "Missing required icon file. The bundle does not contain an app icon for iPhone / iPod Touch of exactly \'57x57\' pixels, in .png format for iOS versions < 7.0."
WARNING ITMS-90025: "Missing recommended icon file. The bundle does not contain an app icon for iPhone / iPod Touch of exactly \'120x120\' pixels, in .png format for iOS versions >= 7.0."
1.2如下图示例:
这几张Icon是设备启动的图标
2.关于真机调试。
2.1关于真机调试设置问题
3.关于打包成上AppStore的App
3.1项目->导出发行版
好了,关于项目中关于IOS的一些修改就介绍这些了,当然,关于内购的一些修改放到最后最后介绍。
第三部分:关于AppStore中的上架信息部分
三部分关于AppStore中的上架信息部分,非常简单,没有什么介绍的,只要按照图片介绍进行填写即可。
第四部分:FlashBuilder中的特殊内购
4.1插件:productStore.ane 下载地址:Adobe账号中的Adobe Gaming SDK中的productStore.ane以及案例
4.2 项目.xml中插入productStore.ane的引用
<extensions> <extensionID>com.adobe.ane.productStore</extensionID> </extensions>
4.3项目中添加productStore.ane
项目->属性->ActionScript构建路径->本机扩展->添加ane
4.4打包时注意导入productStore.ane
4.5应用支付原理
(1)App请求订单号->苹果服务器处理->App收到苹果服务器处理结果(交易取消,交易成功,交易失败)。<1>成功,收到票据,票据发送到苹果服务器验证->苹果反馈验证成功->App发送商品给玩家。
(2)一般App收到票据发送给道具服务器(App自己的服务器)->道具服务器发送给苹果进行验证->道具服务器验证成功之后->道具服务器通知App发送商品给玩家。
(3)本案例进行本地验证
(4)记住苹果验证成功具有理想性和欺骗性,还要继续进行验证(如App id,交易的账单号,交易的时间戳等)防止玩家进行插件刷单。
4.6iTunes Connect中的设置
(1)协议、税务和银行业务中的相关信息设置,如果缺少这一步,内购功能是不会成功的。这是苹果给你钱啊!
(2)弄一个测试账号:用户和职能->沙箱技术测试员。
4.7内购商品的分类以及设置
注意:一定要选对你的商品类型。
4.8关于productStore.ane的理解
4.8.1 ProductStore的API
写程序的第一行代码,一定要到iTunesConnect中上创建应用内支付商品,否则,程序请求商品ID无效,应用就无法测试。
ProductStore中的API只有这几个类:
ProductStore
ProductEvent
Product
Transaction
TransactionEvent
4.8.2ProductStore中的9个事件侦听
(1)ProductEvent.PRODUCT_DETAILS_SUCCESS商品列表获取成功
(2)ProductEvent.PRODUCT_DETAILS_FAIL商品列表获取失败
(3)TransactionEvent.PURCHASE_TRANSACTION_SUCCESS交易成功
(4)TransactionEvent.PURCHASE_TRANSACTION_CANCEL交易取消
(5)TransactionEvent.PURCHASE_TRANSACTION_FAIL交易失败
(6)TransactionEvent.FINISH_TRANSACTION_SUCCESS交易完成成功
(7)TransactionEvent.RESTORE_TRANSACTION_SUCCESS恢复交易成功
(8)TransactionEvent.RESTORE_TRANSACTION_FAIL恢复交易失败
(9)TransactionEvent.RESTORE_TRANSACTION_COMPLETE恢复交易完成
4.8.3在应用程序运行后,尽早创建一个ProductStore的Singleton(单利)示例,并保持它存在于整个应用的生命周期内
productStore=new ProductStore()
4.8.4三个请求事件
(1)productStore.requestProductsDetails(ProductListId)使用商品ID来请求商品的详细信息 (商品列表数组)
(2)productStore.makePurchaseTransaction(buyProductId,1);//触发购买的交易请求 ("商品ID",数量);
(3)productStore.restoreTransactions();//恢复请求
4.8.5进行一些支持判断
//检测苹果设备是否支持Productsore protected function check_isSupported():void{ var t1:Boolean=ProductStore.isSupported; } //检测用户是否通过应用直接购买 protected function is_Available():void { var t2:Boolean=productStore.available; }
4.8.6尽早的进行请求商品信息,并保持在内存中
public function get_Product():void { trace("----------get_Product-------start---------"); productStore.addEventListener(ProductEvent.PRODUCT_DETAILS_SUCCESS,productDetailsSucceeded)//商品列表获取成功 productStore.addEventListener(ProductEvent.PRODUCT_DETAILS_FAIL,productDetailsFailed)//商品列表获取失败 ProductListId=new Vector.<String>(5); var AppBundleId:String="com.gamevil.Kungfu.defug."; ProductListId[0]=AppBundleId+"MonthCardThirtyRMB4";//月卡充值30元 ProductListId[1]=AppBundleId+"ThirtyRMB4";//充值30元 ProductListId[2]=AppBundleId+"OneHuandedAndEightRMB4";//充值108元 ProductListId[3]=AppBundleId+"OneHundredAndNinietyEightRMB4";//充值198元 ProductListId[4]=AppBundleId+"FourHundredAndEightyEIghtRMB4";//充值488元 productStore.requestProductsDetails(ProductListId);//可以使用商品ID来请求商品的详细信息 trace("----------get_Product-------end---------"); } //商品列表请求成功 public function productDetailsSucceeded(e:ProductEvent):void { // trace("in productDetailsSucceeded "+e); var i:uint=0; trace("商品列表请求成功 "); while(e.products&&i<e.products.length) { // //获取每一个商品的详细信息<itunesconnect.apple.com成功设置每一个商品ID> var p:Product=e.products[i]; trace("999999 "+p.identifier); // trace("title: "+p.title);//商品名称 // trace("description:"+p.description);//商品描述 // trace("indetitier:"+p.identifier);//商品ID // trace("priceLocale:"+p.priceLocale);//商品价格本地化信息 // trace("price:"+p.price);//商品价格 i++; } } //商品列表请求失败 public function productDetailsFailed(e:ProductEvent):void { var i:uint=0; while(e.invalidIdentifiers&&i<e.invalidIdentifiers.length) { trace("888888"+e.invalidIdentifiers[i]); i++; } }
(1)这里的商品ID都要在iTunesconnect中的内购成功设置,否则就监听不成功
(2)监听成功的商品信息被ANE封装在一个Product类中,属性如下:
identifier:商品ID
title:商品名称
description:描述文字
price:商品价格(已经折合为用户商品的本地货币单位)
priceLocal:商品价格本地化信息(用来获取货币单位的显示)。
4.8.7有关交易触发和维护
public function buyTheApp (buyProductId:String):void { trace("8888888 BUYTime"+BUYTime); if(BUYTime==true){ BUYTime=false; productStore.addEventListener(TransactionEvent.PURCHASE_TRANSACTION_SUCCESS,purchaseTransactionSucceeded);//交易成功 productStore.addEventListener(TransactionEvent.PURCHASE_TRANSACTION_CANCEL,purchaseTransactionCanceled);//交易取消 productStore.addEventListener(TransactionEvent.PURCHASE_TRANSACTION_FAIL,purchaseTransactionFailed);//交易失败 productStore.makePurchaseTransaction(buyProductId,1);//触发购买的交易请求 ("商品ID",1); } } //交易成功 protected function purchaseTransactionSucceeded(e:TransactionEvent):void { productStore.removeEventListener(TransactionEvent.PURCHASE_TRANSACTION_SUCCESS,purchaseTransactionSucceeded);//交易成功 productStore.removeEventListener(TransactionEvent.PURCHASE_TRANSACTION_CANCEL,purchaseTransactionCanceled);//交易取消 productStore.removeEventListener(TransactionEvent.PURCHASE_TRANSACTION_FAIL,purchaseTransactionFailed);//交易失败 BUYTime=true; trace("hongqianjin //交易成功 "); var i:uint=0; var t:Transaction; while(e.transactions&&i<e.transactions.length) { t=e.transactions[i]; purchaseIdentifier=t.identifier;//保存交易的id i++; productStore.addEventListener(TransactionEvent.FINISH_TRANSACTION_SUCCESS,finishTransactionSucceeded); productStore.finishTransaction(t.identifier); // 验证收据 var encodedReceipt:String=Base64.encode(t.receipt); var req:URLRequest=new URLRequest("https://buy.itunes.apple.com/verifyReceipt");//沙盒测试,如上线则sandbox改成为buy req.method=URLRequestMethod.POST; req.data="{\\"receipt-data\\":\\""+encodedReceipt+"\\"}"; var ldr:URLLoader=new URLLoader(req); ldr.load(req); ldr.addEventListener(Event.COMPLETE,function(e:Event):void{ var People:*=JSON.parse(ldr.data); if(People["status"]==21007)//如果是环境测试,则放到环境验证服务器验证 { var req1:URLRequest=new URLRequest("https://sandbox.itunes.apple.com/verifyReceipt");//沙盒测试,如上线则sandbox改成为buy req1.method=URLRequestMethod.POST; req1.data="{\\"receipt-data\\":\\""+encodedReceipt+"\\"}"; var ldr1:URLLoader=new URLLoader(req1); ldr1.load(req1); ldr1.addEventListener(Event.COMPLETE,function(e:Event):void{ var People1:*=JSON.parse(ldr1.data); if(!People1["status"]) { if(People1["receipt"].bid!="com.gamevil.Kungfu.defug")//如果不是次Kungf游戏,则保持 { // trace("游戏不是Kungfu"); }else if(People1["receipt"].transaction_id!=purchaseIdentifier) { //trace("不是此次交易"); }else { //trace("返回成功"); //这地方发放玩家商品 } }else { // trace(" 票剧违法"); } }); } else if(!People["status"])//返回0,则成功 { if(People["receipt"].bid!="com.gamevil.Kungfu.defug")//如果不是次Kungf游戏,则保持 { trace("游戏不是Kungfu"); }else if(People["receipt"].transaction_id!=purchaseIdentifier) { trace("不是此次交易"); }else { trace("返回成功"); //这地方发放给玩家商品 } }else { trace(" 票剧违法"); } }); } getPendingTransaction(productStore); } //交易取消 protected function purchaseTransactionCanceled(e:TransactionEvent):void{ logn.off(); productStore.removeEventListener(TransactionEvent.PURCHASE_TRANSACTION_SUCCESS,purchaseTransactionSucceeded);//交易成功 productStore.removeEventListener(TransactionEvent.PURCHASE_TRANSACTION_CANCEL,purchaseTransactionCanceled);//交易取消 productStore.removeEventListener(TransactionEvent.PURCHASE_TRANSACTION_FAIL,purchaseTransactionFailed);//交易失败 BUYTime=true; trace("hongqinajin +//交易取消"); var i:uint=0; while(e.transactions&&i<e.transactions.length) { var t:Transaction=e.transactions[i]; //printTransaction(t); i++; trace("FinishTransactions"+t.identifier); productStore.addEventListener(TransactionEvent.FINISH_TRANSACTION_SUCCESS,finishTransactionSucceeded); productStore.finishTransaction(t.identifier);//结束这个交易。调用这个API会将该交易从系统维护的交易队列中移除 } getPendingTransaction(productStore); } //交易失败 protected function purchaseTransactionFailed(e:TransactionEvent):void { productStore.removeEventListener(TransactionEvent.PURCHASE_TRANSACTION_SUCCESS,purchaseTransactionSucceeded);//交易成功 productStore.removeEventListener(TransactionEvent.PURCHASE_TRANSACTION_CANCEL,purchaseTransactionCanceled);//交易取消 productStore.removeEventListener(TransactionEvent.PURCHASE_TRANSACTION_FAIL,purchaseTransactionFailed);//交易失败 BUYTime=true; trace("ii交易失败"); var i:uint=0; while(e.transactions&&i<e.transactions.length) { var t:Transaction=e.transactions[i]; //printTransaction(t); i++; trace("FinishTransactions"+t.identifier); productStore.addEventListener(TransactionEvent.FINISH_TRANSACTION_SUCCESS,finishTransactionSucceeded); productStore.finishTransaction(t.identifier); } getPendingTransaction(productStore); } //完成交易成功完成 protected function finishTransactionSucceeded(e:TransactionEvent):void { trace("i交易完成 "+e); BUYTime=true; var i:uint=0; while(e.transactions&&i<e.transactions.length) { var t:Transaction=e.transactions[i]; //printTransaction(t); productStore.removeEventListener(TransactionEvent.FINISH_TRANSACTION_SUCCESS,finishTransactionSucceeded); i++; trace("FinishTrinsactions"+t.productIdentifier); trace("FinishTrinsactions"+t.identifier); } }
(1)进入买卖的时候,就开始进行了交易的结果的三大监听
productStore.addEventListener(TransactionEvent.PURCHASE_TRANSACTION_SUCCESS,purchaseTransactionSucceeded);//交易成功
productStore.addEventListener(TransactionEvent.PURCHASE_TRANSACTION_CANCEL,purchaseTransactionCanceled);//交易取消
productStore.addEventListener(TransactionEvent.PURCHASE_TRANSACTION_FAIL,purchaseTransactionFailed);//交易失败
(2)触发购买
productStore.makePurchaseTransaction(buyProductId,1);//触发购买的交易请求 ("商品ID",1);
这句代码是想Store发送了一个购买请求,商品号是ProductId,数量为1。同时ane负责与AppStore之间的交易,会有三种结果(交易成功,交易取消交易失败)。设备系统会弹出一个原生对话框,取消or购买。点击取消会触发productStore.addEventListener(TransactionEvent.PURCHASE_TRANSACTION_CANCEL,purchaseTransactionCanceled);//交易取消
这个事件。
点击购买,会触发
productStore.addEventListener(TransactionEvent.PURCHASE_TRANSACTION_SUCCESS,purchaseTransactionSucceeded);//交易成功
这个事件
(3)从程序角度来说,每一个交易是Transcation。它包含了下面的属性:
identifier:交易ID,每次交易都有一个unique的ID,由应用商店生成。
date:交易时间
error:交易错误信息
originalTransaction:原始交易(针对恢复非消费型商品的交易)
productIdentifier:交易所涉及的商品ID
productQuantity:交易所涉及的商品数量
receipt:交易成功后的数据(由ANE封装的Product和Transaction类,保存了IOS SDK中的StoreKit的原生类属性,连命名都一样)。
4.8.8Transaction队列
//交易队列中 public function getPendingTransaction(prdStore:ProductStore):void { var transactions:Vector.<Transaction>=prdStore.pendingTransactions; var i:uint=0; // trace("hongqianjin:transactions="+transactions); while(transactions&&i<transactions.length) { var t:Transaction=transactions[i]; // printTransaction(t); i++; } }
设备应用商品中,维护者一个Transaction队列。每个交易的生命周期不受应用程序的生命周期限制,即使在交易过程中退出,交易依然存在。下次登录应用并初始化ProductStore时,应用商品在第一个时间派发时间告知交易结果。这就是为什么尽早初始化ProductStore。
4.8.9打印交易的信息
//打印交易的信息 public function printTransaction(t:Transaction):void { trace("------------in Print Transaction-----------------"); trace("identifier:"+t.identifier);//交易ID,每次交易都有一个uniqueID,由应用商店生成 trace("productIdentifier:"+t.productIdentifier);//交易所涉及的商品ID trace("productQuantity:"+t.productQuantity);//交易所涉及的商品数量 trace("data:"+t.date);//交易时间 trace("receipt:"+t.receipt);//交易成功后的收据 trace("error:"+t.error);//交易错误信息 trace("originalTransaction:"+t.originalTransaction);//原始交易(针对恢复非消费型商品的交易) if(t.originalTransaction) printTransaction(t.originalTransaction); trace("------end of print transaction--------------------"); }
4.8.10 结束交易
productStore.finishTransaction(t.identifier);//结束这个交易。调用这个API会将该交易从系统维护的交易队列中移除
这一个代码非常的重要,在三大交易结果中都需要调用这个API会将它中该交易从系统维护的交易队列中移除。不然会很容易造成“该商品已经购买,将免费帮你恢复”从而造成该交易购买不成功。
4.8.10 三大交易结果的解释
(1)productStore.addEventListener(TransactionEvent.PURCHASE_TRANSACTION_SUCCESS,purchaseTransactionSucceeded);//交易成功
purchaseTransactionSucceeded这个函数中处理交易成功的事情,交易成功之后不要立即给玩家发送商品,应该拿票据到AppStore商店里进行验证该此交易,是为了保证该次交易的合法性,而不是第三方软件的造假行为。交易成功也需要结束交易。
(2)
productStore.addEventListener(TransactionEvent.PURCHASE_TRANSACTION_CANCEL,purchaseTransactionCanceled);//交易取消
productStore.addEventListener(TransactionEvent.PURCHASE_TRANSACTION_FAIL,purchaseTransactionFailed);//交易失败
当用户取消交易或者交易失败的时候,或者一个新的交易请求属于重复购买行为。应用商品会派发Cancel或者Fail这两个事件。但都需要结束交易。
4.8.11验证收据
(1)交易收据以字符串的形式保存在Transaction类的receipt属性里,开发者可以选择在客户端提交验证,也可以让自己的服务器来发送。
(2)验证的目前就是为了判断该交易的合法性。现在大量的插件很容易进行刷单,所以需要进行判断该交易的合法性。验证的方式是将收据以JSON对象的方式发送到苹果地址,然后判断返回的结果。
(3)返回的结果即使正确在本地也需要进一步判断,例如以 AppId,时间戳,交易单号等。现在的刷单插件实在是比较强大。
(4)先用Base64把数据编码然后在发送给AppStore服务器验证
(6)记住我们自己的验证和苹果的审核都是在苹果的沙盒环境下进行的,而玩家却是在正式的服务器下进行购买。这就是所谓的二次验证。苹果的玩家购买环境服务器:
https://buy.itunes.apple.com/verifyReceipt 苹果的沙盒环境:https://sandbox.itunes.apple.com/verifyReceipt
(7) 票据发送给苹果服务器的代码示例:
var encodedReceipt:String=Base64.encode(t.receipt); var req:URLRequest=new URLRequest("https://buy.itunes.apple.com/verifyReceipt");//沙盒测试,如上线则sandbox改成为buy req.method=URLRequestMethod.POST; req.data="{\\"receipt-data\\":\\""+encodedReceipt+"\\"}"; var ldr:URLLoader=new URLLoader(req); ldr.load(req); loader.addEventListener(Event.COMPLETE,onReceiptComplete); loader.addEventListener(Event.IO_ERROR,onReceiptError);
(8)响应的结果也是JOSN对象:
{
"receipt":{(receipt here)},"status":0
}
(I)status :0是说明该交易是有效的,否则是无效的,2007是测试用的sandbox,却被发送到玩家购买的服务器中去了。还有很多,这里就不介绍了。
(II)receipt here :这里记录了关于本次交易的很多信息,例如时间戳,交易ID,交易数量,交易商品,交易AppID等等,这些信息都是可以进行本地化验证的依据。
4.8.12 这里恢复商品,只贴出代码。
//恢复内购 protected function restore_Transactions():void { trace("in restore_Transactions"以上是关于关于FlashBuilder4.7关于IOS打包上架AppStore的主要内容,如果未能解决你的问题,请参考以下文章