(转)Cocos 2d-X Lua 游戏添加苹果内购(二) OC和Lua交互代码详解

这是第二篇



Cocos 2d-X Lua 游戏添加苹果内购(一) 图文详解准备流程

这是前面的第一篇,详细的说明了怎样添加内购项目以及填写银行信息提交以及沙盒测试员的添加使用以及需要我们注意的东西,结果,被移除首页了!前面第一篇的内容是这篇的基础,前面那些不弄好,下面的商品信息你是请求不到的,这点需要大家特别注意...有需要前面提到的内容的孩子可以点击链接进去自己看看!!

这篇就具体的总结我们Lua和OC交互的内容以及内购具体的代码以及结果的测试说明:

内购部分OC的代码实现



先自己总结一下整个支付的流程,下面的代码部分我们也就按照这个支付流程来解读:

最开始你首先要做的就是给你的项目添加: StoreKit.framework 框架

以及在你需要写支付的类中导入: #import <StoreKit/StoreKit.h>

并且你还得遵守 <SKPaymentTransactionObserver,SKProductsRequestDelegate > 两个协议,后面会实现他们相应的代理方法。

接下来你初始化了你支付类需要你初始化的东西之后,就开始判断用户有没有禁止了苹果支付,代码如下:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

// 在这里先判断是否可以调用支付

-(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 向苹果服务器请求商品的信息,具体的代码如下:


1

2

3

4

5

6

7

8

9

10

- (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。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

#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(@"没有设置购买的数量!!");

    }

}

这一步就走到我们接收到了商品的信息,接下来要做的事按照前面给的流程图,就需要我们开启一个内购的监听。再给大家看到你请求到的商品的基本信息,如下:

具体的上面的内容是什么,大家可以对比上面的输出的时候循环里面我加了它们各自的信息。

可以看到我们现在是添加了一个监控,开始监控和结束监控的代码如下:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

- (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;

    }

}

上面我们就开启了监听,也开始了支付,中间的怎样使用沙盒测试账号进行购买的测试,以及过程中需要注意的事项我们已经总结过了,可以看前面的文章。

等支付完成之后,我们就可以收到来自苹果支付结果的回调了,具体的回调处理下面的代码中有详细的注释:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

#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

下面是具体的支付结果的验证代码:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

#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  着重看一下下面几句代码:


1

2

3

4

5

6

7

8

9

10

11

-- 点击跳转到苹果支付界面

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 的,具体的根据下面的代码解释:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

// 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或者下面留言给我!!

原文地址:https://www.cnblogs.com/zhangxiaoxu/p/7729693.html

原文地址:https://www.cnblogs.com/wodehao0808/p/12654843.html

时间: 2024-10-11 05:47:34

(转)Cocos 2d-X Lua 游戏添加苹果内购(二) OC和Lua交互代码详解的相关文章

(转)Cocos 2d-X Lua 游戏添加苹果内购(一) 图文详解准备流程

事前准备 最近给游戏添加了苹果的内购,这一块的东西也是刚刚做完,总结一下,其实这里不管是游戏还是我们普通的App添加内购这一块的东西都是差不多的,多出来的部分就是我们Lua和OC的交互的部分,以前刚开始做它们交互的部分的时候,准备总结一下Lua和OC的交互的,就是它们相互的调用和传参,在这两篇文章中顺便就一起总结了.要准备内购的事情首先你要有一个开发者账号!当然是加入开发者的账号,不是一个Apple ID 登录Developer之后就OK的,这点相信做iOS的小伙伴都清楚,就不再累赘啦!下面我们

苹果内购二次验证 PHP代码

<?php //用户发来的参数 $receipt_data = $_POST['receipt-data']; //验证参数 if (strlen($receipt_data)<20){ $echo = array('buy'=>'0','message'=>'非法参数'); exit(json_encode($echo)); } //请求验证 $html = acurl($receipt_data); $data = json_decode($html,1); //如果是沙盒数据

关于苹果内购(IAP)的一些问题以及那些坑

最近在研究苹果内购功能,所以,在网上找了一些资料,进行学习.但是,内购功能在实现的过程中,有很多坑,笔者算是真的遇到了好多啊,下面也是自己对内购的一些心得与体会吧! 我这里说的可能不太详尽,所以,我先把再网上看到的一些帖子贴在这里,以便大家做内购的时候,方便查找相关信息. 这里是一篇写的比较全面的帖子,但是没有写中间问题处理: <iOS开发内购全套图文教程> 在网上搜了一些相关的帖子,简单归纳总结了一下,觉得论坛里有一个叫Teng的世界的大神,写了三篇博客,写的很详细: [IAP支付之一]In

苹果内购流程详解

苹果内购主要分为两部分 itunnes 相关配置 一.在itunnes 上配置相关信息资料 首先需要配置一些前提条件,详细步骤如下 1.进入itunnes选择协议,税务银行业务 2.完善资料信息,主要有3项(联系人,银行信息,税务信息) 4.完善联系人信息,一般填申请开发者账号人员的信息,可以修改 5.完善银行信息 需要银行支付号(就是开户银行的一个标识),银行卡持有人名字 6.完善税务信息 7.以下默认选择no 8.确认界面,下图可能不太对,当时我是上下2个界面,第四项要上下一致才行 二.上面

苹果内购和 Apple Pay

作者:CC老师_MissCC链接:http://www.jianshu.com/p/e3bc47e81785來源:简书 苹果内购 1.什么是内购? 如果你购买的商品,是在本app中使用和消耗的,就一定要用内购,否则会被拒绝上线,例如:游戏币,在线书籍,app中使用的道具等虚拟产品.如果购买的就是普通的商品,例如淘宝买东西等,就不需要用内购.内购的话,苹果公司需要抽取30%佣金. 当然,打赏功能被纳入内购项目中.所以例如微信打赏功能.直播项目打赏主播都必须采用内购. 可以简单理解成,带有内购功能的

苹果内购服务器验证之receipt返回多组in_app思考

最近有部分用户反映,苹果内购充值失败,经过测试总结有几个关键点出现问题 1.app购买成功苹果没有返回票据,属于票据遗漏(取决于苹果服务器的响应状况),只能客户端进行监听刷新等处理 2.app连续购买的过程中,前几次苹果没有返回票据,几次之后,苹果返回了一个有效的票据,app提交给服务器进行验证的过程中in_app出现多组数据的情况,这种情况还是能充值成功了,只是不能全部到账 3.app连续购买,有一次正常返回票据,在提交给服务器的过程中出现意外,但实际服务端已经接受到票据,为用户成功充值,但a

苹果内购艰辛审核之路

开发: 具体的步骤及代码就不说了,网上有很多类似的文章大家可以看一下,具体就是分为两大不走: (1)去苹果后台注册银行卡信息,添加内购项目 (2)就是代码中集成内购 我今天要说的主要是在你要做上面两步的内容的时候需要注意的事情 首先在做第一步操作的时候注册银行卡信息基本上不会有太大的问题这边可以随便网上搜一个教程按照步骤走就行了,在添加内购项目的时候需要注意几点: (a)描述内购的时候千万不要用多少人民币来描述举个列子: 假如你要添加一个一元的内购产品,一般的产品都会这样子描述内购 后面化掉的内

iOS:苹果内购实践

iOS 苹果的内购 一.介绍 苹果规定,凡是虚拟的物品(例如:QQ音乐的乐币)进行交易时,都必须走苹果的内购通道,苹果要收取大约30%的抽成,所以不允许接入第三方的支付方式(微信.支付宝等),当然开发者可以设置后门,在审核时避开审核人员.这个是有风险的,一旦发现,app会被立即下架,还是老老实实接入内购吧. 二.注意 内购接入还是比较简单的,苹果提供了专门的框架<StoreKit/StoreKit.h>,只要按照它提供的api进行开发就行.然而,接入的过程还是有需要注意的地方,分别是:漏单处理

苹果内购支付对接

public bool ValidateApplePay() { //客户端post过来的参数 string appleReceipt = Request.Form["appleReceipt"]; //苹果内购的验证收据 string orderId = PayHelper.GetOrderIDByPrefix("AP");  //订单编号 string amount = Request.Form["amount"];