苹果ID支付怎么更改支付啊!ID里面百度有钱信用支付。为什么支付不了。老是显示信息错误

7037人阅读
IAP/GameCenter(6)
1. 适用情况
想使用In-App Purchase(以下简称IAP)完成App内付费前,先确定需求是不是能用这个方案来满足。
除了IAP以外,还有支付宝SDK、信用卡等其他方式完成软件内付费。
在苹果制定的游戏规则中,所有在App内提供的服务需要付费时,都应当使用IAP,比如软件功能、游戏道具;所有在App外提供的服务需要付费时,都应使用其他支付方式,比如Uber的信用卡,淘宝、快的打车的支付宝SDK等。
在IAP里,可以出售:
数字内容:比如杂志、图片、游戏关卡解锁、相机付费滤镜等;软件功能:如各种扩展features;一次性服务:比如一次语音通话等。注意是「一次性」服务,不是一次「性服务」。
在IAP里,不能出售:
现实世界的商品或服务:比如刚才提到的一次「性服务」。严格遵守此方案有个好处:IAP 如果被破解,用户无法得到大量实物,开发商也不会有很大经济损失。非要做的话想绕过也是可以的:用 IAP 购买代币,审核通过后用代币购买实物。其他中规定的不允许的内容:比如刚才提到的一次「性服务」。
顺便说下,有次大网易的同事分享时提到:使用兑换码兑换App内服务是一条高压线。像Uber和Amazon里允许有码,是因为他们的码是用在现实世界的产品或服务上的。
如果你确定内购需求符合IAP的使用要求,可以继续往下读了。
2. 购买及发放虚拟产品流程
官方给出的流程图是这样的:
获取内购列表(从App内读取或从自己服务器读取)App Store请求可用的内购列表向用户展示内购列表用户选择了内购列表,再发个购买请求收到购买完成的回掉发放虚拟产品
3.虚拟产品
虚拟产品的分类
虚拟产品分为以下几种类型:
消耗品(Consumable products):比如游戏内金币等。不可消耗品(Non-consumable products):简单来说就是一次购买,终身可用(用户可随时从App Store restore)。自动更新订阅品(Auto-renewable subscriptions):和不可消耗品的不同点是有失效时间。比如一整年的付费周刊。在这种模式下,开发者定期投递内容,用户在订阅期内随时可以访问这些内容。订阅快要过期时,系统将自动更新订阅(如果用户同意)。非自动更新订阅品(Non-renewable subscriptions):一般使用场景是从用户从IAP购买后,购买信息存放在自己的开发者服务器上。失效日期/可用是由开发者服务器自行控制的,而非由App Store控制,这一点与自动更新订阅品有差异。免费订阅品(Free subscriptions):在Newsstand中放置免费订阅的一种方式。免费订阅永不过期。只能用于Newsstand-enabled apps。
类型2、3、5都是以Apple ID为粒度的。比如小张有三个iPad,有一个Apple ID购买了不可消耗品,则三个iPad上都可以使用。
类型1、4一般来说则是现买现用。如果开发者自己想做更多控制,一般选4。
几种产品的区别如下(表格懒得翻译了):
Table 1-1 Comparison of product types
Product type
Non-consumable
Consumable
Users can buy
Multiple times
Appears in the receipt
Synced across devices
By the system
Not synced
By the system
Not restored
Table 1-2 Comparison of subscription types
Subscription type
Auto-renewable
Non-renewing
Users can buy
Multiple times
Multiple times
Appears in the receipt
Synced across devices
By the system
By your app
By the system
By the system
By your app
By the system
关于自动更新订阅品更新周期组(Auto-Renewable Subscription Duration Families):
每种订阅品的每种更新周期可以在iTunes Connect中设置一个单独的价格。图中给出了一种订阅品的不同长度的更新周期的价格截图:
你可以把每种订阅品的每个长度的更新周期看成一个单独的产品,每个产品有自己独有的时长、价格、市场促销属性。
为了让用户可以从中挑选一个偏好的更新周期,上图中我们为此种订阅的每个长度的更新周期分别设值了一个单独的价格,有一周的、一个月的、两个月的、季度的、半年的和一年的。
上图中这种订阅品的全部六种更新周期合起来称为一个自动更新订阅品更新周期组(Auto-Renewable Subscription Duration Families)。
4. 人肉和iTunes Connect交互
填写银行卡与纳税信息
登录点击Agreements, Tax, and Banking,填写Contact Info, Bank Info, Tax Info。如果填过就不用再填了。刚刚填写完成后,各种信息正在审核,如下图:
即使信息正在审核,沙箱环境下也是可以访问IAP服务的,并不需要等审核完成才能测试。
新建虚拟产品
登录点击My Apps进入想使用IAP的App详情选择In-App Purchases
点击Create New选择IAP虚拟产品类型。注意虚拟产品一旦新建,类型无法修改。
填入Internal Name。只能在iTunes Connect中看到这个名字。不会出现在App Store中。最长255字节。
填入Product ID。每件产品有一个单独的Product ID,Product
ID用于从App Store获取价格信息,以及付费时标识是哪种产品被购买了。例如com.163.neteasemusic.skin.dog。这个新建之后也是不能修改的。
然后是设置价格。Cleared For Sale选为YES是虚拟产品被审核通过自动上架。NO是手动上架。Price Tier则是价格。
多语言描述,这个是给用户看的。
是否要在iTunes托管可购买内容,这个后面再说
填入review notes和review用的截图。
保存。保存后除了类型和Product ID都可以修改
新建完,不用等待苹果审核就可以在沙箱环境使用了。
新建测试帐号
登录点击Users and Roles点击Sandbox Testers添加Tester。添加后在测试机上用tester帐号登录app store
附:在苹果托管不可消耗品(Non-consumable products)的内容需知
托管内容仅限于针对不可消耗品。
首次创建不可消耗品时可以选择把内容托管到苹果服务器,当然也可以随时将自己服务器上的内容迁移到苹果服务器由苹果托管。
需要使用托管功能的话,首先在iTunes Connect中提交不可消耗品让苹果审核。然后在Xcode中选取In-App Purchase Content template创建虚拟产品, 放入需要托管的内容, 然后使用Archive功能上传。或者使用Xcode为每一种虚拟产品创建一个.pkg文件,然后使用Application Loader一次性上传。
具体细节请参考中和In-App Purchase有关的章节。
关于和iTunes Connect的交互,更多细节请参考。
5. 代码里该做的事情
获取产品列表
首先读取出App中内嵌的或是服务端中的Product IDs。使用SKProductRequest向苹果服务器验证哪些Product IDs是可用的。注意不要在[Class load]里发送请求,一定要等到App didFinishLauching之后再发,不然无法接到请求返回。核心代码如下:
#import &StoreKit/StoreKit.h&
#define kInAppPurchaseProUpgradeProductId
@&com.163.neteasemusic.skin.dog&
NSSet *productIDs = [NSSet setWithObject:kInAppPurchaseProUpgradeProductId];
SKProductsRequest *request= [[SKProductsRequest alloc] initWithProductIdentifiers:productIDs];
request.delegate = self;
[request start];
- (void)productsRequest:(SKProductsRequest *)request
didReceiveResponse:(SKProductsResponse *)response
NSArray *myProducts = response.products;
for (SKProduct *product in myProducts)
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error
//处理错误
向自己的服务器生成订单
如果需要经过自己的服务器做二次验证,建议在调用苹果支付接口前做这一步。
订单中必须要保存的是订单ID和用户想要购买的商品ID。这个记录是为了在二次验证时服务端做检查,防止 A 商品的 receipt 被用户拿来做 B 商品的购买结果校验。
发送购买请求
#import &StoreKit/StoreKit.h&
SKProduct *product = &# products request中返回的SKProduct #&;
SKMutablePayment *payment = [SKMutablePayment paymentWithProduct:product];
payment.quantity = 2;
[[SKPaymentQueue defaultQueue] addPayment:payment];
#import &StoreKit/StoreKit.h&
SKProduct *product = &# products request中返回的SKProduct #&;
SKPayment *payment = [SKPayment paymentWithProduct:product];
[[SKPaymentQueue defaultQueue] addPayment:payment];
观察购买状态
首先在程序启动时注册观察者
#import &StoreKit/StoreKit.h&
[[SKPaymentQueue defaultQueue] addTransactionObserver:observer];
并且实现回调,处理相应的购买返回。
- (void)paymentQueue:(SKPaymentQueue *)queue
updatedTransactions:(NSArray *)transactions
for (SKPaymentTransaction *transaction in transactions)
switch (transaction.transactionState)
// Call the appropriate custom method for the transaction state.
case SKPaymentTransactionStatePurchasing:
[self showTransactionAsInProgress:transaction deferred:NO];
case SKPaymentTransactionStateDeferred:
[self showTransactionAsInProgress:transaction deferred:YES];
case SKPaymentTransactionStateFailed:
[self failedTransaction:transaction];
case SKPaymentTransactionStatePurchased:
[self completeTransaction:transaction];
case SKPaymentTransactionStateRestored:
[self restoreTransaction:transaction];
// For debugging
NSLog(@&Unexpected transaction state %@&, @(transaction.transactionState));
需要监听SKPaymentQueue的更多状态变更,请实现协议中提供的更多方法。
在收到Purchased或Restored回调后,持久化购买记录以及receipt data。
然后通知PaymentQueue,购买已经完成了。对finishTransaction则会触发系统IAP的UI刷新:
SKPaymentTransaction *transaction = &# The current payment #&;
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
另外在发放功能或道具之前,最好在自己服务端做一次二次校验,防止越狱插件或者Wifi的HTTP代理伪造购买记录。
二次验证防止破解
越狱插件或者HTTP代理均可让用户做到伪造购买记录。当我们收到购买完成的回调后,最好经过自己服务器验证购买是否合法。
经过 App Store 验证
以下代码用Cocoa实现了二次验证的过程。但是这个过程最好通过自己的后台服务器来做,不然非常容易在客户端被伪造返回结果。
这里使用Cocoa实现只是为了阐述请求与返回值的格式。 发送二次验证请求:
#define SANDBOX_VERIFY_RECEIPT_URL
[NSURL URLWithString:@&https://sandbox./verifyReceipt&]
#define APP_STORE_VERIFY_RECEIPT_URL
[NSURL URLWithString:@&https://buy./verifyReceipt&]
#ifdef DEBUG
#define VERIFY_RECEIPT_URL SANDBOX_VERIFY_RECEIPT_URL
#define VERIFY_RECEIPT_URL APP_STORE_VERIFY_RECEIPT_URL
- (void)verifyTransaction:(SKPaymentTransaction *)transaction
NSData *transactionReceipt = transaction.transactionReceipt;
NSString *base64String = [OTBase64Helper base64forData:transactionReceipt];
NSDictionary *receiptDictionary = @{@&receipt-data&:base64String};
NSData *data = [receiptDictionary JSONData];
if (_receiptRequest)
[_receiptRequest cancel];
_receiptRequest = nil;
_receiptRequest = [[ASIFormDataRequest alloc] initWithURL:VERIFY_RECEIPT_URL];
_receiptRequest.userInfo = @{@&ProductIdentifier& : transaction.payment.productIdentifier};
_receiptRequest.delegate = self;
[_receiptRequest appendPostData:data];
[_receiptRequest startAsynchronous];
接收二次验证结果:
- (void)requestFinished:(ASIHTTPRequest *)request
NSString *responseString = [request responseString];
NSDictionary *dictionary = [responseString objectFromJSONString];
NSString *productId = dictionary[@&receipt&][@&product_id&];
NSNumber *status = dictionary[@&status&];
if (status.intValue == 0)
//校验成功,发放内容
//status code 0为成功
//校验失败,不做处理或相应惩罚
//21000 App Store不能读取你提供的JSON对象
//21002 receipt-data域的数据有问题
//21003 receipt无法通过验证
//21004 提供的shared secret不匹配你账号中的shared secret
//21005 receipt服务器当前不可用
//21006 receipt合法,但是订阅已过期。服务器接收到这个状态码时,receipt数据仍然会解码并一起发送
//21007 receipt是Sandbox receipt,但却发送至生产系统的验证服务
//21008 receipt是生产receipt,但却发送至Sandbox环境的验证服务
- (void)requestFailed:(ASIHTTPRequest *)request
//出错处理
苹果的返回值如下:
&receipt&: {
&original_purchase_date_pst&:& 04:00:37 America/Los_Angeles&,
&purchase_date_ms&:&9&,
&unique_identifier&:&secret9f135e2cd8f7dda951a15c01cd2220c60b&,
&original_transaction_id&:&3770&,
&bvrs&:&2.6.0&,
&transaction_id&:&3770&,
&quantity&:&1&,
&unique_vendor_identifier&:&SECRETCD-89AD-45C4-CCA9E8F36&,
&item_id&:&SECRET509&,
&product_id&:&com.your.iap.product.id&,
&purchase_date&:& 11:00:37 Etc/GMT&,
&original_purchase_date&:& 11:00:37 Etc/GMT&,
&purchase_date_pst&:& 04:00:37 America/Los_Angeles&,
&bid&:&com.your.app.bundle.id&,
&original_purchase_date_ms&:&9&
&status&: 0
纯本地验证
除了网络验证以外,苹果提供了纯粹的本地验证方式:.
Receipt data 经过 App Store 证书签名,所以第三方无法凭空生成能够通过此法验证的 receipt data。只要做好证书校验,无需担心用户会伪造 receipt data。
在客户端使用这种方式可以做到防止被通用破解方式破解,但并不能防止针对特定 App 的破解。
实际上,这种验证方式是苹果为服务端设计的。Receipt data 的格式遵守ASN.1格式,服务端安装asn1c就可以解析 receipt data,并不需要纯手写一份解析代码。只要服务端代码和 asn1c 不出 bug,在服务端使用这种方式验证就是安全的。
第三方网站验证
有些第三方网站提供了经服务端的验证服务。比如. 但是我并没有用过,所以不知道具体效果如何。毕竟第三方服务无法做到在用户发起购买之前生成订单记录,与购买后验证结果比对,所以我还是比较担心第三方验证服务的安全性的。而且鸡国网络连国外验证服务器,你懂的。。
总之想要万无一失,建议开发自己的验证接口。
更多验证相关问题,请参考
大多数产品在验证成功后,才是真正的发放内容、道具等。特别是充值后立即消费的虚拟货币基本都是这么处理的。
但是据我猜测, IAP 的设计者是想让开发者在购买完成时发放内容、道具,在二次验证失败时以删除内容、道具等方式来进行处罚。这样做的好处是:服务端不做小票对应商品验证/失效小票记录的话(后面会提到具体做法。带侥幸心理不做这个是很危险的,我们第二天就被 hack 了),用户无法通过向服务端重复发送同一张有效小票并关联不同的订单来伪造购买记录。
6.服务端二次验证后再发放数据中的安全问题
由于是和钱关系最紧密的功能,IAP安全性显得无比重要。
如果是用的“经服务端二次验证成功发放道具”的逻辑,而非“购买成功发放道具,二次验证失败惩罚处理”,则在我的实践过程中,以下几件事是必须要做的。
客户端防止用户数据丢失
不像支付宝SDK那样全部校验在服务端做,用IAP时部分流程的完整性是需要客户端保证的。
在transaction完成后,和服务端的二次验证完成前,要对transaction.transactionReceipt做持久化。
删除此持久化的时机应当是收到从服务端发回的二次验证请求的响应时,确认服务端已和苹果完成通信之后(服务端返回和苹果连接失败则不应删除已保存的receiptData)。
服务端防止被盗
由于和开发者自身网站业务耦合紧,这部分内容任意一篇 IAP 的文档中都没有提到。但是在我实践中,这部分工作一旦有疏漏,被 hack 是分分钟的事。强烈建议认真阅读本部分,并在服务端完成类似实践。
服务端防盗主要有两点:
1. 自己服务器的线上环境避免使用苹果sandbox环境做二次验证,防止公司内部使用同一个apple developer id的人建立sandbox test user监守自盗。
2. 对验证通过的小票做废弃记录,防止黑客使用同一个小票反复验证购买。
再回顾一下,苹果二次验证接口的返回值如下:
&receipt&: {
&original_purchase_date_pst&:& 04:00:37 America/Los_Angeles&,
&purchase_date_ms&:&9&,
&unique_identifier&:&secret9f135e2cd8f7dda951a15c01cd2220c60b&,
&original_transaction_id&:&3770&,
&bvrs&:&2.6.0&,
&transaction_id&:&3770&,
&quantity&:&1&,
&unique_vendor_identifier&:&SECRETCD-89AD-45C4-CCA9E8F36&,
&item_id&:&SECRET509&,
&product_id&:&com.your.iap.product.id&,
&purchase_date&:& 11:00:37 Etc/GMT&,
&original_purchase_date&:& 11:00:37 Etc/GMT&,
&purchase_date_pst&:& 04:00:37 America/Los_Angeles&,
&bid&:&com.your.app.bundle.id&,
&original_purchase_date_ms&:&9&
&status&: 0
第5点中提到过,后台需要在发起支付前针对每一笔支付生成一个订单。服务端使用客户端发来的receiptData得到苹果的二次验证返回时,首先比较订单中的商品和返回值中的 product_id 是否对应,若不一致则用户用作弊手段传了另一个商品的 receiptData 过来,视本次支付无效。若一致,则需要判断返回值中的unique_identifier是否被使用过;若未被使用过,则视此次交易完成,并将此unique_identifier标记为已使用;若使用过,则用户使用作弊手段传了之前购买时的 receiptData
7.切换线上/测试环境
需要在代码里显示声明的环境,就只有二次验证地址:
#define SANDBOX_VERIFY_RECEIPT_URL
[NSURL URLWithString:@&https://sandbox./verifyReceipt&]
#define APP_STORE_VERIFY_RECEIPT_URL
[NSURL URLWithString:@&https://buy./verifyReceipt&]
而调用苹果接口时连接的是线上环境还是测试环境,猜测是由编译 App 的证书决定的。目前看来,开发证书编译后,连接的是苹果的 Sandbox 环境;AppStore 上下载的则是连接苹果的线上环境。
另外再次强调,除非少量必要的自己线上环境的测试需要连接苹果的 Sandbox 验证服务之外,自己服务端的二次验证 API 应该严格做到自己的环境是线上环境,则连接苹果的线上环境二次验证接口。防止监守自盗的情况出现。
上文介绍了现在整个IAP支付的流程,可能随着版本的更新,个别过程不太一样,但都是大同小异
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:726127次
积分:7992
积分:7992
排名:第1628名
原创:104篇
转载:118篇
评论:147条
(2)(8)(10)(4)(5)(2)(13)(7)(2)(10)(16)(10)(2)(2)(3)(3)(6)(2)(8)(10)(3)(7)(17)(7)(1)(4)(8)(13)(9)(4)(1)(8)(11)(1)(1)关于苹果内支付In-App Purchases - 简书
下载简书移动应用
写了28068字,被76人关注,获得了116个喜欢
关于苹果内支付In-App Purchases
原文地址:
【提示:以下创建App部分内容,你不用非要等项目能打包了才开始做,可以随时并且随便的创建个测试项目即可,因为嵌入付费并不要求上传App的ipa包的!!】
**第一步:你需要在iTunesConnect中创建个新的App,然后为这个App设置一些产品(付费道具)等;**
OK,这里Himi稍微解释下,iTunesConnect是苹果提供的一个平台,主要提供AP发布和管理App的,最重要的功能是创建管理项目信息,项目付费产品(道具)管理、付费的测试账号、提交App等等,这里就简单介绍这么多,关于产品一词在此我们可以理解成游戏道具即可;在苹果看来所有付费都属于产品 =。 =千万不要纠结字眼哦~
OK,打开iTunesConnect网站:[/WebObjects/iTunesConnect.woa](/WebObjects/iTunesConnect.woa) (注意:企业级的用户必须使用公司主开发者账号登陆才可!)
成功登陆后的页面如下:
![点击查看原始大小图片](http://upload-images.jianshu.io/upload_images/038f4ef8781aa.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
这里大概说下重要的一些项:
** Contracts, Tax, and Banking
: 管理银行账号、联系人以及税等等;这里要根据提示完成对应的信息填写!一定要详细填写喔~**
Manage Users :管理用户的,比如主账号以及测试付费的(测试App)账号;
Manage Your Applictions:管理应用程序的,你所有发布的应用和每个应用的状态都在这里面;
下面我们新建一个App项目,大家放心,我们这里创建的是不会直接提交给App审核的,所以放心创建,只要控制好App的状态不要是待审核状态即可,不过即使你不小心将项目提交了,也没事,直接更改App状态即可了;
选择**Manage Your Applictions**选项,然后新建一个项目:【Add New App】,根据提示来填写吧,这里就不细致说明了~
创建好一个App之后,在点击**Manage Your Applictions**后的界面应该如下:
![点击查看原始大小图片](http://upload-images.jianshu.io/upload_images/fae9a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
这里你将看到自己创建的App,点击你创建的App项目,这里Himi创建的项目名字叫”ProjectForBuyTest“,点击你的App进入如下界面:
点击查看原始大小图片
(注意:这里的Bundle ID一定要跟你的项目中的info.plist中的Bundle ID保证一致!!!!)
这里可以管理你的项目的信息、状态、是否嵌入GameCenter等等选项,那么本章我们重点介绍如何使用IAp沙盒测试程序内付费,所以这里我们点击右上角的”Manage In-App Purchases“选项进入创建产品(游戏道具)界面如下:
点击查看原始大小图片
上图中的下方看到Himi创建过的四个产品(道具)了,你可以点击”Create New“选项新建一个产品(付费道具),点击新建如下界面:
点击查看原始大小图片
上图中Himi没有截图出所有的选项,这里大概介绍下,这个界面是选择你的消费道具的种类,种类说明如下:
类型选择有四种选择:
1.Consumable(消耗品): 每次下载都需要付费;
2.Non-consumable(非消耗品): 仅需付费一次;
3.Auto-Renewable Subscriptions:自动订阅;
4.Free Subscription:免费订阅
最下方是你沙盒测试的截图,暂且不管即可;
这里Himi选择Consumable选项,比如很多游戏都是购买金币啦这样子就可以选择这个;然后出现如下界面:
点击查看原始大小图片
Reference Name: 付费产品(道具的)参考名称
Product ID(产品ID): 你产品的唯一id。通常格式是 com.xx.yy,但它可以是任何形式,不要求以程序的App ID作为前缀。
Add Language: 添加产品名称与描述语言;
Price Tier:选择价格,这里你选择价格后,会出现如上图最下方的价格对照表
Screenshot(截屏): 展示你产品的截屏。(这个直接无视,测试App务必要管这个的)
Product ID(产品ID)可以创建多个,比如我想游戏中分为0.99$ 、1.99$等道具那就创建对应多个产品ID!
我们填写好了”Reference Name“与”Product ID“以及”Price Tier“后,点击”Add Language“选项然后出现如下界面:
点击查看原始大小图片
上图中的选项:
Language:语言
Displayed Name(显示名称): 用户看到的产品名称。
Description(描述): 对产品进行描述。
Ok,一路 Save保存回到”Manage In-App Purchases“界面中会看到我们新建的产品(道具)如下:
点击查看原始大小图片
大家可以看到新建的产品(道具)ID:这里Himi创建的产品ID是com.himi.wahaha ,这里要记住这个产品ID哦~
第二步:申请测试账号,利用沙盒测试模拟AppStore购买道具流程!
回到itunesconnect主页中,选择“Manage Users”然后选择“Test User”,然后出现的界面如下图:
AE_1OI}COQE3U7KM2_VRB)I.jpg
D99F15A8-FF7-BB14DFEBE01B.png
这里已经创建了几个测试账号了,点击界面中的 “Add New User”进行创建即可;记住账号和密码哈,记不住就删掉重新建 娃哈哈~(切记:不能用于真正的AppStore中使用此账号,不仅不能用,而且一旦AppStore发现后果你懂得~)
这里重要两点提醒:1.测试时候在沙盒创建的账号必须不是真的存在的AppStore账号。2.购买的时候要保证AppStore账号处于注销状态,否则会购买失败的。
第三步:在项目中申请购买产品代码以及监听;
这里关于购买的代码部分呢,我都有备注的,这里就不详细讲解了,只是在代码后介绍几点值得注意的地方:
这里新建的一个Cocos2d的项目,然后给出HelloWorldLayer.h以及HelloWorldLayer.m的全部代码,所有购买代码也全在里面也对应有Himi的注释!
HelloWorldLayer.h
HelloWorldLayer.h//
buytest////
Created by 华明 李 on 11-10-29.//
Copyright Himi 2011年. All rights reserved.//
// When you import this file, you import all the cocos2d classes
import "cocos2d.h"
import &UIKit/UIKit.h&
import &StoreKit/StoreKit.h&
IAP0p99=10,
IAP24p99,}buyCoinsT
@interface HelloWorldLayer : CCLayer&SKProductsRequestDelegate,SKPaymentTransactionObserver&{
+(CCScene *)
(void) requestProUpgradeProductD-(void)RequestProductD-(bool)CanMakeP-(void)buy:(int)
(void)paymentQueue:(SKPaymentQueue )queue updatedTransactions:(NSArray )-(void) PurchasedTransaction: (SKPaymentTransaction *)
(void) completeTransaction: (SKPaymentTransaction *)
(void) failedTransaction: (SKPaymentTransaction )-(void) paymentQueueRestoreCompletedTransactionsFinished: (SKPaymentTransaction )-(void) paymentQueue:(SKPaymentQueue ) paymentQueue restoreCompletedTransactionsFailedWithError:(NSError )
(void) restoreTransaction: (SKPaymentTransaction )-(void)provideContent:(NSString )-(void)recordTransaction:(NSString *)@end
HelloWorldLayer.m
IapLayer.m////
Created by Himi on 11-5-25.//
Copyright 2011年 李华明 . All rights reserved.//
import "HelloWorldLayer.h"
define ProductID_IAP0p99 @"com.buytest.one"//$0.99
define ProductID_IAP1p99 @"com.buytest.two" //$1.99
define ProductID_IAP4p99 @"com.buytest.three" //$4.99
define ProductID_IAP9p99 @"com.buytest.four" //$19.99
define ProductID_IAP24p99 @"com.buytest.five" //$24.99
@implementation HelloWorldLayer+(CCScene ) scene{
CCScene scene = [CCScene node];
HelloWorldLayer layer = [HelloWorldLayer node];
[scene addChild: layer];}-(id)init{
if ((self = [super init])) {
CGSize size = [[CCDirector sharedDirector] winSize];
CCSprite iap_bg
= [CCSprite spriteWithFile:@"Icon.png"];
[iap_bg setPosition:ccp(size.width/2,size.height/2)];
[self addChild:iap_bg z:0];
//---------------------
//----监听购买结果
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
//申请购买
IAP0p99=10,
}buyCoinsT
[self buy:IAP24p99];
-(void)buy:(int)type{
if ([SKPaymentQueue canMakePayments]) {
//[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
[self RequestProductData];
CCLOG(@"允许程序内付费购买");
CCLOG(@"不允许程序内付费购买");
UIAlertView *alerView =
[[UIAlertView alloc] initWithTitle:@"Alert"
message:@"You can‘t purchase in app store(Himi说你没允许应用程序内购买)"
delegate:nil cancelButtonTitle:NSLocalizedString(@"Close(关闭)",nil) otherButtonTitles:nil];
[alerView show];
[alerView release];
-(bool)CanMakePay{
return [SKPaymentQueue canMakePayments];}
-(void)RequestProductData{
CCLOG(@"---------请求对应的产品信息------------");
NSArray *product =
switch (buyType) {
case IAP0p99:
product=[[NSArray alloc] initWithObjects:ProductID_IAP0p99,nil];
case IAP1p99:
product=[[NSArray alloc] initWithObjects:ProductID_IAP1p99,nil];
case IAP4p99:
product=[[NSArray alloc] initWithObjects:ProductID_IAP4p99,nil];
case IAP9p99:
product=[[NSArray alloc] initWithObjects:ProductID_IAP9p99,nil];
case IAP24p99:
product=[[NSArray alloc] initWithObjects:ProductID_IAP24p99,nil];
NSSet *nsset = [NSSet setWithArray:product];
SKProductsRequest *request=[[SKProductsRequest alloc] initWithProductIdentifiers: nsset];
request.delegate=
[request start];
[product release];
}//&SKProductsRequestDelegate& 请求协议//收到的产品信息
(void)productsRequest:(SKProductsRequest )request didReceiveResponse:(SKProductsResponse )response{
NSLog(@"-----------收到产品反馈信息--------------");
NSArray myProduct = response.
NSLog(@"产品Product ID:%@",response.invalidProductIdentifiers);
NSLog(@"产品付费数量: %d", [myProduct count]);
// populate UI
for(SKProduct product in myProduct){
NSLog(@"product info");
NSLog(@"SKProduct 描述信息%@", [product description]);
NSLog(@"产品标题 %@" , product.localizedTitle);
NSLog(@"产品描述信息: %@" , product.localizedDescription);
NSLog(@"价格: %@" , product.price);
NSLog(@"Product id: %@" , product.productIdentifier);
SKPayment *payment =
switch (buyType) {
case IAP0p99:
= [SKPayment paymentWithProductIdentifier:ProductID_IAP0p99];
//支付$0.99
case IAP1p99:
= [SKPayment paymentWithProductIdentifier:ProductID_IAP1p99];
//支付$1.99
case IAP4p99:
= [SKPayment paymentWithProductIdentifier:ProductID_IAP4p99];
//支付$9.99
case IAP9p99:
= [SKPayment paymentWithProductIdentifier:ProductID_IAP9p99];
//支付$19.99
case IAP24p99:
= [SKPayment paymentWithProductIdentifier:ProductID_IAP24p99];
//支付$29.99
CCLOG(@"---------发送购买请求------------");
[[SKPaymentQueue defaultQueue] addPayment:payment];
[request autorelease];
(void)requestProUpgradeProductData{
CCLOG(@"------请求升级数据---------");
NSSet productIdentifiers = [NSSet setWithObject:@"com.productid"];
SKProductsRequest productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:productIdentifiers];
productsRequest.delegate =
[productsRequest start];
}//弹出错误信息
(void)request:(SKRequest )request didFailWithError:(NSError )error{
CCLOG(@"-------弹出错误信息----------");
UIAlertView *alerView =
[[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Alert",NULL) message:[error localizedDescription]
delegate:nil cancelButtonTitle:NSLocalizedString(@"Close",nil) otherButtonTitles:nil];
[alerView show];
[alerView release];}
-(void) requestDidFinish:(SKRequest *)request{
NSLog(@"----------反馈信息结束--------------");
-(void) PurchasedTransaction: (SKPaymentTransaction )transaction{
CCLOG(@"-----PurchasedTransaction----");
NSArray transactions =[[NSArray alloc] initWithObjects:transaction, nil];
[self paymentQueue:[SKPaymentQueue defaultQueue] updatedTransactions:transactions];
[transactions release];}
//&SKPaymentTransactionObserver& 千万不要忘记绑定,代码如下://----监听购买结果//[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
(void)paymentQueue:(SKPaymentQueue )queue updatedTransactions:(NSArray )transactions//交易结果{
CCLOG(@"-----paymentQueue--------");
for (SKPaymentTransaction *transaction in transactions)
switch (transaction.transactionState)
case SKPaymentTransactionStatePurchased://交易完成
[self completeTransaction:transaction];
CCLOG(@"-----交易完成 --------");
CCLOG(@"不允许程序内付费购买");
UIAlertView *alerView =
[[UIAlertView alloc] initWithTitle:@"Alert"
message:@"Himi说你购买成功啦~娃哈哈"
delegate:nil cancelButtonTitle:NSLocalizedString(@"Close(关闭)",nil) otherButtonTitles:nil];
[alerView show];
[alerView release];
case SKPaymentTransactionStateFailed://交易失败
[self failedTransaction:transaction];
CCLOG(@"-----交易失败 --------");
UIAlertView *alerView2 =
[[UIAlertView alloc] initWithTitle:@"Alert"
message:@"Himi说你购买失败,请重新尝试购买~"
delegate:nil cancelButtonTitle:NSLocalizedString(@"Close(关闭)",nil) otherButtonTitles:nil];
[alerView2 show];
[alerView2 release];
case SKPaymentTransactionStateRestored://已经购买过该商品
[self restoreTransaction:transaction];
CCLOG(@"-----已经购买过该商品 --------");
case SKPaymentTransactionStatePurchasing:
//商品添加进列表
CCLOG(@"-----商品添加进列表 --------");
(void) completeTransaction: (SKPaymentTransaction *)transaction
CCLOG(@"-----completeTransaction--------");
// Your application should implement these two methods.
NSString *product = transaction.payment.productI
if ([product length] & 0) {
NSArray *tt = [product componentsSeparatedByString:@"."];
NSString *bookid = [tt lastObject];
if ([bookid length] & 0) {
[self recordTransaction:bookid];
[self provideContent:bookid];
// Remove the transaction from the payment queue.
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
//记录交易-(void)recordTransaction:(NSString *)product{
CCLOG(@"-----记录交易--------");}
//处理下载内容-(void)provideContent:(NSString *)product{
CCLOG(@"-----下载--------");}
(void) failedTransaction: (SKPaymentTransaction *)transaction{
NSLog(@"失败");
if (transaction.error.code != SKErrorPaymentCancelled)
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}-(void) paymentQueueRestoreCompletedTransactionsFinished: (SKPaymentTransaction *)transaction{
(void) restoreTransaction: (SKPaymentTransaction *)transaction
NSLog(@" 交易恢复处理");
-(void) paymentQueue:(SKPaymentQueue ) paymentQueue restoreCompletedTransactionsFailedWithError:(NSError )error{
CCLOG(@"-------paymentQueue----");}
pragma mark connection delegate
(void)connection:(NSURLConnection )connection didReceiveData:(NSData )data{
NSLog(@"%@",
[[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease]);}
(void)connectionDidFinishLoading:(NSURLConnection *)connection{
(void)connection:(NSURLConnection )connection didReceiveResponse:(NSURLResponse )response{
switch([(NSHTTPURLResponse *)response statusCode]) {
(void)connection:(NSURLConnection )connection didFailWithError:(NSError )error {
NSLog(@"test");}
-(void)dealloc{
[super dealloc];}@end
**代码注释的相当清楚了,没有什么可解释的,这里说几点值得注意的地方:**
1.添加对应对应代码时不要忘记,添加框架 StoreKit.framework,如何添加框架请看我的博文!
越狱机器无法沙盒测试!模拟器的话,Himi用4.3模拟器不可以,因为提示没有开启程序内付费- -(我都没看到模拟器有store的选项,so~);但是使用iOS5的模拟器可以测试沙盒,但是执行的顺序会有些问题,但是还没真机的童鞋可以使用,建议一切以真机实测为准
千万不要忘记在iTunesConnect中创建App Bundle ID一定要跟你的项目中的info.plist中的Bundle ID保证一致!!!!4. 以上代码中你需要修改的就是我在HelloWorldLayer.m类中的宏定义的Product ID(产品ID),例如Himi刚才新建了一个产品ID是“com.himi.wahaha"
然后我运行项目截图如下以及运行控制台打印的信息如下:
点击查看原始大小图片
点击Buy之后运行截图以及打印信息:
点击查看原始大小图片
输入测试账号密码后以及打印信息:
点击查看原始大小图片
![害羞](http://upload-images.jianshu.io/upload_images/f9cded17cb1c64b.gif?imageMogr2/auto-orient/strip)**这里最后一张截图是没有购买成功,这里是故意截图出来的,原因就是想告诉童鞋们:**
如果你的产品信息能够正常得到,但是始终无法成功的话,不要着急,因为你的产品要进入iTunes Connect,并且Apple准备好沙箱环境需要一些时间。之前遇到过,然后在一天后我没有修改任何一行代码,但产品ID变为有效并能成功购买。=。 =郁闷ing~~
起始要使产品发布到Apple的网络系统是需要一段时间的!
8F74CBE4A06C55AF65B82C09C08B3CE2.png
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
打开微信“扫一扫”,打开网页后点击屏幕右上角分享按钮
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
选择支付方式:}

我要回帖

更多关于 apple id有钱支付失败 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信