xcode專案設定
把Capabilities中的In-App Purchase打開
程式碼: 這邊只列出重點部分
class PurchaseVC: UIViewController , UITableViewDelegate , UITableViewDataSource , SKProductsRequestDelegate , SKPaymentTransactionObserver{
@IBOutlet weak var tableView: UITableView!
var productIDs: [String] = [String]() // 產品ID(Consumable_Product、Not_Consumable_Product)
var productsArray: [SKProduct] = [SKProduct]() // 存放 server 回應的產品項目
var selectedProductIndex: Int! // 點擊到的購買項目
var isProgress: Bool = false // 是否有交易正在進行中
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.delegate = self
self.tableView.dataSource = self
self.productIDs.append("Consumable_Product") // 消耗性產品
self.productIDs.append("NotConsumable_Product") // 非消耗性產品
self.requestProductInfo()
// Do any additional setup after loading the view.
}
override func viewWillDisappear(_ animated: Bool) {
SKPaymentQueue.default().remove(self) //移除觀察者
}
func requestProductInfo() {
if SKPaymentQueue.canMakePayments() {
// 取得所有在 iTunes Connect 所建立的內購項目
let productIdentifiers: Set<String> = NSSet(array: self.productIDs) as! Set<String>
let productRequest: SKProductsRequest = SKProductsRequest(productIdentifiers: productIdentifiers)
productRequest.delegate = self
productRequest.start() // 開始請求內購產品
} else {
print("取不到任何內購的商品...")
}
}
// 詢問是否購買或回復的 Action Sheet
func showActionSheet(_ product: Product) {
// 代表有購買項目正在處理中
if self.isProgress {
return
}
var message: String!
var buyAction: UIAlertAction?
var restoreAction: UIAlertAction?
switch product {
case .consumable, .nonConsumable:
// 購買消耗性、非消耗性產品
message = "確定購買產品?"
buyAction = UIAlertAction(title: "購買", style: UIAlertActionStyle.default) { (action) -> Void in
if SKPaymentQueue.canMakePayments() {
// 設定交易流程觀察者,會在背景一直檢查交易的狀態,成功與否會透過 protocol 得知
SKPaymentQueue.default().add(self)
// 取得內購產品
let payment = SKPayment(product: self.productsArray[self.selectedProductIndex])
// 購買消耗性、非消耗性動作將會開始在背景執行(updatedTransactions delegate 會接收到兩次)
SKPaymentQueue.default().add(payment)
self.isProgress = true
}
}
default:
// 復原購買產品
message = "是否回復?"
restoreAction = UIAlertAction(title: "回復", style: UIAlertActionStyle.default) { (action) -> Void in
if SKPaymentQueue.canMakePayments() {
SKPaymentQueue.default().restoreCompletedTransactions()
self.isProgress = true
}
}
}
let actionSheetController = UIAlertController(title: "測試內購", message: message, preferredStyle: UIAlertControllerStyle.actionSheet)
let cancelAction = UIAlertAction(title: "取消", style: UIAlertActionStyle.cancel, handler: nil)
actionSheetController.addAction(buyAction != nil ? buyAction! : restoreAction!)
actionSheetController.addAction(cancelAction)
self.present(actionSheetController, animated: true, completion: nil)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! IAPTableViewCell
let product = self.productsArray[indexPath.row] // 請求的產品
cell.productLabel.text = "\(product.localizedTitle)\n\(product.localizedDescription)"
// localizedTitle(商品名稱)、localizedDescription(商品描述)
cell.priceLabel.text = "$\(product.price)"
return cell
}
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
// 送出購買則會 update 一次,購買成功 server 又會回傳一次 update
for transaction in transactions {
switch transaction.transactionState {
case SKPaymentTransactionState.purchased:
print("交易成功...")
// 不管交易是否成功,都要把這筆交易finish掉
SKPaymentQueue.default().finishTransaction(transaction)
self.isProgress = false
// 移除觀查者
SKPaymentQueue.default().remove(self)
// 付款成功,回傳給ViewController
delegate.didBuySomething(self, self.selectedProductIndex == 0 ? Product.consumable : Product.nonConsumable)
self.dismiss(animated: true, completion: nil)
case SKPaymentTransactionState.failed:
print("交易失敗...")
SKPaymentQueue.default().finishTransaction(transaction)
self.isProgress = false
if let error = transaction.error as? SKError {
switch error.code {
case .paymentCancelled:
// 輸入 Apple ID 密碼時取消
print("Transaction Cancelled: \(error.localizedDescription)")
case .paymentInvalid:
print("Transaction paymentInvalid: \(error.localizedDescription)")
case .paymentNotAllowed:
print("Transaction paymentNotAllowed: \(error.localizedDescription)")
default:
print("Transaction: \(error.localizedDescription)")
}
}
SKPaymentQueue.default().finishTransaction(transaction)
self.isProgress = false
case SKPaymentTransactionState.restored:
print("復原成功...")
SKPaymentQueue.default().finishTransaction(transaction)
self.isProgress = false
// 回復成功,回傳給ViewController
self.delegate.didBuySomething(self, Product.restore)
self.showMessage(.restore)
default:
print(transaction.transactionState.rawValue)
break
}
}
}
func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
print("invalidProductIdentifiers: \(response.invalidProductIdentifiers.description)")
// invalidProductIdentifiers.description 會印出不合法的內購項目,例如:沒有設定價錢、已停用的等等
if response.products.count != 0 {
// 取得IAP產品
for product in response.products {
self.productsArray.append(product)
}
self.tableView.reloadData()
}
else {
print("取不到任何商品...")
}
}
// 復原購買失敗
func paymentQueue(_ queue: SKPaymentQueue, restoreCompletedTransactionsFailedWithError error: Error) {
print("復原購買失敗...")
print(error.localizedDescription)
}
// 回復購買成功(若沒實作該 delegate 會有問題產生)
func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) {
print("復原購買成功...")
}