Cocos 2d-X Lua 游戏添加苹果内购(二) OC和Lua交互代码详解
这是第二篇
Cocos 2d-X Lua 游戏添加苹果内购(一) 图文详解准备流程
这是前面的第一篇,详细的说明了怎样添加内购项目以及填写银行信息提交以及沙盒测试员的添加使用以及需要我们注意的东西,结果,被移除首页了!前面第一篇的内容是这篇的基础,前面那些不弄好,下面的商品信息你是请求不到的,这点需要大家特别注意...有需要前面提到的内容的孩子可以点击链接进去自己看看!!
这篇就具体的总结我们Lua和OC交互的内容以及内购具体的代码以及结果的测试说明:
内购部分OC的代码实现
先自己总结一下整个支付的流程,下面的代码部分我们也就按照这个支付流程来解读:
最开始你首先要做的就是给你的项目添加: StoreKit.framework 框架
以及在你需要写支付的类中导入: #import <StoreKit/StoreKit.h>
并且你还得遵守 <SKPaymentTransactionObserver,SKProductsRequestDelegate > 两个协议,后面会实现他们相应的代理方法。
接下来你初始化了你支付类需要你初始化的东西之后,就开始判断用户有没有禁止了苹果支付,代码如下:
- // 在这里先判断是否可以调用支付
- -(void)isPay{
- // 判断用户是否禁止了苹果支付
- if ([SKPaymentQueue canMakePayments]) {
- // 1.获取产品信息列表
- [self requestProductData:PRODUCTID];
- }else{
- self.alertTitle = @"充值失败";
- self.alertMessage = @"您禁止了支付权限!";
- #if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_8_0
- // 不能进行支付进行提示
- [self.viewController presentViewController:self.alertController animated:YES completion:nil];
- #else
- [self.viewController.view addSubview:self.alertView];
- #endif
- }
- }
按照我们最开始时候的流程,接下来就需要我们去创建 SKProductsRequest 向苹果服务器请求商品的信息,具体的代码如下:
- - (void)requestProductData:(NSString *)productId{
- NSArray * productArray = [[NSArray alloc]initWithObjects:productId, nil];
- NSSet * productSet = [NSSet setWithArray:productArray];
- // 创建支付请求
- SKProductsRequest * productRequest = [[SKProductsRequest alloc]initWithProductIdentifiers:productSet];
- productRequest.delegate = self;
- [productRequest start];
- }
注意: 上面代码中的 productId 就是我们刚开始在开发者后台创建新的内购产品时候的产品ID,要是不理解的强烈建议先看第一篇文章,得知道什么是产品ID。
- #pragma mark -- SKRequestDelegate
- // 下面的方法会接收苹果服务器返回的商品的产品信息
- // Sent immediately before -requestDidFinish:
- - (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
- // 接收商品的信息
- NSArray * productInfo = response.products;
- if (productInfo.count ==0) {
- NSLog(@"接收到的商品的信息为空!");
- return;
- }else{
- // 打印出商品信息 以下的信息全都是在你开发者账号下面添加了内购项目中填写
- for (SKProduct *pro in productInfo) {
- NSLog(@"显示名称:%@", [pro localizedTitle]);
- NSLog(@"描述:%@", [pro localizedDescription]);
- NSLog(@"你设置的价格单位:%@", [pro price]); // 注意这里的单位,是用你在内购项目里面设置的定价
- NSLog(@"单位:%@", [pro.priceLocale objectForKey:NSLocaleCurrencySymbol]);
- NSLog(@"CNY:%@", [pro.priceLocale objectForKey:NSLocaleCurrencyCode]);
- NSLog(@"测试商品ID:%@", [pro productIdentifier]);
- }
- }
- // SKProduct对象包含了在App Store上注册的商品的本地化信息。
- SKProduct *storeProduct = nil;
- for (SKProduct * pro in productInfo) {
- if ([pro.productIdentifier isEqualToString:PRODUCTID]) {
- storeProduct = pro;
- }
- }
- //创建一个支付对象,并放到队列中
- self.skMutablePayment = [SKMutablePayment paymentWithProduct:storeProduct];
- //设置购买的数量 具体的交易金额就是这里的 数量 * 开发者账号定价
- if (self.payParments != 0) {
- //开始调用支付
- self.skMutablePayment.quantity = self.payParments;
- [[SKPaymentQueue defaultQueue] addPayment:self.skMutablePayment];
- // 开始一个内购监听
- [self startObserver];
- }else{
- NSLog(@"没有设置购买的数量!!");
- }
- }
这一步就走到我们接收到了商品的信息,接下来要做的事按照前面给的流程图,就需要我们开启一个内购的监听。再给大家看到你请求到的商品的基本信息,如下:
具体的上面的内容是什么,大家可以对比上面的输出的时候循环里面我加了它们各自的信息。
可以看到我们现在是添加了一个监控,开始监控和结束监控的代码如下:
- - (void)startObserver {
- if (!self.isObserver) {
- [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
- NSLog(@"开始监听 ------ 内购");
- self.isObserver = YES;
- }
- }
- - (void)stopObserver {
- if (self.isObserver) {
- [[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
- NSLog(@"移除监听 ------ 内购");
- self.isObserver = NO;
- }
- }
上面我们就开启了监听,也开始了支付,中间的怎样使用沙盒测试账号进行购买的测试,以及过程中需要注意的事项我们已经总结过了,可以看前面的文章。
等支付完成之后,我们就可以收到来自苹果支付结果的回调了,具体的回调处理下面的代码中有详细的注释:
- #pragma mark -- SKPaymentTransactionObserver
- //<SKPaymentTransactionObserver>千万不要忘记绑定,代码如下:
- //监听购买结果
- //[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
- - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray<SKPaymentTransaction *> *)transactions
- {
- SKPaymentTransaction *transaction = transactions.lastObject;
- switch (transaction.transactionState) {
- // 购买成功
- case SKPaymentTransactionStatePurchased: {
- NSData *data = [NSData dataWithContentsOfFile:[[[NSBundle mainBundle] appStoreReceiptURL] path]];
- // 刚才交易的内购的清单
- NSString *receipt = [data base64EncodedStringWithOptions:0];
- NSLog(@"刚才交易的内购的清单:%@",receipt);
- // 对交易结果进行二次验证
- [self verifyPruchase];
- }
- break;
- // 交易失败
- case SKPaymentTransactionStateFailed: {
- NSLog(@"交易失败");
- // 交易失败也要回调服务端
- // 将交易从交易队列中删除
- [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
- }
- break;
- case SKPaymentTransactionStateRestored: {
- NSLog(@"这是你已经购买过该商品!");
- [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
- }
- break;
- case SKPaymentTransactionStatePurchasing: {
- NSLog(@"商品添加进列表");
- }
- break;
- default: {
- NSLog(@"这是什么情况啊?");
- }
- break;
- }
- }
接下来在按照流程走的话就到二次验证支付结果的,其实我觉得这个过程放在移动端问题也不大,整个过程走Https,也没有什么关键的数据怕数问题,不像支付宝和微信支付签名的过程一样那么重要,不过需要注意的是验证这个结果得地址是分开的,也就是在开发测试阶段和上线阶段的地址是不同的,发送网络POST请求,对购买凭据进行验证:
测试验证地址:https://sandbox.itunes.apple.com/verifyReceipt
正式验证地址:https://buy.itunes.apple.com/verifyReceipt
下面是具体的支付结果的验证代码:
- #pragma mark 验证购买凭据
- - (void)verifyPruchase {
- // 验证凭据,获取到苹果返回的交易凭据
- // appStoreReceiptURL iOS7.0增加的,购买交易完成后,会将凭据存放在该地址
- NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
- // 从沙盒中获取到购买凭据
- NSData *receiptData = [NSData dataWithContentsOfURL:receiptURL];
- // 发送网络POST请求,对购买凭据进行验证
- //测试验证地址:https://sandbox.itunes.apple.com/verifyReceipt
- //正式验证地址:https://buy.itunes.apple.com/verifyReceipt
- NSURL *url = [NSURL URLWithString:@"https://sandbox.itunes.apple.com/verifyReceipt"];
- NSMutableURLRequest *urlRequest =
- [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10.0f];
- urlRequest.HTTPMethod = @"POST";
- NSString *encodeStr = [receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
- NSString *payload = [NSString stringWithFormat:@"{\"receipt-data\" : \"%@\"}", encodeStr];
- NSData *payloadData = [payload dataUsingEncoding:NSUTF8StringEncoding];
- urlRequest.HTTPBody = payloadData;
- // 提交验证请求,并获得官方的验证JSON结果 iOS9后更改了另外的一个方法
- NSData *result = [NSURLConnection sendSynchronousRequest:urlRequest returningResponse:nil error:nil];
- // 官方验证结果为空
- if (result == nil) {
- NSLog(@"交易验证失败");
- return;
- }
- // 二次验证返回,在这里给服务端返回验证结果
- NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:result options:NSJSONReadingAllowFragments error:nil];
- if (dict != nil) {
- // 比对字典中以下信息基本上可以保证数据安全
- // bundle_id , application_version , product_id , transaction_id
- NSLog(@"验证成功!购买的商品的信息是:%@", dict);
- // 在这里回调Lua文件支付的结果
- NSDictionary * receipt = dict[@"receipt"];
- // 在连续的交易中,会有多笔交易产生
- NSArray * in_app = receipt[@"in_app"];
- if (in_app.count !=0) {
- for (NSDictionary * dic in in_app) {
- // 订单号回调Lua
- NSString * transaction_id = dic[@"transaction_id"];
- [self toLuaFunc:self.handlerID backMsg:[transaction_id UTF8String]];
- }
- }
- }else{
- // 交易不成功,回调Lua
- NSString * transaction_id = @" ";
- [self toLuaFunc:self.handlerID backMsg:[transaction_id UTF8String]];
- }
- }
这样整个支付其实已经算是完成了的,看的仔细的朋友应该看到请求到回调结果之后我们OC回调Lua了,在这里成功之后我们是回调了Lua,在Lua俩面利用Socket向服务器去发送一个支付的确定的结果,下面我们说说Lua和OC的相互调用。
Lua 调用 OC
下面是自己在做的一个Lua文件和OC交互的一个大概的示意图,如下:
通过上面的一个示意图,在对比下面的代码,我们一句句的分析一下整个流程:
首先是第一步: Lua 文件调用 Bridge_ios 着重看一下下面几句代码:
- -- 点击跳转到苹果支付界面
- function Bridge_ios.presentApplePayWithParams(payParam, callback)
- --quantity 价格 callback回调
- local params = {quantity = payParam, scriptHandler = callback}
- local ok,ret = luaoc.callStaticMethod(BRIDGE_CLASS,"presentApplePayController",params)
- if not ok then
- -- 返回值
- print("luaj error:"..ret)
- end
- end
其实直接调用OC的就是 luaoc.callStaticMethod 这个方法,这个方法解释一下:
1、luaoc local luaoc = require "cocos.cocos2d.luaoc" 这个Lua引入就像OC 的#import 一样!
2、BRIDGE_CLASS 这个表示你和OC的那个类进行交互。
3、"presentApplePayController" 接下来的这个参数,就是你OC类里面写的类方法!
4、params 顾名思义就是参数的意思,Lua 这里传过去的是以 表 的形式,相信懂Lua 的你也清楚,什么是表!
再说说这个参数:看这一句 local params = {quantity = payParam, scriptHandler = callback}
我们把参数写成了表的形式,用OC理解牛把它当成一个字典!quantity 是键 payParam 就是你要传的值 , scriptHandler 是键,callback是值,只不过它是函数,OC需要回调的函数,具体的用法我们下面说回调的时候再说。
在上面的调用中:BRIDGE_CLASS 就是 AppController。
OC 回调 Lua
前面说完了Lua 调用OC,下面接着说说 OC是怎样回调Lua 的,具体的根据下面的代码解释:
- // Lua 回调函数
- - (void) toLuaFunc: (int)funC backMsg:(std::string)msg{
- #if CC_ENABLE_SCRIPT_BINDING
- int handler = funC;
- if (-1 != handler)
- {
- auto sc = cocos2d::Director::getInstance()->getScheduler();
- sc->performFunctionInCocosThread([handler, msg](){
- cocos2d::LuaBridge::pushLuaFunctionById(handler);
- cocos2d::LuaStack *stack = cocos2d::LuaBridge::getStack();
- stack->pushString(msg.c_str());
- stack->executeFunction(1);
- cocos2d::LuaBridge::releaseLuaFunctionById(handler);
- });
- }
- #endif
- }
最后我们一句一句的解释一下上面这个OC回调Lua的过程:
LuaBridge::pushLuaFunctionById(handlerID); //压入需要调用的方法id(假设方法为XG)
LuaStack *stack = LuaBridge::getStack(); //获取lua栈
stack->pushString("oc call lua method..."); //将需要通过方法XG传递给lua的参数压入lua栈,这里也就是设置OC回调给Lua的参数
stack->executeFunction(1); //根据压入的方法id调用方法XG,并把XG方法参数传递给lua代码
LuaBridge::releaseLuaFunctionById(handlerID); //最后记得释放一下function
上面的这整个过程,再结合我们第一篇文章写得苹果内购的图文详解流程,基本上一个完整的游戏添加内购的过程就算是结束了,要是有什么问题可以在我的主页找我的QQ或者下面留言给我!!
Cocos 2d-X Lua 游戏添加苹果内购(一) 图文详解准备流程
Cocos 2d-X Lua 游戏添加苹果内购(二) OC和Lua交互代码详解的更多相关文章
- Cocos 2d-X Lua 游戏添加苹果内购(一) 图文详解准备流程
事前准备 最近给游戏添加了苹果的内购,这一块的东西也是刚刚做完,总结一下,其实这里不管是游戏还是我们普通的App添加内购这一块的东西都是差不多的,多出来的部分就是我们Lua和OC的交互的部分,以前刚开 ...
- 添加“返回顶部”小图标按钮的JS(JavaScript)代码详解
如何给自己的网站添加方便快捷的"返回顶部"小图标按钮呢?如下图: JS源代码: /** * JavaScript脚本实现回到页面顶部示例 * @param acceleration ...
- 苹果内购服务器验证之receipt返回多组in_app思考
最近有部分用户反映,苹果内购充值失败,经过测试总结有几个关键点出现问题 1.app购买成功苹果没有返回票据,属于票据遗漏(取决于苹果服务器的响应状况),只能客户端进行监听刷新等处理 2.app连续购买 ...
- iOS:苹果内购实践
iOS 苹果的内购 一.介绍 苹果规定,凡是虚拟的物品(例如:QQ音乐的乐币)进行交易时,都必须走苹果的内购通道,苹果要收取大约30%的抽成,所以不允许接入第三方的支付方式(微信.支付宝等),当然开发 ...
- iOS开发苹果内购的介绍与实现
1.iOS开发苹果内购的介绍 1.1 介绍 苹果规定,凡是虚拟的物品(例如:QQ音乐的乐币)进行交易时,都必须走苹果的内购通道,苹果要收取大约30%的抽成,所以不允许接入第三方的支付方式(微信.支付宝 ...
- 苹果内购和 Apple Pay
作者:CC老师_MissCC链接:http://www.jianshu.com/p/e3bc47e81785來源:简书 苹果内购 1.什么是内购? 如果你购买的商品,是在本app中使用和消耗的,就一定 ...
- apicloud含有微信支付。支付宝支付和苹果内购的代码
apicloud含有微信支付.支付宝支付和苹果内购的代码 <!DOCTYPE html> <html> <head> <meta charset=" ...
- 苹果IOS内购二次验证返回state为21002的坑
项目是三四年前的老项目,之前有IOS内购二次验证的接口,貌似很久都没用了,然而最近IOS的妹子说接口用不了,让我看看啥问题.接口流程时很简单的,就是前端IOS在购买成功之后,接收到receipt后进行 ...
- IOS,苹果内购和添加广告
内购——应用内购买 通过苹果应用程序商店有三种主要赚钱的方式: 直接收费(与国内大部分用户的消费习惯相悖) 广告(降低用户体验 应用程序名称带Lite可以添加广告) O2O -> Online推 ...
随机推荐
- 201521123045 《Java程序设计》 第10周学习总结
---恢复内容开始--- 1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结异常与多线程相关内容. 2. 书面作业 1.finally 题目4-21.1 截图你的提交结果(出现学号) ...
- 聊聊React高阶组件(Higher-Order Components)
使用 react已经有不短的时间了,最近看到关于 react高阶组件的一篇文章,看了之后顿时眼前一亮,对于我这种还在新手村晃荡.一切朝着打怪升级看齐的小喽啰来说,像这种难度不是太高同时门槛也不是那么低 ...
- Hibernate第三篇【主配置文件、映射文件、复合主键映射】
前言 目前已经学了如何搭建Hibernate的开发环境,以及Hibernate对应的API了-在快速入门还没讲解的就是配置文件是怎么配置的.因此,本博文主要讲解主配置文件以及映射配置文件.. 主配置文 ...
- PHP命令注入笔记
一.PHP命令注入介绍 在学习php相关的攻击时,遇到了Command Injection,即命令注入攻击,是指这样一种攻击手段,黑客通过把HTML代码输入一个输入机制(例如缺乏有效验证限制的表格域) ...
- MVC查询数据接收及校验
本来想写一篇aspx的TreeView控件绑值的文章的,在写案例的时候,写了一半,发现有些地方还得考虑以下,就留待下次了. 这一篇的话,是最近在开发一个项目的时候,有大量的页面和数据表,需要花式查询, ...
- ajax 发送json 后台接收 遍历保存进数据库
前台怎么拿参数的我就不管了我也不会 反正用这个ajax没错 ajax 代码 一定要写明http请求类型 { contentType:"application/x-www-form-ur ...
- 一次生产环境下MongoDB备份还原数据
最近开发一个版本的功能当中用到了MongoDB分页,懒于造数据,于是就研究了下从生产环境上导出数据到本地来进行测试. 研究了一下,发现MongoDB的备份还原和MySQL语法还挺类似,下面请看详细介绍 ...
- 富文本编辑器CKEditor的使用
由于最近在架构一个pc端b/s结构的项目,项目中有个论坛模块,当用户发帖时,需要用到富文本编辑器,考虑了一下,决定使用CKEditor富文本编辑器,虽然现在问世的富文本编辑器很丰富,比如还有百度的UE ...
- 轻量级文本编辑器,Notepad最佳替代品:Notepad++
目录 正文之前 1. 目的 2. 原帖 3. 为何推荐Notepad++ 3.1. Notepad++的一些基本特点 3.2. notepad,notepad2,notepad++,ultraEdit ...
- cookie存储中文
写cookie Cookie chineseCookie = new Cookie( "chineseCookie ", URLEncoder. ...