首先你必须熟悉内支付的流程,可以到网上搜搜看。
前几天因为需要做了个苹果的内支付,当中遇到了不少苦头,感觉网上的各种博客对内支付这一块都是点到即止,没有真真正正实现自己的需求。正所谓辛苦就有所得,通过东接西搬,终于内力小成,特与大家分享一下自己的经验。这里我假设你已经把商品的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]; 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];//可以添加多个商品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]; [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],@"params", @"1136",@"uid", 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
如果你有更好的内支付码码 请你不要吝啬告诉我 QQ:1065874255 多谢.
<p><span style="font-size:18px; color:#999999"><strong> </strong></span></p>