內(nèi)購(gòu)所需要的資料整理總結(jié)屁倔。
引用大佬的經(jīng)典文章:https://www.cnblogs.com/TheYouth/p/6847014.html
思維導(dǎo)圖
重點(diǎn)總結(jié):
1.獲取內(nèi)購(gòu)列表(從App內(nèi)讀取或從自己服務(wù)器讀缺住)
2.App Store請(qǐng)求可用的內(nèi)購(gòu)列表
3.向用戶展示內(nèi)購(gòu)列表
4.用戶選擇了內(nèi)購(gòu)列表,再發(fā)個(gè)購(gòu)買請(qǐng)求,收到購(gòu)買完成的回調(diào)(購(gòu)買完成后會(huì)把錢(qián)打給申請(qǐng)內(nèi)購(gòu)的銀行卡內(nèi))
5.購(gòu)買流程結(jié)束后, 向服務(wù)器發(fā)起驗(yàn)證憑證以及支付結(jié)果的請(qǐng)求
6.自己的服務(wù)器將支付結(jié)果信息返回給前端并發(fā)放虛擬產(chǎn)品
7.服務(wù)端的工作比較簡(jiǎn)單,分4步:
-7.1.接收ios端發(fā)過(guò)來(lái)的購(gòu)買憑證供炎。
-7.2.判斷憑證是否已經(jīng)存在或驗(yàn)證過(guò),然后存儲(chǔ)該憑證疾党。
-7.3.將該憑證發(fā)送到蘋(píng)果的服務(wù)器驗(yàn)證音诫,并將驗(yàn)證結(jié)果返回給客戶端。
-7.4.如果需要雪位,修改用戶相應(yīng)的會(huì)員權(quán)限竭钝。
-7.5.考慮到網(wǎng)絡(luò)異常情況,服務(wù)器的驗(yàn)證應(yīng)該是一個(gè)可恢復(fù)的隊(duì)列,如果網(wǎng)絡(luò)失敗了香罐,應(yīng)該 進(jìn)行重試卧波。
簡(jiǎn)單來(lái)說(shuō)就是將該購(gòu)買憑證用Base64編碼,然后POST給蘋(píng)果的驗(yàn)證服務(wù)器庇茫,蘋(píng)果將驗(yàn)證結(jié)果以JSON形式返回港粱。
一、使用注意事項(xiàng)及遇到的坑
1.使用注意
-1.1. 代碼中的_currentProId所填寫(xiě)的是你的購(gòu)買項(xiàng)目的的ID旦签,這個(gè)和第二步創(chuàng)建的內(nèi)購(gòu)的productID要一致查坪,產(chǎn)品id與_currentProId一致。
-1.2. 在監(jiān)聽(tīng)購(gòu)買結(jié)果后宁炫,一定要調(diào)用[[SKPaymentQueue defaultQueue] finishTransaction:tran];來(lái)允許你從支付隊(duì)列中移除交易偿曙。
-1.3. 真機(jī)測(cè)試的時(shí)候,一定要退出原來(lái)的賬號(hào)(app store 登錄的賬號(hào)退出)羔巢,才能用沙盒測(cè)試賬號(hào)望忆。
-1.4. 請(qǐng)務(wù)必使用真機(jī)來(lái)測(cè)試,一切以真機(jī)為準(zhǔn)竿秆。
-1.5. 項(xiàng)目的Bundle identifier需要與您申請(qǐng)AppID時(shí)填寫(xiě)的bundleID一致启摄,不然會(huì)無(wú)法請(qǐng)求到商品信息。
-1.6. 沙盒環(huán)境測(cè)試appStore內(nèi)購(gòu)流程的時(shí)候袍辞,請(qǐng)使用沒(méi)越獄的設(shè)備鞋仍。
-1.7. 二次驗(yàn)證,請(qǐng)注意區(qū)分宏搅吁, 測(cè)試用沙盒驗(yàn)證,App Store審核的時(shí)候也使用的是沙盒購(gòu)買落午,所以驗(yàn)證購(gòu)買憑證的時(shí)候需要判斷返回Status Code決定是否去沙盒進(jìn)行二次驗(yàn)證谎懦,為了線上用戶的使用,驗(yàn)證的順序肯定是先驗(yàn)證正式環(huán)境溃斋,此時(shí)若返回值為21007,就需要去沙盒二次驗(yàn)證,因?yàn)榇速?gòu)買的是在沙盒進(jìn)行的当编。
-1.8.貨幣類型(Bank Account Currency) :填CNY(如果你的app在中國(guó)使用的話)箩绍。
2.獲取不到商品信息
-2.1.確定配置環(huán)節(jié)正確。
-2.2.確定是真機(jī)測(cè)試且手機(jī)沒(méi)有越獄梳侨。
-2.3.確定內(nèi)購(gòu)商品添加到了需要內(nèi)購(gòu)功能的App中蛉威。
-2.4.確定當(dāng)前運(yùn)行的App的Bundle ID和后臺(tái)配置的App的Bundle ID是一致的。
-2.5.可以嘗試先刪除舊App走哺,再重新編譯生成新的蚯嫌,避免新App未覆蓋錯(cuò)誤。
-2.6.這里要提一點(diǎn),沙盒的測(cè)試賬號(hào)和你請(qǐng)求商品信息沒(méi)有關(guān)系择示。請(qǐng)求商品信息的流程是束凑,你在后臺(tái)配置好了內(nèi)購(gòu)商品,并且將其添加到了需要集成內(nèi)購(gòu)功能的App中栅盲,然后你請(qǐng)求商品汪诉。請(qǐng)求到商品后的流程是這樣的,蘋(píng)果系統(tǒng)會(huì)自動(dòng)彈出登錄框讓你登錄賬號(hào)谈秫。然后根據(jù)提示操作進(jìn)行購(gòu)買摩瞎,這里的賬號(hào)就是你配置的沙盒測(cè)試賬號(hào)。
二孝常、為什么要使用內(nèi)購(gòu)旗们?和內(nèi)購(gòu)是什么?
1.如果你購(gòu)買的商品构灸,是在本app中使用和消耗的上渴,就一定要用內(nèi)購(gòu),否則會(huì)被拒絕上線喜颁,例如:游戲幣稠氮,在線書(shū)籍,app中使用的道具等。
本例中半开,就是直播中你用來(lái)打賞用的金幣隔披,那東西可就屬于消耗型的。
2.如果是直接購(gòu)買商城之類的快遞包郵的那些東東寂拆,那就直接調(diào)用支付寶奢米,微信啦,之類的三方支付就好了纠永,淘寶鬓长,京東都玩過(guò)哈!
比較坑的一點(diǎn)就是尝江,內(nèi)購(gòu)的話涉波,還要和蘋(píng)果3/7分成,那就可以說(shuō)炭序,充值相同的錢(qián)啤覆,相對(duì)來(lái)說(shuō),iOS是比安卓虧的惭聂!
三窗声、怎樣使用內(nèi)購(gòu)?
1.使用內(nèi)購(gòu)需要哪些資料
1張visa銀行卡彼妻,appid嫌佑,1張銀行卡與蘋(píng)果三七分打錢(qián)用
(1)協(xié)議豆茫、稅務(wù)和銀行業(yè)務(wù)
聯(lián)系人信息:(appid賬號(hào)人)姓名,郵箱屋摇,電話號(hào)碼揩魂,地址(城市、具體街道分行寫(xiě))
visa銀行卡信息:開(kāi)戶行炮温,開(kāi)戶行所在地址火脉,開(kāi)戶行的郵政編碼,開(kāi)戶行持有人卡號(hào)柒啤,開(kāi)戶行持有人姓名
稅務(wù)信息:1.會(huì)問(wèn)你是不是美國(guó)居民選擇NO. 2. 有沒(méi)有在美國(guó)從事商業(yè)性活動(dòng)倦挂,選擇NO. 之后填寫(xiě)個(gè)人或組織名稱,所在國(guó)家担巩,受益方式(獨(dú)立開(kāi)發(fā)者選擇個(gè)人)方援,居住地址,郵寄地址涛癌,聲明人犯戏,頭銜
(2)內(nèi)購(gòu)產(chǎn)品id的配置 (開(kāi)發(fā)人員配置)
內(nèi)購(gòu)產(chǎn)品:如果是金幣或其它消耗品的產(chǎn)品的話用消耗性型項(xiàng)目,參考名稱(內(nèi)購(gòu)項(xiàng)目拳话,比如金幣100),產(chǎn)品id,定價(jià)信息先匪,使用內(nèi)購(gòu)的快照,顯示名稱弃衍,描述呀非。
(3)用戶職能
測(cè)試員:添加水箱測(cè)試員及沙箱賬號(hào),水箱測(cè)試賬號(hào)不能是正常使用的appid賬號(hào)镜盯,直接使用一個(gè)沒(méi)有注冊(cè)過(guò)的郵箱賬號(hào)即可岸裙。
姓名,測(cè)試賬號(hào)密碼形耗,appstore地區(qū)(必須填對(duì))哥桥。
四、操作流程圖解與代碼
功能簡(jiǎn)介 :
1.我的App主要用于管理自己的App應(yīng)用激涤,例如編輯資料,上架判呕,下架等倦踢。
2.銷售和趨勢(shì)主要是來(lái)查看App在各個(gè)平臺(tái)的下載量,收入等方面數(shù)據(jù),里面有曲線圖等圖文結(jié)合的方式給我們參考侠草。
3.付款和財(cái)務(wù)報(bào)告顯示的是你的收入以及付款等相關(guān)信息辱挥。
4.iAd主要是跟廣告有關(guān),開(kāi)發(fā)者可以登錄到Workbench边涕,通過(guò)iAd對(duì)應(yīng)用的廣告進(jìn)行控制晤碘。
5.用戶和職能用于生成相應(yīng)賬號(hào)褂微,例如蘋(píng)果沙盒測(cè)試賬號(hào)。
6.協(xié)議园爷,稅務(wù)和銀行業(yè)務(wù)則是你銀行相關(guān)賬戶的信息設(shè)置宠蚂。
流程
1.注冊(cè)app,填寫(xiě)協(xié)議、稅務(wù)和銀行業(yè)務(wù)
注冊(cè)app,需要設(shè)置Bundle identifier童社,此個(gè)步驟這里就不在寫(xiě)了
填寫(xiě)協(xié)議求厕、稅務(wù)和銀行業(yè)務(wù)
選擇申請(qǐng)合同類型
頁(yè)面內(nèi)容:
Request Contracts(申請(qǐng)合同)
Contracts In Effect(已生效合同)。
合同類型:
iOS Free Application(免費(fèi)應(yīng)用合同)
iOS Paid Application(付費(fèi)應(yīng)用合同)
iAd App NetNetwork(廣告合同)
- 設(shè)置協(xié)議稅務(wù)扰楼、銀行卡信息
當(dāng)我們點(diǎn)擊申請(qǐng)iOS Paid Application合同后呀癣,該合同的狀態(tài)會(huì)變成如下的樣子,
2.1設(shè)置聯(lián)系人信息
如果你沒(méi)有添加過(guò)聯(lián)系人弦赖,你需要通過(guò)Add New Contact按鈕來(lái)添加一個(gè)新的聯(lián)系人项栏。然后指定聯(lián)系人的職務(wù),職務(wù)如下:
Senior Management:高管
Financial:財(cái)務(wù)
Technical:技術(shù)支持
Legal:法務(wù)
Marketing:市場(chǎng)推廣
如果你是獨(dú)立開(kāi)發(fā)者蹬竖,可以全部填你自己一個(gè)人沼沈。
銀行卡信息
稅務(wù)報(bào)表
選擇U.S Tax Forms案腺,選擇后會(huì)問(wèn)你兩個(gè)問(wèn)題庆冕,
第一個(gè)問(wèn)題如下:詢問(wèn)你是否是美國(guó)居民,有沒(méi)有美國(guó)伙伴關(guān)系或者美國(guó)公司劈榨,如果沒(méi)有直接選擇No访递。
接下來(lái)第二個(gè)問(wèn)題如下:詢問(wèn)你有沒(méi)有在美國(guó)的商業(yè)性活動(dòng),沒(méi)有也直接選No同辣。
然后填寫(xiě)保稅表
3.配置內(nèi)購(gòu)產(chǎn)品ID
完成以上操作拷姿,并且蘋(píng)果審核完畢之后,就可以配置內(nèi)購(gòu)產(chǎn)品了旱函。
登錄 iTunesConnect -->我的App 模塊找到需要內(nèi)購(gòu)的App响巢,最后找到頁(yè)面如下:
填寫(xiě)沙箱測(cè)試員和添加內(nèi)購(gòu)產(chǎn)品注意事項(xiàng)
1踪古、郵箱必須是沒(méi)有注冊(cè)或者說(shuō)關(guān)聯(lián)過(guò)appstore的郵箱。
2券腔、密碼必須有一個(gè)是大寫(xiě)字母有一個(gè)是小寫(xiě)字母(蘋(píng)果規(guī)定的,理解)伏穆。
3、內(nèi)購(gòu)屏幕截圖規(guī)格必須是312*290纷纫,且最低分辨率是72ppi枕扫。
4、內(nèi)購(gòu)的價(jià)格是蘋(píng)果規(guī)定的不能自定義(坑啊)辱魁。
4.增加內(nèi)購(gòu)測(cè)試賬號(hào)
4.1 內(nèi)購(gòu)測(cè)試之前準(zhǔn)備
4.1.1烟瞧、什么是內(nèi)購(gòu)測(cè)試賬號(hào)(what)及為什么使用內(nèi)購(gòu)測(cè)試賬號(hào)(why)?
iOS應(yīng)用里面用到了蘋(píng)果應(yīng)用內(nèi)付費(fèi)(IAP)功能诗鸭,在項(xiàng)目上線前一定要進(jìn)行功能測(cè)試。測(cè)試肯定是需要的参滴,何況這個(gè)跟money有關(guān)强岸。。卵洗。開(kāi)發(fā)完成了之后请唱,如何進(jìn)行測(cè)試呢?難道我測(cè)試個(gè)內(nèi)購(gòu)功能要自己掏錢(qián)过蹂?就算是也是公司掏錢(qián)十绑,但是蘋(píng)果要吃掉3成的啊,想想如果是99刀的商品酷勺,點(diǎn)下購(gòu)買的時(shí)候心里都有點(diǎn)發(fā)慌本橙。。脆诉。
蘋(píng)果當(dāng)然沒(méi)這么坑了甚亭,測(cè)試內(nèi)購(gòu),蘋(píng)果提供了沙盒賬號(hào)(也叫沙箱賬號(hào))的方式击胜。這個(gè)沙箱賬號(hào)其實(shí)是虛擬的AppleID亏狰,在開(kāi)發(fā)者賬號(hào)后臺(tái)的iTune Connect上配置了之后就能使用沙盒賬號(hào)測(cè)試內(nèi)購(gòu),有了沙盒賬號(hào)偶摔,就能體驗(yàn)一把土豪的感覺(jué)了暇唾,游戲鉆石什么的隨便充,反正不用我的錢(qián)辰斋。
注意:你可以把沙盒賬號(hào)看做是一個(gè)虛擬的AppleID策州,這個(gè)AppleID只有進(jìn)行內(nèi)購(gòu)測(cè)試的功能。重要宫仗,重要够挂,重要,這個(gè)虛擬的賬號(hào)只能在自己的測(cè)試號(hào)中使用藕夫,如果在其它地方如appstore使用的話會(huì)提示賬號(hào)無(wú)效之類的話孽糖。
4.1.2、如何使用內(nèi)購(gòu)測(cè)試賬號(hào)(how)毅贮?
4.1.2.1作用內(nèi)購(gòu)賬號(hào)的前提
1)內(nèi)購(gòu)的商品ID梭姓,價(jià)格等相關(guān)信息已經(jīng)錄入到開(kāi)發(fā)者后臺(tái)了(不然那你買什么)
2)開(kāi)發(fā)者后臺(tái)已經(jīng)創(chuàng)建好沙盒測(cè)試賬號(hào)了(下面我們會(huì)將如何創(chuàng)建)
3)你要有一部真機(jī)(iPhone或iPad都行,別用模擬器就好嫩码。而且不能是越獄機(jī))
4)bundleID別搞錯(cuò)了,開(kāi)發(fā)者賬號(hào)罪既、證書(shū)铸题、bundleID要一致
5)如果你是第一次在這個(gè)開(kāi)發(fā)者賬號(hào)上集成內(nèi)購(gòu)功能铡恕,
請(qǐng)先將iTune Connect上的稅務(wù)協(xié)議都填寫(xiě)好,否則內(nèi)購(gòu)時(shí)會(huì)發(fā)現(xiàn)商品ID無(wú)效丢间。
重要探熔,如果不添加稅務(wù)協(xié)議會(huì)報(bào)錯(cuò),找不到商品烘挫。
5.代碼及業(yè)務(wù)邏輯
業(yè)務(wù)邏輯
5.1.獲取內(nèi)購(gòu)列表(從App內(nèi)讀取或從自己服務(wù)器讀纫)
5.2.App Store請(qǐng)求可用的內(nèi)購(gòu)列表
5.3.向用戶展示內(nèi)購(gòu)列表
5.4.用戶選擇了內(nèi)購(gòu)列表其垄,再發(fā)個(gè)購(gòu)買請(qǐng)求,收到購(gòu)買完成的回調(diào)(購(gòu)買完成后會(huì)把錢(qián)打給申請(qǐng)內(nèi)購(gòu)的銀行卡內(nèi))
5.5.購(gòu)買流程結(jié)束后, 向服務(wù)器發(fā)起驗(yàn)證憑證以及支付結(jié)果的請(qǐng)求
5.6.自己的服務(wù)器將支付結(jié)果信息返回給前端并發(fā)放虛擬產(chǎn)品
服務(wù)端的工作比較簡(jiǎn)單卤橄,分4步:
5.7.接收ios端發(fā)過(guò)來(lái)的購(gòu)買憑證绿满。
5.8.判斷憑證是否已經(jīng)存在或驗(yàn)證過(guò),然后存儲(chǔ)該憑證窟扑。
5.9.將該憑證發(fā)送到蘋(píng)果的服務(wù)器驗(yàn)證喇颁,并將驗(yàn)證結(jié)果返回給客戶端。
如果需要嚎货,修改用戶相應(yīng)的會(huì)員權(quán)限橘霎。
考慮到網(wǎng)絡(luò)異常情況,服務(wù)器的驗(yàn)證應(yīng)該是一個(gè)可恢復(fù)的隊(duì)列殖属,如果網(wǎng)絡(luò)失敗了姐叁,應(yīng)該進(jìn)行重試。
簡(jiǎn)單來(lái)說(shuō)就是將該購(gòu)買憑證用Base64編碼忱辅,然后POST給蘋(píng)果的驗(yàn)證服務(wù)器七蜘,蘋(píng)果將驗(yàn)證結(jié)果以JSON形式返回。
代碼部分
.h部分
//
// StoreIPAManager.h
// FamilyNames
//
// Created by mac126 on 2019/1/12.
// Copyright ? 2019 裴鐸. All rights reserved.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
typedef enum {
SIAPPurchSuccess = 0, // 購(gòu)買成功
SIAPPurchFailed = 1, // 購(gòu)買失敗
SIAPPurchCancle = 2, // 取消購(gòu)買
SIAPPurchVerFailed = 3, // 訂單校驗(yàn)失敗
SIAPPurchVerSuccess = 4, // 訂單校驗(yàn)成功
SIAPPurchNotArrow = 5, // 不允許內(nèi)購(gòu)
}SIAPPurchType;
typedef void (^IAPCompletionHandle)(SIAPPurchType type,NSData *data);
@interface StoreIPAManager : NSObject
/**
獲取內(nèi)購(gòu)業(yè)務(wù)對(duì)象
@return 內(nèi)購(gòu)業(yè)務(wù)對(duì)象
*/
+ (instancetype)shareSIAPManager;
/**
開(kāi)始內(nèi)購(gòu)
@param purchID 蘋(píng)果后臺(tái)的虛擬商品ID
@param handle 請(qǐng)求事務(wù)回調(diào)類型墙懂,返回的數(shù)據(jù)
*/
- (void)startPurchWithID:(NSString *)purchID completeHandle:(IAPCompletionHandle)handle;
@end
NS_ASSUME_NONNULL_END
.m部分
//
// StoreIPAManager.m
// FamilyNames
//
// Created by mac126 on 2019/1/12.
// Copyright ? 2019 裴鐸. All rights reserved.
//
/** 打印日志
#ifdef DEBUG //調(diào)試階段
#define PDFUNC K_Log(@"%s",__func__);
#define K_Log(...); NSLog(__VA_ARGS__);
#else //發(fā)布階段
#define K_Log(...);
#endif
*/
#import "StoreIPAManager.h"
#import "NSData+Base64.h"
#import <StoreKit/StoreKit.h>
@interface StoreIPAManager ()<SKPaymentTransactionObserver,SKProductsRequestDelegate>{
NSString *_purchID;
IAPCompletionHandle _handle;
}
@end
@implementation StoreIPAManager
#pragma mark - ??life cycle
+ (instancetype)shareSIAPManager{
static StoreIPAManager *IAPManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken,^{
IAPManager = [[StoreIPAManager alloc] init];
});
return IAPManager;
}
- (instancetype)init{
self = [super init];
if (self) {
// 購(gòu)買監(jiān)聽(tīng)寫(xiě)在程序入口,程序掛起時(shí)移除監(jiān)聽(tīng),這樣如果有未完成的訂單將會(huì)自動(dòng)執(zhí)行并回調(diào) paymentQueue:updatedTransactions:方法
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
}
return self;
}
- (void)dealloc{
[[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
}
#pragma mark - ??public
- (void)startPurchWithID:(NSString *)purchID completeHandle:(IAPCompletionHandle)handle{
if (purchID) {
if ([SKPaymentQueue canMakePayments]) {
// 開(kāi)始購(gòu)買服務(wù)
_purchID = purchID;
_handle = handle;
NSSet *nsset = [NSSet setWithArray:@[purchID]];
SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:nsset];
request.delegate = self;
[request start];
}else{
[self handleActionWithType:SIAPPurchNotArrow data:nil];
}
}
}
#pragma mark - ??private
- (void)handleActionWithType:(SIAPPurchType)type data:(NSData *)data{
switch (type) {
case SIAPPurchSuccess:
K_Log(@"購(gòu)買成功");
break;
case SIAPPurchFailed:
K_Log(@"購(gòu)買失敗");
break;
case SIAPPurchCancle:
K_Log(@"用戶取消購(gòu)買");
break;
case SIAPPurchVerFailed:
K_Log(@"訂單校驗(yàn)失敗");
break;
case SIAPPurchVerSuccess:
K_Log(@"訂單校驗(yàn)成功");
break;
case SIAPPurchNotArrow:
K_Log(@"不允許程序內(nèi)付費(fèi)");
break;
default:
break;
}
if(_handle){
_handle(type,data);
}
}
#pragma mark - ??delegate
// 交易結(jié)束
- (void)completeTransaction:(SKPaymentTransaction *)transaction{
// Your application should implement these two methods.
NSString *productIdentifier = transaction.payment.productIdentifier;
NSData *transactionReceiptData = [NSData dataWithContentsOfURL:[[NSBundle mainBundle] appStoreReceiptURL]];
NSString *receipt = [transactionReceiptData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
if ([productIdentifier length] > 0) {
// 向自己的服務(wù)器驗(yàn)證購(gòu)買憑證橡卤,把receipt傳給后臺(tái)
// 這一步盡量用NSURLSession寫(xiě),AFN等第三方容易造成21002(蘋(píng)果服務(wù)器返回的狀態(tài)碼损搬,代表數(shù)據(jù)缺失)
// AFN等第三方在內(nèi)部對(duì)數(shù)據(jù)進(jìn)行反序列化的時(shí)候丟失數(shù)據(jù)了, 如果不用自動(dòng)反序列化可能會(huì)好一點(diǎn),
// 自行嘗試那種都可以只要數(shù)據(jù)完整的傳給后臺(tái),后臺(tái)完整的傳給蘋(píng)果服務(wù)器就OK
NSMutableDictionary * parameters = [NSMutableDictionary dictionary];
parameters[@"自己后臺(tái)定義的字段"] = receipt;
NSURL * url = [NSURL URLWithString:@"自己后臺(tái)服務(wù)器地址")];
NSMutableURLRequest *storeRequest = [NSMutableURLRequest requestWithURL:url];
[storeRequest setHTTPMethod:@"POST"];
// 設(shè)置頭部參數(shù)
//[storeRequest addValue:@"如果需要把token拼在頭部發(fā)給后臺(tái)" forHTTPHeaderField:@"token"];
// 遍歷字典碧库,以“key=value&”的方式創(chuàng)建參數(shù)字符串。
NSMutableString *parameterString = [[NSMutableString alloc]init];
int pos = 0;
for (NSString * key in parameters.allKeys) {
// 拼接字符串
[parameterString appendFormat:@"%@=%@", key, parameters[key]];
if(pos< parameters.allKeys.count - 1){
[parameterString appendString:@"&"];
}
pos++;
}
// NSString轉(zhuǎn)成NSData數(shù)據(jù)類型巧勤。
NSData *parametersData = [parameterString dataUsingEncoding:NSUTF8StringEncoding];
[storeRequest setHTTPBody:parametersData];
[[[NSURLSession sharedSession] dataTaskWithRequest:storeRequest completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (error) {
K_Log(@"本次網(wǎng)絡(luò)請(qǐng)求請(qǐng)求失敗嵌灰,并不是購(gòu)買失敗");
} else {
K_Log(@"請(qǐng)求成功");
}
}] resume];
}
[self verifyPurchaseWithPaymentTransaction:transaction isTestServer:NO];
}
// 交易失敗
- (void)failedTransaction:(SKPaymentTransaction *)transaction{
if (transaction.error.code != SKErrorPaymentCancelled) {
[self handleActionWithType:SIAPPurchFailed data:nil];
}else{
[self handleActionWithType:SIAPPurchCancle data:nil];
}
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
- (void)verifyPurchaseWithPaymentTransaction:(SKPaymentTransaction *)transaction isTestServer:(BOOL)flag{
//交易驗(yàn)證
NSURL *recepitURL = [[NSBundle mainBundle] appStoreReceiptURL];
NSData *receipt = [NSData dataWithContentsOfURL:recepitURL];
if(!receipt){
// 交易憑證為空驗(yàn)證失敗
[self handleActionWithType:SIAPPurchVerFailed data:nil];
return;
}
// 購(gòu)買成功將交易憑證發(fā)送給服務(wù)端進(jìn)行再次校驗(yàn)
[self handleActionWithType:SIAPPurchSuccess data:receipt];
NSError *error;
NSDictionary *requestContents = @{
@"receipt-data": [receipt base64EncodedStringWithOptions:0]
};
NSData *requestData = [NSJSONSerialization dataWithJSONObject:requestContents
options:0
error:&error];
if (!requestData) { // 交易憑證為空驗(yàn)證失敗
[self handleActionWithType:SIAPPurchVerFailed data:nil];
return;
}
//In the test environment, use https://sandbox.itunes.apple.com/verifyReceipt
//In the real environment, use https://buy.itunes.apple.com/verifyReceipt
NSString *serverString = @"https://buy.itunes.apple.com/verifyReceipt";
if (flag) {
serverString = @"https://sandbox.itunes.apple.com/verifyReceipt";
}
NSURL *storeURL = [NSURL URLWithString:serverString];
NSMutableURLRequest *storeRequest = [NSMutableURLRequest requestWithURL:storeURL];
[storeRequest setHTTPMethod:@"POST"];
[storeRequest setHTTPBody:requestData];
[[[NSURLSession sharedSession] dataTaskWithRequest:storeRequest completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (error) {
// 無(wú)法連接服務(wù)器,購(gòu)買校驗(yàn)失敗
[self handleActionWithType:SIAPPurchVerFailed data:nil];
} else {
NSError *error;
NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
if (!jsonResponse) {
// 蘋(píng)果服務(wù)器校驗(yàn)數(shù)據(jù)返回為空校驗(yàn)失敗
[self handleActionWithType:SIAPPurchVerFailed data:nil];
}
// 先驗(yàn)證正式服務(wù)器,如果正式服務(wù)器返回21007再去蘋(píng)果測(cè)試服務(wù)器驗(yàn)證,沙盒測(cè)試環(huán)境蘋(píng)果用的是測(cè)試服務(wù)器
NSString *status = [NSString stringWithFormat:@"%@",jsonResponse[@"status"]];
if (status && [status isEqualToString:@"21007"]) {
[self verifyPurchaseWithPaymentTransaction:transaction isTestServer:YES];
}else if(status && [status isEqualToString:@"0"]){
[self handleActionWithType:SIAPPurchVerSuccess data:nil];
}
K_Log(@"----驗(yàn)證結(jié)果 %@",jsonResponse);
}
}] resume];
// 驗(yàn)證成功與否都注銷交易,否則會(huì)出現(xiàn)虛假憑證信息一直驗(yàn)證不通過(guò),每次進(jìn)程序都得輸入蘋(píng)果賬號(hào)
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
#pragma mark - SKProductsRequestDelegate
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response{
NSArray *product = response.products;
if([product count] <= 0){
K_Log(@"--------------沒(méi)有商品------------------");
return;
}
SKProduct *p = nil;
for(SKProduct *pro in product){
if([pro.productIdentifier isEqualToString:_purchID]){
p = pro;
break;
}
}
K_Log(@"productID:%@", response.invalidProductIdentifiers);
K_Log(@"產(chǎn)品付費(fèi)數(shù)量:%lu",(unsigned long)[product count]);
K_Log(@"%@",[p description]);
K_Log(@"%@",[p localizedTitle]);
K_Log(@"%@",[p localizedDescription]);
K_Log(@"%@",[p price]);
K_Log(@"%@",[p productIdentifier]);
K_Log(@"發(fā)送購(gòu)買請(qǐng)求");
SKPayment *payment = [SKPayment paymentWithProduct:p];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
//請(qǐng)求失敗
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error{
K_Log(@"------------------錯(cuò)誤-----------------:%@", error);
}
- (void)requestDidFinish:(SKRequest *)request{
K_Log(@"------------反饋信息結(jié)束-----------------");
}
#pragma mark - SKPaymentTransactionObserver
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray<SKPaymentTransaction *> *)transactions{
for (SKPaymentTransaction *tran in transactions) {
switch (tran.transactionState) {
case SKPaymentTransactionStatePurchased:{
[self completeTransaction:tran];
}break;
case SKPaymentTransactionStatePurchasing:{
K_Log(@"商品添加進(jìn)列表");
}break;
case SKPaymentTransactionStateRestored:{
K_Log(@"已經(jīng)購(gòu)買過(guò)商品");
// 消耗型不支持恢復(fù)購(gòu)買
[[SKPaymentQueue defaultQueue] finishTransaction:tran];
}break;
case SKPaymentTransactionStateFailed:{
[self failedTransaction:tran];
}break;
default: break;
}
}
}
@end
/*注意事項(xiàng):
1.沙盒環(huán)境測(cè)試appStore內(nèi)購(gòu)流程的時(shí)候,請(qǐng)使用沒(méi)越獄的設(shè)備颅悉。
2.請(qǐng)務(wù)必使用真機(jī)來(lái)測(cè)試沽瞭,一切以真機(jī)為準(zhǔn)。
3.項(xiàng)目的Bundle identifier需要與您申請(qǐng)AppID時(shí)填寫(xiě)的bundleID一致剩瓶,不然會(huì)無(wú)法請(qǐng)求到商品信息驹溃。
4.如果是你自己的設(shè)備上已經(jīng)綁定了自己的AppleID賬號(hào)請(qǐng)先注銷掉,否則你哭爹喊娘都不知道是怎么回事城丧。
5.訂單校驗(yàn) 蘋(píng)果審核app時(shí),仍然在沙盒環(huán)境下測(cè)試豌鹤,所以需要先進(jìn)行正式環(huán)境驗(yàn)證亡哄,如果發(fā)現(xiàn)是沙盒環(huán)境則轉(zhuǎn)到沙盒驗(yàn)證。
識(shí)別沙盒環(huán)境訂單方法:
1.根據(jù)字段 environment = sandbox布疙。
2.根據(jù)驗(yàn)證接口返回的狀態(tài)碼,如果status=21007蚊惯,則表示當(dāng)前為沙盒環(huán)境。
蘋(píng)果反饋的狀態(tài)碼:
21000 App Store無(wú)法讀取你提供的JSON數(shù)據(jù)
21002 訂單數(shù)據(jù)不符合格式灵临,數(shù)據(jù)缺失了
21003 訂單無(wú)法被驗(yàn)證
21004 你提供的共享密鑰和賬戶的共享密鑰不一致
21005 訂單服務(wù)器當(dāng)前不可用
21006 訂單是有效的截型,但訂閱服務(wù)已經(jīng)過(guò)期。當(dāng)收到這個(gè)信息時(shí)俱诸,解碼后的收據(jù)信息也包含在返回內(nèi)容中
21007 訂單信息是測(cè)試用(sandbox)菠劝,但卻被發(fā)送到產(chǎn)品環(huán)境中驗(yàn)證
21008 訂單信息是產(chǎn)品環(huán)境中使用,但卻被發(fā)送到測(cè)試環(huán)境中驗(yàn)證
*/
在需要的位置倒入業(yè)務(wù)類頭文件睁搭,調(diào)用下面的代碼
// ID:iTunesConnect 蘋(píng)果后臺(tái)配置的產(chǎn)品ID
// 這里應(yīng)該彈出指示器(作用是禁止用戶多次操作)赶诊,避免用戶多次點(diǎn)擊購(gòu)買按鈕后程序會(huì)崩潰
[[StoreIPAManager shareSIAPManager] startPurchWithID:ID completeHandle:^(SIAPPurchType type,NSData *data) {
//請(qǐng)求事務(wù)回調(diào)類型,返回的數(shù)據(jù)园骆,
//結(jié)束指示器舔痪。
K_Log(@"SIAPPurchType :%d", type);
K_Log(@"購(gòu)買結(jié)束返回:%@", data);
}];