首先你必须熟悉内支付的流程,可以到网上搜搜看。
前几天因为需要做了个苹果的内支付,当中遇到了不少苦头,感觉网上的各种博客对内支付这一块都是点到即止,没有真真正正实现自己的需求。正所谓辛苦就有所得,通过东接西搬,终于内力小成,特与大家分享一下自己的经验。这里我假设你已经把商品的ID什么的都已经注册成功了,测试账号也有了。
下面直接贴代码了~~~~
- //
- // IAPHelperViewController.h
- //
- #import <UIKit/UIKit.h>
- #import <StoreKit/StoreKit.h>
- @interface IAPHelperViewController : UIViewController<SKProductsRequestDelegate,SKPaymentTransactionObserver>
- {
- NSString* kMyInAppIdentifier;
- BOOL receiptValid;
- UIActivityIndicatorView* spinner;
- }
- + (IAPHelperViewController *) sharedIAPHelper;
- - (void) requestProUpgradeProductData;
- - (void) requestProductData;
- - (void) buyGolds:(int)buyType;
- - (BOOL) canInAppPayment;
- - (void) paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions;
- - (void) purchasedTransaction:(SKPaymentTransaction *)transaction;
- - (void) completeTransaction:(SKPaymentTransaction *)transaction;
- - (void) failedTransaction:(SKPaymentTransaction *)transaction;
- - (void) paymentQueueRestoreCompletedTransactionsFinished: (SKPaymentTransaction *)transaction;
- - (void) paymentQueue:(SKPaymentQueue *) paymentQueue restoreCompletedTransactionsFailedWithError:(NSError *)error;
- - (void) restoreTransaction:(SKPaymentTransaction *)transaction;
- - (void) recordTransaction:(SKPaymentTransaction *)product;
- - (void) verifyReceiptFromMyServer:(SKPaymentTransaction *)transaction;
- @end
- //
- // IAPHelperViewController.m
- //
- #import "IAPHelperViewController.h"
- #include "cocos2d.h"
- using namespace cocos2d;
- const static int IAP1p99 = flag1;//主要是标记用户点击了那个商品
- const static int IAP2p99 = flag2;
- const static int IAP3p99 = flag3;
- const static int IAP4p99 = flag4;
- const static int IAP5p99 = flag5;
- #define MyServerURL @"your server address , not apple"
- #define ProductOne @"product 1 id"
- #define ProductThree @"....... 2 .."
- #define ProductFive @"....... 3 .."
- #define ProductFifteen @"....... 4 .."
- #define ProductFifty @"...... 5 .."
- #define MyFrint NSLog(@"\n%s %d\n",__FUNCTION__, __LINE__)
- @interface IAPHelperViewController ()
- @end
- @implementation IAPHelperViewController
- //定义单例变量
- static IAPHelperViewController* singleton = nil;
- //单例模式
- + (IAPHelperViewController *) sharedIAPHelper
- {
- MyFrint;
- if (singleton == nil) {
- singleton = [[IAPHelperViewController alloc] initWithNibName:nil bundle:nil];
- }
- return singleton;
- }
- //初始化 这里应该根据自己的情况初始化 我用的是C++写的界面 OC不鸟~~~~~~
- - (id) initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
- {
- MyFrint;
- self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
- if (self) {
- receiptValid = NO;
- }
- return self;
- }
- //加载视图
- - (void) viewDidLoad
- {
- MyFrint;
- [super viewDidLoad];
- self.view.backgroundColor = [UIColor orangeColor];
- }
- //请求升级数据
- - (void) requestProUpgradeProductData
- {
- MyFrint;
- NSSet* productIdentifiers = [NSSet setWithObjects:ProductOne, ProductThree, ProductFive, ProductFifteen, ProductFifty, nil nil];
- SKProductsRequest* request = [[SKProductsRequest alloc] initWithProductIdentifiers:productIdentifiers];
- request.delegate = self;
- [request start];
- }
- //点击商品按钮
- - (void) buyGolds:(int)buyType
- {
- MyFrint;//根据点击标记 找到对应的商品IDs
- switch (buyType) {
- case IAP1p99: { kMyInAppIdentifier = ProductOne; break; }
- case IAP2p99: { kMyInAppIdentifier = ProductThree; break; }
- case IAP3p99: { kMyInAppIdentifier = ProductFive; break; }
- case IAP4p99: { kMyInAppIdentifier = ProductFifteen; break; }
- case IAP5p99: { kMyInAppIdentifier = ProductFifty; break; }
- default: break;
- }
- [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
- if ([self canInAppPayment]) {
- NSLog(@"允许应用内支付");
- //首先向AppStore请求恢复交易
- [[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
- } else {
- NSLog(@"不允许应用内支付");
- }
- }
- //判断是否可以应用内支付
- - (BOOL) canInAppPayment
- {
- MyFrint;//你应该根据是否想让越狱的iOS设备允许内支付
- if ([[NSFileManager defaultManager] fileExistsAtPath:@"/Applications/Cydia.app"]){
- NSLog(@"Jailbroken detected!");
- return NO;
- }
- if ([SKPaymentQueue canMakePayments]) {
- return YES;
- } else {
- return NO;
- }
- }
- //输入正确的帐号后进入 (如果是测试 必须必须使用你注册的沙河账号和密码)
- - (void) paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentTransaction *)transaction
- {
- MyFrint;
- [self requestProductData];
- }
- //请求购买的商品信息
- - (void) requestProductData
- {
- MyFrint;
- NSSet * set = [NSSet setWithObjects:kMyInAppIdentifier, nil nil];//可以添加多个商品ID
- SKProductsRequest* request = [[SKProductsRequest alloc] initWithProductIdentifiers:set];
- request.delegate = self;
- [request start];
- }
- #pragma mark SKProductsRequestDelegate 请求协议 ----------------------------------------------------------
- //收到产品信息
- - (void) productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
- {
- // NSArray *myProduct = response.products;
- // NSLog(@"产品付费数量: %d", [myProduct count]);
- // NSLog(@"产品Product ID:%@",response.invalidProductIdentifiers);
- // SKPayment *payment = nil;
- // // populate UI
- // for(SKProduct *product in myProduct)
- // {
- // NSLog(@"\n\nproduct info");
- // NSLog(@"SKProduct 描述信息%@", [product description]);
- // NSLog(@"产品标题 %@" , product.localizedTitle);
- // NSLog(@"产品描述信息: %@" , product.localizedDescription);
- // NSLog(@"价格: %@" , product.price);
- // NSLog(@"Product id: %@\n\n" , product.productIdentifier);
- // payment = [SKPayment paymentWithProduct:product];
- // }
- MyFrint;
- SKPayment* payment = [SKPayment paymentWithProduct:[response.products lastObject]];
- NSLog(@"\nPayment:\n productIdentifier=%@, \nrequestData=%@, \nquanlity=%d, \n",payment.productIdentifier,[payment.requestData description],payment.quantity);
- [[SKPaymentQueue defaultQueue] addPayment:payment];
- [request release];//请求已经生效
- }
- //请求错误信息
- - (void) request:(SKRequest *)request didFailWithError:(NSError *)error
- {
- MyFrint;
- UIAlertView *alerView = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Alert",NULL)
- message:[error localizedDescription]
- delegate:nil
- cancelButtonTitle:NSLocalizedString(@"Close",nil)
- otherButtonTitles:nil];
- [alerView show];
- [alerView release];
- [request release];//请求失效
- }
- - (void) purchasedTransaction:(SKPaymentTransaction *)transaction
- {
- MyFrint;
- }
- - (void) requestDidFinish:(SKRequest *)request
- {
- MyFrint;
- }
- //然后当用户输入正确的appStore帐号密码后进入(再次说明 如果是测试 必须是你注册的测试账号 不能使用真实的AppleID 否则后果~~~~)
- //方法在新交易被创建或更新时都会被调用
- - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
- {
- MyFrint;
- for ( SKPaymentTransaction* transaction in transactions )
- {
- switch (transaction.transactionState)
- {
- case SKPaymentTransactionStatePurchased:
- {//交易完成
- [self completeTransaction:transaction];
- }
- break;
- case SKPaymentTransactionStateFailed:
- {//交易失败
- [self failedTransaction:transaction];
- }
- break;
- case SKPaymentTransactionStateRestored:
- {//已经购买过该商品
- [self restoreTransaction:transaction];
- }
- case SKPaymentTransactionStatePurchasing:
- {//商品添加进列表
- NSLog(@"商品添加进列表==");
- break;
- }
- default:
- break;
- }
- }
- }
- //玩家等待 这里我想加个转动条 但没成功OC不鸟呀
- - (void) activityIndicatorRun
- {
- spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
- spinner.color = [UIColor whiteColor];
- spinner.frame = CGRectMake(100, 180.0f, 137.0f, 137.0f);
- [spinner startAnimating];
- }
- //玩家结束等待
- - (void) activityIndicatorStop
- {
- [spinner stopAnimating];
- }
- //observer在用户成功购买后提供相应的product
- - (void)completeTransaction:(SKPaymentTransaction *)transaction
- {
- MyFrint;
- [self activityIndicatorRun];
- [self verifyReceiptFromMyServer:transaction];//这个方法是内支付最重要的方法
- [self activityIndicatorStop];
- if (receiptValid) {
- [self recordTransaction:transaction];
- //提示服务器刷新数据 ---
- CCNotificationCenter::sharedNotificationCenter()->postNotification(kGNetworkNotificationName_Shop_BuyCredit_Successed, NULL);
- } else {
- [self failedTransaction:transaction];
- }
- [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
- }
- //处理还原购买 结束交易
- - (void) restoreTransaction: (SKPaymentTransaction *)transaction
- {
- MyFrint;
- [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
- }
- //处理失败购买 结束交易
- - (void) failedTransaction: (SKPaymentTransaction *)transaction
- {
- MyFrint;
- if (transaction.error.code != SKErrorPaymentCancelled) {
- NSLog(@"购买失败");
- }
- <span style="white-space:pre"> </span>//根据项目的实际运行情况看 下面的完全多余.
- <pre code_snippet_id="105137" snippet_file_name="blog_20131209_2_9929406" name="code" class="objc"> //else {
- // NSLog(@"用户取消购买");
- //}
- [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
- }
- //取消登陆帐号密码操作
- - (void) paymentQueue:(SKPaymentQueue *) paymentQueue restoreCompletedTransactionsFailedWithError:(NSError *)error
- {
- MyFrint;
- }
- //用户成功购买
- - (void) recordTransaction:(SKPaymentTransaction *)product
- {
- MyFrint;
- UIAlertView* view = [[UIAlertView alloc] initWithTitle:@"购买结果" message:@"购买成功" delegate:nil cancelButtonTitle:@"确定" otherButtonTitles: nil nil];
- [view show];
- [view release];
- }
- //向服务器请求验证
- - (void) verifyReceiptFromMyServer:(SKPaymentTransaction *)transaction
- {
- MyFrint;
- //初始化为合法
- if ([transaction.payment.productIdentifier isEqualToString:kMyInAppIdentifier]) {
- //base64 encode the receipt data for sending
- if ([transaction.transactionReceipt base64Encoding] != nil) {
- //将数据http post请求发送到我的服务器
- //data=base64(json(post))
- //字典内容
- NSDictionary* postDict = [[NSDictionary alloc] initWithObjectsAndKeys:@"ios",@"mode",
- @"verify",@"method",
- [NSDictionary dictionaryWithObjectsAndKeys:[self base64Encode:transaction.transactionReceipt],@"receipt",@"YES",@"test", nil nil],@"params",
- @"1136",@"uid", nil nil];
- //字典转化为json数据
- NSError* error = nil;
- NSData* jsonData = [NSJSONSerialization dataWithJSONObject:postDict options:NSJSONWritingPrettyPrinted error:&error];
- if([jsonData length]>0 && error == nil){
- }else if([jsonData length] == 0 && error == nil){
- NSLog(@"No data returned after serialization.");
- receiptValid = NO;
- }else if(error != nil){
- NSLog(@"An error happened = %@",error);
- receiptValid = NO;
- }
- //json数据base64
- NSString* base64 = [self base64Encode:jsonData];
- if ([base64 length]==0 || [base64 isEqualToString:@""]) {
- NSLog(@"Base64 error");
- receiptValid = NO;
- }
- NSURL* url = [NSURL URLWithString:MyServerURL];
- NSMutableURLRequest* theRequest = [NSMutableURLRequest requestWithURL:url];
- [theRequest setHTTPMethod:@"POST"];
- [theRequest setValue:[NSString stringWithFormat:@"%d", [jsonData length]] forHTTPHeaderField:@"Content-Length"];
- [theRequest setHTTPBody:[[NSString stringWithFormat:@"data=%@", base64] dataUsingEncoding:NSUTF8StringEncoding]];
- //发送同步请求 因为你将使用自己的服务器返回的数据来判断是否成功
- NSURLResponse* response = [[NSURLResponse alloc] init];
- NSError* myerror = [[NSError alloc] init];
- NSMutableData* receiveData = (NSMutableData *)[NSURLConnection sendSynchronousRequest:theRequest returningResponse:&response error:&myerror];
- //NSURLConnection* theConnection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
- //接收到服务器返回的数据
- NSString *receiptStatus = [[NSString alloc] initWithBytes: [receiveData mutableBytes] length:[receiveData length] encoding:NSUTF8StringEncoding];
- NSLog(@"\nreceiptStatus=%@",receiptStatus);
- NSString* str = @"\"status\":0";
- if ([receiptStatus length]==0 || [receiptStatus isEqualToString:@""]) {
- receiptValid = NO;
- }
- if ([receiptStatus rangeOfString:str].location != NSNotFound) {
- NSLog(@"Yes Yes Yes!!!");
- receiptValid = YES;
- } else {
- receiptValid = NO;
- }
- [receiptStatus release];
- [myerror release];
- [response release];
- [postDict release];
- } else {
- NSLog(@"当向服务器发送验证数据时出错!!!");
- receiptValid = NO;
- }
- }
- else
- {
- NSLog(@"无效的商品ID");
- receiptValid = NO;
- }
- }
- //将NSData64位编码
- - (NSString*) base64Encode:(NSData *)theData
- {
- const uint8_t* input = (const uint8_t*)[theData bytes];
- NSInteger length = [theData length];
- static char table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
- NSMutableData* data = [NSMutableData dataWithLength:((length + 2) / 3) * 4];
- uint8_t* output = (uint8_t*)data.mutableBytes;
- NSInteger i;
- for (i=0; i < length; i += 3) {
- NSInteger value = 0;
- NSInteger j;
- for (j = i; j < (i + 3); j++) {
- value <<= 8;
- if (j < length) {
- value |= (0xFF & input[j]);
- }
- }
- NSInteger theIndex = (i / 3) * 4;
- output[theIndex + 0] = table[(value >> 18) & 0x3F];
- output[theIndex + 1] = table[(value >> 12) & 0x3F];
- output[theIndex + 2] = (i + 1) < length ? table[(value >> 6) & 0x3F] : '=';
- output[theIndex + 3] = (i + 2) < length ? table[(value >> 0) & 0x3F] : '=';
- }
- return [[[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding] autorelease];
- }
- //收到内存警告释放资源
- - (void) didReceiveMemoryWarning
- {
- [super didReceiveMemoryWarning];
- // Dispose of any resources that can be recreated.
- }
- //回收内存
- -(void) dealloc
- {
- [[SKPaymentQueue defaultQueue] removeTransactionObserver:self];//解除监听
- [spinner release]; spinner = nil;
- [super dealloc];
- }
- @end