SDK接入(3)之iOS內(nèi)支付(In-App Purchase)接入
繼整理了Android平臺的SDK接入過程谱煤。再來分享下iOS平臺的內(nèi)支付(In-App Purchase)接入,作為筆者在游戲開發(fā)中實際遇到的兴使,覺得有必要分享下赃磨,同時也當作是對工作的總結(jié)瞬浓,就放在該SDK接入系列文章中了惠遏。
作為SDK接入系列舀武,同時也是Android平臺的SDK接入有:
SDK接入(2)之Android Google Play內(nèi)支付(in-app Billing)接入
SDK接入(1)之Android Facebook SDK接入
這里提一點拄养,SDK的接入,官方文檔肯定最詳細,最準確瘪匿,而且有時效性跛梗,接入流程變化,API修改更新棋弥,肯定最終都以官方的為準核偿。那么,蘋果官方內(nèi)支付(IAP)接入文檔地址為:
iOS內(nèi)支付流程
1.商品種類
在了解蘋果IAP內(nèi)支付之前顽染,有必要先了解下蘋果的商品種類漾岳。在蘋果In-App Purchase Programming Guide文檔上寫明了,商品種類分為如下幾種粉寞。接過GooglePlay支付的會發(fā)現(xiàn)尼荆,這點還是很相似的。
(1)消耗類商品
每次使用都須從新購買唧垦。
(2)非消耗類商品
購買一次即可捅儒。系統(tǒng)會自己購買狀態(tài),且會同步所有用戶設(shè)備都一直保持可用狀態(tài)振亮。
(3)自動再生訂閱
例如:一本書的章節(jié)內(nèi)容巧还。
(4)非自動再生訂閱
例如:一個航班表。
(5)免費訂閱
例如:報刊雜志等双炕。
消耗類與非消耗類商品的區(qū)別:
訂閱類商品的區(qū)別:
2.支付流程
對于IAP整個下單到支付過程狞悲,下圖很形象的說明了該步驟:
(1) 應(yīng)用向服務(wù)器發(fā)送請求,獲得一份產(chǎn)品列表妇斤。
(2) 服務(wù)器返回包含
商品標識符
的列表摇锋。(3) 應(yīng)用向App Store發(fā)送請求,得到商品的信息站超。
(4) App Store返回商品信息荸恕。
(5) 應(yīng)用把返回的商品信息顯示在UI界面上。
(6) 用戶選擇某個商品死相。
(7) 應(yīng)用向App Store發(fā)送支付請求融求。
(8) App Store處理支付請求并返回交易完成信息。
(9) 應(yīng)用從信息中獲得數(shù)據(jù)算撮,并發(fā)送至服務(wù)器生宛。
(10) 服務(wù)器紀錄數(shù)據(jù),并進行校驗肮柜。
(11) 服務(wù)器將數(shù)據(jù)發(fā)給App Store來驗證該交易的有效性陷舅。
(12) App Store對收到的數(shù)據(jù)進行解析,返回該數(shù)據(jù)和說明其是否有效的標識审洞。
(13) 服務(wù)器讀取返回的數(shù)據(jù)莱睁,確定用戶購買的內(nèi)容。
(14) 服務(wù)器將購買的內(nèi)容傳遞給程序。
3.配置商品
(1)打開iTunes Connect后臺
用開發(fā)者帳號仰剿,登錄iTunes Connect,企業(yè)級用戶需用主開發(fā)者帳號创淡。
(2)配置iTunes Connect
在iTunes Connect后臺添加應(yīng)用,并配置App內(nèi)購買項目
南吮,由于我們游戲中的鉆石琳彩、金幣等都屬于消耗型商品,因此旨袒,直接選的這個汁针。需注意下配置的Bundle id須和項目plist中的Bundle id一致术辐。并添加沙箱測試帳號砚尽。
注意:商品Id不可重復(fù),如果刪除某個商品辉词,以后這個商品的ID也不可用必孤,即使它已經(jīng)被刪除了;另外類型也不能改瑞躺,選錯了只能重新增加一個商品敷搪。
iOS內(nèi)支付接入
1. 項目工程引入StoreKit.framework
2. 這里推薦一個叫IAPHelper
的開源封裝,有效的封裝支付流程幢哨,進一步簡化了接入的效率赡勘。所以,下面也是基于該項目進行的接入捞镰。IAPHelper
可自行Github搜索闸与。
(1)InAppRageIAPHelper.m。在init中初始化商品id列表岸售。
#import "InAppRageIAPHelper.h"
#import "InAppRageIAPHelper.h"
@implementation InAppRageIAPHelper
@synthesize orderInfo = _orderInfo;
static InAppRageIAPHelper * _sharedHelper;
+ (InAppRageIAPHelper *) sharedHelper {
if (_sharedHelper != nil) {
return _sharedHelper;
}
_sharedHelper = [[InAppRageIAPHelper alloc] init];
return _sharedHelper;
}
- (void)dealloc
{
[_orderInfo release];
_orderInfo = nil;
[super dealloc];
}
- (id)init {
NSSet *productIdentifiers = [NSSet setWithObjects:
@"com.game.test.10001",
@"com.game.test.10002",
@"com.game.test.10003",
@"com.game.test.10004",
@"com.game.test.10005",
nil];
if ((self = [super initWithProductIdentifiers:productIdentifiers])) {
}
return self;
}
@end
(2)注冊本地通知践樱。一般在應(yīng)用啟動時,添加如下代碼:(productsLoaded凸丸、productPurchased拷邢、productPurchaseFailed分別對應(yīng)支付過程中三種加載中,支付完成屎慢,支付失敗狀態(tài)回調(diào)瞭稼,可根據(jù)實際情況作對應(yīng)的處理)
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(productsLoaded:) name:kProductsLoadedNotification object:nil];
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(productPurchased:) name:kProductPurchasedNotification object:nil];
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(productPurchaseFailed:) name:kProductPurchaseFailedNotification object:nil];
[[InAppRageIAPHelper sharedHelper]requestProducts];
(3)發(fā)起支付。
if ([SKPaymentQueue canMakePayments]) {
[[InAppRageIAPHelper sharedHelper]buyProductIdentifier:[self getItemId] game_order:[self getOrderId]];
} else {
// 不允許程序內(nèi)付費購買
}
(4)支付成功的回調(diào)腻惠。這里將AppStore返回的數(shù)據(jù)环肘,進行Base64加密,然后再發(fā)送給游戲服務(wù)器進行校驗妖枚。同時廷臼,本地也會存儲返回的票據(jù)receipt,防止在發(fā)送給服務(wù)器過程中請求失敗,造成的充值成功但不到賬的漏單
現(xiàn)象荠商。
-(NSData*)receiptWithTransation:(SKPaymentTransaction*) transcation {
NSData *receipt = nil;
if ([[NSBundle mainBundle]respondsToSelector:@selector(appStoreReceiptURL)]) {
NSURL *receiptUrl = [[NSBundle mainBundle]appStoreReceiptURL];
receipt = [NSData dataWithContentsOfURL:receiptUrl];
} else {
if ([transcation respondsToSelector:@selector(transactionReceipt)]) {
receipt = [transcation transactionReceipt];
}
}
return receipt;
}
-(void)productPurchased:(NSNotification*) notification {
[NSObject cancelPreviousPerformRequestsWithTarget:self];
SKPaymentTransaction *transaction = (SKPaymentTransaction*)notification.object;
NSData *receipt = [self receiptWithTransation:transaction];
NSString *base64Receipt = [receipt base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
[[InAppRageIAPHelper sharedHelper].orderInfo setObject:[NSString stringWithFormat:@"%@", base64Receipt] forKey:@"originReceipt"];
NSString *m_params = [self makeHttpParams:base64Receipt];
if (m_params != nil) {
[self saveReceipt:m_params];
[self postGameServer:[self getPayUrl] params:m_params];
}
}
(5)漏單檢測寂恬。下次,啟動時莱没,會進行漏單檢測初肉。若存在本地票據(jù)receipt,就向游戲服務(wù)器發(fā)起請求饰躲。直到游戲服務(wù)器返回成功牙咏,再刪除本地的票據(jù)receipt。
NSUserDefaults *userDefalut = [NSUserDefaults standardUserDefaults];
NSMutableDictionary *receiptDict = [NSMutableDictionary dictionaryWithDictionary:[userDefalut objectForKey:@"receipts"]];
NSEnumerator *enumerator = [receiptDict objectEnumerator];
for (NSObject *obj in enumerator) {
[self postGameServer:[self getPayUrl] params:[NSString stringWithFormat:@"orderdata=%@",obj]];
}
(6)由于嘹裂,我們的校驗是放在服務(wù)器進行的妄壶,所以,這里就不進行過多的介紹了寄狼。簡單說下丁寄,App Store正式環(huán)境校驗地址是https://buy.itunes.apple.com/verifyReceipt ,測試環(huán)境校驗地址是:https://sandbox.itunes.apple.com/verifyReceipt泊愧。
iOS支付安全問題
對于某些越獄設(shè)備來說伊磺,如果校驗流程有漏洞的話,使用某些神器删咱,就能繞過Appstore的付費流程,偽造訂單屑埋,達到免支付體驗各種付費功能。
其中列舉如下神器:
(1)ap cracker:越獄軟件可以截獲付費請求痰滋,并直接返回付費成功摘能。
(2)iap free: 截獲付費請求的同時,還能截獲客戶端發(fā)起的驗證請求 即寡,返回驗證成功的數(shù)據(jù) 徊哑,返回的數(shù)據(jù)和官方的數(shù)據(jù)并不是完全一樣,可以識別出來是否作弊聪富,但是不保證永久有效莺丑。
因此,首先在支付成功之后墩蔓,要將支付成功返回的票據(jù)發(fā)送給服務(wù)器梢莽,在服務(wù)器端作驗證,根據(jù)服務(wù)器的驗證結(jié)果來做相應(yīng)的處理奸披。其次昏名,本地對應(yīng)偽造的票據(jù)進行過濾。