最近寫的兩個項目都涉及到了購買虛擬商品,根據蘋果的要求,所以項目中集成了蘋果內購功能。 網上關于蘋果內購的資料很全面,寫在此處只不過對于項目的一些總計而已。廢話不多說,直接開始。
一、首先
image.png
1.登錄 AppStoreConnect 選擇 協議、稅務和銀行業務選項。
image.png
2.協議、稅務、銀行業務、聯系信息這些如實填寫就好。其中需要注意的是,地址和聯系人、銀行看準是讓填漢字還是英文。稅務選擇美國,銀行也是選擇的不過都是英文的,需要將銀行卡的開戶行翻譯成英文然后去列表里找到對應的總行或支行即可。
image.png
二、設置商品
1.回到 APP 選項,找到要添加內購的應用選擇管理。
image.png
image.png
添加新的內購產品,這里要選擇產品類型,主要類型有以下幾種,根據自己需要選擇對應類型即可。
image.png
填寫內購產品的名稱,如“包月會員” “會員充值”等, 后面產品ID為自己設置的一串數字,用來標識該產品的唯一性,如果這個產品下架了或是沒有這個產品了,這個產品ID也會失效,再創建新的產品這個ID值也不能再用了。
image.png
image.png
這里的審核信息是必傳項,截圖需要是你在項目中測試內購支付的截圖。這里測試的話需要用到一個沙河測試賬號。賬號怎么來的,看下面。
image.png
三、 沙盒測試賬號
選擇用戶和訪問->沙盒->添加測試員
選擇用戶和訪問
image.png
新測試員,按圖填寫信息即可,其中電子郵件隨便填即可,不一定非得是正確的郵箱,只要是是郵箱格式正確即可,如(aabbcc@163.com),不過一定要記住郵箱地址和密碼,在添加好測試員之后,項目里點擊支付喚起蘋果內購彈框后需要填寫的就是這個郵箱和密碼。而不是自己Apple ID 的郵箱。填寫過之后,可以在手機(iPhone)上設置里 -> App Store 選項里可以看到沙盒賬戶,可以對該賬號進行退出登錄等操作。
image.png
image.png
四、以上信息都配置好之后,就是代碼了。
-
個人建議將蘋果內購寫成工具類,這樣以后再遇到支付項目,可以直接拿來用。
1.1 先引入 import StoreKit 1.2 聲明一個代理 protocol ZIAPManagerDelegate: class { /** 支付成功回調 */ func rechargeSuccess(rechargeInfo: [String: AnyObject]) /** 支付失敗回調 */ func rechargeFailed() } class ZIAPManager: NSObject { // MARK: - Public public weak var managerDelegate: ZIAPManagerDelegate? /** 添加/移除支付觀察者(直接在支付 VC 里調用即可,千萬記得要移除觀察者) */ public func addPaymentObserver() { SKPaymentQueue.default().add(self) } public func removePaymentObserver() { SKPaymentQueue.default().remove(self) } /** 購買商品,傳入商品id 和 商品價格 */ public func buyWithProductId(_ pId: String, pPrice: String) { self.productId = pId self.pPrice = pPrice // 判斷程序是否支持蘋果內購 if SKPaymentQueue.canMakePayments() { SVProgressHUD.show(withStatus: "支付中...") self.getProductData(pId) } else { SVProgressHUD.showMessage("不允許程序內付費") } } // MARK: - Private Method private func getProductData(_ productId: String) { let set = NSSet.init(object: productId) let request = SKProductsRequest(productIdentifiers: set as! Set<String>) request.delegate = self request.start() } // MARK: - Property Private fileprivate var productId: String = "" fileprivate var pPrice: String = "" fileprivate var retryCount: Int = 0 fileprivate var verifySuccess: Bool = false /** AppStoreUrl /// 沙盒測試驗證地址 public let SANDBOXUrl: String = "https://sandbox.itunes.apple.com/verifyReceipt" /// 正式驗證地址 public let AppStoreUrl: String = "https://buy.itunes.apple.com/verifyReceipt" */ fileprivate var verifyAddress: String = AppStoreUrl // MARK: - Sigleton private static let instance = ZIAPManager() class var shareInstance: ZIAPManager { return instance } private override init() {} }
1.2 實現 SKProductsRequestDelegate 代理方法
extension ZIAPManager: SKProductsRequestDelegate {
func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
let products = response.products
guard products.count > 0 else { return }
var requestProduct: SKProduct?
for pro in products {
if pro.productIdentifier == self.productId {
requestProduct = pro
}
}
if let pro = requestProduct {
let payment = SKPayment(product: pro)
SKPaymentQueue.default().add(payment)
}
}
func requestDidFinish(_ request: SKRequest) {
SVProgressHUD.show(withStatus: "支付中...")
}
func request(_ request: SKRequest, didFailWithError error: Error) {
SVProgressHUD.dismiss()
}
}
1.3 實現 SKPaymentTransactionObserver 回調方法
extension ZIAPManager: SKPaymentTransactionObserver {
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
SVProgressHUD.dismiss()
for transaction in transactions {// 當交易隊列里面添加的每一筆交易狀態發生變化的時候調用
switch transaction.transactionState {
case .purchased:
print("支付成功")
SVProgressHUD.show(withStatus: "支付中...")
// 本地驗證憑證
verifyPurchaseWithPaymentTrasaction()
finishTransaction(queue, transaction: transaction)
case .failed:
print("支付失敗")
var errMsg = "支付失敗"
if let error = transaction.error {
if error.localizedDescription.length > 0 {
errMsg = error.localizedDescription
}
}
SVProgressHUD.showMessage(errMsg)
finishTransaction(queue, transaction: transaction)
case .restored:
print("已購買過此商品")
SVProgressHUD.showMessage("已購買過此商品")
finishTransaction(queue, transaction: transaction)
case .deferred:
print("延遲處理")
case .purchasing:
print("商品添加進列表")
SVProgressHUD.show(withStatus: "支付中...")
default: break
}
}
}
private func finishTransaction(_ queue: SKPaymentQueue, transaction: SKPaymentTransaction) {
SKPaymentQueue.default().finishTransaction(transaction)
}
/// 重復提交驗證
private func retryVerify() {
if self.retryCount < 3 && self.verifySuccess == false {
verifyPurchaseWithPaymentTrasaction()
self.retryCount += 1
}
}
/// 驗證購買
private func verifyPurchaseWithPaymentTrasaction() {
if let url = Bundle.main.appStoreReceiptURL {
do {
let receiptData = try Data(contentsOf: url)
// 發送網絡POST請求,對購買憑據進行驗證
if let sandBoxUrl = URL(string: verifyAddress) {
var urlRequest = URLRequest(url: sandBoxUrl, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 30.0)
urlRequest.httpMethod = "POST"
let encodeStr = receiptData.base64EncodedString(options: .endLineWithLineFeed)
let payload = "{\"receipt-data\" : \"\(encodeStr)\"}"
urlRequest.httpBody = payload.data(using: .utf8)
let task = URLSession.shared.dataTask(with: urlRequest, completionHandler: { [weak self] (data, response, error) in
guard let strongSelf = self else { return }
if error == nil {
do {
if let dict = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String: AnyObject] {
print("支付收據驗證結果===\(dict)")
if let status = dict["status"] as? NSNumber {
if status.intValue == 0 {
strongSelf.verifySuccess = true
strongSelf.retryCount = 0
print("支付收據驗證成功")
if let delegate = strongSelf.managerDelegate {
// 這里可以將蘋果返回的票據證明傳給自己的服務器,由自己的服務驗證是否支付成功,也可以不走服務器驗證。走服務器驗證是為了更加準確。
delegate.rechargeSuccess(rechargeInfo: ["receipt-data": encodeStr])
}
} else {
// 這個 21007 錯誤碼判斷,是因為默認驗證地址是AppStore線上驗證地址,所以在測試的時候會報這個錯誤,所以要將驗證地址改為測試線。也可以自己在測試的時候寫測試地址,上線時候改為線上地址,就不會報這個錯誤了。
if status.intValue == 21007 {
strongSelf.verifyAddress = SANDBOXUrl
strongSelf.verifyPurchaseWithPaymentTrasaction()
} else {
print("支付收據驗證失敗")
if let delegate = strongSelf.managerDelegate {
delegate.rechargeFailed()
}
}
}
}
}
} catch let error {
print(error)
}
} else {
print("支付收據驗證失敗")
strongSelf.verifySuccess = false
strongSelf.retryVerify()
}
})
task.resume()
}
} catch let error {
print(error)
}
}
}
}
好了,以上就是關于蘋果內購的全部流程。僅供參考。