iap支付总结

最近应公司要求,集成iap支付,由于之前没做过,公司就我一个iOS,所以遇见不少坑,最后终于写完了,鉴于网上代码多的是缺胳膊少腿,所以我来一份全的,无废话的. 包括服务器验证,检验漏单的两种方式的.希望可以帮助到后续的伙伴们.

In-App-Purchase

首先、 先熟悉一下整个iap支付的基本流程

1. 程序向服务器发送请求,获得一份产品列表。

2. 服务器返回包含产品标识符的列表。 // 就是获得产品ID < productID>

3. 程序向App Store发送请求,得到产品的信息。

4. App Store返回产品信息。

5. 程序把返回的产品信息显示给用户(App的store界面)

6. 用户选择某个产品

7. 程序向App Store发送支付请求

8. App Store处理支付请求并返回交易完成信息。

9. 程序从信息中获得数据,并发送至服务器。

10. 服务器纪录数据,并进行审(我们的)查。

11. 服务器将数据发给App Store来验证该交易的有效性。

12. App Store对收到的数据进行解析,返回该数据和说明其是否有效的标识。

13. 服务器读取返回的数据,确定用户购买的内容。

14. 服务器将购买的内容传递给程序。

其次、你需要在开发者中--Itunes Connect  ---我的App-----创建新应用----填写计费点,产品ID<功能-内部付费>

-- 账户与职能----创建沙盒测试账号                           // <由于这部分内容比较简单不赘述>

然后, 就是iOS端的代码开发了

第一步:

1、 在工程中引入 storekit.framework 和 #import <StoreKit/StoreKit.h>

2、 获得所有的付费Product ID列表。这个可以用常量存储在本地,也可以由自己的服务器返回。

代码: 因为我是接着别人的代码写,之前用ASIHttpRequest 进行网络请求,所以我也继承了,你们用AFN,就用AFN

#import "BPIAPPurchaseClass.h"

#import <StoreKit/StoreKit.h>

#import "ASIHTTPRequest.h"

#import "BPOrderInfo.h"

#import "MBProgressHUD.h"

@interface BPIAPPurchaseClass ()<SKProductsRequestDelegate,SKPaymentTransactionObserver,MBProgressHUDDelegate,ASIHTTPRequestDelegate,HttpRequestBaseDelegate>

{

NSString* BPyInAppIdentifier;

BPOrderInfo *orderInfoPlist;   // 订单Model

UIActivityIndicatorView* spinner;

NSArray *products;    // 商品Array

NSArray *orderInfoArray;

MBProgressHUD *hud;

ASINetworkQueue *RequestQueue;

id<HttpRequestBaseDelegate> delegate;

SKPaymentTransaction *transactionList;

NSString * money;

SKProduct *skProduct;

NSArray *productArray;

NSDictionary *productDic;

}

@end

-(void)dealloc

{

[orderInfoPlist release];

[[SKPaymentQueue defaultQueue] removeTransactionObserver:self];

[super dealloc];

}

- (instancetype)init

{

self = [super init];

if (self) {

[[SKPaymentQueue defaultQueue] addTransactionObserver:self];

}

return self;

}

+(BPIAPPurchaseClass *)SharedBPApplePayPlatform{

static BPIAPPurchaseClass *applePayClass = nil;

static dispatch_once_t onceToken;

dispatch_once(&onceToken, ^{

applePayClass = [[BPIAPPurchaseClass alloc]init];;

});

return applePayClass;

}

// 加载菊花

-(void)showBPProgressHUD

{

UIWindow* window = [UIApplication sharedApplication].keyWindow;

if (!window)

{

window = [[UIApplication sharedApplication].windows lastObject];

}

NSLog(@"rootViewController = %@",window.rootViewController);

hud = [[MBProgressHUD alloc]initWithView:window.rootViewController.view];

hud.center = window.center;

[window.rootViewController.view addSubview:hud];

hud.color = [UIColor blackColor];

//   hud.alpha = 0.5;

hud.delegate = self;

hud.removeFromSuperViewOnHide = YES;

hud.labelText = @"正在请求数据,请稍候";

[hud show:YES];

[hud release];

}

//请求数据,也可以直接在按钮里调用,传过来 product_id

-(void)ApplePurchaseWithProductInfo:(BPOrderInfo *)orderInfo

{

BPyInAppIdentifier = orderInfo.productId;

orderInfoPlist = orderInfo;

//      [self showBPProgressHUD];   //加载菊花打开

NSArray *array = @[BPyInAppIdentifier];

if ( [SKPaymentQueue canMakePayments]) {

NSSet *set = [NSSet setWithArray:array];

SKProductsRequest * appStoreRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:set];

appStoreRequest.delegate = self;

[appStoreRequest start];

NSLog(@"允许应用内支付");

}else{

NSLog(@"不允许应用内支付");

}

}

//判断是否可以应用内支付

- (BOOL) canInAppPayment

{

//你应该根据是否想让越狱的iOS设备允许内支付

if ([[NSFileManager defaultManager] fileExistsAtPath:@"/Applications/Cydia.app"]){

NSLog(@"越狱版设备!");

return NO;

}

if ([SKPaymentQueue canMakePayments]) {

return YES;

} else {

return NO;

}

}

#pragma mark -SKProductsRequestDelegate 获取appstroe产品信息

-(void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response

{

NSLog(@"@--------------收到appStore返回产品信息");

NSArray *gameProduct = response.products;

if (gameProduct.count == 0) {

NSLog(@"无法获取产品信息,购买失败");

return;

}

for (SKProduct *product in gameProduct) {

money = [NSString stringWithFormat:@"%@",product.price];

NSLog(@"@-------money = %@-------收到appStore返回产品信息",money);

NSLog(@"产品id==%@",response.invalidProductIdentifiers);

NSLog(@"产品数量==========%lu",(unsigned long)gameProduct.count);

NSLog(@"产品标题 %@ ", product.localizedTitle);

NSLog(@"产品描述信息: %@ ", product.localizedDescription);

NSLog(@"价格: %@" , product.price);

NSLog(@"Product id: %@ ", product.productIdentifier);

SKMutablePayment *Mpayment = [SKMutablePayment paymentWithProduct:product];

if ([[SKPaymentQueue defaultQueue] respondsToSelector:@selector(addPayment:)]) {

[[SKPaymentQueue defaultQueue] addPayment:Mpayment];  //请求已经生效

NSLog(@"请求生效");

}

}

[request release];

}

- (void)completeTransaction:(SKPaymentTransaction *)transaction {

NSLog(@"-------------------支付完成-----transaction=%@------------",transaction.transactionIdentifier);

//    [hud hide:YES];

orderInfoPlist.orderId = transaction.transactionIdentifier;

NSString * productIdentifier = transaction.payment.productIdentifier;

if ([productIdentifier length]>0) {

// 向自己的服务器验证购买凭证

[self verifyReceiptFromCompanyServer:transaction];   //这个方法是内支付最重要的方法

}

[[SKPaymentQueue defaultQueue] finishTransaction: transaction];

}

- (void)request:(SKRequest *)request didFailWithError:(NSError *)error

{

//处理错误

//   [hud hide:YES];

[request cancel];

request = nil;

}

// 交易失败,通知IAP进行UI刷新

- (void)failedTransaction:(SKPaymentTransaction *)transaction {

if(transaction.error.code != SKErrorPaymentCancelled) {

NSLog(@"购买失败");

[BPCustomPromptBox showWithTitle:[BPLanguage getStringForKey:@"支付请求失效" InTable:@"BPMultiLanguage"] AndDisappearSecond:2];

} else {

NSLog(@"用户取消交易");

}

[[SKPaymentQueue defaultQueue] finishTransaction: transaction];

}

// 二次验证的过程

//向服务器请求求验证

// Send the Receipt Data to the App Store

#pragma amrk --- 注意iOS9的session请求

- (void)verifyReceiptFromCompanyServer:(SKPaymentTransaction *)transaction

{

NSURLRequest *urlRequest = [NSURLRequest requestWithURL:[[NSBundle mainBundle] appStoreReceiptURL]];//苹果推荐

if (!urlRequest) { /* ... error ... */

return;

}

NSURLSession *session = [NSURLSession sharedSession];

NSURLSessionTask *task = [session dataTaskWithRequest:urlRequest completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {

//        NSLog(@"二次验证的data ====== %@",data);

NSLog(@"这是iap支付的二次验证流程");

NSString *base64String = [ASIHTTPRequest base64forData:data];

//        NSDictionary *receiptDictionary = @{@"receipt-data":base64String};

//

//         NSLog(@"二次验证的receiptDictionary ====== %@",receiptDictionary);

//

//        NSData *Receivedata = [NSJSONSerialization dataWithJSONObject:receiptDictionary options:NSJSONWritingPrettyPrinted error:&error];

//      NSLog(@"二次验证的提交验证的数据data ====== %@",Receivedata);

if(!orderInfoPlist.callBackUrl)

{

orderInfoPlist.callBackUrl = @"0";

}

NSMutableDictionary *dicc = [NSMutableDictionary dictionary];

[dicc setValue:base64String forKey:@"receipt"];

[dicc setValue:money forKey:@"money"];

[dicc setValue:[NSString stringWithFormat:@"%d",orderInfoPlist.ServerId] forKey:@"serverId"];

[dicc setValue:version forKey:@"version"];

[dicc setValue:[NSString stringWithFormat:@"%d",(int)[[NSDate date] timeIntervalSince1970]] forKey:@"time"];

[dicc setValue:[BPUserPreferences CurrentChannelId] forKey:@"channelId"];

[dicc setValue:transaction.transactionIdentifier  forKey:@"orderId"];

NSString *str_sing = [BPUtility md5String:[NSString stringWithFormat:@"%@&key=jM2ilRIsu9dhDOwH",[self getHttpSing:dicc]]];

//        NSString *str = [self getHttpSing:dicc];

//

//        NSLog(@"排序后的字符串 = %@",str);

NSLog(@"dic.count = %ld",(unsigned long)dicc.count);

#pragma mark ----------- 交易信息本地化---------------------

NSString *library = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) firstObject];

library = [library stringByAppendingFormat:@"/Caches"];

NSFileManager *manger = [NSFileManager defaultManager];

NSString  *path2 = [library stringByAppendingPathComponent:@"signReceipt"];

NSString  *path3 = [library stringByAppendingPathComponent:@"orderInfo"];

[manger createDirectoryAtPath:path2 withIntermediateDirectories:YES attributes:nil error:nil]; // 签名文件夹

NSString *signPath = [NSString stringWithFormat:@"%@/%@",path2,@"sign.txt"];

NSMutableArray *SignArray = [NSMutableArray array];

[SignArray addObject:str_sing];

[SignArray writeToFile:signPath  atomically:YES];

NSLog(@"本地化签名信息path = %@",signPath);

[manger createDirectoryAtPath:path3 withIntermediateDirectories:YES attributes:nil error:nil]; // 签名文件夹

NSString *orderPath = [NSString stringWithFormat:@"%@/%@",path3,@"orderInfo.txt"];

[dicc writeToFile:orderPath  atomically:YES];

NSLog(@"本地化用户信息path = %@",orderPath);

//  NSLog(@"dicc==== %@",signDic);

BPHttpRequestBase *httpBase = [[BPHttpRequestBase alloc]init];

ASIFormDataRequest *receiptRequest  = [httpBase CreateRequestWithUrl:@"服务器二次验证url" SetInfoTag:@"IapReceiptVerify"];

httpBase.delegate = self;

if (!receiptRequest)

{

[receiptRequest cancel];

receiptRequest = nil;

}

[receiptRequest setRequestMethod:@"POST"];

[receiptRequest addPostValue:@"webpay" forKey:@"action"];

[receiptRequest addPostValue:@"ios" forKey:@"mode"];

[receiptRequest addPostValue: str_sing forKey:@"sign"];

[receiptRequest addPostValue:[NSString stringWithFormat:@"%d",(int)[[NSDate date] timeIntervalSince1970]] forKey:@"time"];

[receiptRequest addPostValue:[BPUtility macaddress] forKey:@"mac"];

[receiptRequest addPostValue:transaction.transactionIdentifier  forKey:@"orderId"];    //**订单号

[receiptRequest addPostValue:@"verifyReceipt" forKey:@"method"];

[receiptRequest addPostValue:money forKey:@"money"];     // 产品的钱数

[receiptRequest addPostValue:base64String forKey:@"receipt"];

receiptRequest.delegate = self;

[httpBase.RequestQueue addOperation:receiptRequest];

[httpBase.RequestQueue go];

[receiptRequest startAsynchronous];   //异步请求序列

}];

[task resume];

}

*/

-(void)requestFinished:(ASIHTTPRequest *)request

{

NSString *responseString = [request responseString];

if (![request responseString]) {

return ;

}

NSLog(@"request.url== %@-----二次验证结果----responseString= %@",request.url,responseString);

NSDictionary *userInfo = request.userInfo;

[BPQLoadingView hideWithAnimated:NO];

if([[userInfo objectForKey:@"RequestTag"] isEqualToString:@"IapReceiptVerify"])

{

NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:[request responseData] options:NSJSONReadingMutableContainers error:nil];

NSLog(@"是否漏单服务器返回验证结果jsonDic = %@",dic);

int response = [[dic objectForKey:@"result"] intValue];

NSString *product_id = [dic objectForKey:@"product_id"];

// iap二次验证成功,支付成功

if(response == 0)

{

NSLog(@"支付验证成功,无漏单");

}

if(response == 21003)

{

NSLog(@"警告:出现漏单啦!!!");

//        BPOrderInfo *orderInfo = [[BPOrderInfo alloc]init];

//            orderInfo.productId = product_id;

//        [[BPIAPPurchaseClass SharedBPApplePayPlatform] ApplePurchaseWithProductInfo:orderInfo];

//        [orderInfo release];

NSLog(@"漏单的产品ID = %@",product_id);

}

if (response == -1) {

NSLog(@"支付签名错误");

}if (response == -3){

NSLog(@"订单已经到账");

}

}

}

#pragma mark - 购买商品

- (void)buyProduct:(SKProduct *)product

{

// 1.创建票据

SKPayment *payment = [SKPayment paymentWithProduct:product];

// 2.将票据加入到交易队列中

[[SKPaymentQueue defaultQueue] addPayment:payment];

}

- (void)requestFailed:(ASIHTTPRequest *)request

{

//出错处理

//    [hud hide:YES];

}

#pragma mark-SKPayment TransactionObserver支付结果

//----监听购买结果

//然后当用户输入正确的appStore帐号密码后进入(再次说明 如果是测试 必须是你注册的测试账号 不能使用真实的AppleID )

//方法在新交易被创建或更新时都会被调用

- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions

{

for (SKPaymentTransaction *transaction in transactions) {

switch (transaction.transactionState) {

case SKPaymentTransactionStatePurchased: //交易完成

NSLog(@"交易完成transactionIdentifier= %@", transaction.transactionIdentifier);

[self completeTransaction:transaction];

break;

case SKPaymentTransactionStateFailed://交易失败

[self failedTransaction:transaction];

//                [BPCustomPromptBox showWithTitle:[BPLanguage getStringForKey:@"交易失败, 请重试" InTable:@"BPMultiLanguage"] AndDisappearSecond:2];

NSLog(@"交易失败");

break;

case SKPaymentTransactionStateRestored://已经购买过该商品

[self restoreTransaction:transaction];

NSLog(@"已买过商品");

break;

case SKPaymentTransactionStatePurchasing: //商品添加进列表

NSLog(@"商品添加进列表");

break;

default:

//             [hud hide:YES];

break;

}

}

}

- (void)paymentQueue:(SKPaymentQueue *)queue removedTransactions:(NSArray *)transactions

{

NSLog(@"---------------移除商品-------------");

//     [hud hide:YES];

}

- (void)paymentQueue:(SKPaymentQueue *)queue restoreCompletedTransactionsFailedWithError:(NSError *)error

{

NSLog(@"---------------重复支付失败-------------");

}

-(void)restoreTransaction: (SKPaymentTransaction *)transaction

{

NSLog(@"---------------重复支付-----------------");

// 对于已经购买的产品,恢复其处理逻辑

[[SKPaymentQueue defaultQueue] finishTransaction:transaction];

}

- (void)requestDidFinish:(SKRequest *)request {

NSLog(@"请求结束");

//     [hud hide:YES];

}

// 签名字典排序

- (NSString *)getHttpSing:(NSMutableDictionary *)dic

{

NSString *str = nil;

NSMutableArray *parameters_array = [NSMutableArray arrayWithArray:[dic allKeys]];

[parameters_array sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {

return [obj1 compare:obj2];

//return [obj2 compare:obj1];//降序

} ];

for (int i = 0; i<parameters_array.count; i++) {

NSString *key = [parameters_array objectAtIndex: i];

NSString * value = [dic objectForKey:key];

if (i==0) {

str = [NSString stringWithFormat:@"%@=%@",key,value] ;

}else{

str = [NSString stringWithFormat:@"%@&%@=%@",str,key,value];

}

}

return str;

}

楼主博客技术支持QQ: 1428542683

时间: 2025-01-11 13:15:36

iap支付总结的相关文章

【IAP支付之三】苹果IAP安全支付与防范 receipt收据验证

这里网上的朋友已经介绍的很详细了,具体的链接已经无法找到了. 这里主要说几点本人在开发中遇到的问题: 1.漏单必须要处理,玩家花RMB购买的东西却丢失了,是绝对不能容忍的.所谓的漏单就是玩家已经正常付费,却没有拿到该拿的道具. 解决:只要购买成功,便将购买记录(receipt等账单信息)保存下来,然后将账单信息传送给我们游戏服务器,游戏服务器获得账单后,和苹果服务器验证,账单有效的话,回馈给游戏服务器处理,游戏服务器处理后,返回给游戏客户端处理,处理完毕,将本地保存的购买记录删除. 2.漏单的检

【IAP支付之二】In app purchase 本地购买和服务器购买两种购买模式

开发者在游戏中通过iap让玩家支付,实际开发中遇到的问题: 1.StoreKit和AppStore之间的通信 一开始有担心游戏内部和AppStore通信的中间环节,其实这个完全不用考虑,StoreKit和AppStore之间的通信完全是有苹果内部处理的,不存在任何问题,我们所需要关心的就是返回给我们支付结果后(购买成功.失败.取消...)等环节的处理 Store Kit代表App和App Store之间进行通信.程序将从App Store接收那些你想要提供的产品的信息(网站后台配置的),并将它们

iOS支付

1.IOS purchase 介绍 所谓的IOS 内支付就是在应用中内嵌Store,在iOS APP 中使用Store Kit framework来实现In-App Purchase,Store Kit会连接App Store,代替应用安全地完成用户支付的支付行为.Store Kit提示用户授权支付,然后通知应用“用户已经完成支付”,这样应用就可以提供用户购买的东西. 2.IOS内支付开发流程 IOS 内支付有两种模式: 1) 内置模式 2) 服务器模式 内置模式的流程可以简单的总结为以下几步:

cocos2dx平台对接(Amazon IAP,Chartboost,Email)

主要目的是备忘,如果有幸能帮上其他朋友,那最好了 我的游戏使用的是cocos2dx 2.2.5,当然,使用2.1.x ~ 2.2.5版本都是可以的,没有什么变动. 一.Java调用C++ 1.在JAVA项目中,声明一个带“native”的静态函数,比如 (假设包名是 com.ooxx.mygame class MyActivity{ … public static native int onIAPCallback(int select); … } 在JAVA项目中,你就当这个函数是一个普通的静态

移动mm 话费支付接入过程(ane)

以下记录移动mm 话费支付接入的过程 1.强联网.弱联网区别,sdk是否有区分?用户体验部分由什么不同和差异? 区别在于强联网是网络通道(wifi/gprs/3g),弱联网是走短信通道,用户层面差异在于强联网需要手机联网支付,弱联网随时随地都可以支付. 强联网和弱联网采用不同的sdk,强联网对外开放无需申请,弱联网需要申请才能接入.并且有接入门框. 2.接入关键流程 1)注册开发者账号 2)填写开发商各种资料,电子签约(需要审核,不影响sdk接入)(这一步弄好了才能收到钱,哈哈) 3)创建iap

IAP 破解漏洞验证

IAP支付有个漏洞,用户使用的可能是IAP Free 或者俄罗斯破解什么的,所产生的交易号:170000029449420 product_id:com.zeptolab.ctrbonus.superpower1 忽略了除了去Appstore验证receipt 还需要验证product_id 并且交易号transaction_id 是唯一的 记录一下

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

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

微信WeixinJSBridge API使用实例

http://www.jb51.net/article/66642.htm 这篇文章主要介绍了微信WeixinJSBridge API使用实例,本文直接给出HTML代码,代码中包含了很多实用功能,如图片预览.分享到微博.隐藏右上角按钮.获取网络状态.发起公众号微信支付等内容,需要的朋友可以参考下 注意:请在微信中测试 1 <!DOCTYPE html> 2 <html> 3 <head> 4 <title>微信WeixinJSBridge API</t

微信WeixinJSBridge API

<!DOCTYPE html><html> <head> <title>微信WeixinJSBridge API</title> <meta charset="utf-8" /> <script type="text/javascript">(function(){var a=document.getElementsByTagName("html")[0];win