前言:什么時候用內(nèi)購?---平臺有虛擬貨幣交易而非真實(shí)貨品交易且有充值的時候戏溺,打比方題主項目是在平臺充值優(yōu)幣(相當(dāng)于金幣)渣蜗,然后用該貨幣去購買資源等作用,并非一種真實(shí)的物品交易旷祸。在Appstore市場不選擇內(nèi)購則有可能面臨下架風(fēng)險耕拷。
一、簽訂內(nèi)購協(xié)議:
1托享、簽訂前的準(zhǔn)備
(1)聯(lián)系人信息(可以只填一人骚烧,這人有一定代表性即可,我全部填寫的我們總監(jiān)闰围,省事):職稱和郵箱止潘、電話;
(2)公司賬戶信息:公司結(jié)算賬戶賬號辫诅、開戶行行號(實(shí)在不知道的問清楚開戶行地址也行凭戴,后面具體介紹根據(jù)地址找到行號)、持卡人姓名(名在前姓在后炕矮,我們是公司賬號名么夫,就得要公司的英文賬戶名)。
2肤视、開始簽訂:登錄ITunes Connect點(diǎn)擊左上角進(jìn)入:
點(diǎn)擊協(xié)議進(jìn)入:
3档痪、填寫聯(lián)系人信息:
沒簽訂之前contact Info 下按鈕顯示的是setup,點(diǎn)擊進(jìn)入:
我這里為了省事高管邢滑、財務(wù)腐螟、技術(shù)人員、法人、市場全部都可以填寫一個具有代表性的人乐纸;
4衬廷、添加銀行業(yè)務(wù):
銀行國家我們填寫中國:
填寫code銀行開戶行行號,不知道行號的可以根據(jù)銀行名字汽绢、城市和郵編搜索出一系列的支行行號吗跋,再根據(jù)開戶銀行地址選擇所對應(yīng)的行號;
CNAPS Code:就是開戶行行號宁昭;
5跌宛、填寫稅務(wù)信息:
點(diǎn)擊第一個就行;
進(jìn)入U.S Tax后积仗,會有兩個選項疆拘,是否是美國人或者親屬是美國人,根據(jù)實(shí)際情況填寫Yes/No寂曹,還有一個是否參與美國活動哎迄,也是一樣的填寫Yes/No,一般都是NO稀颁,根據(jù)實(shí)際情況來吧芬失;
中間一截都不用管楣黍,最后勾選同意就行的匾灶,當(dāng)然謹(jǐn)慎者有研究精神者多去看看是很好的;
協(xié)議簽訂完了租漂,可是完成了一件大事阶女!
二、創(chuàng)建內(nèi)購項目并提交:
進(jìn)入內(nèi)購項目哩治,點(diǎn)擊?即添加內(nèi)購項目秃踩;
我們是購買金幣類型,所以是消耗型項目业筏;
創(chuàng)建產(chǎn)品的時候憔杨,產(chǎn)品ID是唯一的,最好定義bundleId+產(chǎn)品標(biāo)識蒜胖;
創(chuàng)建內(nèi)購產(chǎn)品審核備注那里填寫創(chuàng)建的沙盒測試賬號以供上線審核:
沙盒測試賬號密碼不可修改消别,忘記了可以重新創(chuàng)建一個即可,點(diǎn)擊編輯可刪除該賬號:
三台谢、代碼:
去蘋果服務(wù)器請求:
#pragma mark - Appstore Request
- (void)requestProductData:(NSString *)type {/**<去蘋果服務(wù)器請求商品*/
NSLog(@"-------------請求對應(yīng)的產(chǎn)品信息----------------");
[[WBProgressHUDView sharedInstance] showInView:self.view withTitleLabelText:@"請稍后..." withUserInterfaceEnable:YES];
NSArray *product = [[NSArray alloc] initWithObjects:type,nil];
NSSet *set = [NSSet setWithArray:product];
SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:set];
request.delegate = self;
[request start];
}
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {/**<收到產(chǎn)品返回信息*/
NSLog(@"--------------收到產(chǎn)品反饋消息---------------------");
NSArray *product = response.products;
if([product count] == 0){
[[WBProgressHUDView sharedInstance] dismissFromSuperView];
NSLog(@"--------------沒有商品------------------");
return;
}
NSLog(@"productID:%@", response.invalidProductIdentifiers);
NSLog(@"產(chǎn)品付費(fèi)數(shù)量:%lu",(unsigned long)[product count]);
SKProduct *p = nil;
for (SKProduct *pro in product) {
NSLog(@"%@", [pro description]);
NSLog(@"%@", [pro localizedTitle]);
NSLog(@"%@", [pro localizedDescription]);
NSLog(@"%@", [pro price]);
NSLog(@"%@", [pro productIdentifier]);
if([pro.productIdentifier isEqualToString:self.productId]){
p = pro;
}
}
SKPayment *payment = [SKPayment paymentWithProduct:p];
NSLog(@"發(fā)送購買請求");
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error {/**<請求失敗*/
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"溫馨提示" message:@"支付失斞翱瘛!" delegate:nil cancelButtonTitle:@"確定" otherButtonTitles:nil, nil];
[alertView show];
NSLog(@"------------------錯誤-----------------:%@", error);
}
- (void)requestDidFinish:(SKRequest *)request {/**<請求結(jié)束*/
[[WBProgressHUDView sharedInstance] dismissFromSuperView];
NSLog(@"------------反饋信息結(jié)束-----------------");
}
從蘋果沙盒環(huán)境驗(yàn)證購買:(適用于本地測試朋沮,真實(shí)環(huán)境請切換至服務(wù)器驗(yàn)證蛇券,** 測試的時候一定要記得是沙盒環(huán)境!K崖狻轻腺! **)
#pragma mark - Verify in Environment
/**<沙盒測試環(huán)境驗(yàn)證*/
#define SANDBOX @"https://sandbox.itunes.apple.com/verifyReceipt"
/**<正式環(huán)境驗(yàn)證*/
#define AppStore @"https://buy.itunes.apple.com/verifyReceipt"
/**
* 驗(yàn)證購買,避免越獄軟件模擬蘋果請求達(dá)到非法購買問題
* (用于本地測試戒傻,實(shí)際需轉(zhuǎn)至服務(wù)器驗(yàn)證防止刷單漏單)
*/
- (void)verifyPurchaseWithPaymentTransaction {
/**<從沙盒中獲取交易憑證并且拼接成請求體數(shù)據(jù)*/
NSURL *receiptUrl = [[NSBundle mainBundle] appStoreReceiptURL];
NSData *receiptData = [NSData dataWithContentsOfURL:receiptUrl];
NSString *receiptString=[receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];/**<轉(zhuǎn)化為base64字符串*/
NSString *bodyString = [NSString stringWithFormat:@"{\"receipt-data\" : \"%@\"}", receiptString];/**<拼接請求數(shù)據(jù)*/
NSData *bodyData = [bodyString dataUsingEncoding:NSUTF8StringEncoding];
/**<創(chuàng)建請求到蘋果官方進(jìn)行購買驗(yàn)證*/
NSURL *url = [NSURL URLWithString:SANDBOX];
NSMutableURLRequest *requestM=[NSMutableURLRequest requestWithURL:url];
requestM.HTTPBody=bodyData;
requestM.HTTPMethod=@"POST";
/**<創(chuàng)建連接并發(fā)送同步請求*/
NSError *error=nil;
NSData *responseData=[NSURLConnection sendSynchronousRequest:requestM returningResponse:nil error:&error];
if (error) {
NSLog(@"驗(yàn)證購買過程中發(fā)生錯誤菜枷,錯誤信息:%@",error.localizedDescription);
return;
}
NSDictionary *dic=[NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingAllowFragments error:nil];
NSLog(@"%@",dic);
if([dic[@"status"] intValue] == 0){
NSLog(@"購買成功苍糠!");
NSDictionary *dicReceipt = dic[@"receipt"];
NSDictionary *dicInApp = [dicReceipt[@"in_app"] firstObject];
NSString *productIdentifier = dicInApp[@"product_id"];/**<讀取產(chǎn)品標(biāo)識*/
/**<如果是消耗品則記錄購買數(shù)量,非消耗品則記錄是否購買過*/
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if ([productIdentifier isEqualToString:@"com.k12chn.UCoin6"] || [productIdentifier isEqualToString:@"com.k12chn.UCoin80"] || [productIdentifier isEqualToString:@"com.k12chn.UCoin120"]) {
NSInteger purchasedCount = [defaults integerForKey:productIdentifier];/**<已購買數(shù)量*/
[[NSUserDefaults standardUserDefaults] setInteger:(purchasedCount+1) forKey:productIdentifier];
} else {
[defaults setBool:YES forKey:productIdentifier];
}
/**<在此處對購買記錄進(jìn)行存儲啤誊,可以存儲到開發(fā)商的服務(wù)器端*/
} else {
NSLog(@"購買失敗岳瞭,未通過驗(yàn)證!");
}
}
題主公司項目服務(wù)器驗(yàn)證購買請求參數(shù):
NSURL *receiptUrl = [[NSBundle mainBundle] appStoreReceiptURL];
NSData *receiptData = [NSData dataWithContentsOfURL:receiptUrl];
NSString *receiptString = [receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];//轉(zhuǎn)化為base64字符串
NSString *url = @"/index.php?app=account_epay&act=UcoinExcharge";
NSDictionary *dic = @{
@"userId":userInfoModel.UserId,
@"receiptdata":receiptString,
@"ucoin":ucoin,
@"money":price
};
購買結(jié)果監(jiān)聽:
#pragma mark - Observe Result
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transaction{
for(SKPaymentTransaction *tran in transaction) {/**<監(jiān)聽購買結(jié)果*/
switch (tran.transactionState) {
case SKPaymentTransactionStatePurchased: {
NSLog(@"交易完成");
/**<發(fā)送到蘋果服務(wù)器驗(yàn)證憑證*/
[self requstVerifyPurchase];
[[SKPaymentQueue defaultQueue] finishTransaction:tran];
}
break;
case SKPaymentTransactionStatePurchasing:
NSLog(@"商品添加進(jìn)列表");
break;
case SKPaymentTransactionStateRestored: {
NSLog(@"已經(jīng)購買過商品");
[[SKPaymentQueue defaultQueue] finishTransaction:tran];
}
break;
case SKPaymentTransactionStateFailed: {
NSLog(@"交易失敗");
[[SKPaymentQueue defaultQueue] finishTransaction:tran];
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"溫馨提示" message:@"購買失斘们隆瞳筏!" delegate:nil cancelButtonTitle:@"確定" otherButtonTitles:nil, nil];
[alertView show];
}
break;
default:
break;
}
}
}
- (void)completeTransaction:(SKPaymentTransaction *)transaction {/**<交易結(jié)束*/
NSLog(@"交易結(jié)束");
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
- (void)dealloc {
[[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
}
OK啦,成功告一段落了牡昆,但是最后要注意的是:
測試的時候需要先進(jìn)設(shè)置->iTunes Store 與 App Store 里面退出原來真實(shí)的Apple ID(退出就好姚炕,無需急著登錄測試賬號),然后在app中測試購買的時候回跳出請輸入Appstore賬號丢烘,這時輸入我們的沙盒測試賬號即可柱宦。
最后來處理掉單問題了:
兩種情況:1、付款了Appstore服務(wù)器出問題回調(diào)不成功播瞳;
2掸刊、AppStore返會成功但自己服務(wù)器驗(yàn)證未通過未發(fā)放對應(yīng)貨幣。
針對情況1:我在AppDelegate里重新監(jiān)聽向AppStore購買請求結(jié)果赢乓,如果存在訂單但又回執(zhí)失敗的話忧侧,是會重新發(fā)送請求的;
針對情況2:我在購買監(jiān)聽回調(diào)中做了個3次循環(huán)驗(yàn)證牌芋,如果超過3次還失敗蚓炬,那也無需繼續(xù)請求,說明服務(wù)器出問題了躺屁。
(ps:總覺得這些處理內(nèi)心不是很踏實(shí)不是很滿意肯夏,求有其它好處理方式的共享,感謝~)
內(nèi)購除了開發(fā)過程坑犀暑,還有收賬過程也是有點(diǎn)意思驯击,因?yàn)槭呛M馊胭~,銀行會打電話給公司母怜,需提供形式發(fā)票等一系列資料才能完整到公司銀行賬戶余耽。財務(wù)一開始沒有及時收賬,導(dǎo)致銀行又原路退回到蘋果公司苹熏。后面天天逼著我一個小小開發(fā)者做發(fā)票碟贾,也是趕鴨子上架币喧,各種百度,嘗試性地問財務(wù)要一個平時的發(fā)票格式袱耽,我依葫蘆畫瓢把一些內(nèi)購買數(shù)據(jù)和賬單里一些數(shù)據(jù)填上去杀餐,結(jié)果神奇般地后面的款項都收到了,于是又回頭去聯(lián)系蘋果財務(wù)朱巨,請求將先前的退款重新發(fā)起史翘,我們及時提供形式發(fā)票等資料就可以入賬。那真的是一波三折~知道這個過程的冀续,還是很easy琼讽。不知道的,度娘上資料都微乎其微洪唐!