|
@@ -19,6 +19,9 @@ public class PurchaseManager: NSObject {
|
|
|
|
|
|
//商品信息
|
|
|
public var purchaseProducts:[PurchaseProduct] = []
|
|
|
+
|
|
|
+ //购买次数的商品
|
|
|
+ public var purchaseNumProducts: [String] = []
|
|
|
|
|
|
struct Config {
|
|
|
static let verifyUrl = "https://buy.itunes.apple.com/verifyReceipt"
|
|
@@ -26,7 +29,7 @@ public class PurchaseManager: NSObject {
|
|
|
}
|
|
|
|
|
|
lazy var products: [SKProduct] = []
|
|
|
-
|
|
|
+ var paymentProductIdentifier:String?
|
|
|
var onPurchaseStateChanged: PurchaseStateChangeHandler?
|
|
|
|
|
|
// 会员信息
|
|
@@ -34,6 +37,15 @@ public class PurchaseManager: NSObject {
|
|
|
//原始订单交易id dict
|
|
|
var originalTransactionIdentifierDict:[String:String] = [:]
|
|
|
|
|
|
+ private lazy var onceVerifyPayResult: Void = {
|
|
|
+ dePrint("这段代码只会执行一次")
|
|
|
+#if DEBUG
|
|
|
+ verifyPayResult(transaction: SKPaymentTransaction(), useSandBox: true,uiBlock:nil)
|
|
|
+#else
|
|
|
+ verifyPayResult(transaction: SKPaymentTransaction(), useSandBox: false,uiBlock:nil)
|
|
|
+#endif
|
|
|
+ }()
|
|
|
+
|
|
|
override init() {
|
|
|
super.init()
|
|
|
|
|
@@ -42,14 +54,6 @@ public class PurchaseManager: NSObject {
|
|
|
if let info = UserDefaults.standard.object(forKey: kPremiumExpiredInfoKey) as? [String: Any] {
|
|
|
vipInformation = info
|
|
|
}
|
|
|
-
|
|
|
-#if DEBUG
|
|
|
- verifyPayResult(transaction: SKPaymentTransaction(), useSandBox: true)
|
|
|
-#else
|
|
|
- verifyPayResult(transaction: SKPaymentTransaction(), useSandBox: false)
|
|
|
-#endif
|
|
|
-
|
|
|
-
|
|
|
}
|
|
|
|
|
|
public var expiredDate: Date? {
|
|
@@ -73,9 +77,6 @@ public class PurchaseManager: NSObject {
|
|
|
if let expDate = expiredDate {
|
|
|
return dateformat.string(from: expDate)
|
|
|
} else {
|
|
|
-#if DEBUG
|
|
|
- return dateformat.string(from: Date())
|
|
|
-#endif
|
|
|
return "--"
|
|
|
}
|
|
|
}
|
|
@@ -105,9 +106,9 @@ public class PurchaseManager: NSObject {
|
|
|
|
|
|
public var vipType: PremiumPeriod {
|
|
|
#if DEBUG
|
|
|
- return .none
|
|
|
+// return .none
|
|
|
// return .year(.year)
|
|
|
-// return .year(.yearPro)
|
|
|
+ return .year(.yearPro)
|
|
|
// return .week(.week)
|
|
|
// return .week(.weekPro)
|
|
|
#endif
|
|
@@ -252,10 +253,10 @@ extension PurchaseManager {
|
|
|
///请求商品
|
|
|
public func requestProducts() {
|
|
|
if !products.isEmpty {
|
|
|
- purchase(self, didChaged: .loadSuccess, object: nil)
|
|
|
+ purchase(self, didChaged: .loadSuccess, object: nil,transaction: nil)
|
|
|
}
|
|
|
|
|
|
- purchase(self, didChaged: .loading, object: nil)
|
|
|
+ purchase(self, didChaged: .loading, object: nil,transaction: nil)
|
|
|
let productIdentifiers = Set(purchaseProducts.map({ $0.productId }))
|
|
|
debugPrint("PurchaseManager requestProducts = \(productIdentifiers)")
|
|
|
let request = SKProductsRequest(productIdentifiers: productIdentifiers)
|
|
@@ -263,33 +264,61 @@ extension PurchaseManager {
|
|
|
request.start()
|
|
|
}
|
|
|
|
|
|
- public func restorePremium() {
|
|
|
- purchase(self, didChaged: .restoreing, object: nil)
|
|
|
+ public func restorePremium(showUI:Bool = true) {
|
|
|
+ if showUI {
|
|
|
+ paymentProductIdentifier = "restoreCompletedTransactions"
|
|
|
+ }
|
|
|
+
|
|
|
+ purchase(self, didChaged: .restoreing, object: nil,transaction: nil)
|
|
|
SKPaymentQueue.default().restoreCompletedTransactions()
|
|
|
- debugPrint("PurchaseManager restoreCompletedTransactions")
|
|
|
+ debugPrint("PurchaseManager restoreCompletedTransactions restorePremium")
|
|
|
}
|
|
|
|
|
|
/// 购买支付
|
|
|
public func pay(for period: PremiumPeriod) {
|
|
|
+ if let product = product(for: period){
|
|
|
+ paymentProductIdentifier = product.productIdentifier
|
|
|
+ }
|
|
|
+
|
|
|
guard SKPaymentQueue.canMakePayments() else {
|
|
|
- purchase(self, didChaged: .payFail, object: "Payment failed, please check your payment account")
|
|
|
+ purchase(self, didChaged: .payFail, object: "Payment failed, please check your payment account",transaction: nil)
|
|
|
+ debugPrint("PurchaseManager payFail pay failed, please check your payment account")
|
|
|
return
|
|
|
}
|
|
|
|
|
|
guard SKPaymentQueue.default().transactions.count <= 0 else {
|
|
|
- purchase(self, didChaged: .payFail, object: "You have outstanding orders that must be paid for before a new subscription can be placed.")
|
|
|
- restorePremium()
|
|
|
+// purchase(self, didChaged: .payFail, object: "You have outstanding orders that must be paid for before a new subscription can be placed.",transaction: nil)
|
|
|
+ debugPrint("PurchaseManager paymentQueue transactions.count SKPaymentQueue= \(SKPaymentQueue.default().transactions.count)")
|
|
|
+ debugPrint("PurchaseManager payFail pay period restorePremium = \(period)")
|
|
|
+ if isPurchaseTimes(productId:paymentProductIdentifier) == false{ //是否是购买次数的商品
|
|
|
+ restorePremium()
|
|
|
+ }
|
|
|
return
|
|
|
}
|
|
|
if let product = product(for: period) {
|
|
|
- purchase(self, didChaged: .paying, object: nil)
|
|
|
+ purchase(self, didChaged: .paying, object: nil,transaction: nil)
|
|
|
let payment = SKPayment(product: product)
|
|
|
+ debugPrint("PurchaseManager payFail pay product = \(product.localizedDescription)")
|
|
|
SKPaymentQueue.default().add(payment)
|
|
|
- debugPrint("PurchaseManager pay period = \(period)")
|
|
|
+ debugPrint("PurchaseManager payFail pay period = \(period)")
|
|
|
}else{
|
|
|
- purchase(self, didChaged: .payFail, object: "Payment failed, no this item")
|
|
|
+ debugPrint("PurchaseManager payFail pay 找不到要买的商品 = \(period)")
|
|
|
+ purchase(self, didChaged: .payFail, object: "Payment failed, no this item",transaction: nil)
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+
|
|
|
+ func clearPaymentProductIdentifier(){
|
|
|
+ paymentProductIdentifier = nil
|
|
|
+ }
|
|
|
+
|
|
|
+ //是否是购买次数的商品
|
|
|
+ func isPurchaseTimes(productId:String?) -> Bool{
|
|
|
+ if let productId = productId,purchaseNumProducts.contains(productId) == true{
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ return false
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
// MARK: 商品回调
|
|
@@ -298,17 +327,18 @@ extension PurchaseManager: SKProductsRequestDelegate {
|
|
|
public func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
|
|
|
let products = response.products
|
|
|
self.products = products
|
|
|
- purchase(self, didChaged: .loadSuccess, object: nil)
|
|
|
-
|
|
|
- debugPrint("PurchaseManager productsRequest didReceive = \(products)")
|
|
|
- DispatchQueue.main.async {
|
|
|
- NotificationCenter.default.post(name: .kPurchasePrepared, object: nil)
|
|
|
- }
|
|
|
+ purchase(self, didChaged: .loadSuccess, object: nil,transaction: nil)
|
|
|
+ NotificationCenter.default.post(name: .kPurchasePrepared, object: nil)
|
|
|
+ dePrint("PurchaseManager productsRequest didReceive = \(products)")
|
|
|
+
|
|
|
+ //拿到商品后,主动拉取一次苹果订单,处理会员
|
|
|
+ _ = onceVerifyPayResult
|
|
|
+ dePrint("PurchaseManager onceVerifyPayResult")
|
|
|
}
|
|
|
|
|
|
public func request(_ request: SKRequest, didFailWithError error: Error) {
|
|
|
debugPrint("PurchaseManager productsRequest error = \(error)")
|
|
|
- purchase(self, didChaged: .loadFail, object: error.localizedDescription)
|
|
|
+ purchase(self, didChaged: .loadFail, object: error.localizedDescription,transaction: nil)
|
|
|
|
|
|
}
|
|
|
}
|
|
@@ -331,7 +361,7 @@ extension PurchaseManager: SKPaymentTransactionObserver {
|
|
|
switch transaction.transactionState {
|
|
|
case .purchasing:
|
|
|
// Transaction is being added to the server queue.
|
|
|
- purchase(self, didChaged: .paying, object: nil)
|
|
|
+ purchase(self, didChaged: .paying, object: nil,transaction: transaction)
|
|
|
|
|
|
case .purchased:
|
|
|
SKPaymentQueue.default().finishTransaction(transaction)
|
|
@@ -342,19 +372,19 @@ extension PurchaseManager: SKPaymentTransactionObserver {
|
|
|
|
|
|
// Transaction is in queue, user has been charged. Client should complete the transaction.
|
|
|
#if DEBUG
|
|
|
- verifyPayResult(transaction: transaction, useSandBox: true)
|
|
|
+ verifyPayResult(transaction: transaction, useSandBox: true,uiBlock: purchase(_:didChaged:object:transaction:))
|
|
|
#else
|
|
|
- verifyPayResult(transaction: transaction, useSandBox: false)
|
|
|
+ verifyPayResult(transaction: transaction, useSandBox: false,uiBlock: purchase(_:didChaged:object:transaction:))
|
|
|
#endif
|
|
|
|
|
|
|
|
|
case .failed:
|
|
|
|
|
|
SKPaymentQueue.default().finishTransaction(transaction)
|
|
|
- // Transaction was cancelled or failed before being added to the server queue.
|
|
|
-
|
|
|
-
|
|
|
- if let error = transaction.error as NSError? {
|
|
|
+
|
|
|
+ //对于不包含次数购买的商品,(也就是订阅续费类),做特殊报错处理
|
|
|
+ if isPurchaseTimes(productId:transaction.payment.productIdentifier) == false,
|
|
|
+ let error = transaction.error as NSError? {
|
|
|
// 1. 检查内层错误
|
|
|
if let underlyingError = error.userInfo[NSUnderlyingErrorKey] as? NSError {
|
|
|
if underlyingError.domain == "ASDServerErrorDomain" && underlyingError.code == 3532 {
|
|
@@ -374,13 +404,17 @@ extension PurchaseManager: SKPaymentTransactionObserver {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- var message = "Payment Failed".localized
|
|
|
+ var message = "Payment Failed"
|
|
|
if let error = transaction.error as? SKError{
|
|
|
if error.code == SKError.paymentCancelled {
|
|
|
- message = "The subscription was canceled".localized
|
|
|
+ message = "The subscription was canceled"
|
|
|
+ }else{
|
|
|
+ message = error.localizedDescription
|
|
|
}
|
|
|
+ //error.errorCode == 3532 //用户已订阅,禁止重复购买
|
|
|
}
|
|
|
- purchase(self, didChaged: .payFail, object: message)
|
|
|
+ dePrint("PurchaseManager payFail failed message = \(message)")
|
|
|
+ purchase(self, didChaged: .payFail, object: message,transaction: transaction)
|
|
|
|
|
|
case .restored:
|
|
|
SKPaymentQueue.default().finishTransaction(transaction)
|
|
@@ -393,12 +427,12 @@ extension PurchaseManager: SKPaymentTransactionObserver {
|
|
|
if let original = transaction.original,
|
|
|
original.transactionState == .purchased {
|
|
|
#if DEBUG
|
|
|
- verifyPayResult(transaction: transaction, useSandBox: true)
|
|
|
+ verifyPayResult(transaction: transaction, useSandBox: true,uiBlock: purchase(_:didChaged:object:transaction:))
|
|
|
#else
|
|
|
- verifyPayResult(transaction: transaction, useSandBox: false)
|
|
|
+ verifyPayResult(transaction: transaction, useSandBox: false,uiBlock: purchase(_:didChaged:object:transaction:))
|
|
|
#endif
|
|
|
} else {
|
|
|
- purchase(self, didChaged: .restoreFail, object: "Failed to restore subscribe, please try again")
|
|
|
+ purchase(self, didChaged: .restoreFail, object: "Failed to restore subscribe, please try again",transaction: transaction)
|
|
|
}
|
|
|
|
|
|
case .deferred: // The transaction is in the queue, but its final status is pending external action.
|
|
@@ -410,14 +444,14 @@ extension PurchaseManager: SKPaymentTransactionObserver {
|
|
|
}
|
|
|
|
|
|
public func paymentQueue(_ queue: SKPaymentQueue, restoreCompletedTransactionsFailedWithError error: Error) {
|
|
|
- purchase(self, didChaged: .restoreFail, object: nil)
|
|
|
+ purchase(self, didChaged: .restoreFail, object: nil,transaction: nil)
|
|
|
}
|
|
|
|
|
|
public func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) {
|
|
|
if let trans = queue.transactions.first(where: { $0.transactionState == .purchased }) {
|
|
|
- verifyPayResult(transaction: trans, useSandBox: false)
|
|
|
+ verifyPayResult(transaction: trans, useSandBox: false,uiBlock: purchase(_:didChaged:object:transaction:))
|
|
|
} else if queue.transactions.isEmpty {
|
|
|
- purchase(self, didChaged: .restoreFail, object: "You don't have an active subscription")
|
|
|
+ purchase(self, didChaged: .restoreFail, object: "You don't have an active subscription",transaction: nil)
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -434,13 +468,13 @@ extension PurchaseManager: SKPaymentTransactionObserver {
|
|
|
}
|
|
|
|
|
|
extension PurchaseManager {
|
|
|
- func verifyPayResult(transaction: SKPaymentTransaction, useSandBox: Bool) {
|
|
|
- purchase(self, didChaged: .verifying, object: nil)
|
|
|
+ func verifyPayResult(transaction: SKPaymentTransaction, useSandBox: Bool, uiBlock:((PurchaseManager,PremiumRequestState,Any?,SKPaymentTransaction?)->Void)?) {
|
|
|
+ uiBlock?(self,.verifying,nil,transaction)
|
|
|
|
|
|
guard let url = Bundle.main.appStoreReceiptURL,
|
|
|
let receiptData = try? Data(contentsOf: url) else {
|
|
|
-// purchase(self, didChaged: .verifyFail, object: "凭证文件为空")
|
|
|
- purchase(self, didChaged: .verifyFail, object: "")
|
|
|
+ debugPrint("凭证文件为空")
|
|
|
+ uiBlock?(self,.verifyFail,"凭证文件为空",transaction)
|
|
|
return
|
|
|
}
|
|
|
|
|
@@ -449,8 +483,8 @@ extension PurchaseManager {
|
|
|
"password": password,
|
|
|
]
|
|
|
guard let requestData = try? JSONSerialization.data(withJSONObject: requestContents) else {
|
|
|
-// purchase(self, didChaged: .verifyFail, object: "凭证文件为空")
|
|
|
- purchase(self, didChaged: .verifyFail, object: "")
|
|
|
+ debugPrint("凭证文件为空")
|
|
|
+ uiBlock?(self,.verifyFail,"凭证文件为空",transaction)
|
|
|
return
|
|
|
}
|
|
|
|
|
@@ -462,18 +496,18 @@ extension PurchaseManager {
|
|
|
debugPrint("PurchaseManager verifyPayResult = \(jsonResponse)")
|
|
|
let status = jsonResponse["status"]
|
|
|
if let status = status as? String, status == "21007" {
|
|
|
- self.verifyPayResult(transaction: transaction, useSandBox: true)
|
|
|
+ self.verifyPayResult(transaction: transaction, useSandBox: true,uiBlock: uiBlock)
|
|
|
} else if let status = status as? Int, status == 21007 {
|
|
|
- self.verifyPayResult(transaction: transaction, useSandBox: true)
|
|
|
+ self.verifyPayResult(transaction: transaction, useSandBox: true,uiBlock: uiBlock)
|
|
|
} else if let status = status as? String, status == "0" {
|
|
|
- self.handlerPayResult(transaction: transaction, resp: jsonResponse)
|
|
|
+ self.handlerPayResult(transaction: transaction, resp: jsonResponse,uiBlock: uiBlock)
|
|
|
} else if let status = status as? Int, status == 0 {
|
|
|
- self.handlerPayResult(transaction: transaction, resp: jsonResponse)
|
|
|
+ self.handlerPayResult(transaction: transaction, resp: jsonResponse,uiBlock: uiBlock)
|
|
|
} else {
|
|
|
- self.purchase(self, didChaged: .verifyFail, object: "验证结果状态码错误:\(status.debugDescription)")
|
|
|
+ uiBlock?(self,.verifyFail,"验证结果状态码错误:\(status.debugDescription)",transaction)
|
|
|
}
|
|
|
} else {
|
|
|
- self.purchase(self, didChaged: .verifyFail, object: "验证结果为空")
|
|
|
+ uiBlock?(self,.verifyFail,"验证结果为空",transaction)
|
|
|
debugPrint("PurchaseManager 验证结果为空")
|
|
|
}
|
|
|
}
|
|
@@ -490,26 +524,26 @@ extension PurchaseManager {
|
|
|
*/
|
|
|
}
|
|
|
|
|
|
- func handlerPayResult(transaction: SKPaymentTransaction, resp: [String: Any]) {
|
|
|
+ func handlerPayResult(transaction: SKPaymentTransaction, resp: [String: Any], uiBlock:((PurchaseManager,PremiumRequestState,Any?,SKPaymentTransaction?)->Void)?) {
|
|
|
var isLifetime = false
|
|
|
- // 终生会员
|
|
|
- if let receipt = resp["receipt"] as? [String: Any],
|
|
|
- let in_app = receipt["in_app"] as? [[String: Any]] {
|
|
|
- if let lifetimeProductId = purchaseProducts.first(where: { $0.period == .lifetime })?.productId,
|
|
|
- let _ = in_app.filter({ ($0["product_id"] as? String) == lifetimeProductId }).first(where: { item in
|
|
|
- if let purchase_date = item["purchase_date"] as? String,
|
|
|
- !purchase_date.isEmpty {
|
|
|
- return true
|
|
|
- } else if let purchase_date_ms = item["purchase_date_ms"] as? String,
|
|
|
- !purchase_date_ms.isEmpty {
|
|
|
- return true
|
|
|
- }
|
|
|
- return false
|
|
|
- }) {
|
|
|
- updateExpireTime(lifetimeExpireTime, for: lifetimeProductId)
|
|
|
- isLifetime = true
|
|
|
- }
|
|
|
- }
|
|
|
+ // 终生会员//这个项目中没有终生会员
|
|
|
+// if let receipt = resp["receipt"] as? [String: Any],
|
|
|
+// let in_app = receipt["in_app"] as? [[String: Any]] {
|
|
|
+// if let lifetimeProductId = purchaseProducts.first(where: { $0.period == .lifetime })?.productId,
|
|
|
+// let _ = in_app.filter({ ($0["product_id"] as? String) == lifetimeProductId }).first(where: { item in
|
|
|
+// if let purchase_date = item["purchase_date"] as? String,
|
|
|
+// !purchase_date.isEmpty {
|
|
|
+// return true
|
|
|
+// } else if let purchase_date_ms = item["purchase_date_ms"] as? String,
|
|
|
+// !purchase_date_ms.isEmpty {
|
|
|
+// return true
|
|
|
+// }
|
|
|
+// return false
|
|
|
+// }) {
|
|
|
+// updateExpireTime(lifetimeExpireTime, for: lifetimeProductId)
|
|
|
+// isLifetime = true
|
|
|
+// }
|
|
|
+// }
|
|
|
|
|
|
if !isLifetime {
|
|
|
let info = resp["latest_receipt_info"] as? [[String: Any]]
|
|
@@ -522,9 +556,9 @@ extension PurchaseManager {
|
|
|
|
|
|
DispatchQueue.main.async {
|
|
|
if transaction.transactionState == .restored {
|
|
|
- self.purchase(self, didChaged: .restoreSuccess, object: nil)
|
|
|
+ uiBlock?(self, .restoreSuccess, nil, transaction)
|
|
|
} else {
|
|
|
- self.purchase(self, didChaged: .paySuccess, object: nil)
|
|
|
+ uiBlock?(self, .paySuccess, nil, transaction)
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -545,8 +579,19 @@ public extension PurchaseManager {
|
|
|
return isVip
|
|
|
}
|
|
|
|
|
|
- func purchase(_ manager: PurchaseManager, didChaged state: PremiumRequestState, object: Any?){
|
|
|
- onPurchaseStateChanged?(manager,state,object)
|
|
|
+ func purchase(_ manager: PurchaseManager, didChaged state: PremiumRequestState, object: Any?,transaction:SKPaymentTransaction?) {
|
|
|
+ dePrint("transaction.payment.productIdentifier=\(transaction?.payment.productIdentifier),paymentProductIdentifier=\(paymentProductIdentifier)")
|
|
|
+
|
|
|
+ if let paymentProductIdentifier = paymentProductIdentifier {
|
|
|
+ if transaction == nil{
|
|
|
+ onPurchaseStateChanged?(manager, state, object)
|
|
|
+ }else if let productIdentifier = transaction?.payment.productIdentifier, productIdentifier == paymentProductIdentifier {
|
|
|
+ onPurchaseStateChanged?(manager, state, object)
|
|
|
+ }else if transaction?.transactionState == .restored,paymentProductIdentifier == "restoreCompletedTransactions"{
|
|
|
+ onPurchaseStateChanged?(manager, state, object)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
}
|
|
|
}
|
|
|
|