//
|
// StoreTransaction.swift
|
// ExplorerProject
|
//
|
// Created by 无故事王国 on 2024/4/1.
|
// Copyright © 2024 younger_times. All rights reserved.
|
//
|
|
import SwiftyStoreKit
|
import StoreKit
|
import HandyJSON
|
|
struct PurchaseModel:HandyJSON{
|
var environment: String?
|
var receipt:PurchaseReceiptModel?
|
var status: Int = 0
|
}
|
|
struct PurchaseReceiptModel:HandyJSON{
|
var adam_id: Int = 0
|
var app_item_id: Int = 0
|
var application_version: Int = 0
|
var bundle_id: String?
|
var download_id: Int = 0
|
var in_app = [PurchageInAppModel]()
|
var original_application_version: String?
|
var original_purchase_date: String?
|
var original_purchase_date_ms: Int = 0
|
var original_purchase_date_pst: String?
|
var receipt_creation_date: String?
|
var receipt_creation_date_ms: Int = 0
|
var receipt_creation_date_pst: String?
|
var receipt_type: String?
|
var request_date: String?
|
var request_date_ms: Int = 0
|
var request_date_pst: String?
|
var version_external_identifier: Int = 0
|
}
|
|
struct PurchageInAppModel:HandyJSON{
|
|
}
|
|
struct PurchaseResultModel:HandyJSON{
|
var environment: String?
|
var receipt: PurchaseReceipt?
|
var status: Int = 0
|
}
|
|
struct PurchaseReceipt:HandyJSON{
|
var adam_id: Int = 0
|
var app_item_id: Int = 0
|
var application_version: String?
|
var bundle_id: String?
|
var download_id: Int = 0
|
var in_app = [PurchaseInAPP]()
|
var original_application_version: String?
|
var original_purchase_date: String?
|
var original_purchase_date_ms: String?
|
var original_purchase_date_pst: String?
|
var receipt_creation_date: String?
|
var receipt_creation_date_ms: String?
|
var receipt_creation_date_pst: String?
|
var receipt_type: String?
|
var request_date: String?
|
var request_date_ms: String?
|
var request_date_pst: String?
|
var version_external_identifier: Int = 0
|
}
|
|
struct PurchaseInAPP:HandyJSON{
|
var in_app_ownership_type: String?
|
var is_trial_period: String?
|
var original_purchase_date: String?
|
var original_purchase_date_ms: String?
|
var original_purchase_date_pst: String?
|
var original_transaction_id: String?
|
var product_id: String?
|
var purchase_date: String?
|
var purchase_date_ms: String?
|
var purchase_date_pst: String?
|
var quantity: String?
|
var transaction_id: String?
|
|
}
|
|
class InPurchaseManager:NSObject{
|
|
private static var _sharedInstance: InPurchaseManager!
|
private var productList = Set<String>()
|
private(set) var products = Set<SKProduct>()
|
|
@discardableResult
|
public class func instance() -> InPurchaseManager {
|
guard let instance = _sharedInstance else {
|
_sharedInstance = InPurchaseManager()
|
return _sharedInstance!
|
}
|
return instance
|
}
|
|
func setProductList(_ list:Set<String>,clouse:@escaping (Set<SKProduct>)->Void){
|
productList = list
|
|
SwiftyStoreKit.retrieveProductsInfo(list) { result in
|
if result.retrievedProducts.count > 0 {
|
InPurchaseManager._sharedInstance.products = result.retrievedProducts
|
clouse(result.retrievedProducts)
|
}
|
}
|
}
|
|
static func testrefundRequest(for transactionID:UInt64) async{
|
do{
|
if #available(iOS 15.0, *) {
|
let windowScene = await UIApplication.shared.connectedScenes.first
|
|
let result = try await StoreKit.Transaction.beginRefundRequest(for: transactionID, in: windowScene as! UIWindowScene)
|
switch result {
|
case .success:
|
alert(msg: "退款申请发起成功")
|
case .userCancelled:break
|
@unknown default:break
|
}
|
} else {
|
// Fallback on earlier versions
|
}
|
}catch{ error
|
|
print(error)
|
|
|
}
|
}
|
|
func dismiss(){
|
InPurchaseManager._sharedInstance = nil
|
}
|
|
/// 购买
|
/// - Parameters:
|
/// - ID: ID值
|
/// - quantity: 购买数量
|
/// - applicationUsername: 购买用户名的唯一标识
|
class func purchaseProduct(ID:String,quantity:Int = 1,applicationUsername:String, completion: @escaping ( PurchaseResultModel) -> Void,errorClouse:@escaping (Error)->Void){
|
var purcahseProduct:SKProduct?
|
for product in _sharedInstance.products {
|
if product.productIdentifier == ID{
|
purcahseProduct = product;break
|
}
|
}
|
|
guard let product = purcahseProduct else {errorClouse(NSError(domain: "Empty Product ID", code: 1000));return}
|
var inSanbox:Bool = false
|
|
#if DEBUG
|
inSanbox = true
|
#endif
|
|
SwiftyStoreKit.completeTransactions { purchases in
|
if let product = purchases.first{
|
switch product.transaction.transactionState {
|
case .purchased:
|
alertSuccess(msg: "完成购买")
|
case .purchasing:
|
showHUD("购买中")
|
case .failed:
|
alertError(msg: "购买失败")
|
case .restored:
|
alertSuccess(msg: "恢复购买")
|
default:break
|
}
|
}
|
}
|
|
SwiftyStoreKit.purchaseProduct(product, quantity: quantity, atomically: true, applicationUsername: applicationUsername, simulatesAskToBuyInSandbox: false, paymentDiscount: nil) { result in
|
switch result {
|
case .success(let purchase):
|
var type:AppleReceiptValidator.VerifyReceiptURLType = .production
|
if inSanbox{
|
type = .sandbox
|
}
|
|
let recepit = AppleReceiptValidator(service: type, sharedSecret: nil)
|
SwiftyStoreKit.verifyReceipt(using: recepit) { result in
|
switch result {
|
case .success(let receipt):
|
if let model = PurchaseResultModel.deserialize(from: receipt){
|
completion(model)
|
}
|
break
|
case .error(let error):
|
errorClouse(error)
|
// print(error.localizedDescription)
|
}
|
}
|
case .error(let error):
|
// print(error.localizedDescription)
|
errorClouse(error)
|
}
|
}
|
}
|
|
/// 恢复购买
|
class func resotrePurchase(applicationUsername:String, completion: @escaping ( PurchaseResultModel) -> Void,errorClouse:@escaping (Error)->Void){
|
|
SwiftyStoreKit.completeTransactions { purchases in
|
if let product = purchases.first{
|
switch product.transaction.transactionState {
|
case .purchased:
|
alertSuccess(msg: "完成购买")
|
case .purchasing:
|
showHUD("购买中")
|
case .failed:
|
alertError(msg: "购买失败")
|
case .restored:
|
alertSuccess(msg: "恢复购买")
|
default:break
|
}
|
}
|
}
|
|
|
SwiftyStoreKit.restorePurchases(atomically: true, applicationUsername: applicationUsername) { result in
|
if result.restoreFailedPurchases.count > 0 {
|
//恢复失败
|
print("restore Failed:\(result.restoredPurchases)")
|
}else if result.restoredPurchases.count > 0 {
|
print("restore Success")
|
var inSanbox:Bool = false
|
|
#if DEBUG
|
inSanbox = true
|
#endif
|
var type:AppleReceiptValidator.VerifyReceiptURLType = .production
|
if inSanbox{
|
type = .sandbox
|
}
|
let recepit = AppleReceiptValidator(service: type, sharedSecret: nil)
|
SwiftyStoreKit.verifyReceipt(using: recepit) { result in
|
switch result {
|
case .success(let receipt):
|
if let model = PurchaseResultModel.deserialize(from: receipt){
|
completion(model)
|
}
|
break
|
case .error(let error):
|
errorClouse(error)
|
}
|
}
|
}else{
|
print("Nothing to Restore")
|
}
|
}
|
}
|
|
//获取APP 首次安装时间
|
func test1(){
|
|
let queue = DispatchQueue(label: "inapppurchasequeue")
|
queue.async {
|
let receiptURL = Bundle.main.appStoreReceiptURL
|
guard receiptURL?.path != nil else { return }
|
do {
|
let receiptData = try Data(contentsOf: receiptURL!, options: .alwaysMapped)
|
let base64EncodedReceipt = receiptData.base64EncodedString(options: [])
|
var verifyReceipt:String = AppleReceiptValidator.VerifyReceiptURLType.sandbox.rawValue
|
#if !DEBUG
|
verifyReceipt = AppleReceiptValidator.VerifyReceiptURLType.production.rawValue
|
#endif
|
let purchaseURL = URL(string: verifyReceipt)!
|
|
var request = URLRequest(url: purchaseURL)
|
request.httpMethod = "POST"
|
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
|
let jsonBody: [String: Any] = [
|
"receipt-data": base64EncodedReceipt,
|
"password": ShareAppleKey // 你的共享密钥
|
]
|
|
let jsonData = try JSONSerialization.data(withJSONObject: jsonBody, options: [])
|
request.httpBody = jsonData
|
|
let session = URLSession.shared
|
let task = session.dataTask(with: request) { (data, response, error) in
|
if let error = error {return}
|
guard let data = data else { return }
|
do {
|
if let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
|
print("获取内购信息成功")
|
print(json)
|
if let purchageModel = PurchaseModel.deserialize(from: json),purchageModel.status == 0{
|
|
}
|
}
|
} catch {
|
print("JSON processing failed: \(error)")
|
}
|
}
|
task.resume()
|
} catch {
|
print("Error reading receipt data【凭证不存在】: \(error)")
|
}
|
}
|
}
|
}
|