服務(wù)器模式:
使用這種方式,要提供另外的服務(wù)器將產(chǎn)品發(fā)送給程序了罪。 服務(wù)器交付適用于訂閱锭环、內(nèi)容類商品和服務(wù),因為商品可以作為數(shù)據(jù)發(fā)送泊藕,而不需改動程序束辅辩。
例如,一個游戲提供的新的內(nèi)容(關(guān)卡等)。 Store Kit不會對服務(wù)器端的設(shè)計和交互做出定義玫锋,這方面工作需要你來完成蛾茉。 而且,Store
Kit不提供驗證用戶身份的機(jī)制撩鹿,你需要來設(shè)計谦炬。 如果你的程序需要以上功能,例如节沦,紀(jì)錄特定用戶的訂閱計劃键思, 你需要自己來設(shè)計和實現(xiàn)。
服務(wù)器類型的購買過程
1. 程序向服務(wù)器發(fā)送請求甫贯,獲得一份產(chǎn)品列表吼鳞。
2. 服務(wù)器返回包含產(chǎn)品標(biāo)識符的列表。
3. 程序向App Store發(fā)送請求获搏,得到產(chǎn)品的信息赖条。
4. App Store返回產(chǎn)品信息失乾。
5. 程序把返回的產(chǎn)品信息顯示給用戶(App的store界面)
6. 用戶選擇某個產(chǎn)品
7. 程序向App Store發(fā)送支付請求
8. App Store處理支付請求并返回交易完成信息常熙。
9. 程序從信息中獲得數(shù)據(jù),并發(fā)送至服務(wù)器碱茁。
10. 服務(wù)器紀(jì)錄數(shù)據(jù)裸卫,并進(jìn)行審(我們的)查。
11. 服務(wù)器將數(shù)據(jù)發(fā)給App Store來驗證該交易的有效性纽竣。
12. App Store對收到的數(shù)據(jù)進(jìn)行解析墓贿,返回該數(shù)據(jù)和說明其是否有效的標(biāo)識。
13. 服務(wù)器讀取返回的數(shù)據(jù)蜓氨,確定用戶購買的內(nèi)容聋袋。
14. 服務(wù)器將購買的內(nèi)容傳遞給程序。
Apple建議在服務(wù)器端存儲產(chǎn)品標(biāo)識穴吹,而不要將其存儲在plist中幽勒。 這樣就可以在不升級程序的前提下添加新的產(chǎn)品。
在服務(wù)器模式下港令, 你的程序?qū)@得交易(transaction)相關(guān)的信息啥容,并將它發(fā)送給服務(wù)器。服務(wù)器可以驗證收到的數(shù)據(jù)顷霹,并將其解碼以確定需要交付的內(nèi)容咪惠。 這個流程將在“驗證store收據(jù)”一節(jié)討論。
對于服務(wù)器模式淋淀,我們有安全性和可靠性方面的顧慮遥昧。 你應(yīng)該測試整個環(huán)境來避免威脅。《Secure Coding Guide》文檔中有相關(guān)的提示說明炭臭。
雖然非消耗性商品可以用內(nèi)置模式來恢復(fù)叫乌,訂閱類商品必須通過服務(wù)器來恢復(fù)。你要負(fù)責(zé)紀(jì)錄訂閱信息徽缚、恢復(fù)數(shù)據(jù)憨奸。 消耗類商品也可以通過服務(wù)器方式來紀(jì)錄。例如凿试,由服務(wù)器提供的一項服務(wù)排宰, 你可能需要用戶在多個設(shè)備上重新獲得結(jié)果。
蘋果服務(wù)端配置指南:
使用IAP內(nèi)購的準(zhǔn)備工作那婉。通常需要經(jīng)過以下幾個步驟(下面的準(zhǔn)備工作是針對真機(jī)的Provisioning Profile配置過程板甘,模擬器無法測試IAP內(nèi)購):
1.在蘋果開發(fā)者中心創(chuàng)建支持IAP服務(wù)的App ID并指定具體的Bundle ID,假設(shè)是“com.tj.xxx”(注意這個Bundle ID就是日后要開發(fā)的游戲的Bundle ID)详炬。
2.基于“com.tj.xxx”創(chuàng)建開發(fā)者配置文件(或描述文件)并導(dǎo)入對應(yīng)的設(shè)備(創(chuàng)建過程中選擇支持IAP內(nèi)購服務(wù)的App ID盐类,這樣iOS設(shè)備在運(yùn)行指定Boundle ID應(yīng)用程序就知道此應(yīng)用支持IAP內(nèi)購服務(wù))。
3.在iTunes Connect中創(chuàng)建一個應(yīng)用(假設(shè)叫“IAPTest”呛谜,這是一款含有內(nèi)購的游戲)并指定“套裝ID”為之前創(chuàng)建的“com.tj.xxx”在跳,讓應(yīng)用和這個App關(guān)聯(lián)(注意這個應(yīng)用不需要提交)。
4.在iTunes Connect的“用戶和職能”中創(chuàng)建沙盒測試用戶隐岛。(測試階段用沙盒用戶可以進(jìn)行購買猫妙,購買任何東西不用擔(dān)心被扣錢)。
5.到iTuens Connect中設(shè)置“App 內(nèi)購買項目”聚凹,這里仍然以上面的“IAPest”項目為例割坠,假設(shè)這個游戲中有一種道具,為“能量瓶”(為玩家提供能量)妒牙,@“能量瓶”屬于消耗品彼哼,用完一次必須再次購買。
6.到iTunes Connect中找到“協(xié)議湘今、稅務(wù)和銀行業(yè)務(wù)”增加“iOS Paid Applications”協(xié)議敢朱,并完成所有配置后等待審核通過(注意這一步如果不設(shè)置在應(yīng)用程序中無法獲得可購買產(chǎn)品)。
在iOS“設(shè)置”中找到”iTunes Store與App Store“象浑,在這里可以選擇使用沙盒用戶登錄或者處于注銷狀態(tài)蔫饰,但是一定注意不能使用真實用戶登錄,否則下面的購買測試不會成功愉豺,因為到目前為止我們的應(yīng)用并沒有真正通過蘋果官方審核篓吁,所以只能用沙盒測試用戶。
7.有了上面的設(shè)置之后保證應(yīng)用程序Bundle ID和iTunes Connect中的Bundle ID(或者說App ID中配置的Bundle ID)一致即可準(zhǔn)備開發(fā)蚪拦。
ios客戶端
#import
@interface JarIAPManager ()
{
id _observer;
}
@end
@implementation JarIAPManager
+ (instancetype)defaultManager{
static JarIAPManager *defaultManager = nil;
static dispatch_once_t onceToken = 0;
dispatch_once(&onceToken, ^{
defaultManager = [[JarIAPManager alloc]? init];
});
return defaultManager;
}
- (instancetype)init{
self = [super init];
if (self) {
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
_observer = [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidEnterBackgroundNotification object:self queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
for (SKPaymentTransaction * paymentTransaction in [SKPaymentQueue defaultQueue].transactions) {
[[SKPaymentQueue defaultQueue] finishTransaction:paymentTransaction];
}
}];
}
return self;
}
- (void)dealloc{
[[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
[[NSNotificationCenter defaultCenter] removeObserver:_observer];
_observer = nil;
}
-(BOOL)iapEnable{
return [SKPaymentQueue canMakePayments];
}
#pragma mark --purchase product
//根據(jù)產(chǎn)品標(biāo)識符去購買產(chǎn)品--[購買結(jié)果]
- (void)purchaseProductWithIdenfifier:(NSString *)productIdentifier Order:(NSString *)Order{
//該字符串標(biāo)識一個特定的產(chǎn)品和用戶原意購買的數(shù)量
SKMutablePayment * payment = [[SKMutablePayment alloc] init];
payment.productIdentifier = productIdentifier;
NSData *datas = [Order dataUsingEncoding:NSUTF8StringEncoding];
payment.requestData =datas;
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
//SKPaymentTransactionObserver協(xié)議---[更常用的做法還是等待支付隊列告知交易狀態(tài)的更新杖剪。]
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions{
__weak typeof(self)weakSelf = self;
for (SKPaymentTransaction * paymentTransaction in transactions) {
//小票狀態(tài)--》支付交易的狀態(tài)
switch (paymentTransaction.transactionState) {
case SKPaymentTransactionStatePurchasing:
{
NSLog(@"JarIAPManager: Transaction is being added to the server queue.");
}
break;
case SKPaymentTransactionStatePurchased:
{
NSLog(@"JarIAPManager: Transaction is in queue, user has been charged.? Client should complete the transaction.");
[weakSelf purchaseSuccessForTransaction:paymentTransaction];
}
break;
case SKPaymentTransactionStateFailed:
{
NSLog(@"JarIAPManager: Transaction was cancelled or failed before being added to the server queue.");
[weakSelf purchaseFailedForTransaction:paymentTransaction];
}
break;
case SKPaymentTransactionStateRestored:
{
NSLog(@"JarIAPManager: Transaction was restored from user's purchase history.? Client should complete the transaction.");
[[SKPaymentQueue defaultQueue] finishTransaction:paymentTransaction];
}
default:
break;
}
}
}
執(zhí)行
- (void)purchaseFailedForTransaction:(SKPaymentTransaction *)transaction{
NSLog(@"失敗流水--》%@",transaction.transactionIdentifier);
if (transaction != nil) {
[[Toast makeText:@"交易取消冻押。" duration:3000] show:NO];
}
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
- (void)purchaseSuccessForTransaction:(SKPaymentTransaction *)transaction{
__weak typeof(self)weakSelf = self;
// 驗證憑證,獲取蘋果返回的交易憑證
// appStoreReceiptURL iOS7.0增加的盛嘿,購買交易完成后洛巢,會將憑證存儲在該地址
NSURL * receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
// 從沙盒中獲取到購買憑證
NSData * receiptData = [NSData dataWithContentsOfURL:receiptURL];
//base64加密
NSString *encodeStr = [receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
//打包成字典
NSMutableDictionary * parDic = [NSMutableDictionary dictionaryWithCapacity:3];
NSString *Order = [[NSString alloc] initWithData:transaction.payment.requestData encoding:NSUTF8StringEncoding];
DICT_SET_STRING(Order,TAG_ORDER, parDic);
DICT_SET_STRING(encodeStr, TAG_RECEIPT_DATA, parDic);
NSLog(@"下單成功的Order--》%@",Order);
if (Order !=NULL) {
//提前緩存
[[JarIAPSqliteService sharedInstance] insertTestListTransactionWithDic:parDic];
//??????? //檢測是否已經(jīng)存儲過了
//??????? NSMutableArray *getTestDatas = [[NSMutableArray alloc] init];
//??????? getTestDatas = [[JarIAPSqliteService sharedInstance] getTestList];
//??????? NSLog(@"存儲后的Order個數(shù)-->%ld",(unsigned long)getTestDatas.count);
//??????? if (getTestDatas.count != 0) {
//??????????? for (NSMutableDictionary * dic in getTestDatas) {
//??????????????? NSLog(@"存儲后的Order---》%@",dic[TAG_ORDER]);
//??????????? }
//??????? }else{
//??????????? NSLog(@"存儲為0.");
//??????? }
//發(fā)送到SDK服務(wù)器
[[LoginDataSource sharedInstance] verifyThePurchaseWithDictData:parDic completion:^(NSDictionary *resultData, NSError *error) {
if ([resultData[TAG_RESULT] intValue] == 0 || Order.length != 0) {
[weakSelf actionWithResult:resultData OrderStr:eOrder];//執(zhí)行刪除
}
}];
}
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
參考鏈接:
http://mobile.51cto.com/iphone-410162.htm? 比較全的文檔介紹
http://www.cocoachina.com/ios/20150129/11068.html demo
http://www.2cto.com/kf/201504/389224.html 有訂閱
http://blog.csdn.net/xingchen1106/article/details/45477433
http://blog.jobbole.com/38032/ 唐巧介紹安全
http://blog.csdn.net/fly_fish456/article/details/8955871
http://www.tairan.com/archives/2215/
【后期要理解的安全性,以及沙盒和正式環(huán)境】
http://www.360doc.com/content/14/1113/15/12282510_424834793.shtml
http://www.cocoachina.com/special/iap.html
http://www.2cto.com/kf/201504/389224.html