所以我将我的应用程序从付费更改为免费,我想让付费客户拥有高级功能。一种方法是只检查他们的原始购买版本的应用程序,看看它是否是付费版本,然后给他们高级功能,但是我只能找到用户当前应用程序的版本,而不是原始购买的版本。 。
我一直在阅读,也许这可能与收据验证有关,但是如果有一种方法可以使用户获得该应用的原始购买版本,请有人帮忙。
这是我用来获取其当前版本的代码,而不是原始购买版本。
UPDATE daily_tasks
SET new_date = TRY_CONVERT(datetime, date);
谢谢
答案 0 :(得分:0)
正如我在评论中所写的那样,我不确定这是否是正确的过程,但是对于我制作的应用程序,我使用以下代码检查收据:
如有任何疑问,请参阅我也遵循的the docs。
重要的是,苹果不鼓励直接通过AppStore服务器进行验证(因为无法验证身份,这可能会导致中间人攻击)
使用受信任的服务器与App Store通信。使用自己的服务器,可以将应用程序设计为仅识别和信任服务器,并确保服务器与App Store服务器连接。无法直接在用户设备和App Store之间建立信任连接,因为您无法控制该连接的任何一端,因此很容易受到中间人攻击。
但是,如果可以帮助您,这里有两个Apple端点(调试/生产)。
#if DEBUG
private let appStoreValidationURL = URL(string: "https://sandbox.itunes.apple.com/verifyReceipt")!
#else
private let appStoreValidationURL = URL(string: "https://buy.itunes.apple.com/verifyReceipt")!
#endif
同时,对于需要传递收据的应用SharedSecret
,您可以找到有用的信息here。
private func loadReceipt() throws -> Data {
guard let url = Bundle.main.appStoreReceiptURL else {
throw ReceiptValidationError.noReceiptData
}
do {
let data = try Data(contentsOf: url)
return data
} catch {
print("Error loading receipt data: \(error.localizedDescription)")
throw ReceiptValidationError.noReceiptData
}
}
[...]
// Handle the try. I skipped that to make easier to read
let data = try! loadReceipt()
let base64String = data.base64EncodedString(options: [])
// Encode data in JSON
let content: [String : Any] = ["receipt-data" : base64String,
"password" : sharedSecret,
"exclude-old-transactions" : true]
private func validateLastReceipt(_ data: Data) {
let base64String = data.base64EncodedString(options: [])
// Encode data in JSON
let content: [String : Any] = ["receipt-data" : base64String,
"password" : sharedSecret,
"exclude-old-transactions" : false]
let json = try! JSONSerialization.data(withJSONObject: content, options: [])
// build request
let storeURL = self.appStoreValidationURL
var request = URLRequest(url: storeURL)
request.httpMethod = "POST"
request.httpBody = json
// Make request to app store
URLSession.shared.dataTask(with: request) { (data, res, error) in
guard error == nil, let data = data else {
self.delegate?.validator(self, didFinishValidateWith: error!)
return
}
do {
let decoder = JSONDecoder()
let response = try decoder.decode(ReceiptAppStoreResponse.self, from: data)
} catch {
// Handle error
}
}.resume()
}
这是我构建的“可破坏物”结构。 您将在其中找到检查用户购买商品所需的所有信息!
private struct ReceiptAppStoreResponse: Decodable {
/// Either 0 if the receipt is valid, or one of the error codes listed in Table 2-1.
///
/// For iOS 6 style transaction receipts, the status code reflects the status of the specific transaction’s receipt.
///
/// For iOS 7 style app receipts, the status code is reflects the status of the app receipt as a whole. For example, if you send a valid app receipt that contains an expired subscription, the response is 0 because the receipt as a whole is valid.
let status: Int?
/// A JSON representation of the receipt that was sent for verification.
// let receipt: String?
/// Only returned for receipts containing auto-renewable subscriptions. For iOS 6 style transaction receipts,
/// this is the base-64 encoded receipt for the most recent renewal. For iOS 7 style app receipts, this is the latest
/// base-64 encoded app receipt.
let latestReceipt: String?
/// Only returned for receipts containing auto-renewable subscriptions. For iOS 6 style transaction receipts,
/// this is the JSON representation of the receipt for the most recent renewal. For iOS 7 style app receipts,
/// the value of this key is an array containing all in-app purchase transactions.
/// This excludes transactions for a consumable product that have been marked as finished by your app.
let latestReceiptInfo: [ReceiptInfo]?
/// Only returned for iOS 6 style transaction receipts, for an auto-renewable subscription.
/// The JSON representation of the receipt for the expired subscription.
// let latestExpiredReceiptInfo: String?
/// Only returned for iOS 7 style app receipts containing auto-renewable subscriptions.
/// In the JSON file, the value of this key is an array where each element contains the pending renewal information
/// for each auto-renewable subscription identified by the Product Identifier.
/// A pending renewal may refer to a renewal that is scheduled in the future or a renewal that failed
/// in the past for some reason.
// let pendingRenewalInfo: String?
/// Retry validation for this receipt. Only applicable to status codes 21100-21199
// let isRetryable: Bool?
enum CodingKeys: String, CodingKey {
case status
// case receipt
case latestReceipt = "latest_receipt"
case latestReceiptInfo = "latest_receipt_info"
// case latestExpiredReceiptInfo = "latest_expired_receipt_info"
// case pendingRenewalInfo = "pending_renewal_info"
// case isRetryable = "is-retryable"
}
}
struct ReceiptInfo: Decodable {
let originalTransactionID: String?
let productID: String?
let expiresDateMS: String?
let originalPurchaseDateMS: String?
let isTrialPeriod: String?
let isInIntroOfferPeriod: String?
let purchaseDateMS: String?
enum CodingKeys: String, CodingKey {
case originalTransactionID = "original_transaction_id"
case productID = "product_id"
case expiresDateMS = "expires_date_ms"
case originalPurchaseDateMS = "original_purchase_date_ms"
case isTrialPeriod = "is_trial_period"
case isInIntroOfferPeriod = "is_in_intro_offer_period"
case purchaseDateMS = "purchase_date_ms"
}
func getExpireDate() -> Date? {
let nf = NumberFormatter()
guard let expDateString = self.expiresDateMS, let expDateValue = nf.number(from: expDateString) else {
return nil
}
/// It's expressed as milliseconds since 1970!!!
let date = Date(timeIntervalSince1970: expDateValue.doubleValue / 1000)
return date
}
希望有帮助! :)