import UIKit
import StoreKit//內購頭文件
//內購代理SKPaymentTransactionObserver,SKProductsRequestDelegate
class AttritionBuyTableViewController: UITableViewController ,SKProductsRequestDelegate,SKPaymentTransactionObserver{
//沙盒測試環境驗證
let SANDBOX = "https://sandbox.itunes.apple.com/verifyReceipt"
//正式環境驗證
let AppStore = "https://buy.itunes.apple.com/verifyReceipt"
var productDict:NSMutableDictionary?
let dataArr = ["12元購買"]
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.title = "消耗性內購測試"
self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
SKPaymentQueue.default().add(self)//添加監聽
}
deinit {
SKPaymentQueue.default().remove(self)//移除監聽
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return dataArr.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = dataArr[indexPath.row]
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.buy(string: "123123123")
}
//根據內購種類加參數
func buy(string:String){
//判斷是否允許內購
if SKPaymentQueue.canMakePayments() {
let nsset:NSSet = NSSet.init(array: [string])
let request = SKProductsRequest.init(productIdentifiers: nsset as! Set<String>)
request.delegate = self
request.start()
}else{
print("can not canMakePayments")
}
}
// SKProductsRequestDelegate代理方法
//收到的產品信息
func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
// request
// response 商品的相關信息
//print(@"無效商品列表 :%@",response.invalidProductIdentifiers);
// print("產品id ===== \(response.invalidProductIdentifiers)")
// print("產品付費數量===\(myArr.count)")
if response.products.count == 0 {
print("沒有商品")
return
}
for product in response.products {
//單個產品的相關屬性
// print("SKProduct 描述信息\(pro.description)")
// print("產品標題\(pro.localizedTitle)")
// print("產品描述信息\(pro.localizedDescription)")
// print("價格\(pro.price)")
// print("ProDuct id\(pro.productIdentifier)")
let payment:SKPayment = SKPayment.init(product: product)
SKPaymentQueue.default().add(payment)
}
}
func request(_ request: SKRequest, didFailWithError error: Error) {
print("------------------錯誤-----------------\(error)")
}
func requestDidFinish(_ request: SKRequest) {
print("反饋信息結束----------\(request)")
}
//<SKPaymentTransactionObserver> 千萬不要忘記綁定,代碼如下:
//----監聽購買結果 四種購買結果
// SKPaymentTransactionStatePurchasing: 購買中,此時可更新UI來展現購買的過程
// SKPaymentTransactionStateFailed: 購買錯誤,此時要根據錯誤的代碼給用戶相應的提示
// SKPaymentTransactionStatePurchased: 購買成功,此時要提供給用戶相應的內容
// SKPaymentTransactionStateRestored: 恢復已購產品,此時需要將已經購買的商品恢復給用戶
//[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
for tran in transactions {
switch tran.transactionState {
case SKPaymentTransactionState.purchased:
print("交易完成")
SKPaymentQueue.default().finishTransaction(tran)
self.verifyTransactionResult()
break
case SKPaymentTransactionState.purchasing:
print("商品添加進列表")
break
case SKPaymentTransactionState.restored:
print("已經購買過商品")
break
case SKPaymentTransactionState.failed:
print("交易失敗")
SKPaymentQueue.default().finishTransaction(tran)
break
default:
break
}
}
}
//驗證憑據,獲取到蘋果返回的交易憑據
func verifyTransactionResult(){
// appStoreReceiptURL iOS7.0增加的,購買交易完成後,會將憑據存放在該地址
let url = Bundle.main.appStoreReceiptURL
//從沙盒中獲取到購買憑據
let recriptData = NSData.init(contentsOf: url!)
// let string = "{\"receipt-data\":recript?.base64EncodedString(options: NSData.Base64EncodingOptions(rawValue: 0)),\"sandbox\":\"1\"}"
let encodeStr = recriptData?.base64EncodedString(options: NSData.Base64EncodingOptions.endLineWithLineFeed)
let payload = NSString(string: "{\"receipt-data\" : \"" + encodeStr! + "\"}")
let payloadData = payload.data(using: String.Encoding.utf8.rawValue)
/**
BASE64常用的編碼方案,通常用於數據傳輸,以及加密算法的基礎算法,傳輸過程中能夠保證數據傳輸的穩定性,BASE64是可以編碼和解碼的。
*/
/**
請求後臺接口,服務器處驗證是否支付成功,依據返回結果做相應邏輯處理
與後臺協調好,讓後臺根據你的“sandbox”字段的1,0來區分請求是正式環境還是測試環境
(當然“sandbox”這個字段也可以替換爲你想要的,但是“receipt-data”不能替換,要注意!)
*/
//請求成功的response自己輸出看一下吧,status是0就成功了,這裏就不貼出來了,因爲有一些敏感數據,比如你的bundleID,product_id之類的
//創建請求到蘋果官方進行購買驗證
let payUrl = URL.init(string: SANDBOX)
let requestM = NSMutableURLRequest.init(url: payUrl!)
requestM.httpBody = payloadData
requestM.httpMethod = "POST"
//創建連接併發送同步請求
let respData = try? NSURLConnection.sendSynchronousRequest(requestM as URLRequest, returning: nil)
let dic:NSDictionary = try! JSONSerialization.jsonObject(with: respData!, options: .allowFragments) as! NSDictionary
print(dic)
if (dic)["status"] as! Int == 0 {
print("購買成功")
let dictReceipt = dic["receipt"] as! NSDictionary
let dictInApp:NSArray = dictReceipt["in_app"] as! NSArray
let productIdentifier:String = (dictInApp[0] as! NSDictionary)["product_id"] as! String//讀取產品標識
//如果是消耗品則記錄購買數量,非消耗品則記錄是否購買過
let defaults = UserDefaults.standard
if productIdentifier == "123123123" {
let count = defaults.integer(forKey: productIdentifier)
UserDefaults.standard.set(count + 1, forKey: productIdentifier)
}else{
defaults.set(true, forKey: productIdentifier)
}
}else{
print("購買失敗")
}
}
func paymentQueue(_ queue: SKPaymentQueue, restoreCompletedTransactionsFailedWithError error: Error) {
print(error)
}
}
內購注意事項
1.協議.稅務.和銀行業務按照網上的文檔來就行了.可以參考文檔: http://www.jianshu.com/p/86ac7d3b593a
2.創建內購項目:
a,內購項目類型的選擇,一定要根據自己項目的需要選擇正確,以免造成不必要的損失!
b,產品ID(必須規範,在寫代碼是需要用到!!!);
c,內購項目的相關內容都必須填寫完(狀態爲:準備提交,當狀態爲缺少二進制文件或者其他就不能在提交版本那裏看到創建的內購項目)
3.沙盒測試:沙盒測試賬號爲沒有在App Store上面註冊使用的賬號, 需要在手機設置/iTunes與App Store,將原有的賬號註銷使用沙盒填寫的測試賬號
4.代碼問題:bundle ID ,項目的bundle ID必須和內購的bundle ID一致 ; 內購項目ID必須和填寫的一致 ; 監聽記得添加和移除; 測試和正式的蘋果內購地址需要分開
5.附上蘋果支付錯誤返的狀態碼
- 21000 App Store無法讀取你提供的JSON數據
- 21002 收據數據不符合格式
- 21003 收據無法被驗證
- 21004 你提供的共享密鑰和賬戶的共享密鑰不一致
- 21005 收據服務器當前不可用
- 21006 收據是有效的,但訂閱服務已經過期。當收到這個信息時,解碼後的收據信息也包含在返回內容中
- 21007 收據信息是測試用(sandbox),但卻被髮送到產品環境中驗證
- 21008 收據信息是產品環境中使用,但卻被髮送到測試環境中驗證
暫時就這些了.