關於蘋果內購(IAP)的一些問題以及那些坑
最近在研究蘋果內購功能,所以,在網上找了一些資料,進行學習。但是,內購功能在實現的過程中,有很多坑,筆者算是真的遇到了好多啊,下面也是自己對內購的一些心得與體會吧!
我這裡說的可能不太詳盡,所以,我先把再網上看到的一些帖子貼在這裡,以便大家做內購的時候,方便查詢相關資訊。
這裡是一篇寫的比較全面的帖子,但是沒有寫中間問題處理: <iOS開發內購全套圖文教程>
在網上搜了一些相關的帖子,簡單歸納總結了一下,覺得論壇裡有一個叫Teng的世界的大神,寫了三篇部落格,寫的很詳細:
【IAP支付之一】In-App Purchase Walk Through 整個支付流程
【IAP支付之二】In app purchase 本地購買和伺服器購買兩種購買模式
【IAP支付之三】蘋果IAP安全支付與防範 receipt收據驗證
大家在做內購之前,推薦看一下!
但是,畢竟我們開發的IAP是在蘋果的平臺上面執行,所以,如果英語能力好的話,最好去蘋果官網無看<官方指南>,裡面涉及到了一些論壇的貼子裡沒有提到過的問題,而這些內容,也很有可能會被大家忽略。下面是<官方文件中文翻譯>,可以對照官方文件檢視。但有時候還會出現相關的問題。好吧,廢話不多說,下面開始說IAP的實現以及具體會遇到的問題,我這裡可能會涉及到好多需要注意的問題,流程性的東西會少一些。大家儘量在讀本篇部落格之前,先把上面的幾個部落格看一下。
首先,我們要去iTunes store建立幾個我們需要在內購中使用到的產品,記住,產品的ID一定要唯一。蘋果官方提到了,IAP購買項有幾種型別:
Consumable products:消耗類產品
Non-consumable products:非消耗類產品
Auto-renewable subscriptions:自動更新訂閱產品
Non-renewable subscriptions. 非自動更新訂閱產品
Free subscriptions. 免費訂閱產
我們通常再遊戲中用到的遊戲幣屬於消耗類產品,賽車軌道等屬於非消耗類產品,通常這2種會比較常見。我當時用的是消耗類產品。
當完成產品建立之後,去iTunes store申請一個測試賬號,就要開始編寫程式碼了。在編寫程式碼之前,最重要的,是要了解整個內購實現的流程。這裡找到了一個比較好的對<流程解說的帖子>,下面是流程圖:
歸根結底,其實,我們一直在和APP store在打交道,而並不是和蘋果的伺服器進行打交道,所以,大家要避免這個誤區,而APP store才和蘋果伺服器進行打交道,這一層,其實我們基本是不需要考慮的。
流程:
1.首先,從圖上的第一步,客戶端向自己的伺服器傳送了一個請求,請求產品列表,然後,我們自己的伺服器會返回給客戶端產品的identifiers,也就是我們在建立產品的時候,設定的產品ID,當獲取之後,我們需要根據獲得的identifiers向APP store請求產品的詳細資訊。但對於某些應用來說,可能產品種類沒有什麼變動,所以,就直接將identifiers整合在了應用中,有的是直接放在了plist檔案中,需要的時候,直接呼叫,不需要向伺服器傳送請求,獲得訂單資訊。但這樣也有缺點,當產品發生變動的時候,需要釋出新的版本,更新應用才行,所以,不推薦使用這種方案。
2.當獲取了產品資訊之後,要重新整理UI,展示給使用者,讓使用者選擇需要購買那種產品,然後點選購買按鈕。當使用者購買某個產品的時候,我們的APP會向APP store傳送購買請求,APP store接收到,購買請求之後,會進行訂單的處理,然後,返回給我們購買的結果,同時,從上面的途中,我們還可以看到,返回到客戶端有一個receipt data,這個東西其實是用來進行校驗的證書(其實是很長的字串,大概3000多個字元吧),防止有人使用越獄外掛,從而反覆獲取我們的產品,尤其是類似金幣這種。
3.當客戶端獲得購買結果之後,將支付資訊(包括驗證證書)傳送到伺服器,伺服器向AppStore發起驗證,這個驗證必須是post請求,將資料以json格式傳送過去,同時,receipt要進行base64編碼,當蘋果確認之後,會給我們返回狀態碼,告訴我們是否成功。
這是蘋果官方給出的集中狀態值,蘋果返回回來的資料也是json格式的,會有一個state欄位,當為0的時候,表示成功,我們測試的介面是:https://sandbox.itunes.apple.com/verifyReceipt,生產環境的介面是:https://buy.itunes.apple.com/verifyReceipt
,所以大家要區分好這兩個介面。21007表示將測試環境獲得receipt傳送到了生產環境,21008表示將生產環境的receipt傳送到了測試環境下,其他的返回值,應該都表示驗證失敗,但是,具體是什麼,我也不清楚,英語好的話,可以自己翻譯一下,然後,告訴我。這裡是<蘋果官方驗證文件>,大家可以檢視這個,寫出客戶端驗證的程式碼。因為我不是做服務端的,所以不知道怎麼寫服務端驗證,但是,這兩者應該是相通的,大家可以在下面寫評論,一起討論一下。
4.當伺服器從APPstore獲得返回狀態後,判斷是否有這條購買記錄,如果有,就更新伺服器端資料庫,表示物品已經購買,再給客戶端傳送購買結果。這裡說的APPstore,我再網上找了好多資料,都是這麼說的,但我覺得其實就是蘋果伺服器給提供的介面,只不過為了方便,所以,在畫圖的時候,就都畫成了向APP store傳送驗證,其實,這裡是蘋果伺服器提供的一個介面。
筆者公司當時用的是RMStore這個開源庫,這個用著很方便,所以,大家也可以嘗試一下,但不保證完全沒有問題,因為我在使用的過程中,其實也遇到了一些棘手的問題。大家也可以自己寫支付這個模組,其實,正常的這個流程也不是很麻煩,先把基本流程寫完,再考慮可能出現的問題,就會好很多。我在上面引用的幾個連結裡面,有的連結裡面有具體的程式碼,大家可以參考一下。
用RMStore的話,主要會呼叫這樣一個方法:
1.
-
(
void
)addPayment:(NSString*)productIdentifier
2.
user:(NSString*)userIdentifier
3.
success:(
void
(^)(SKPaymentTransaction
*transaction))successBlock
4.
failure:(
void
(^)(SKPaymentTransaction
*transaction, NSError *error))failureBlock;
先來說說這些引數吧。首先,第一個引數,這個就是我們獲取到的產品的identifier,就是要購買的那個產品的唯一標識;然後是這個user,這個是使用者自定義的一個東西,可以是使用者的UUID或者其他資訊,這個用途很大的。會在後面提到;這裡的success block,實在支付成功後,回撥的內容,只要把成功後進行的操作寫在裡面就可以了,但是,由於成功後,需要的操作也很多,大家一定要把操作封裝一下,在裡面呼叫,否則,邏輯會很亂,而且,下面的failure block中還要對很多異常狀況進行判斷和處理,其中有一個就是“無法連線到iTunes store”,這個問題很麻煩,後面會具體說。一般情況下,如果不考慮user這個變數,可以直接使用下面的方法:
1.
-
(
void
)addPayment:(NSString*)productIdentifier
2.
success:(
void
(^)(SKPaymentTransaction
*transaction))successBlock
3.
failure:(
void
(^)(SKPaymentTransaction
*transaction, NSError *error))failureBlock;
- 這個方法要呼叫上面的方法,但是user預設為nil。
支付流程看起來就是這樣,感覺好像很簡單,但是,這裡面的問題其實很大。上面只是在一切都正常的狀態下,才會走的流程,但是,如果考慮到網路問題、斷網、應用閃退,有越獄外掛等問題,問題就麻煩了,這個歷程,各個過程中需要考慮的問題,其實,還是很多的。好的,下面我們就一步一步開始說IAP實現過程中的各種坑。先重新把上面的圖拿過來。
首先來說第一步:
這一步還是很輕鬆的,我們向伺服器獲取產品identifiers,由於需要進行網路請求,而且是支付,所以,一定要把斷網考慮進來,這個是必須的,那麼,在這一步,我們要判斷,當沒有獲取資料的時候,要提示使用者暫時沒有獲取產品列表資訊,這部分其實還好,不需要考慮太多。
之後的一些過程,就比較複雜了,考慮的東西也會比較多了。首先把剩下的部分拿過來:
這部分問題很多,而且,需要邏輯也很複雜。首先說第七步,這一部分,再應用中,當點選購買的時候,會彈出輸入框,要求輸入賬號和密碼,當點選取消的時候,實際上會呼叫failure block.呼叫failure block的時候,會獲得一條支付資訊transaction和一個error,我們可以判斷transaction的相關資訊,來判斷取消狀態,
1.
if
(transaction.error.code == SKErrorPaymentCancelled)
01.
NS_ASSUME_NONNULL_BEGIN
02.
03.
SK_EXTERN NSString *
const
SKErrorDomain NS_AVAILABLE_IOS(3_0);
04.
05.
// error codes for the SKErrorDomain
06.
enum
{
07.
SKErrorUnknown,
08.
SKErrorClientInvalid,
// client is not allowed to issue the request, etc.
09.
SKErrorPaymentCancelled,
// user cancelled the request, etc.
10.
SKErrorPaymentInvalid,
// purchase identifier was invalid, etc.
11.
SKErrorPaymentNotAllowed,
// this device is not allowed to make the payment
12.
SKErrorStoreProductNotAvailable,
// Product is not available in the current storefront
13.
};
14.
15.
NS_ASSUME_NONNULL_END
屬於同一類問題,也就是上面說的無法連線到iTunes store,雖然知道了這幾種狀態,但是,還是不知道這幾種狀態到底代表什麼。於是就去蘋果的開發文件裡面看了一下,01.
Constants
02.
SKErrorUnknown
03.
Indicates that an unknown or unexpected error occurred.
04.
05.
Available in iOS
3.0
and later.
06.
SKErrorClientInvalid
07.
Indicates that the client is not allowed to perform the attempted action.
08.
09.
Available in iOS
3.0
and later.
10.
SKErrorPaymentCancelled
11.
Indicates that the user cancelled a payment request.
12.
13.
Available in iOS
3.0
and later.
14.
SKErrorPaymentInvalid
15.
Indicates that one of the payment parameters was not recognized by the Apple App Store.
16.
17.
Available in iOS
3.0
and later.
18.
SKErrorPaymentNotAllowed
19.
Indicates that the user is not allowed to authorize payments.
20.
21.
Available in iOS
3.0
and later.
22.
SKErrorStoreProductNotAvailable
23.
Indicates that the requested product is not available in the store.
24.
25.
Available in iOS
6.0
and later.
這是官方的解釋,可以嘗試翻譯一下,瞭解其代表的含義。後來在網上搜尋了一下相關的文章,只找到一個,說了<無法連線到iTunes store>,但這裡寫的幾種狀態,並沒有全部涵蓋,後來我在網上又找了一下,下面是我給出的對無法連線到iTunes store的處理:
01.
if
(transaction.error != nil) {
02.
switch
(transaction.error.code) {
03.
04.
case
SKErrorUnknown:
05.
06.
NSLog(@
"SKErrorUnknown"
);
07.
detail = @
"未知的錯誤,您可能正在使用越獄手機"
;
08.
break
;
09.
10.
case
SKErrorClientInvalid:
11.
12.
NSLog(@
"SKErrorClientInvalid"
);
13.
detail = @
"當前蘋果賬戶無法購買商品(如有疑問,可以詢問蘋果客服)"
;
14.
break
;
15.
16.
case
SKErrorPaymentCancelled:
17.
18.
NSLog(@
"SKErrorPaymentCancelled"
);
19.
detail = @
"訂單已取消"
;
20.
break
;
21.
case
SKErrorPaymentInvalid:
22.
NSLog(@
"SKErrorPaymentInvalid"
);
23.
detail = @
"訂單無效(如有疑問,可以詢問蘋果客服)"
;
24.
break
;
25.
26.
case
SKErrorPaymentNotAllowed:
27.
NSLog(@
"SKErrorPaymentNotAllowed"
);
28.
detail = @
"當前蘋果裝置無法購買商品(如有疑問,可以詢問蘋果客服)"
;
29.
break
;
30.
31.
case
SKErrorStoreProductNotAvailable:
32.
NSLog(@
"SKErrorStoreProductNotAvailable"
);
33.
detail = @
"當前商品不可用"
;
34.
break
;
35.
36.
default
:
37.
38.
NSLog(@
"No Match Found for error"
);
39.
detail = @
"未知錯誤"
;
40.
break
;
41.
}
42.
}
當然,失敗有很多種,這是無法連線到iTunes store,不是網路的問題。上面提到失敗的時候,會有transaction和error兩個返回值,當網路出現問題的時候,error.code是負值。這時,成功的話,沒有這個error資訊,這時,我們就可以判斷到底是怎麼回事了,當返回了error的時候,先判斷transaction.error是否為空,不為空的話,進行上面的switch判斷,為空的話,說明交易的訂單資訊沒有問題,這時候,就只是網路的問題了,就提示使用者網路異常。
當我們向AppStore傳送了請求之後,如果AppStore交易完成之後,也就是上面的成功的success block,我們首先要將訂單資訊儲存到本地,然後傳送給我們自己的伺服器,當我們的伺服器給我們返回資訊的時候,我們再更新UI,同時,刪除本地儲存的訂單資訊。這個訂單資訊,可以儲存在資料庫中,也可以儲存在檔案中,但是,蘋果建議儲存在檔案中,用NSCoding進行編碼儲存,這樣會更好一些。
向自己伺服器傳送訊息的話,還要注意很多東西。這裡面也包括AFNetworking的一些問題。但我不瞭解這是不是偶發的事情。當時出現的問題狀況是這樣的:Error Domain=com.alamofire.error.serialization.response Code=-1016 "Request failed: unacceptable content-type: text/html"
1.
Error Domain=com.alamofire.error.serialization.response Code=-
1016
"Request failed: unacceptable content-type: text/html"
Error Domain=com.alamofire.error.serialization.response Code=-1016 "Request failed: unacceptable con
當出現這種問題的時候,訂單資訊會無法上傳到自己的伺服器,這時候,就出問題了,使用者已經支付了,錢已經扣了,但是,我們的伺服器沒有訂單資訊,所以,無法給使用者發貨,類似這樣損害使用者利益的事情是絕對不被允許的。所以,可以按照上面帖子的說法,修改請求型別,新增對text/html的支援,就可以避免這種問題了。此外,當我們自己的伺服器出錯的時候,當使用者打算將訂單資訊上傳到我們伺服器的時候,此時,伺服器可能會返回一些我們預先設定好的狀態碼,對於這種狀態,我們也要在客戶端進行相應的判斷,當遇到這樣的問題的時候,提示用伺服器出錯,趕緊聯絡我們的客服,進行問題的解決。
上面說到,我們向APP store傳送支付請求的時候,當支付完成的時候,伺服器會將訂單返回給我們,這個時候,我們首先應該做的,其實是將訂單資訊儲存到本地,然後,再向我們自己的伺服器傳送訂單資訊,當伺服器給我們反饋資訊,通知我們成功之後,再刪除本地儲存的訂單資訊。如果失敗的話,我們這裡要設定一個定時器,將未完成的失敗訂單,定時提交到我們的伺服器,從而獲得要購買的商品。但是,如果一直沒有網路怎麼辦?這是,我們就要在每次應用開啟的時候,查詢是否有未完成的訂單資訊,然後將訂單資訊上傳到伺服器,從而獲得我們要購買的商品。
這種狀態處理完了之後,還有其他的一些狀態,例如,網路狀態不好的狀態下,當我們向APP Store發起訂單請求的時候,請求成功了,但是,當APP Store給我們返回訂單的時候,斷網了,或者,此時退出了應用,以及應用閃退,那該怎麼辦呢?其實,蘋果已經替我們想好了這種問題的解決辦法。我們只要在應用啟動的時候,設定一下代理,就可以了,這是<官方文件>,我們需要在應用啟動的時候,設定SKPaymentQueue的代理方法
1.
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
01.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
02.
{
03.
// Attach an observer to the payment queue
04.
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
05.
return
YES;
06.
}
07.
08.
// Called when the application is about to terminate
09.
- (
void
)applicationWillTerminate:(UIApplication *)application
10.
{
11.
// Remove the observer
12.
[[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
13.
}
下面來說下面這個方法:
1.
- (
void
)addPayment:(NSString*)productIdentifier
2.
user:(NSString*)userIdentifier
3.
success:(
void
(^)(SKPaymentTransaction *transaction))successBlock
4.
failure:(
void
(^)(SKPaymentTransaction *transaction, NSError *error))failureBlock;
以上就是我對最近開發中遇到的一些問題的解決,有不全面的地方和說錯的地方,還請大家批評指點。
相關文章
- 蘋果內購IAP防掉單處理蘋果
- 關於內購支付的流程和一些需要注意的坑
- 關於css佈局、居中的問題以及一些小技巧CSS
- 關於CSS的定位問題,你需要注意的一些坑CSS
- 前端踩坑 關於蘋果手機class.on(‘click‘)失效的問題前端蘋果
- 【iOS】IAP內購整個流程iOS
- iOS IAP應用內購詳細步驟和問題總結指南iOS
- 關於Redis的一些小問題Redis
- iOS IAP內購 VS 支付寶iOS
- SQL 語句中關於 NULL 的那些坑SQLNull
- 螢幕旋轉的適配問題以及遇到的一些坑
- 【HMS Core】關於應用內購買金鑰升級問題
- Flutter 接入iOS蘋果內購支付踩坑過程FlutteriOS蘋果
- IAP 內購二次驗證(出現的問題21002及解決方案)
- 關於介面的一些問題
- 內購支付踩過的坑以及自己的解決途徑
- 關於Java NIO的一些問題,求助。Java
- 關於蘋果銷量的一些看法蘋果
- 關於專案內模組引用的問題
- 關於String內的indexOf方法的一些疑問Index
- 關於CSS一些細節問題CSS
- 關於瀏覽器相容的一些問題瀏覽器
- 解答關於學習前端的一些問題前端
- 踩坑CBO,解決那些坑爹的SQL優化問題SQL優化
- 關於IDEA執行ssm專案的一些坑以及ssm的基本的配置IdeaSSM
- 關於原始碼的學習的一些問題原始碼
- 避坑指南:關於SPDK問題分析過程
- 【Mysql】關於一個mysql的坑比時區問題MySql
- 關於Linux一些問題和答案Linux
- Leetcode刷題中關於java的一些小問題LeetCodeJava
- 關於iframe中使用fixed定位的一些問題
- 關於typedef在struct使用上的一些問題Struct
- 關於動態表單遇到的一些問題
- 關於Java中分層中遇到的一些問題Java
- 關於input的一些問題解決方法分享
- 關於資料庫連線的一些小問題資料庫
- PHP語言之華為應用內購買IAP驗籤PHP
- 【支付】Cocos2d-x IOS內購(IAP支付)iOS