關(guān)于協(xié)議稅務(wù)和銀行業(yè)務(wù)的資料填寫以及申請(qǐng)我就不寫了愉老,自己Google+百度吧舌稀。畢竟各位大神寫的也是蠻詳細(xì)的啊犬。直接上代碼了,需要的說明我會(huì)以注釋的形式大概說一下壁查,當(dāng)然有什么不對(duì)的地方歡迎拍磚????(swift2.2觉至,不要問我為什么不升級(jí))
- 1、導(dǎo)入
StoreKit.framework
庫(kù)(target -> General -> Linked Frameworks and Libraries -> 加號(hào)...) - 2睡腿、導(dǎo)入頭文件
import StoreKit
- 3语御、去開發(fā)者賬號(hào)申請(qǐng)產(chǎn)品ID(iTunes Connect -> 我的APP -> 功能 -> APP內(nèi)購(gòu)買項(xiàng)目 -> 點(diǎn)擊加號(hào)填寫相關(guān)的信息 -> 填寫完會(huì)生成一個(gè)
產(chǎn)品 ID
) - 4、代碼:
class InAppPurchaseViewController: UIViewController, SKPaymentTransactionObserver, SKProductsRequestDelegate{
// 判斷是否在測(cè)試環(huán)境下(用于客戶端自己去蘋果服務(wù)器驗(yàn)證付款信息席怪,當(dāng)然不推薦這么做应闯,最好是由服務(wù)器去驗(yàn)證,然后返回給客戶端)
private let appStoreUrl = "https://sandbox.itunes.apple.com/verifyReceipt"http:// 測(cè)試環(huán)境(沙盒賬號(hào)付款)
private let appStoreUrl = "https://buy.itunes.apple.com/verifyReceipt"
override func viewDidLoad() {
super.viewDidLoad()
// 開啟內(nèi)購(gòu)檢測(cè)
SKPaymentQueue.defaultQueue().addTransactionObserver(self)
}
func purchaseBtnClick(sender: AnyObject) {
if SKPaymentQueue.canMakePayments() {
reuqestProductData("在第三步申請(qǐng)的產(chǎn)品ID")
}else{
print("您已設(shè)置不允許程序內(nèi)購(gòu)買")
}
}
private func reuqestProductData(type: String) {
let product = [type]
let set = NSSet(array: product as [AnyObject])
let request = SKProductsRequest(productIdentifiers: set as! Set<String>)
request.delegate = self
request.start()
}
// 監(jiān)聽購(gòu)買結(jié)果
func paymentQueue(queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
for tran in transactions {
// print(tran.error?.description)
switch tran.transactionState {
case .Purchased:
print("購(gòu)買成功")
commitSeversSucceeWithTransaction(tran)
SKPaymentQueue.defaultQueue().finishTransaction(tran)
case .Purchasing:
print("用戶正在購(gòu)買")
case .Restored:
print("已經(jīng)購(gòu)買過商品")
case .Failed:
SKPaymentQueue.defaultQueue().finishTransaction(tran)
print("失敗")
default:
break
}
}
}
// 收到產(chǎn)品返回的信息
func productsRequest(request: SKProductsRequest, didReceiveResponse response: SKProductsResponse) {
if response.products.count == 0 {
print("沒有商品")
}
var prod: SKProduct?
for pro in response.products {
print("------------------")
print(pro.localizedDescription)
print(pro.localizedTitle)
print(pro.price)
print(pro.productIdentifier)
print("------------------")
if pro.productIdentifier == "在第三步申請(qǐng)的產(chǎn)品ID" {
prod = pro
}
}
// 發(fā)送購(gòu)買請(qǐng)求
if let produ = prod {
let payment = SKPayment(product: produ)
SKPaymentQueue.defaultQueue().addPayment(payment)
}
}
// 驗(yàn)證(1挂捻、客戶端自己驗(yàn)證碉纺。2、由后臺(tái)去驗(yàn)證,推薦這個(gè))
private func commitSeversSucceeWithTransaction(transation: SKPaymentTransaction) {
let receiptUrl = NSBundle.mainBundle().appStoreReceiptURL
let receiveData = NSData(contentsOfURL: receiptUrl!)
// 最好轉(zhuǎn)成Base64骨田,萬一返回結(jié)果有特殊字符
let receiptString = receiveData!.base64EncodedStringWithOptions(.EncodingEndLineWithLineFeed)
// 保存本地(dict隨你自己的需求寫就好)
let dict = ["account_id": account, "receipt": "\(receiptString)"]
userDefault.setObject(dict, forKey: UserDefaultKey.appInPurchase)
userDefault.synchronize()
// 客戶端自己去蘋果服務(wù)器驗(yàn)證付款信息
let bodyString = "{\"receipt-data\" : \"\(receiptString)\"}"
let bodyData = bodyString.dataUsingEncoding(NSUTF8StringEncoding)
let url = NSURL(string: appStoreUrl)
let reuqest: NSMutableURLRequest = NSMutableURLRequest(URL: url!)
reuqest.HTTPBody = bodyData
reuqest.HTTPMethod = "POST"
// 請(qǐng)?jiān)徫覒谢=В吘刮抑皇菍懡o自己測(cè)試的(上線的時(shí)候就是由后臺(tái)去驗(yàn)證的了),就直接強(qiáng)制try了盛撑。如果你們真的是要客戶端自己驗(yàn)證的話,就乖乖的do - catch 拋異常吧捧搞。
let responseData = try? NSURLConnection.sendSynchronousRequest(reuqest, returningResponse: nil)
let dic: NSDictionary = try! NSJSONSerialization.JSONObjectWithData(responseData!, options: .AllowFragments) as! NSDictionary
// let statusCode: Int = dic["status"] as! Int
// 我打包裝到手機(jī)(即連到蘋果正式驗(yàn)證環(huán)境抵卫,因?yàn)樘O果審核的時(shí)候就是用你提供的沙盒賬號(hào)測(cè)試的)測(cè)試支付的時(shí)候發(fā)現(xiàn)dic就直接等于21007了,但是debug的時(shí)候dic返回很多相關(guān)的信息(可能這里命名不應(yīng)該叫dic胎撇,表示有點(diǎn)不太懂為什么會(huì)這樣)
// status請(qǐng)參考下面的狀態(tài)碼表
// 由自己的服務(wù)器去驗(yàn)證
把交易成功后獲取的憑證receiptString傳給后臺(tái)介粘,讓后臺(tái)去蘋果的服務(wù)器驗(yàn)證,然后客戶端根據(jù)后臺(tái)返回的數(shù)據(jù)來處理業(yè)務(wù)(比如刷新UI晚树,設(shè)置為會(huì)員等等操作)
validateForServer(receiptString) // 自定義方法
}
// 其他代理方法(請(qǐng)求失敗姻采,請(qǐng)求成功等等的返回,根據(jù)具體的需求自己處理吧)
}
5爵憎、添加沙盒測(cè)試賬號(hào)(iTunes Connect -> 用戶和智能 -> 點(diǎn)擊加號(hào)添加即可)慨亲,然后就自己測(cè)試支付吧。
6宝鼓、需要注意的幾點(diǎn):
1)在監(jiān)聽購(gòu)買結(jié)果后刑棵,一定要調(diào)用SKPaymentQueue.defaultQueue().finishTransaction(tran);來允許你從支付隊(duì)列中移除交易
2)沙盒環(huán)境測(cè)試appStore內(nèi)購(gòu)流程的時(shí)候,請(qǐng)使用未越獄的設(shè)備
3)請(qǐng)務(wù)必使用真機(jī)來測(cè)試愚铡,一切以真機(jī)為準(zhǔn)
4)真機(jī)測(cè)試的時(shí)候蛉签,一定要退出原來的賬號(hào),才能用沙盒測(cè)試賬號(hào)沥寥,否則會(huì)報(bào)錯(cuò)連接不到iTunes
5)開發(fā)環(huán)境下也需要有證書才能測(cè)試碍舍,請(qǐng)確保項(xiàng)目的BundleID需要與您申請(qǐng)AppID時(shí)填寫的bundleID一致
6)價(jià)格不能在后臺(tái)獲取,只能在申請(qǐng)產(chǎn)品ID的時(shí)候選擇邑雅,有多個(gè)價(jià)格片橡?那就多申請(qǐng)幾個(gè)產(chǎn)品ID咯。
7)為保證審核的通過蒂阱,需要在客戶端或server進(jìn)行雙重驗(yàn)證锻全,即,先以線上交易驗(yàn)證地址進(jìn)行驗(yàn)證录煤,如果蘋果正式驗(yàn)證服務(wù)器的返回驗(yàn)證碼code為21007鳄厌,則再一次連接沙盒測(cè)試服務(wù)器進(jìn)行驗(yàn)證即可。在應(yīng)用提審時(shí)妈踊,蘋果IAP提審驗(yàn)證時(shí)是在沙盒環(huán)境的進(jìn)行的了嚎,即:蘋果在審核App時(shí),只會(huì)在sandbox環(huán)境購(gòu)買,其產(chǎn)生的購(gòu)買憑證歪泳,也只能連接蘋果的測(cè)試驗(yàn)證服務(wù)器萝勤,如果沒有做雙驗(yàn)證,需要特別注意此問題呐伞,否則會(huì)被拒敌卓。
8)代碼僅供參考??????
-
7、關(guān)于漏單的處理
// 我還需要理清邏輯+調(diào)試代碼伶氢,端午節(jié)后補(bǔ)充吧趟径,已經(jīng)提前給自己放假,無心寫代碼癣防。(端午已完蜗巧,三天假過得飛快有木有)
關(guān)于漏單:用戶已經(jīng)支付了money,但是我們并沒有將用戶設(shè)置為會(huì)員或者發(fā)放道具等等相關(guān)的操作蕾盯。原因幕屹?可能是驗(yàn)證的時(shí)候出了問題,也可能是沒等到驗(yàn)證完成用戶就直接退出app了级遭,可能...(腦補(bǔ)吧望拖,畢竟bug總是千奇百怪,防不勝防)
我的做法是保存到本地即 NSUserDefaults(代碼見上面挫鸽,我最后還是放棄了這種方法靠娱,用了下面內(nèi)購(gòu)文里的方法)
然后一進(jìn)程序就看看有沒有漏單推薦一篇關(guān)于內(nèi)購(gòu)文,寫的比我這好多了掠兄,各位猿媛請(qǐng)移步吧像云。
8、越獄手機(jī)無解
網(wǎng)上資料比較少蚂夕,目前我可能就先這樣了迅诬,如果點(diǎn)進(jìn)來的你已經(jīng)處理了的話,歡迎補(bǔ)充婿牍。
我們的APP一直在用微信支付和支付寶支付侈贷。產(chǎn)品說以防萬一(我覺得被拒的概率很小),先提前做好等脂。剛知道這個(gè)需求的時(shí)候特想說句mmp俏蛮。后來還是自己默默的翻遍了網(wǎng)上的內(nèi)購(gòu)文。恩上遥,總算可以支付了搏屑,后期如果有什么問題,我會(huì)繼續(xù)補(bǔ)充的粉楚。
鑒于網(wǎng)上的圖文博客比較多辣恋,我基本都是以文字的形式大概說明了一下操作流程亮垫。