前阵子忙着赶项目没什么时间做总结,前两个星期iOS审核通过,项目也顺利在北美上线了。刚好公司组织旅游回来,抽空总结写Unity iOS 内付费的接入。

1、前置条件:

(1)    苹果开发者账号(99美刀一年,没有的话可以在某宝上买个p12正式,自己搞搞开发还是比较合算);

(2)    创建好应用的bundle ID 及相关开发证书和描述文件;

(3)    Itunes Connect 创建对应的App及设置好内购的商品。

2、内购商品创建

因为最近苹果开发者中心又各种改革,创建流程和在网上很多教程都不一样,防止接入者采坑,这里简单的描述下:

(1)创建App:打开itunes connect -> My App -> + (左上角的加号) ,然后填写相关信息,比如bundleID(套装 ID),版面号等,SKU码随便填就好了,点击创建完成

(2)点击你刚创建好的App,选择 app 内购买项目(In app purchase),事先要设置好收款的信用卡账户,否则系统会提示你去设置。进入App 内购设置项后,点击Create New,选择Consumable (消耗型项目),填写相关信息就好了,ProductID(产品ID)必须唯一,最后添加语言和截屏就完成了,下面我们进入编程阶段。

3、Unity与Object-C交互

(1)    Unity和Object-C的交互:Unity官方提供一种交互方式,采用C++ Object-C混编的模式,Unity通过C++ 调用Object-C代码,从而实现Unity与Object-C的交互;

(2)    创建交互的.h 和.mm文件,可能有些人不知怎么在Xcode中创建.mm文件,最简单的方法就是创建.m文件后重命名为.mm文件;

(3)    为了简化Unity和Object-C的交互,我们使用单例的方式实现,最后我们仅需把AppStorePayForUnity.h 和AppStorePayForUnity.mm两个文件放在Unity的Plugins/iOS目录下即可,Unity导出工程后会添加这两个文件。

4、核心实现

(1)   单例创建::在工程中引入storekit.framework 和 #import <StoreKit/StoreKit.h>,并添加SKProductsRequestDelegate 和SKPaymentTransactionObserver两个委托,用于监听购买的回调,具体代码如下:

 1 // AppStorePayForUnity.h
 2 #ifndef _AppStorePayForUnity_h
 3 #define _AppStorePayForUnity_h
 4
 5 #import <StoreKit/StoreKit.h>
 6
 7 @interface AppStorePayForUnity : NSObject <SKProductsRequestDelegate, SKPaymentTransactionObserver>
 8 { 9 }
10
11 @property (nonatomic, retain) NSString *mCallBackObjectName;
12 @property (nonatomic, retain) NSString *mServerId;
13 @property (nonatomic, retain) NSString *mOrderId;
14 @property (nonatomic, retain) NSString *mExInfo;
15
16 + (AppStorePayForUnity*) instance;
 1 // AppStorePayForUnity.h
 2 #import <Foundation/Foundation.h>
 3 #import "AppStorePayForUnity.h"
 4
 5 #ifndef __APPSTORE_IN_UNITY__
 6 #define __APPSTORE_IN_UNITY__
 7 #endif
 8
 9 #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000
10 #define IOS7_SDK_AVAILABLE 1
11 #endif
12
13 #if defined(__cplusplus)
14 extern "C" {15 #endif
16     extern void       UnitySendMessage(const char* obj, const char* method, const char* msg);
17     extern NSString*  AppStoreCreateNSString (const char* string);
18 #if defined(__cplusplus)
19 }
20 #endif
21
22 static AppStorePayForUnity* _instance = nil;
23
24 @implementation AppStorePayForUnity
25
26 @synthesize mCallBackObjectName;
27 @synthesize mServerId;
28 @synthesize mOrderId;
29 @synthesize mExInfo;
30
31 //使用同步创建 保证多线程下也只有一个实例
32 + (AppStorePayForUnity *)instance
33 {34     @synchronized(self)
35     {36         if (_instance == nil)
37         {38             _instance = [[AppStorePayForUnity alloc] init];
39         }
40     }
41     return _instance;
42 }
43
44 - (id)init
45 {46     self = [super init];
47
48     if (self)
49     {50         // 监听购买结果
51         [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
52     }
53     return self;
54 }
55
56 -(void) dealloc
57 {58     [[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
59     self.mCallBackObjectName = nil;
60     [super dealloc];
61 }

(2)Unity必须事先初始化,设置好回调的GameObject对象,用于传递支付信息到Unity游戏中。

 1 //初始化 设置回调的对象
 2 - (void)initAppStorePay:(NSString*)callBackName
 3 { 4     self.mCallBackObjectName = callBackName;
 5 }

(3)当用户点击了一个IAP项目,我们先查询用户是否允许应用内付费,如果不允许则不用进行以下步骤了。代码如下:

 1 //是否有购买权限
 2 - (BOOL)canMakePay
 3 { 4     return [SKPaymentQueue canMakePayments];
 5 }

(4)    我们先通过该IAP的ProductID向AppStore查询,获得SKPayment实例,然后通过SKPaymentQueue的 addPayment方法发起一个购买的操作

 1 // 开始购买商品
 2 - (void)startBuyProduct:(NSString *)serverId orderId:(NSString *)orderId exInfo:(NSString *)exInfo productId:(NSString*)productId
 3 { 4     if (self.canMakePay)
 5     { 6         NSLog(@"-------------- 开始购买 --------------");
 7         self.mServerId = serverId;
 8         self.mOrderId = orderId;
 9         self.mExInfo = exInfo;
10         [self getProductInfoById:productId];
11     }
12     else
13     {14         NSLog(@"------------ App不用允许内购 --------------");
15     }
16 }
17
18 // 下面的ProductId应该是事先在itunesConnect中添加好的,已存在的付费项目。否则查询会失败。
19 -(void)getProductInfoById:(NSString*)productID
20 {21     NSLog(@"----getProductInfoById---------id: %@", productID);
22     NSArray *product = nil;
23     product = [[NSArray alloc] initWithObjects:productID, nil];
24     NSSet *set = [NSSet setWithArray:product];
25     SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:set];
26     //设置并启动监听
27     request.delegate = self;
28     [request start];
29     //[product rele];
30 }
31
32
33 // 以上查询的回调函数
34 - (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
35 {36     NSLog(@"收到商品反馈");
37     NSArray *myProduct = response.products;
38     if (myProduct.count == 0)
39     {40         NSLog(@"无法获取产品信息,购买失败。");
41         return;
42     }
43
44     // test
45     for(SKProduct *temp in myProduct)
46     {47         NSLog(@"ProductInfo");
48         NSLog(@"SKProduct 描述信息%@", [temp description]);
49         NSLog(@"Product id: %@ 价格%@", temp.productIdentifier, temp.price);
50     }
51
52     NSLog(@"发送购买请求");
53     SKPayment * payment = [SKPayment paymentWithProduct:myProduct[0]];
54     [[SKPaymentQueue defaultQueue] addPayment:payment];
55     NSLog(@"-------------------%@", payment);
56 }

(5)    购买结果监听:当苹果服务器返回购买结果时回自动调用paymentQueue方法:

 1 - (void) paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
 2 { 3     NSLog(@"------- payment Queue -----");
 4     for (SKPaymentTransaction *transaction in transactions)
 5     { 6         switch (transaction.transactionState)
 7         { 8             case SKPaymentTransactionStatePurchased://交易完成
 9                 NSLog(@"transactionIdentifier = %@", transaction.transactionIdentifier);
10                 [self completeTransaction:transaction];
11                 break;
12             case SKPaymentTransactionStateFailed://交易失败
13                 [self failedTransaction:transaction];
14                 break;
15             case SKPaymentTransactionStateRestored://已经购买过该商品
16                 [self restoreTransaction:transaction];
17                 break;
18             case SKPaymentTransactionStatePurchasing://商品添加进列表
19                 NSLog(@"商品添加进列表");
20                 break;
21             default:
22                 break;
23         }
24     }
25 }
26
27 // 支付成功
28 - (void) completeTransaction:(SKPaymentTransaction*)transaction
29 {30     NSLog(@"--------------completeTransaction--------------");
31     // Remove the transaction from the payment queue.
32     [[SKPaymentQueue defaultQueue] finishTransaction: transaction];
33
34     // Your application should implement these two methods.
35     NSString * productIdentifier = transaction.payment.productIdentifier;
36
37     if([productIdentifier length] > 0)
38     {39         NSLog(@"productIdentifier : %@", productIdentifier);
40     }
41
42     // 向自己的服务器发送购买凭证
43     [self checkReceiptToServer:transaction];
44 #ifdef __APPSTORE_IN_UNITY__
45     // 通知 unity 购买成功
46     UnitySendMessage(self.mCallBackObjectName.UTF8String,
47                      "DebugUnityMessage", "BuySuccess");
48     UnitySendMessage(self.mCallBackObjectName.UTF8String,
49                      "BuyProductSuccess", productIdentifier.UTF8String);
50 #endif
51 }
52
53 // 支付失败
54 - (void) failedTransaction:(SKPaymentTransaction*)transaction
55 {56     if(transaction.error.code != SKErrorPaymentCancelled)
57     {58         NSLog(@"购买失败");
59 #ifdef __APPSTORE_IN_UNITY__
60         UnitySendMessage(self.mCallBackObjectName.UTF8String,
61                          "DebugUnityMessage", "购买失败");
62 #endif
63     }
64     else
65     {66         NSLog(@"用户取消交易");
67 #ifdef __APPSTORE_IN_UNITY__
68         UnitySendMessage(self.mCallBackObjectName.UTF8String,
69                          "DebugUnityMessage", "用户取消交易");
70 #endif
71     }
72     [[SKPaymentQueue defaultQueue] finishTransaction: transaction];
73 #ifdef __APPSTORE_IN_UNITY__
74     UnitySendMessage(self.mCallBackObjectName.UTF8String,
75                      "BuyProudctFailed", "购买失败");
76 #endif
77 }
78
79 // 对于已购商品,处理恢复购买的逻辑
80 - (void) restoreTransaction:(SKPaymentTransaction*)transaction
81 {82     [[SKPaymentQueue defaultQueue] finishTransaction: transaction];
83 #ifdef __APPSTORE_IN_UNITY__
84     UnitySendMessage(self.mCallBackObjectName.UTF8String,
85                      "DebugUnityMessage", "恢复已购商品");
86 #endif
87 }
88
89 - (void) checkReceiptToServer:(SKPaymentTransaction*)transaction
90 {91     NSString *transactionId = transaction.transactionIdentifier;
92     NSLog(@"-----transactionId--------- %@", transactionId);
93
94     NSData *receipt = nil;
95     NSString *strVersion = nil;
96
97 #if IOS7_SDK_AVAILABLE
98     NSLog(@"---------------------SDK 7.1 ---------------");
99     NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
100     receipt = [NSData dataWithContentsOfURL:receiptURL];
101     strVersion = @"iOS7";
102 #else
103     NSLog(@"---------------------SDK 6.1 ---------------");
104     receipt = transaction.transactionReceipt;
105     strVersion = @"iOS6";
106 #endif
107
108
109     if(!receipt)
110     {111         //no local receipt -- handle the error
112
113     }
114
115     //NSLog(@"receipt data is :%@",receipt);
116     NSError *error;
117     //这个是发给appstore服务端的
118     NSDictionary *requestContents = @{119                                       @"receipt-data": [receipt base64EncodedStringWithOptions:0],
120                                       };
121
122     NSData *requestData = [NSJSONSerialization dataWithJSONObject:requestContents options:0 error:&error];
123
124     //TODO:服务端验证
125     //…….
126 }

5、至此iOS内付费接入已经完成了,附上测试截图一张,详细代码见网盘:http://pan.baidu.com/s/1o60snFk

参考链接:

1、http://blog.devtang.com/blog/2012/12/09/in-app-purchase-check-list/

2、http://game.dapps.net/gamedev/in-app-purchase/3080.html

3、http://www.himigame.com/iphone-cocos2d/550.html

4、http://www.xuanyusong.com/archives/521

Unity游戏iOS AppStore 内付费接入(In app purchase)相关推荐

  1. iOS应用内购买(In App Purchase)总结

    先附上几篇文章: 1.In App Purchases: A Full Walkthrough 这篇文章里说的都很详尽了,代码什么的基本可以照搬. 2.Store Kit Guide(In App P ...

  2. iOS应用内付费(IAP)开发步骤列表

    iOS应用内付费(IAP)开发步骤列表 前两天和服务端同事一起,完成了应用内付费(以下简称IAP, In app purchase)的开发工作.步骤繁多,在此把开发步骤列表整理如下.因为只是步骤列表, ...

  3. iOS应用内付费详解

    Himi  原创, 欢迎转载,转载请在明显处注明! 谢谢. 原文地址:http://blog.csdn.net/xiaominghimi/article/details/6937097 //--201 ...

  4. 如何使得客户端和服务器端完美配合做IOS应用内付费

    配置Developer.apple.com 登录到Developer.apple.com,然后进行以下步骤: 为应用建立建立一个不带通配符的App ID 用该App ID生成和安装相应的Provisi ...

  5. IOS 应用内打开三方地图app直接导航(高德坐标)

    疯狂试探 - (BOOL)canOpenURL:(NSURL *)url NS_AVAILABLE_IOS(3_0); 常用地图应用的url Scheme: //百度地图 baidumap //高德地 ...

  6. iOS应用内置付费详尽攻略

    我最近正在制作的一个程序里面,我就决定先把程序免费(其中只包含一个故事),然后把更多的故事放在in-app purchase里面.在这篇教程里面,你将会学到如何使用程序内置付费来解琐本地程序里面的内容 ...

  7. ios应用内支付过程(使用苹果原生支付方式

    因为ios政策问题,如果开发者需要在ios客户端中加入购买(虚拟货币)项目,需要使用ios应用内付费IAP这种方式,这也是很多苹果APP不和安卓共通的原因.因为苹果需要抽取%30的利益,详细原因就不赘 ...

  8. 虚拟内购服务器,苹果APP内购客户付款成功,没收到相应虚拟产品的解决办法

    一.引导用户走申请苹果的退款 1.告知用户新版本可以使用支付宝.微信支付,更划算 2.苹果可申请90天以内的退款,一般情况申请后48小时内就有反馈. 二.用户坚持补偿虚拟产品 1.如果是今天的交易,让 ...

  9. Cocos2d-x使用iOS游戏内付费IAP(C++篇)

    source file url: http://www.tairan.com/archives/5515 Cocos2d-x使用iOS游戏内付费IAP(C++篇) 前期准备 设备与账号 在开始编码之前 ...

  10. 如何将Unity游戏构建到iOS设备

    介绍 本文章将构建一个示例Unity项目到iOS设备进行测试( 非常详细,简直细到了每个操作,每个情况),原文出自Unity官网教程 Building your Unity game to an iO ...

最新文章

  1. linux下压缩并分割稀疏文件
  2. server 2008访问Server 2003数据慢
  3. SVN的使用(服务端与客户端)
  4. Dell在CRM的创新及该模式的推广
  5. UVa712 S-Trees满二叉树
  6. docker 相关操作
  7. SAP License:CO07利润中心必输
  8. Clustered Data ONTAP Fundamentals课程学习(1)
  9. 六个建议防止SQL注入式攻击
  10. 【LKA】国内车道相关数据
  11. 用费曼学习法学习费曼学习法-读《世界上最好的学习法:费曼学习法》收获
  12. c# 试图加载格式不正确的程序。 (异常来自 HRESULT:0x8007000B)
  13. 计算机科学与技术陈梦如,淮南师范学院马克思主义学院文件.doc
  14. JAVA实现简单的猜数字游戏
  15. guid备份分区表crc错误_硬盘GPT和MBR分区表转换方法
  16. CityMaker学习教程10 示例代码的使用Javascript
  17. 跟ChatGPT,聊聊ChatGPT
  18. [大话设计模式C++版] 第12章 牛市股票还会亏钱 —— 外观模式
  19. 如何在深度学习过程中使用预训练的词表征(持续更新ing...)
  20. p标签内如何让内容换行

热门文章

  1. EXCEL 常用的宏代码大全
  2. uni-app请求后台接口方法封装
  3. hcia第五天 结课
  4. 默 of 2018:年终总结
  5. html打印成功回调,web前端打印实现
  6. 在Java中基于mysql驱动包连接MySQL数据库
  7. 华为智能体发布,智能联接火了
  8. sja1000 can控制器波特率计算方法
  9. 服务器主板能插几块硬盘,如何知道自己的主板最大支持多少的硬盘啊
  10. 三类医疗器械ERP系统软件排行榜