一坡疼, 如何定義內(nèi)購商品彬呻?
線上購買線上使用,不涉及到線下實物的交易都需要用蘋果內(nèi)購柄瑰。簡言之闸氮,就是虛擬商品,比如線上音樂教沾,電子圖書蒲跨,設(shè)計作品,游戲幣等授翻。
二或悲,Appstore Connect線上配置
1.協(xié)議孙咪、稅務(wù)和銀行業(yè)務(wù)填寫;
2.創(chuàng)建內(nèi)購項目翎蹈;
3.設(shè)置沙箱測試賬號;
由于回頭總結(jié)捌臊,截圖未能一一提供杨蛋,在此只提示設(shè)置入口而后按引導(dǎo)一步步去做就行了。
三理澎,代碼集成
1.添加和移除監(jiān)聽設(shè)置
// 添加觀察者
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
// 移除觀察者
[[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
2.通過產(chǎn)品ID獲取產(chǎn)品信息列表
- (void)getProductInfo:(NSString *)productIdentifier{
self.selectProductId = productIdentifier;
if (![SKPaymentQueue canMakePayments]){
if (self.failurePurchaseBlock) {
self.failurePurchaseBlock(@"不允許付費購買商品");
}
return;
}
if (productIdentifier.length > 0){
NSArray * product = [[NSArray alloc] initWithObjects:productIdentifier, nil];
NSSet *set = [NSSet setWithArray:product];
SKProductsRequest * request = [[SKProductsRequest alloc] initWithProductIdentifiers:set];
request.delegate = self;
[request start];//開始請求
}
}
/*
查詢成功后的回調(diào)(經(jīng)由getProductInfo函數(shù)發(fā)起的產(chǎn)品信息查詢逞力,成功后返回執(zhí)行的回調(diào))。
*/
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
{
NSArray *myProduct = response.products;
if (myProduct.count == 0){
if (self.failurePurchaseBlock) {
self.failurePurchaseBlock(@"無法獲取商品信息");
}
return;
}
//選擇用戶選擇的檔位發(fā)起購買請求
for (SKProduct *sKProduct in myProduct) {
NSLog(@"pro info");
NSLog(@"SKProduct 描述信息:%@", sKProduct.description);
NSLog(@"localizedTitle 產(chǎn)品標題:%@", sKProduct.localizedTitle);
NSLog(@"localizedDescription 產(chǎn)品描述信息:%@",sKProduct.localizedDescription);
NSLog(@"price 價格:%@",sKProduct.price);
NSLog(@"productIdentifier Product id:%@",sKProduct.productIdentifier);
if([sKProduct.productIdentifier isEqualToString: self.selectProductId]){
[self buyProduct:sKProduct];
break;
}
}
}
3.發(fā)起購買請求
/*解決掉單的問題:
1.將需要傳給后臺服務(wù)器的參數(shù)(比如訂單id糠爬,用戶id)放到SKMutablePayment的applicationUsername字段里面寇荧;
*/
-(void)buyProduct:(SKProduct *)product{
// 1.創(chuàng)建票據(jù)
NSString *orderId = @"123";
NSString *userId = @"abc";
NSString *userName = [NSString stringWithFormat:@"%@-%@",userId,orderId];
SKMutablePayment *skpayment = [SKMutablePayment paymentWithProduct:product];
skpayment.applicationUsername = userName;
// 2.將票據(jù)加入到交易隊列
[[SKPaymentQueue defaultQueue] addPayment:skpayment];
}
4.實現(xiàn)觀察者監(jiān)聽付錢的代理方法,只要交易發(fā)生變化就會走下面的方法
-(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions{
/*
SKPaymentTransactionStatePurchasing, 正在購買
SKPaymentTransactionStatePurchased, 已經(jīng)購買
SKPaymentTransactionStateFailed, 購買失敗
SKPaymentTransactionStateRestored, 回復(fù)購買中
SKPaymentTransactionStateDeferred 交易還在隊列里面,但最終狀態(tài)還沒有決定
*/
self.sKPaymentQueue = queue;
for (SKPaymentTransaction *transaction in transactions) {
switch (transaction.transactionState) {
case SKPaymentTransactionStatePurchasing:{
NSLog(@"正在購買");
}break;
case SKPaymentTransactionStatePurchased:{
NSLog(@"購買成功");
//這里可以做一個是否同一用戶的判斷执隧,因為如果是更新數(shù)據(jù)時產(chǎn)生的訂單揩抡,跟下單時的用戶未必是同一個用戶
//isLogin:是否登錄狀態(tài); userId:該訂單的用戶id镀琉;currentUserId:當前登錄用戶的id峦嗤;
NSArray *arr = [transaction.payment.applicationUsername componentsSeparatedByString:@"-"];
NSString *userId = @"";
if (arr.count>0) {
userId = arr[0];
}
if (isLogin && [currentUserId isEqualToString: userId]) {
//取出憑證
[self buyAppleStoreProductSucceedWithPaymentTransactionp:transaction];
}
}
break;
case SKPaymentTransactionStateFailed:{
NSLog(@"購買失敗");
if (self.failurePurchaseBlock) {
self.failurePurchaseBlock(@"購買失敗");
}
// 購買失敗也要把這個交易移除掉
[queue finishTransaction:transaction];
}break;
case SKPaymentTransactionStateRestored:{
NSLog(@"回復(fù)購買中,也叫做已經(jīng)購買");
// 回復(fù)購買中也要把這個交易移除掉
[queue finishTransaction:transaction];
}break;
case SKPaymentTransactionStateDeferred:{
NSLog(@"交易還在隊列里面,但最終狀態(tài)還沒有決定");
}break;
default:
break;
}
}
}
5.獲取憑證屋摔,并且將憑證發(fā)送給后臺校驗
-(void)buyAppleStoreProductSucceedWithPaymentTransactionp:(SKPaymentTransaction *)paymentTransactionp {
NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
NSData *receipt = [NSData dataWithContentsOfURL:receiptURL];
NSString *encodedReceipt = @"";
if (!receipt) {
NSLog(@"no receipt");
/* No local receipt -- handle the error. */
} else {
/* Get the receipt in encoded format */
encodedReceipt = [receipt base64EncodedStringWithOptions:0];
}
//調(diào)接口服務(wù)器校驗,該方法自行實現(xiàn)
// [self varifyPurchaseData:paymentTransactionp andReceipt:encodedReceipt];
}
注意:后臺校驗成功后烁设,需要把這個成功的交易移除掉
[self.sKPaymentQueue finishTransaction:transaction];
四,踩過的坑匯總
1.創(chuàng)建沙盒測試賬號的時候報錯:Unknown Errors while creating Sandbox Tester, Please check Error Log, email=***
解決:密碼設(shè)置復(fù)雜一些钓试。
2.購買成功后的校驗處理方案
- 為了保證安全装黑,購買成功后,需要通過綁定產(chǎn)品id弓熏,訂單號恋谭,憑證提供給后臺,讓后臺調(diào)蘋果接口解析憑證進行校驗挽鞠;
- 由于測試和審核的時候用的是沙盒測試疚颊,上線后才用正式環(huán)境,所以后臺需要做二次校驗信认。
- 即是先校驗正式環(huán)境串稀,如果成功則是上線后購買成功;
- 如果校驗失敗狮杨,再校驗測試環(huán)境母截,成功的話則是沙盒測試下購買成功,失敗則是沙盒測試下購買失旈辖獭清寇;
3.審核遇到的問題
(1)Guideline 3.1.1 - Business - Payments - In-App Purchase
We noticed that your app uses in-app purchase products to purchase credits or currencies that are not consumed within the app, which is not appropriate for the App Store.
通過跟蘋果審核的客服電話溝通喘漏, 蘋果內(nèi)購不能包含“提現(xiàn)”等有套現(xiàn)或洗錢嫌疑的功能——去掉“提現(xiàn)”的功能即可;
(2)一個 App 內(nèi)購買項目被退回华烟,并在以下列表中以高亮顯示
點擊高亮的內(nèi)購項目翩迈,編輯一下描述的文字,寫得更加符合項目并且詳細一些盔夜,點擊保存负饲。
4.掉單問題處理
網(wǎng)上有說把創(chuàng)建的訂單信息存儲在本地,但是思量后發(fā)現(xiàn)有不少的問題喂链,如果app被刪掉了返十,存儲的訂單信息一樣會被同步刪除,除非存儲在鑰匙串里面椭微,否則還是有概率調(diào)單洞坑。
后來參考了另一篇文章提到的applicationUsername字段,于是解決了這個問題蝇率,而且不需要做各種存取操作迟杂。
下面看看如何去獲取未完成憑證校驗的訂單(哪些情況會產(chǎn)生掉單):
1.蘋果內(nèi)購過程中,蘋果服務(wù)器響應(yīng)慢本慕,用戶殺掉進程的情況排拷;
2.拿到憑證,傳給公司后臺服務(wù)器過程中锅尘,網(wǎng)絡(luò)問題導(dǎo)致接口訪問失敗等情況监氢。
解決步驟:
- 創(chuàng)建訂單的時候,將orderId和userId存儲在applicationUsername里面(可以將更多的字段轉(zhuǎn)為json字符串鉴象,取出的時候再轉(zhuǎn)為字典);
- 在需要更新蘋果未完成訂單的頁面添加監(jiān)聽方式(即是代碼集成第一步)何鸡,可以在是啟動app頁面纺弊,或刷新金額的頁面;
- 在購買成功的代理中骡男,判斷訂單中的用戶id和當前登錄的用戶id是否一致淆游,如果一致,則取出訂單id和憑證一起傳給后臺服務(wù)器隔盛;(注:該頁面如果沒有登錄Apple ID的話犹菱,會彈出彈框提示用戶登錄)
- 當后臺服務(wù)器接收到憑證等數(shù)據(jù)成功時,通過finishTransaction結(jié)束該交易吮炕。
這樣下來腊脱,幾乎是不會再有掉單的問題了。